* The grand 2005-2006 replace.
[lilypond/patrick.git] / lily / gourlay-breaking.cc
blobdf64d3e171f5a6a7ae02fc6f1898ed911270cc49
1 /*
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>
7 */
9 #include "gourlay-breaking.hh"
11 #include <cmath> // rint
12 #include <cstdio>
13 using namespace std;
15 #include "warn.hh"
16 #include "main.hh"
17 #include "paper-column.hh"
18 #include "paper-score.hh"
19 #include "output-def.hh"
20 #include "simple-spacer.hh"
21 #include "system.hh"
23 /// How often to print operator pacification marks?
24 const int HAPPY_DOTS_I = 3;
26 /**
27 Helper to trace back an optimal path
29 struct Break_node
31 /** this was the previous. If negative, this break should not be
32 considered: this path has infinite energy
35 int prev_break_;
36 /**
37 Which system number so far?
39 int line_;
41 Real demerits_;
42 Column_x_positions line_config_;
44 Break_node ()
46 prev_break_ = -1;
47 line_ = 0;
48 demerits_ = 0;
51 void print () const
53 printf ("prev break %d, line %d, demerits %f\n",
54 prev_break_, line_, demerits_);
58 void
59 print_break_nodes (Array<Break_node> const &arr)
61 for (int i = 0; i < arr.size (); i++)
63 printf ("node %d: ", i);
64 arr[i].print ();
68 /**
69 This algorithms is adapted from the OSU Tech report on breaking lines.
71 this function is longish, but not very complicated.
73 TODO: should rewrite. See the function in scm/page-layout.scm for
74 inspiration.
76 Array<Column_x_positions>
77 Gourlay_breaking::do_solve () const
79 Array<Break_node> optimal_paths;
80 Link_array<Grob> all
81 = pscore_->root_system ()->columns ();
83 Array<int> breaks = find_break_indices ();
85 Break_node first_node;
86 optimal_paths.push (first_node);
88 bool ragged_right = to_boolean (pscore_->layout ()->c_variable ("raggedright"));
89 bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("raggedlast"));
91 Real worst_force = 0.0;
92 for (int break_idx = 1; break_idx < breaks.size (); break_idx++)
95 start with a short line, add measures. At some point
96 the line becomes infeasible. Then we don't try to add more
98 int minimal_start_idx = -1;
99 Column_x_positions minimal_sol;
100 Column_x_positions backup_sol;
102 Real minimal_demerits = infinity_f;
104 for (int start_idx = break_idx; start_idx--;)
106 Link_array<Grob> line = all.slice (breaks[start_idx], breaks[break_idx] + 1);
108 line[0] = dynamic_cast<Item *> (line[0])->find_prebroken_piece (RIGHT);
109 line.top () = dynamic_cast<Item *> (line.top ())->find_prebroken_piece (LEFT);
111 Column_x_positions cp;
112 cp.cols_ = line;
114 Interval line_dims
115 = line_dimensions_int (pscore_->layout (), optimal_paths[start_idx].line_);
116 Simple_spacer_wrapper *sp = generate_spacing_problem (line, line_dims);
117 bool last_line = break_idx == breaks.size () - 1;
118 bool ragged = ragged_right
119 || (last_line && ragged_last);
121 sp->solve (&cp, ragged);
123 delete sp;
125 if (ragged && last_line)
126 cp.force_ = 0.0;
128 if (fabs (cp.force_) > worst_force)
129 worst_force = fabs (cp.force_);
132 We remember this solution as a "should always work
133 solution", in case everything fucks up. */
134 if (start_idx == break_idx - 1)
135 backup_sol = cp;
137 Real this_demerits;
139 if (optimal_paths[start_idx].demerits_ >= infinity_f)
140 this_demerits = infinity_f;
141 else
142 this_demerits = combine_demerits (optimal_paths[start_idx].line_config_, cp)
143 + optimal_paths[start_idx].demerits_;
145 if (this_demerits < minimal_demerits)
147 minimal_start_idx = start_idx;
148 minimal_sol = cp;
149 minimal_demerits = this_demerits;
153 we couldn't satisfy the constraints, this won't get better
154 if we add more columns, so we get on with the next one
156 if (!cp.satisfies_constraints_)
157 break;
160 Break_node bnod;
161 if (minimal_start_idx < 0)
163 bnod.demerits_ = infinity_f;
164 bnod.line_config_ = backup_sol;
165 bnod.prev_break_ = break_idx - 1;
167 else
169 bnod.prev_break_ = minimal_start_idx;
170 bnod.demerits_ = minimal_demerits;
171 bnod.line_config_ = minimal_sol;
173 bnod.line_ = optimal_paths[bnod.prev_break_].line_ + 1;
174 optimal_paths.push (bnod);
176 if (! (break_idx % HAPPY_DOTS_I))
177 progress_indication (String ("[") + to_string (break_idx) + "]");
180 /* do the last one */
181 if (breaks.size () % HAPPY_DOTS_I)
182 progress_indication (String ("[") + to_string (breaks.size ()) + "]");
184 progress_indication ("\n");
186 Array<int> final_breaks;
187 Array<Column_x_positions> lines;
189 /* skip 0-th element, since it is a "dummy" elt*/
190 for (int i = optimal_paths.size () - 1; i > 0;)
192 final_breaks.push (i);
193 int prev = optimal_paths[i].prev_break_;
194 assert (i > prev);
195 i = prev;
198 if (be_verbose_global)
200 message (_f ("Optimal demerits: %f",
201 optimal_paths.top ().demerits_) + "\n");
204 if (optimal_paths.top ().demerits_ >= infinity_f)
205 warning (_ ("no feasible line breaking found"));
207 for (int i = final_breaks.size (); i--;)
209 Column_x_positions cp (optimal_paths[final_breaks[i]].line_config_);
211 lines.push (cp);
212 if (!cp.satisfies_constraints_)
213 warning (_ ("can't find line breaking that satisfies constraints"));
215 return lines;
218 Gourlay_breaking::Gourlay_breaking ()
223 TODO: uniformity parameter to control rel. importance of spacing differences.
225 TODO:
227 mixing break penalties and constraint-failing solutions is confusing.
229 Real
230 Gourlay_breaking::combine_demerits (Column_x_positions const &prev,
231 Column_x_positions const &this_one) const
233 Real break_penalties = 0.0;
234 Grob *pc = this_one.cols_.top ();
235 if (pc->original ())
237 SCM pen = pc->get_property ("penalty");
238 if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
239 break_penalties += scm_to_double (pen);
243 Q: do we want globally non-cramped lines, or locally equally
244 cramped lines?
246 There used to be an example file input/test/uniform-breaking to
247 demonstrate problems with this approach. When music is gradually
248 becoming denser, the uniformity requirement makes lines go from
249 cramped to even more cramped (because going from cramped
250 3meas/line to relatively loose 2meas/line is such a big step.
254 Real demerit = abs (this_one.force_) + abs (prev.force_ - this_one.force_)
255 + break_penalties;
257 if (!this_one.satisfies_constraints_)
260 If it doesn't satisfy constraints, we make this one
261 really unattractive.
263 add 20000 to the demerits, so that a break penalty
264 of -10000 won't change the result */
265 demerit = max ((demerit + 20000), 2000.0);
267 demerit *= 10;
270 return demerit;