2 simple-spacer.cc -- implement Simple_spacer
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 - add support for different stretch/shrink constants?
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"
25 A simple spacing constraint solver. The approach:
27 Stretch the line uniformly until none of the constraints (rods)
28 block. It then is very wide.
30 Compress until the next constraint blocks,
32 Mark the springs over the constrained part to be non-active.
34 Repeat with the smaller set of non-active constraints, until all
35 constraints blocked, or until the line is as short as desired.
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
42 veryveryverylongsyllable2 veryveryverylongsyllable2
43 " "4 veryveryverylongsyllable2 syllable4
46 can be further compressed to
50 veryveryverylongsyllable2 veryveryverylongsyllable2
51 " "4 veryveryverylongsyllable2 syllable4
54 Perhaps this is not a bad thing, because the 1st looks better anyway. */
57 positive force = expanding, negative force = compressing.
60 Simple_spacer::Simple_spacer ()
69 Simple_spacer::force () const
75 Simple_spacer::fits () const
81 Simple_spacer::rod_force (int l, int r, Real dist)
83 Real d = range_ideal_len (l, r);
84 Real c = range_stiffness (l, r, dist > d);
85 Real block_stretch = dist - d;
87 if (isinf (c) && block_stretch == 0) /* take care of the 0*infinity_f case */
89 return c * block_stretch;
93 Simple_spacer::add_rod (int l, int r, Real dist)
95 if (isinf (dist) || isnan (dist))
97 programming_error ("ignoring weird minimum distance");
101 Real block_force = rod_force (l, r, dist);
103 if (isinf (block_force))
105 Real spring_dist = range_ideal_len (l, r);
106 if (spring_dist < dist)
107 for (int i = l; i < r; i++)
110 springs_[i].set_distance (springs_[i].distance () * dist / spring_dist);
112 springs_[i].set_distance (dist / (r - l));
117 force_ = max (force_, block_force);
118 for (int i = l; i < r; i++)
119 springs_[i].set_blocking_force (max (block_force, springs_[i].blocking_force ()));
123 Simple_spacer::range_ideal_len (int l, int r) const
126 for (int i = l; i < r; i++)
127 d += springs_[i].distance ();
132 Simple_spacer::range_stiffness (int l, int r, bool stretch) const
135 for (int i = l; i < r; i++)
136 den += stretch ? springs_[i].inverse_stretch_strength ()
137 : springs_[i].inverse_compress_strength ();
143 Simple_spacer::configuration_length (Real force) const
146 for (vsize i = 0; i < springs_.size (); i++)
147 l += springs_[i].length (force);
153 Simple_spacer::solve (Real line_len, bool ragged)
155 Real conf = configuration_length (force_);
158 line_len_ = line_len;
159 if (conf < line_len_)
160 force_ = expand_line ();
161 else if (conf > line_len_)
162 force_ = compress_line ();
164 if (ragged && force_ < 0)
169 Simple_spacer::expand_line ()
171 double inv_hooke = 0;
172 double cur_len = configuration_length (force_);
175 for (vsize i=0; i < springs_.size (); i++)
176 inv_hooke += springs_[i].inverse_stretch_strength ();
178 if (inv_hooke == 0.0) /* avoid division by zero. If springs are infinitely stiff */
179 return 0.0; /* anyway, then it makes no difference what the force is */
181 assert (cur_len <= line_len_);
182 return (line_len_ - cur_len) / inv_hooke + force_;
186 Simple_spacer::compress_line ()
188 double inv_hooke = 0;
189 double cur_len = configuration_length (force_);
190 double cur_force = force_;
191 bool compressed = false;
193 /* just because we are in compress_line () doesn't mean that the line
194 will actually be compressed (as in, a negative force) because
195 we start out with a stretched line. Here, we check whether we
196 will be compressed or stretched (so we know which spring constant to use) */
197 if (configuration_length (0.0) > line_len_)
200 cur_len = configuration_length (0.0);
205 for (vsize i=0; i < springs_.size (); i++)
206 inv_hooke += compressed
207 ? springs_[i].inverse_compress_strength ()
208 : springs_[i].inverse_stretch_strength ();
210 assert (line_len_ <= cur_len);
212 vector<Spring> sorted_springs = springs_;
213 sort (sorted_springs.begin (), sorted_springs.end (), greater<Spring> ());
215 for (vsize i = 0; i < sorted_springs.size (); i++)
217 Spring sp = sorted_springs[i];
219 assert (sp.blocking_force () <= cur_force);
220 if (isinf (sp.blocking_force ()))
223 double block_dist = (cur_force - sp.blocking_force ()) * inv_hooke;
224 if (cur_len - block_dist < line_len_)
226 cur_force += (line_len_ - cur_len) / inv_hooke;
232 assert (fabs (configuration_length (cur_force) - cur_len) < 1e-6);
236 cur_len -= block_dist;
237 inv_hooke -= sp.inverse_compress_strength ();
238 cur_force = sp.blocking_force ();
246 Simple_spacer::add_spring (Spring const &sp)
248 force_ = max (force_, sp.blocking_force ());
249 springs_.push_back (sp);
253 Simple_spacer::spring_positions () const
258 for (vsize i = 0; i < springs_.size (); i++)
259 ret.push_back (ret.back () + springs_[i].length (ragged_ && force_ > 0 ? 0.0 : force_));
264 Simple_spacer::force_penalty (bool ragged) const
266 /* If we are ragged-right, we don't want to penalise according to the force,
267 but according to the amount of whitespace that is present after the end
270 return max (0.0, line_len_ - configuration_length (0.0));
272 /* Use a convex compression penalty. */
274 return f - (f < 0 ? f*f*f*f*2 : 0);
277 /****************************************************************/
279 struct Rod_description
284 bool operator< (const Rod_description r)
295 Rod_description (vsize r, Real d)
302 struct Column_description
304 vector<Rod_description> rods_;
305 vector<Rod_description> end_rods_; /* use these if they end at the last column of the line */
309 SCM break_permission_;
310 Interval keep_inside_line_;
312 Column_description ()
314 break_permission_ = SCM_EOL;
321 return (scm_is_pair (g->get_object ("between-cols")));
325 maybe_find_prebroken_piece (Grob *g, Direction d)
327 Grob *ret = dynamic_cast<Item*> (g)->find_prebroken_piece (d);
334 next_spaceable_column (vector<Grob*> const &list, vsize starting)
336 for (vsize i = starting+1; i < list.size (); i++)
337 if (!is_loose (list[i]))
342 static Column_description
343 get_column_description (vector<Grob*> const &cols, vsize col_index, bool line_starter)
345 Grob *col = cols[col_index];
347 col = maybe_find_prebroken_piece (col, RIGHT);
349 Column_description description;
350 Grob *next_col = next_spaceable_column (cols, col_index);
352 description.spring_ = Spaceable_grob::get_spring (col, next_col);
354 Grob *end_col = dynamic_cast<Item*> (cols[col_index+1])->find_prebroken_piece (LEFT);
356 description.end_spring_ = Spaceable_grob::get_spring (col, end_col);
358 for (SCM s = Spaceable_grob::get_minimum_distances (col);
359 scm_is_pair (s); s = scm_cdr (s))
361 Grob *other = unsmob_grob (scm_caar (s));
362 vsize j = binary_search (cols, other, Paper_column::less_than, col_index);
365 if (cols[j] == other)
366 description.rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
367 else /* it must end at the LEFT prebroken_piece */
368 description.end_rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
372 if (!line_starter && to_boolean (col->get_property ("keep-inside-line")))
373 description.keep_inside_line_ = col->extent (col, X_AXIS);
375 description.break_permission_ = col->get_property ("line-break-permission");
380 get_line_forces (vector<Grob*> const &columns,
381 Real line_len, Real indent, bool ragged)
383 vector<vsize> breaks;
385 vector<Grob*> non_loose;
386 vector<Column_description> cols;
387 SCM force_break = ly_symbol2scm ("force");
389 for (vsize i = 0; i < columns.size (); i++)
390 if (!is_loose (columns[i]) || Paper_column::is_breakable (columns[i]))
391 non_loose.push_back (columns[i]);
394 breaks.push_back (0);
395 cols.push_back (Column_description ());
396 for (vsize i = 1; i + 1 < non_loose.size (); i++)
398 if (Paper_column::is_breakable (non_loose[i]))
399 breaks.push_back (cols.size ());
401 cols.push_back (get_column_description (non_loose, i, false));
403 breaks.push_back (cols.size ());
404 force.resize (breaks.size () * breaks.size (), infinity_f);
406 for (vsize b = 0; b + 1 < breaks.size (); b++)
408 cols[breaks[b]] = get_column_description (non_loose, breaks[b], true);
409 vsize st = breaks[b];
411 for (vsize c = b+1; c < breaks.size (); c++)
413 vsize end = breaks[c];
414 Simple_spacer spacer;
416 for (vsize i = breaks[b]; i < end - 1; i++)
417 spacer.add_spring (cols[i].spring_);
418 spacer.add_spring (cols[end-1].end_spring_);
421 for (vsize i = breaks[b]; i < end; i++)
423 for (vsize r = 0; r < cols[i].rods_.size (); r++)
424 if (cols[i].rods_[r].r_ < end)
425 spacer.add_rod (i - st, cols[i].rods_[r].r_ - st, cols[i].rods_[r].dist_);
426 for (vsize r = 0; r < cols[i].end_rods_.size (); r++)
427 if (cols[i].end_rods_[r].r_ == end)
428 spacer.add_rod (i - st, end - st, cols[i].end_rods_[r].dist_);
429 if (!cols[i].keep_inside_line_.is_empty ())
431 spacer.add_rod (i - st, end - st, cols[i].keep_inside_line_[RIGHT]);
432 spacer.add_rod (0, i - st, -cols[i].keep_inside_line_[LEFT]);
435 spacer.solve ((b == 0) ? line_len - indent : line_len, ragged);
436 force[b * breaks.size () + c] = spacer.force_penalty (ragged);
441 force[b * breaks.size () + c] = -200000;
443 force[b * breaks.size () + c] = infinity_f;
446 if (end < cols.size () && cols[end].break_permission_ == force_break)
454 get_line_configuration (vector<Grob*> const &columns,
459 vector<Column_description> cols;
460 Simple_spacer spacer;
461 Column_x_positions ret;
463 ret.cols_.push_back (dynamic_cast<Item*> (columns[0])->find_prebroken_piece (RIGHT));
464 for (vsize i = 1; i + 1 < columns.size (); i++)
466 if (is_loose (columns[i]))
467 ret.loose_cols_.push_back (columns[i]);
469 ret.cols_.push_back (columns[i]);
471 ret.cols_.push_back (dynamic_cast<Item*> (columns.back ())->find_prebroken_piece (LEFT));
473 /* since we've already put our line-ending column in the column list, we can ignore
474 the end_XXX_ fields of our column_description */
475 for (vsize i = 0; i + 1 < ret.cols_.size (); i++)
477 cols.push_back (get_column_description (ret.cols_, i, i == 0));
478 spacer.add_spring (cols[i].spring_);
480 for (vsize i = 0; i < cols.size (); i++)
482 for (vsize r = 0; r < cols[i].rods_.size (); r++)
483 spacer.add_rod (i, cols[i].rods_[r].r_, cols[i].rods_[r].dist_);
485 if (!cols[i].keep_inside_line_.is_empty ())
487 spacer.add_rod (i, cols.size (), cols[i].keep_inside_line_[RIGHT]);
488 spacer.add_rod (0, i, -cols[i].keep_inside_line_[LEFT]);
492 spacer.solve (line_len, ragged);
493 ret.force_ = spacer.force_penalty (ragged);
495 ret.config_ = spacer.spring_positions ();
496 for (vsize i = 0; i < ret.config_.size (); i++)
497 ret.config_[i] += indent;
499 ret.satisfies_constraints_ = spacer.fits ();
502 Check if breaking constraints are met.
504 for (vsize i = 1; i + 1 < ret.cols_.size (); i++)
506 SCM p = ret.cols_[i]->get_property ("line-break-permission");
507 if (p == ly_symbol2scm ("force"))
508 ret.satisfies_constraints_ = false;