]> git.donarmstrong.com Git - lilypond.git/blob - lily/simple-spacer.cc
Merge branch 'master' of git+ssh://jneem@git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / lily / simple-spacer.cc
1 /*
2   simple-spacer.cc -- implement Simple_spacer
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1999--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7
8   TODO:
9   - add support for different stretch/shrink constants?
10 */
11
12 #include <cstdio>
13
14 #include "column-x-positions.hh"
15 #include "dimensions.hh"
16 #include "international.hh"
17 #include "libc-extension.hh"    // isinf
18 #include "paper-column.hh"
19 #include "simple-spacer.hh"
20 #include "spaceable-grob.hh"
21 #include "spring.hh"
22 #include "warn.hh"
23
24 /*
25   A simple spacing constraint solver. The approach:
26
27   Stretch the line uniformly until none of the constraints (rods)
28   block.  It then is very wide.
29
30   Compress until the next constraint blocks,
31
32   Mark the springs over the constrained part to be non-active.
33
34   Repeat with the smaller set of non-active constraints, until all
35   constraints blocked, or until the line is as short as desired.
36
37   This is much simpler, and much much faster than full scale
38   Constrained QP. On the other hand, a situation like this will not
39   be typeset as dense as possible, because
40
41   c4                   c4           c4                  c4
42   veryveryverylongsyllable2         veryveryverylongsyllable2
43   " "4                 veryveryverylongsyllable2        syllable4
44
45
46   can be further compressed to
47
48
49   c4    c4                        c4   c4
50   veryveryverylongsyllable2       veryveryverylongsyllable2
51   " "4  veryveryverylongsyllable2      syllable4
52
53
54   Perhaps this is not a bad thing, because the 1st looks better anyway.  */
55
56 /*
57   positive force = expanding, negative force = compressing.
58 */
59
60 Simple_spacer::Simple_spacer ()
61 {
62   line_len_ = 0.0;
63   force_ = 0.0;
64   fits_ = true;
65   ragged_ = true;
66 }
67
68 Real
69 Simple_spacer::force () const
70 {
71   return force_;
72 }
73
74 bool
75 Simple_spacer::fits () const
76 {
77   return fits_;
78 }
79
80 Real
81 Simple_spacer::rod_force (int l, int r, Real dist)
82 {
83   Real c = range_stiffness (l, r);
84   Real d = range_ideal_len (l, r);
85   Real block_stretch = dist - d;
86   return c * block_stretch;
87 }
88
89 void
90 Simple_spacer::add_rod (int l, int r, Real dist)
91 {
92   if (isinf (dist) || isnan (dist))
93     {
94       programming_error ("ignoring weird minimum distance");
95       return;
96     }
97
98   Real block_force = rod_force (l, r, dist);
99
100   if (isinf (block_force))
101     {
102       Real spring_dist = range_ideal_len (l, r);
103       if (spring_dist < dist)
104         for (int i = l; i < r; i++)
105           springs_[i].ideal_ *= dist / spring_dist;
106
107       return;
108     }
109   force_ = max (force_, block_force);
110   for (int i = l; i < r; i++)
111     springs_[i].block_force_ = max (block_force, springs_[i].block_force_);
112 }
113
114 Real
115 Simple_spacer::range_ideal_len (int l, int r) const
116 {
117   Real d = 0.;
118   for (int i = l; i < r; i++)
119     d += springs_[i].ideal_;
120   return d;
121 }
122
123 Real
124 Simple_spacer::range_stiffness (int l, int r) const
125 {
126   Real den = 0.0;
127   for (int i = l; i < r; i++)
128     den += springs_[i].inverse_hooke_;
129
130   return 1 / den;
131 }
132
133 Real
134 Simple_spacer::configuration_length (Real force) const
135 {
136   Real l = 0.;
137   for (vsize i = 0; i < springs_.size (); i++)
138     l += springs_[i].length (force);
139
140   return l;
141 }
142
143 void
144 Simple_spacer::solve (Real line_len, bool ragged)
145 {
146   Real conf = configuration_length (force_);
147
148   ragged_ = ragged;
149   line_len_ = line_len;
150   if (conf < line_len_)
151     force_ = expand_line ();
152   else if (conf > line_len_)
153     force_ = compress_line ();
154
155   if (ragged && force_ < 0)
156     fits_ = false;
157 }
158
159 Real
160 Simple_spacer::expand_line ()
161 {
162   double inv_hooke = 0;
163   double cur_len = configuration_length (force_);
164
165   fits_ = true;
166   for (vsize i=0; i < springs_.size (); i++)
167     inv_hooke += springs_[i].inverse_hooke_;
168
169   assert (cur_len <= line_len_);
170   return (line_len_ - cur_len) / inv_hooke + force_;
171 }
172
173 Real
174 Simple_spacer::compress_line ()
175 {
176   double inv_hooke = 0;
177   double cur_len = configuration_length (force_);
178   double cur_force = force_;
179
180   fits_ = true;
181   for (vsize i=0; i < springs_.size (); i++)
182     inv_hooke += springs_[i].inverse_hooke_;
183
184   assert (line_len_ <= cur_len);
185
186   vector<Spring_description> sorted_springs = springs_;
187   sort (sorted_springs.begin (), sorted_springs.end (), greater<Spring_description> ());
188   for (vsize i = 0; i < sorted_springs.size (); i++)
189     {
190       Spring_description sp = sorted_springs[i];
191
192       assert (sp.block_force_ <= cur_force);
193       if (isinf (sp.block_force_))
194         break;
195
196       double block_dist = (cur_force - sp.block_force_) * inv_hooke;
197       if (cur_len - block_dist < line_len_)
198         {
199          cur_force += (line_len_ - cur_len) / inv_hooke;
200          cur_len = line_len_;
201
202          /*
203            Paranoia check.
204           */
205          assert (fabs (configuration_length (cur_force) - cur_len) < 1e-6);
206          return cur_force;
207         }
208       
209       cur_len -= block_dist;
210       inv_hooke -= sp.inverse_hooke_;
211       cur_force = sp.block_force_;
212     }
213
214   fits_ = false;
215   return cur_force;
216 }
217
218 void
219 Simple_spacer::add_spring (Real ideal, Real inverse_hooke)
220 {
221   Spring_description description;
222
223   description.ideal_ = ideal;
224   description.inverse_hooke_ = inverse_hooke;
225   if (!description.is_sane ())
226     {
227       programming_error ("insane spring found, setting to unit");
228
229       description.inverse_hooke_ = 1.0;
230       description.ideal_ = 1.0;
231     }
232
233   description.block_force_ = -description.ideal_ / description.inverse_hooke_;
234   // block at distance 0
235
236   springs_.push_back (description);
237 }
238
239 vector<Real>
240 Simple_spacer::spring_positions () const
241 {
242   vector<Real> ret;
243   ret.push_back (0.);
244
245   for (vsize i = 0; i < springs_.size (); i++)
246     ret.push_back (ret.back () + springs_[i].length (ragged_ && force_ > 0 ? 0.0 : force_));
247   return ret;
248 }
249
250 /****************************************************************/
251
252 Spring_description::Spring_description ()
253 {
254   ideal_ = 0.0;
255   inverse_hooke_ = 0.0;
256   block_force_ = 0.0;
257 }
258
259 bool
260 Spring_description::is_sane () const
261 {
262   return (inverse_hooke_ >= 0)
263     && ideal_ > 0
264     && !isinf (ideal_) && !isnan (ideal_)
265     && (inverse_hooke_ == 0.0 || fabs (inverse_hooke_) > 1e-8)
266     ;
267 }
268
269 Real
270 Spring_description::length (Real f) const
271 {
272   return ideal_ + max (f, block_force_) * inverse_hooke_;
273 }
274
275 /****************************************************************/
276
277 /*
278   TODO: should a add penalty for widely varying spring forces (caused
279   by constraints, eg.
280
281
282   .     =====
283   .     |   |
284   .o|o|x ##x
285   .
286
287   The ## forces the notes apart; we shouldn't allow the O's to touch
288   this closely.
289 */
290
291 struct Rod_description
292 {
293   vsize r_;
294   Real dist_;
295
296   bool operator< (const Rod_description r)
297   {
298     return r_ < r.r_;
299   }
300
301   Rod_description ()
302   {
303     r_ = 0;
304     dist_ = 0;
305   }
306
307   Rod_description (vsize r, Real d)
308   {
309     r_ = r;
310     dist_ = d;
311   }
312 };
313
314 struct Column_description
315 {
316   vector<Rod_description> rods_;
317   vector<Rod_description> end_rods_;   /* use these if they end at the last column of the line */
318   Real ideal_;
319   Real inverse_hooke_;
320   Real end_ideal_;
321   Real end_inverse_hooke_;
322   SCM break_permission_;
323   Interval keep_inside_line_;
324
325   Column_description ()
326   {
327     ideal_ = 0;
328     inverse_hooke_ = 0;
329     end_ideal_ = 0;
330     end_inverse_hooke_ = 0;
331     break_permission_ = SCM_EOL;
332   }
333 };
334
335 static bool
336 is_loose (Grob *g)
337 {
338   return (scm_is_pair (g->get_object ("between-cols")));
339 }
340
341 static Grob*
342 maybe_find_prebroken_piece (Grob *g, Direction d)
343 {
344   Grob *ret = dynamic_cast<Item*> (g)->find_prebroken_piece (d);
345   if (ret)
346     return ret;
347   return g;
348 }
349
350 static Grob*
351 next_spaceable_column (vector<Grob*> const &list, vsize starting)
352 {
353   for (vsize i = starting+1; i < list.size (); i++)
354     if (!is_loose (list[i]))
355       return list[i];
356   return 0;
357 }
358
359 static Column_description
360 get_column_description (vector<Grob*> const &cols, vsize col_index, bool line_starter)
361 {
362   Grob *col = cols[col_index];
363   if (line_starter)
364     col = maybe_find_prebroken_piece (col, RIGHT);
365
366   Column_description description;
367   Grob *next_col = next_spaceable_column (cols, col_index);
368   if (next_col)
369     Spaceable_grob::get_spring (col, next_col, &description.ideal_, &description.inverse_hooke_);
370   Grob *end_col = dynamic_cast<Item*> (cols[col_index+1])->find_prebroken_piece (LEFT);
371   if (end_col)
372     Spaceable_grob::get_spring (col, end_col, &description.end_ideal_, &description.end_inverse_hooke_);
373
374   for (SCM s = Spaceable_grob::get_minimum_distances (col);
375        scm_is_pair (s); s = scm_cdr (s))
376     {
377       Grob *other = unsmob_grob (scm_caar (s));
378       vsize j = binary_search (cols, other, Paper_column::less_than, col_index);
379       if (j != VPOS)
380         {
381           if (cols[j] == other)
382             description.rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
383           else /* it must end at the LEFT prebroken_piece */
384             description.end_rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
385         }
386     }
387   
388   if (!line_starter && to_boolean (col->get_property ("keep-inside-line")))
389     description.keep_inside_line_ = col->extent (col, X_AXIS);
390
391   description.break_permission_ = col->get_property ("line-break-permission");
392   return description;
393 }
394
395 vector<Real>
396 get_line_forces (vector<Grob*> const &columns,
397                  Real line_len, Real indent, bool ragged)
398 {
399   vector<vsize> breaks;
400   vector<Real> force;
401   vector<Grob*> non_loose;
402   vector<Column_description> cols;
403   SCM force_break = ly_symbol2scm ("force");
404
405   for (vsize i = 0; i < columns.size (); i++)
406     if (!is_loose (columns[i]) || Paper_column::is_breakable (columns[i]))
407       non_loose.push_back (columns[i]);
408
409   breaks.clear ();
410   breaks.push_back (0);
411   cols.push_back (Column_description ());
412   for (vsize i = 1; i + 1 < non_loose.size (); i++)
413     {
414       if (Paper_column::is_breakable (non_loose[i]))
415         breaks.push_back (cols.size ());
416
417       cols.push_back (get_column_description (non_loose, i, false));
418     }
419   breaks.push_back (cols.size ());
420   force.resize (breaks.size () * breaks.size (), infinity_f);
421
422   for (vsize b = 0; b + 1 < breaks.size (); b++)
423     {
424       cols[breaks[b]] = get_column_description (non_loose, breaks[b], true);
425       vsize st = breaks[b];
426
427       for (vsize c = b+1; c < breaks.size (); c++)
428         {
429           vsize end = breaks[c];
430           Simple_spacer spacer;
431
432           for (vsize i = breaks[b]; i < end - 1; i++)
433             spacer.add_spring (cols[i].ideal_, cols[i].inverse_hooke_);
434           spacer.add_spring (cols[end-1].end_ideal_, cols[end-1].end_inverse_hooke_);
435
436
437           for (vsize i = breaks[b]; i < end; i++)
438             {
439               for (vsize r = 0; r < cols[i].rods_.size (); r++)
440                 if (cols[i].rods_[r].r_ < end)
441                   spacer.add_rod (i - st, cols[i].rods_[r].r_ - st, cols[i].rods_[r].dist_);
442               for (vsize r = 0; r < cols[i].end_rods_.size (); r++)
443                 if (cols[i].end_rods_[r].r_ == end)
444                   spacer.add_rod (i - st, end - st, cols[i].end_rods_[r].dist_);
445               if (!cols[i].keep_inside_line_.is_empty ())
446                 {
447                   spacer.add_rod (i - st, end - st, cols[i].keep_inside_line_[RIGHT]);
448                   spacer.add_rod (0, i - st, -cols[i].keep_inside_line_[LEFT]);
449                 }
450             }
451           spacer.solve ((b == 0) ? line_len - indent : line_len, ragged);
452
453           /* add a (convex) penalty for compression. We do this _only_ in get_line_forces,
454              not get_line_configuration. This is temporary, for backwards compatibility;
455              the old line/page-breaking stuff ignores page breaks when it calculates line
456              breaks, so compression penalties can result in scores (eg. wtk-fugue) blowing
457              up to too many pages. */
458           Real f = spacer.force ();
459           force[b * breaks.size () + c] = f - (f < 0 ? f*f*f*f*4 : 0);
460
461           if (!spacer.fits ())
462             {
463               if (c == b + 1)
464                 force[b * breaks.size () + c] = -200000;
465               else
466                 force[b * breaks.size () + c] = infinity_f;
467               break;
468             }
469           if (end < cols.size () && cols[end].break_permission_ == force_break)
470             break;
471         }
472     }
473   return force;
474 }
475
476 Column_x_positions
477 get_line_configuration (vector<Grob*> const &columns,
478                         Real line_len,
479                         Real indent,
480                         bool ragged)
481 {
482   vector<Column_description> cols;
483   Simple_spacer spacer;
484   Column_x_positions ret;
485
486   ret.cols_.push_back (dynamic_cast<Item*> (columns[0])->find_prebroken_piece (RIGHT));
487   for (vsize i = 1; i + 1 < columns.size (); i++)
488     {
489       if (is_loose (columns[i]))
490         ret.loose_cols_.push_back (columns[i]);
491       else
492         ret.cols_.push_back (columns[i]);
493     }
494   ret.cols_.push_back (dynamic_cast<Item*> (columns.back ())->find_prebroken_piece (LEFT));
495
496   /* since we've already put our line-ending column in the column list, we can ignore
497      the end_XXX_ fields of our column_description */
498   for (vsize i = 0; i + 1 < ret.cols_.size (); i++)
499     {
500       cols.push_back (get_column_description (ret.cols_, i, i == 0));
501       spacer.add_spring (cols[i].ideal_, cols[i].inverse_hooke_);
502     }
503   for (vsize i = 0; i < cols.size (); i++)
504     {
505       for (vsize r = 0; r < cols[i].rods_.size (); r++)
506         spacer.add_rod (i, cols[i].rods_[r].r_, cols[i].rods_[r].dist_);
507
508       if (!cols[i].keep_inside_line_.is_empty ())
509         {
510           spacer.add_rod (i, cols.size (), cols[i].keep_inside_line_[RIGHT]);
511           spacer.add_rod (0, i, -cols[i].keep_inside_line_[LEFT]);
512         }
513     }
514
515   spacer.solve (line_len, ragged);
516   ret.force_ = spacer.force ();
517
518   /*
519     We used to have a penalty for compression, no matter what, but that
520     fucked up wtk1-fugue2 (taking 3 full pages.)
521   */
522   ret.config_ = spacer.spring_positions ();
523   for (vsize i = 0; i < ret.config_.size (); i++)
524     ret.config_[i] += indent;
525
526   ret.satisfies_constraints_ = spacer.fits ();
527
528   /*
529     Check if breaking constraints are met.
530   */
531   for (vsize i = 1; i + 1 < ret.cols_.size (); i++)
532     {
533       SCM p = ret.cols_[i]->get_property ("line-break-permission");
534       if (p == ly_symbol2scm ("force"))
535         ret.satisfies_constraints_ = false;
536     }
537
538   return ret;
539 }
540