]> git.donarmstrong.com Git - lilypond.git/blob - lily/simple-spacer.cc
Merge branch 'cvs-head' of ssh+git://hanwen@repo.or.cz/srv/git/lilypond into master...
[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--2006 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 void
360 get_column_spring (Grob *this_col, Grob *next_col, Real *ideal, Real *inv_hooke)
361 {
362   Spring_smob *spring = 0;
363
364   for (SCM s = this_col->get_object ("ideal-distances");
365        !spring && scm_is_pair (s);
366        s = scm_cdr (s))
367     {
368       Spring_smob *sp = unsmob_spring (scm_car (s));
369
370       if (sp->other_ == next_col)
371         spring = sp;
372     }
373
374   if (!spring)
375     programming_error (_f ("No spring between column %d and next one",
376                            Paper_column::get_rank (this_col)));
377
378   *ideal = (spring) ? spring->distance_ : 5.0;
379   *inv_hooke = (spring) ? spring->inverse_strength_ : 1.0;
380 }
381
382 static Column_description
383 get_column_description (vector<Grob*> const &cols, vsize col_index, bool line_starter)
384 {
385   Grob *col = cols[col_index];
386   if (line_starter)
387     col = maybe_find_prebroken_piece (col, RIGHT);
388
389   Column_description description;
390   Grob *next_col = next_spaceable_column (cols, col_index);
391   if (next_col)
392     get_column_spring (col, next_col, &description.ideal_, &description.inverse_hooke_);
393   Grob *end_col = dynamic_cast<Item*> (cols[col_index+1])->find_prebroken_piece (LEFT);
394   if (end_col)
395     get_column_spring (col, end_col, &description.end_ideal_, &description.end_inverse_hooke_);
396
397   for (SCM s = Spaceable_grob::get_minimum_distances (col);
398        scm_is_pair (s); s = scm_cdr (s))
399     {
400       Grob *other = unsmob_grob (scm_caar (s));
401       vsize j = binary_search (cols, other, Paper_column::less_than, col_index);
402       if (j != VPOS)
403         {
404           if (cols[j] == other)
405             description.rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
406           else /* it must end at the LEFT prebroken_piece */
407             description.end_rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
408         }
409     }
410   
411   if (!line_starter && to_boolean (col->get_property ("keep-inside-line")))
412     description.keep_inside_line_ = col->extent (col, X_AXIS);
413
414   description.break_permission_ = col->get_property ("line-break-permission");
415   return description;
416 }
417
418 vector<Real>
419 get_line_forces (vector<Grob*> const &columns,
420                  Real line_len, Real indent, bool ragged)
421 {
422   vector<vsize> breaks;
423   vector<Real> force;
424   vector<Grob*> non_loose;
425   vector<Column_description> cols;
426   SCM force_break = ly_symbol2scm ("force");
427
428   for (vsize i = 0; i < columns.size (); i++)
429     if (!is_loose (columns[i]) || Paper_column::is_breakable (columns[i]))
430       non_loose.push_back (columns[i]);
431
432   breaks.clear ();
433   breaks.push_back (0);
434   cols.push_back (Column_description ());
435   for (vsize i = 1; i < non_loose.size () - 1; i++)
436     {
437       if (Paper_column::is_breakable (non_loose[i]))
438         breaks.push_back (cols.size ());
439
440       cols.push_back (get_column_description (non_loose, i, false));
441     }
442   breaks.push_back (cols.size ());
443   force.resize (breaks.size () * breaks.size (), infinity_f);
444
445   for (vsize b = 0; b < breaks.size () - 1; b++)
446     {
447       cols[breaks[b]] = get_column_description (non_loose, breaks[b], true);
448       vsize st = breaks[b];
449
450       for (vsize c = b+1; c < breaks.size (); c++)
451         {
452           vsize end = breaks[c];
453           Simple_spacer spacer;
454
455           for (vsize i = breaks[b]; i < end - 1; i++)
456             spacer.add_spring (cols[i].ideal_, cols[i].inverse_hooke_);
457           spacer.add_spring (cols[end-1].end_ideal_, cols[end-1].end_inverse_hooke_);
458
459
460           for (vsize i = breaks[b]; i < end; i++)
461             {
462               for (vsize r = 0; r < cols[i].rods_.size (); r++)
463                 if (cols[i].rods_[r].r_ < end)
464                   spacer.add_rod (i - st, cols[i].rods_[r].r_ - st, cols[i].rods_[r].dist_);
465               for (vsize r = 0; r < cols[i].end_rods_.size (); r++)
466                 if (cols[i].end_rods_[r].r_ == end)
467                   spacer.add_rod (i - st, end - st, cols[i].end_rods_[r].dist_);
468               if (!cols[i].keep_inside_line_.is_empty ())
469                 {
470                   spacer.add_rod (i - st, end - st, cols[i].keep_inside_line_[RIGHT]);
471                   spacer.add_rod (0, i - st, cols[i].keep_inside_line_[LEFT]);
472                 }
473             }
474           spacer.solve ((b == 0) ? line_len - indent : line_len, ragged);
475
476           /* add a (convex) penalty for compression. We do this _only_ in get_line_forces,
477              not get_line_configuration. This is temporary, for backwards compatibility;
478              the old line/page-breaking stuff ignores page breaks when it calculates line
479              breaks, so compression penalties can result in scores (eg. wtk-fugue) blowing
480              up to too many pages. */
481           Real f = spacer.force ();
482           force[b * breaks.size () + c] = f - (f < 0 ? f*f*f*f*4 : 0);
483
484           if (!spacer.fits ())
485             {
486               if (c == b + 1)
487                 force[b * breaks.size () + c] = -200000;
488               else
489                 force[b * breaks.size () + c] = infinity_f;
490               break;
491             }
492           if (end < cols.size () && cols[end].break_permission_ == force_break)
493             break;
494         }
495     }
496   return force;
497 }
498
499 Column_x_positions
500 get_line_configuration (vector<Grob*> const &columns,
501                         Real line_len,
502                         Real indent,
503                         bool ragged)
504 {
505   vector<Column_description> cols;
506   Simple_spacer spacer;
507   Column_x_positions ret;
508
509   ret.cols_.push_back (dynamic_cast<Item*> (columns[0])->find_prebroken_piece (RIGHT));
510   for (vsize i = 1; i < columns.size () - 1; i++)
511     {
512       if (is_loose (columns[i]))
513         ret.loose_cols_.push_back (columns[i]);
514       else
515         ret.cols_.push_back (columns[i]);
516     }
517   ret.cols_.push_back (dynamic_cast<Item*> (columns.back ())->find_prebroken_piece (LEFT));
518
519   /* since we've already put our line-ending column in the column list, we can ignore
520      the end_XXX_ fields of our column_description */
521   for (vsize i = 0; i < ret.cols_.size () - 1; i++)
522     {
523       cols.push_back (get_column_description (ret.cols_, i, i == 0));
524       spacer.add_spring (cols[i].ideal_, cols[i].inverse_hooke_);
525     }
526   for (vsize i = 0; i < cols.size (); i++)
527     {
528       for (vsize r = 0; r < cols[i].rods_.size (); r++)
529         spacer.add_rod (i, cols[i].rods_[r].r_, cols[i].rods_[r].dist_);
530
531       if (!cols[i].keep_inside_line_.is_empty ())
532         {
533           spacer.add_rod (i, cols.size (), cols[i].keep_inside_line_[RIGHT]);
534           spacer.add_rod (0, i, cols[i].keep_inside_line_[LEFT]);
535         }
536     }
537
538   spacer.solve (line_len, ragged);
539   ret.force_ = spacer.force ();
540
541   /*
542     We used to have a penalty for compression, no matter what, but that
543     fucked up wtk1-fugue2 (taking 3 full pages.)
544   */
545   ret.config_ = spacer.spring_positions ();
546   for (vsize i = 0; i < ret.config_.size (); i++)
547     ret.config_[i] += indent;
548
549   ret.satisfies_constraints_ = spacer.fits ();
550
551   /*
552     Check if breaking constraints are met.
553   */
554   for (vsize i = 1; i < ret.cols_.size () - 1; i++)
555     {
556       SCM p = ret.cols_[i]->get_property ("line-break-permission");
557       if (p == ly_symbol2scm ("force"))
558         ret.satisfies_constraints_ = false;
559     }
560
561   return ret;
562 }
563