]> git.donarmstrong.com Git - lilypond.git/blob - lily/gourlay-breaking.cc
* flower
[lilypond.git] / lily / gourlay-breaking.cc
1 /*
2   gourlay-breaking.cc -- implement Gourlay_breaking
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "gourlay-breaking.hh"
10
11 #include <math.h>               // rint
12 #include <cstdio>
13
14 #include "warn.hh"
15 #include "main.hh"
16 #include "paper-column.hh"
17 #include "paper-score.hh"
18 #include "output-def.hh"
19 #include "simple-spacer.hh"
20 #include "system.hh"
21
22 /// How often to print operator pacification marks?
23 const int HAPPY_DOTS_I = 3;
24
25 /**
26    Helper to trace back an optimal path
27 */
28 struct Break_node
29 {
30   /** this was the previous. If negative, this break should not be
31       considered: this path has infinite energy
32
33   */
34   int prev_break_;
35   /**
36      Which system number so far?
37   */
38   int line_;
39
40   Real demerits_;
41   Column_x_positions line_config_;
42
43   Break_node ()
44   {
45     prev_break_ = -1;
46     line_ = 0;
47     demerits_ = 0;
48   }
49
50   void print () const
51   {
52     printf ("prev break %d, line %d, demerits %f\n",
53             prev_break_, line_, demerits_);
54   }
55 };
56
57 void
58 print_break_nodes (Array<Break_node> const &arr)
59 {
60   for (int i = 0; i < arr.size (); i++)
61     {
62       printf ("node %d: ", i);
63       arr[i].print ();
64     }
65 }
66
67 /**
68    This algorithms is adapted from the OSU Tech report on breaking lines.
69
70    this function is longish, but not very complicated.
71
72    TODO: should rewrite. See the function in scm/page-layout.scm for
73    inspiration.
74 */
75 Array<Column_x_positions>
76 Gourlay_breaking::do_solve () const
77 {
78   Array<Break_node> optimal_paths;
79   Link_array<Grob> all
80     = pscore_->system_->columns ();
81
82   Array<int> breaks = find_break_indices ();
83
84   Break_node first_node;
85   optimal_paths.push (first_node);
86
87   bool ragged_right = to_boolean (pscore_->layout_->c_variable ("raggedright"));
88   bool ragged_last = to_boolean (pscore_->layout_->c_variable ("raggedlast"));
89
90   Real worst_force = 0.0;
91   for (int break_idx = 1; break_idx< breaks.size (); break_idx++)
92     {
93       /*
94         start with a short line, add measures. At some point
95         the line becomes infeasible. Then we don't try to add more
96       */
97       int minimal_start_idx = -1;
98       Column_x_positions minimal_sol;
99       Column_x_positions backup_sol;
100
101       Real minimal_demerits = infinity_f;
102
103       for (int start_idx = break_idx; start_idx--;)
104         {
105           Link_array<Grob> line = all.slice (breaks[start_idx], breaks[break_idx] + 1);
106
107           line[0] = dynamic_cast<Item *> (line[0])->find_prebroken_piece (RIGHT);
108           line.top () = dynamic_cast<Item *> (line.top ())->find_prebroken_piece (LEFT);
109
110           Column_x_positions cp;
111           cp.cols_ = line;
112
113           Interval line_dims
114             = line_dimensions_int (pscore_->layout_, optimal_paths[start_idx].line_);
115           Simple_spacer_wrapper *sp = generate_spacing_problem (line, line_dims);
116           bool last_line = break_idx == breaks.size () - 1;
117           bool ragged = ragged_right
118             || (last_line && ragged_last);
119
120           sp->solve (&cp, ragged);
121
122           delete sp;
123
124           if (ragged && last_line)
125             cp.force_ = 0.0;
126
127           if (fabs (cp.force_) > worst_force)
128             worst_force = fabs (cp.force_);
129
130           /*
131             We remember this solution as a "should always work
132             solution", in case everything fucks up.  */
133           if (start_idx == break_idx - 1)
134             backup_sol = cp;
135
136           Real this_demerits;
137
138           if (optimal_paths[start_idx].demerits_ >= infinity_f)
139             this_demerits = infinity_f;
140           else
141             this_demerits = combine_demerits (optimal_paths[start_idx].line_config_, cp)
142               + optimal_paths[start_idx].demerits_;
143
144           if (this_demerits < minimal_demerits)
145             {
146               minimal_start_idx = start_idx;
147               minimal_sol = cp;
148               minimal_demerits = this_demerits;
149             }
150
151           /*
152             we couldn't satisfy the constraints, this won't get better
153             if we add more columns, so we get on with the next one
154           */
155           if (!cp.satisfies_constraints_)
156             break;
157         }
158
159       Break_node bnod;
160       if (minimal_start_idx < 0)
161         {
162           bnod.demerits_ = infinity_f;
163           bnod.line_config_ = backup_sol;
164           bnod.prev_break_ = break_idx - 1;
165         }
166       else
167         {
168           bnod.prev_break_ = minimal_start_idx;
169           bnod.demerits_ = minimal_demerits;
170           bnod.line_config_ = minimal_sol;
171         }
172       bnod.line_ = optimal_paths[bnod.prev_break_].line_ + 1;
173       optimal_paths.push (bnod);
174
175       if (! (break_idx % HAPPY_DOTS_I))
176         progress_indication (String ("[") + to_string (break_idx) + "]");
177     }
178
179   /* do the last one */
180   if (breaks.size () % HAPPY_DOTS_I)
181     progress_indication (String ("[") + to_string (breaks.size ()) + "]");
182
183   progress_indication ("\n");
184
185   Array<int> final_breaks;
186   Array<Column_x_positions> lines;
187
188   /* skip 0-th element, since it is a "dummy" elt*/
189   for (int i = optimal_paths.size () - 1; i> 0;)
190     {
191       final_breaks.push (i);
192       int prev = optimal_paths[i].prev_break_;
193       assert (i > prev);
194       i = prev;
195     }
196
197   if (be_verbose_global)
198     {
199       progress_indication (_f ("Optimal demerits: %f",
200                                optimal_paths.top ().demerits_) + "\n");
201     }
202
203   if (optimal_paths.top ().demerits_ >= infinity_f)
204     warning (_ ("No feasible line breaking found"));
205
206   for (int i = final_breaks.size (); i--;)
207     {
208       Column_x_positions cp (optimal_paths[final_breaks[i]].line_config_);
209
210       lines.push (cp);
211       if (!cp.satisfies_constraints_)
212         warning ("Could not find line breaking that satisfies constraints.");
213     }
214   return lines;
215 }
216
217 Gourlay_breaking::Gourlay_breaking ()
218 {
219 }
220
221 /*
222   TODO: uniformity parameter to control rel. importance of spacing differences.
223
224   TODO:
225
226   mixing break penalties and constraint-failing solutions is confusing.
227 */
228 Real
229 Gourlay_breaking::combine_demerits (Column_x_positions const &prev,
230                                     Column_x_positions const &this_one) const
231 {
232   Real break_penalties = 0.0;
233   Grob *pc = this_one.cols_.top ();
234   if (pc->original_)
235     {
236       SCM pen = pc->get_property ("penalty");
237       if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
238         {
239           break_penalties += scm_to_double (pen);
240         }
241     }
242
243   /*
244     Q: do we want globally non-cramped lines, or locally equally
245     cramped lines?
246
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.
252
253   */
254
255   Real demerit = abs (this_one.force_) + abs (prev.force_ - this_one.force_)
256     + break_penalties;
257
258   if (!this_one.satisfies_constraints_)
259     {
260       /*
261         If it doesn't satisfy constraints, we make this one
262         really unattractive.
263
264         add 20000 to the demerits, so that a break penalty
265         of -10000 won't change the result */
266       demerit = (demerit + 20000) >? 2000;
267
268       demerit *= 10;
269     }
270
271   return demerit;
272 }
273