2 gourlay-breaking.cc -- implement Gourlay_breaking
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
8 #include <math.h> // rint
11 #include "gourlay-breaking.hh"
12 #include "column-x-positions.hh"
15 #include "paper-column.hh"
16 #include "paper-score.hh"
17 #include "paper-def.hh"
18 #include "simple-spacer.hh"
21 /// How often to print operator pacification marks?
22 const int HAPPY_DOTS_I
= 3;
25 Helper to trace back an optimal path
28 /** this was the previous. If negative, this break should not be
29 considered: this path has infinite energy
34 Which system number so far?
39 Column_x_positions line_config_
;
50 printf ("prev break %d, line %d, demerits %f\n",
51 prev_break_
, line_
, demerits_
);
56 print_break_nodes (Array
<Break_node
> const & arr
)
58 for (int i
=0; i
< arr
.size (); i
++)
60 printf ( "node %d: ", i
);
66 This algorithms is adapted from the OSU Tech report on breaking lines.
68 this function is longish, but not very complicated.
70 Array
<Column_x_positions
>
71 Gourlay_breaking::do_solve () const
73 Array
<Break_node
> optimal_paths
;
74 Link_array
<Grob
> all
=
75 pscore_
->system_
->columns ();
77 Array
<int> breaks
= find_break_indices ();
79 Break_node first_node
;
80 optimal_paths
.push (first_node
);
82 Real worst_force
= 0.0;
83 for (int break_idx
= 1; break_idx
< breaks
.size (); break_idx
++)
86 start with a short line, add measures. At some point
87 the line becomes infeasible. Then we don't try to add more
89 int minimal_start_idx
= -1;
90 Column_x_positions minimal_sol
;
91 Column_x_positions backup_sol
;
93 Real minimal_demerits
= infinity_f
;
95 bool ragged
= to_boolean (pscore_
->paper_
->get_scmvar ("raggedright"));
97 for (int start_idx
= break_idx
; start_idx
--;)
99 Link_array
<Grob
> line
= all
.slice (breaks
[start_idx
], breaks
[break_idx
]+1);
101 line
[0] = dynamic_cast<Item
*> (line
[0]) ->find_prebroken_piece (RIGHT
);
102 line
.top () = dynamic_cast<Item
*> (line
.top ())->find_prebroken_piece (LEFT
);
104 Column_x_positions cp
;
108 = pscore_
->paper_
->line_dimensions_int (optimal_paths
[start_idx
].line_
);
109 Simple_spacer
* sp
= generate_spacing_problem (line
, line_dims
);
110 sp
->solve (&cp
, ragged
);
113 if (fabs (cp
.force_
) > worst_force
)
114 worst_force
= fabs (cp
.force_
);
117 We remember this solution as a "should always work
118 solution", in case everything fucks up. */
119 if (start_idx
== break_idx
- 1)
124 if (optimal_paths
[start_idx
].demerits_
>= infinity_f
)
125 this_demerits
= infinity_f
;
127 this_demerits
= combine_demerits (optimal_paths
[start_idx
].line_config_
, cp
)
128 + optimal_paths
[start_idx
].demerits_
;
130 if (this_demerits
< minimal_demerits
)
132 minimal_start_idx
= start_idx
;
134 minimal_demerits
= this_demerits
;
138 we couldn't satisfy the constraints, this won't get better
139 if we add more columns, so we get on with the next one
141 if (!cp
.satisfies_constraints_
)
147 if (minimal_start_idx
< 0)
149 bnod
.demerits_
= infinity_f
;
150 bnod
.line_config_
= backup_sol
;
151 bnod
.prev_break_
= break_idx
- 1;
155 bnod
.prev_break_
= minimal_start_idx
;
156 bnod
.demerits_
= minimal_demerits
;
157 bnod
.line_config_
= minimal_sol
;
159 bnod
.line_
= optimal_paths
[bnod
.prev_break_
].line_
+ 1;
160 optimal_paths
.push (bnod
);
162 if (! (break_idx
% HAPPY_DOTS_I
))
163 progress_indication (String ("[") + to_string (break_idx
) + "]");
166 /* do the last one */
167 if (breaks
.size () % HAPPY_DOTS_I
)
168 progress_indication (String ("[") + to_string (breaks
.size ()) + "]");
170 progress_indication ("\n");
172 Array
<int> final_breaks
;
173 Array
<Column_x_positions
> lines
;
175 /* skip 0-th element, since it is a "dummy" elt*/
176 for (int i
= optimal_paths
.size ()-1; i
> 0;)
178 final_breaks
.push (i
);
179 int prev
= optimal_paths
[i
].prev_break_
;
184 if (verbose_global_b
)
186 progress_indication (_f ("Optimal demerits: %f",
187 optimal_paths
.top ().demerits_
) + "\n");
190 if (optimal_paths
.top ().demerits_
>= infinity_f
)
191 warning (_ ("No feasible line breaking found"));
193 for (int i
= final_breaks
.size (); i
--;)
195 Column_x_positions
cp (optimal_paths
[final_breaks
[i
]].line_config_
);
198 if (!cp
.satisfies_constraints_
)
199 warning ("Could not find line breaking that satisfies constraints.");
205 Gourlay_breaking::Gourlay_breaking ()
212 TODO: uniformity parameter to control rel. importance of spacing differences.
216 mixing break penalties and constraint-failing solutions is confusing.
219 Gourlay_breaking::combine_demerits (Column_x_positions
const &prev
,
220 Column_x_positions
const &this_one
) const
222 Real break_penalties
= 0.0;
223 Grob
* pc
= this_one
.cols_
.top ();
226 SCM pen
= pc
->get_property ("penalty");
227 if (gh_number_p (pen
) && fabs (gh_scm2double (pen
)) < 10000)
229 break_penalties
+= gh_scm2double (pen
);
234 Q: do we want globally non-cramped lines, or locally equally
237 There used to be an example file input/test/uniform-breaking to
238 demonstrate problems with this approach. When music is gradually
239 becoming denser, the uniformity requirement makes lines go from
240 cramped to even more cramped (because going from cramped
241 3meas/line to relatively loose 2meas/line is such a big step.
245 Real demerit
= abs (this_one
.force_
) + abs (prev
.force_
- this_one
.force_
)
248 if (!this_one
.satisfies_constraints_
)
251 If it doesn't satisfy constraints, we make this one
254 add 20000 to the demerits, so that a break penalty
255 of -10000 won't change the result */
256 demerit
= (demerit
+ 20000) >? 2000;