2 gourlay-breaking.cc -- implement Gourlay_breaking
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "gourlay-breaking.hh"
11 #include <cmath> // rint
15 #include "international.hh"
17 #include "output-def.hh"
18 #include "paper-column.hh"
19 #include "paper-score.hh"
20 #include "simple-spacer.hh"
24 /// How often to print operator pacification marks?
25 const int HAPPY_DOTS = 3;
28 Helper to trace back an optimal path
32 /** this was the previous. If negative, this break should not be
33 considered: this path has infinite energy
38 Which system number so far?
43 Column_x_positions line_config_;
54 printf ("prev break %d, line %d, demerits %f\n",
55 prev_break_, line_, demerits_);
60 print_break_nodes (Array<Break_node> const &arr)
62 for (int i = 0; i < arr.size (); i++)
64 printf ("node %d: ", i);
70 This algorithms is adapted from the OSU Tech report on breaking lines.
72 this function is longish, but not very complicated.
74 TODO: should rewrite. See the function in scm/page-layout.scm for
77 Array<Column_x_positions>
78 Gourlay_breaking::do_solve () const
80 Array<Break_node> optimal_paths;
82 = pscore_->root_system ()->columns ();
84 Array<int> breaks = find_break_indices ();
86 Break_node first_node;
87 optimal_paths.push (first_node);
89 bool ragged_right = to_boolean (pscore_->layout ()->c_variable ("raggedright"));
90 bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("raggedlast"));
92 Real worst_force = 0.0;
93 for (int break_idx = 1; break_idx < breaks.size (); break_idx++)
96 start with a short line, add measures. At some point
97 the line becomes infeasible. Then we don't try to add more
99 int minimal_start_idx = -1;
100 Column_x_positions minimal_sol;
101 Column_x_positions backup_sol;
103 Real minimal_demerits = infinity_f;
105 for (int start_idx = break_idx; start_idx--;)
107 Link_array<Grob> line = all.slice (breaks[start_idx], breaks[break_idx] + 1);
109 line[0] = dynamic_cast<Item *> (line[0])->find_prebroken_piece (RIGHT);
110 line.top () = dynamic_cast<Item *> (line.top ())->find_prebroken_piece (LEFT);
112 Column_x_positions cp;
116 = line_dimensions_int (pscore_->layout (), optimal_paths[start_idx].line_);
117 Simple_spacer_wrapper *sp = generate_spacing_problem (line, line_dims);
118 bool last_line = break_idx == breaks.size () - 1;
119 bool ragged = ragged_right
120 || (last_line && ragged_last);
122 sp->solve (&cp, ragged);
126 if (ragged && last_line)
129 if (fabs (cp.force_) > worst_force)
130 worst_force = fabs (cp.force_);
133 We remember this solution as a "should always work
134 solution", in case everything fucks up. */
135 if (start_idx == break_idx - 1)
140 if (optimal_paths[start_idx].demerits_ >= infinity_f)
141 this_demerits = infinity_f;
143 this_demerits = combine_demerits (optimal_paths[start_idx].line_config_, cp)
144 + optimal_paths[start_idx].demerits_;
146 if (this_demerits < minimal_demerits)
148 minimal_start_idx = start_idx;
150 minimal_demerits = this_demerits;
154 we couldn't satisfy the constraints, this won't get better
155 if we add more columns, so we get on with the next one
157 if (!cp.satisfies_constraints_)
162 if (minimal_start_idx < 0)
164 bnod.demerits_ = infinity_f;
165 bnod.line_config_ = backup_sol;
166 bnod.prev_break_ = break_idx - 1;
170 bnod.prev_break_ = minimal_start_idx;
171 bnod.demerits_ = minimal_demerits;
172 bnod.line_config_ = minimal_sol;
174 bnod.line_ = optimal_paths[bnod.prev_break_].line_ + 1;
175 optimal_paths.push (bnod);
177 if (! (break_idx % HAPPY_DOTS))
178 progress_indication (std::string ("[") + to_string (break_idx) + "]");
181 /* do the last one */
182 if (breaks.size () % HAPPY_DOTS)
183 progress_indication (std::string ("[") + to_string (breaks.size ()) + "]");
185 progress_indication ("\n");
187 Array<int> final_breaks;
188 Array<Column_x_positions> lines;
190 /* skip 0-th element, since it is a "dummy" elt*/
191 for (int i = optimal_paths.size () - 1; i > 0;)
193 final_breaks.push (i);
194 int prev = optimal_paths[i].prev_break_;
199 if (be_verbose_global)
201 message (_f ("Optimal demerits: %f",
202 optimal_paths.top ().demerits_) + "\n");
205 if (optimal_paths.top ().demerits_ >= infinity_f)
206 warning (_ ("no feasible line breaking found"));
208 for (int i = final_breaks.size (); i--;)
210 Column_x_positions cp (optimal_paths[final_breaks[i]].line_config_);
213 if (!cp.satisfies_constraints_)
214 warning (_ ("can't find line breaking that satisfies constraints"));
219 Gourlay_breaking::Gourlay_breaking ()
224 TODO: uniformity parameter to control rel. importance of spacing differences.
228 mixing break penalties and constraint-failing solutions is confusing.
231 Gourlay_breaking::combine_demerits (Column_x_positions const &prev,
232 Column_x_positions const &this_one) const
234 Real break_penalties = 0.0;
235 Grob *pc = this_one.cols_.top ();
238 SCM pen = pc->get_property ("penalty");
239 if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
240 break_penalties += scm_to_double (pen);
244 Q: do we want globally non-cramped lines, or locally equally
247 There used to be an example file input/test/uniform-breaking to
248 demonstrate problems with this approach. When music is gradually
249 becoming denser, the uniformity requirement makes lines go from
250 cramped to even more cramped (because going from cramped
251 3meas/line to relatively loose 2meas/line is such a big step.
255 Real demerit = abs (this_one.force_) + abs (prev.force_ - this_one.force_)
258 if (!this_one.satisfies_constraints_)
261 If it doesn't satisfy constraints, we make this one
264 add 20000 to the demerits, so that a break penalty
265 of -10000 won't change the result */
266 demerit = max ((demerit + 20000), 2000.0);