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;
86 return c * block_stretch;
90 Simple_spacer::add_rod (int l, int r, Real dist)
92 if (isinf (dist) || isnan (dist))
94 programming_error ("ignoring weird minimum distance");
98 Real block_force = rod_force (l, r, dist);
100 if (isinf (block_force))
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].set_distance (springs_[i].distance () * dist / spring_dist);
109 force_ = max (force_, block_force);
110 for (int i = l; i < r; i++)
111 springs_[i].set_blocking_force (max (block_force, springs_[i].blocking_force ()));
115 Simple_spacer::range_ideal_len (int l, int r) const
118 for (int i = l; i < r; i++)
119 d += springs_[i].distance ();
124 Simple_spacer::range_stiffness (int l, int r, bool stretch) const
127 for (int i = l; i < r; i++)
128 den += stretch ? springs_[i].inverse_stretch_strength ()
129 : springs_[i].inverse_compress_strength ();
135 Simple_spacer::configuration_length (Real force) const
138 for (vsize i = 0; i < springs_.size (); i++)
139 l += springs_[i].length (force);
145 Simple_spacer::solve (Real line_len, bool ragged)
147 Real conf = configuration_length (force_);
150 line_len_ = line_len;
151 if (conf < line_len_)
152 force_ = expand_line ();
153 else if (conf > line_len_)
154 force_ = compress_line ();
156 if (ragged && force_ < 0)
161 Simple_spacer::expand_line ()
163 double inv_hooke = 0;
164 double cur_len = configuration_length (force_);
167 for (vsize i=0; i < springs_.size (); i++)
168 inv_hooke += springs_[i].inverse_stretch_strength ();
170 assert (cur_len <= line_len_);
171 return (line_len_ - cur_len) / inv_hooke + force_;
175 Simple_spacer::compress_line ()
177 double inv_hooke = 0;
178 double cur_len = configuration_length (force_);
179 double cur_force = force_;
180 bool compressed = false;
182 /* just because we are in compress_line () doesn't mean that the line
183 will actually be compressed (as in, a negative force) because
184 we start out with a stretched line. Here, we check whether we
185 will be compressed or stretched (so we know which spring constant to use) */
186 if (configuration_length (0.0) > line_len_)
189 cur_len = configuration_length (0.0);
194 for (vsize i=0; i < springs_.size (); i++)
195 inv_hooke += compressed
196 ? springs_[i].inverse_compress_strength ()
197 : springs_[i].inverse_stretch_strength ();
199 assert (line_len_ <= cur_len);
201 vector<Spring> sorted_springs = springs_;
202 sort (sorted_springs.begin (), sorted_springs.end (), greater<Spring> ());
204 for (vsize i = 0; i < sorted_springs.size (); i++)
206 Spring sp = sorted_springs[i];
208 assert (sp.blocking_force () <= cur_force);
209 if (isinf (sp.blocking_force ()))
212 double block_dist = (cur_force - sp.blocking_force ()) * inv_hooke;
213 if (cur_len - block_dist < line_len_)
215 cur_force += (line_len_ - cur_len) / inv_hooke;
221 assert (fabs (configuration_length (cur_force) - cur_len) < 1e-6);
225 cur_len -= block_dist;
226 inv_hooke -= sp.inverse_compress_strength ();
227 cur_force = sp.blocking_force ();
235 Simple_spacer::add_spring (Spring const &sp)
237 force_ = max (force_, sp.blocking_force ());
238 springs_.push_back (sp);
242 Simple_spacer::spring_positions () const
247 for (vsize i = 0; i < springs_.size (); i++)
248 ret.push_back (ret.back () + springs_[i].length (ragged_ && force_ > 0 ? 0.0 : force_));
253 Simple_spacer::force_penalty (bool ragged) const
255 /* If we are ragged-right, we don't want to penalise according to the force,
256 but according to the amount of whitespace that is present after the end
259 return max (0.0, line_len_ - configuration_length (0.0));
261 /* Use a convex compression penalty. */
263 return f - (f < 0 ? f*f*f*f*4 : 0);
266 /****************************************************************/
268 struct Rod_description
273 bool operator< (const Rod_description r)
284 Rod_description (vsize r, Real d)
291 struct Column_description
293 vector<Rod_description> rods_;
294 vector<Rod_description> end_rods_; /* use these if they end at the last column of the line */
298 SCM break_permission_;
299 Interval keep_inside_line_;
301 Column_description ()
303 break_permission_ = SCM_EOL;
310 return (scm_is_pair (g->get_object ("between-cols")));
314 maybe_find_prebroken_piece (Grob *g, Direction d)
316 Grob *ret = dynamic_cast<Item*> (g)->find_prebroken_piece (d);
323 next_spaceable_column (vector<Grob*> const &list, vsize starting)
325 for (vsize i = starting+1; i < list.size (); i++)
326 if (!is_loose (list[i]))
331 static Column_description
332 get_column_description (vector<Grob*> const &cols, vsize col_index, bool line_starter)
334 Grob *col = cols[col_index];
336 col = maybe_find_prebroken_piece (col, RIGHT);
338 Column_description description;
339 Grob *next_col = next_spaceable_column (cols, col_index);
341 description.spring_ = Spaceable_grob::get_spring (col, next_col);
343 Grob *end_col = dynamic_cast<Item*> (cols[col_index+1])->find_prebroken_piece (LEFT);
345 description.end_spring_ = Spaceable_grob::get_spring (col, end_col);
347 for (SCM s = Spaceable_grob::get_minimum_distances (col);
348 scm_is_pair (s); s = scm_cdr (s))
350 Grob *other = unsmob_grob (scm_caar (s));
351 vsize j = binary_search (cols, other, Paper_column::less_than, col_index);
354 if (cols[j] == other)
355 description.rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
356 else /* it must end at the LEFT prebroken_piece */
357 description.end_rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
361 if (!line_starter && to_boolean (col->get_property ("keep-inside-line")))
362 description.keep_inside_line_ = col->extent (col, X_AXIS);
364 description.break_permission_ = col->get_property ("line-break-permission");
369 get_line_forces (vector<Grob*> const &columns,
370 Real line_len, Real indent, bool ragged)
372 vector<vsize> breaks;
374 vector<Grob*> non_loose;
375 vector<Column_description> cols;
376 SCM force_break = ly_symbol2scm ("force");
378 for (vsize i = 0; i < columns.size (); i++)
379 if (!is_loose (columns[i]) || Paper_column::is_breakable (columns[i]))
380 non_loose.push_back (columns[i]);
383 breaks.push_back (0);
384 cols.push_back (Column_description ());
385 for (vsize i = 1; i + 1 < non_loose.size (); i++)
387 if (Paper_column::is_breakable (non_loose[i]))
388 breaks.push_back (cols.size ());
390 cols.push_back (get_column_description (non_loose, i, false));
392 breaks.push_back (cols.size ());
393 force.resize (breaks.size () * breaks.size (), infinity_f);
395 for (vsize b = 0; b + 1 < breaks.size (); b++)
397 cols[breaks[b]] = get_column_description (non_loose, breaks[b], true);
398 vsize st = breaks[b];
400 for (vsize c = b+1; c < breaks.size (); c++)
402 vsize end = breaks[c];
403 Simple_spacer spacer;
405 for (vsize i = breaks[b]; i < end - 1; i++)
406 spacer.add_spring (cols[i].spring_);
407 spacer.add_spring (cols[end-1].end_spring_);
410 for (vsize i = breaks[b]; i < end; i++)
412 for (vsize r = 0; r < cols[i].rods_.size (); r++)
413 if (cols[i].rods_[r].r_ < end)
414 spacer.add_rod (i - st, cols[i].rods_[r].r_ - st, cols[i].rods_[r].dist_);
415 for (vsize r = 0; r < cols[i].end_rods_.size (); r++)
416 if (cols[i].end_rods_[r].r_ == end)
417 spacer.add_rod (i - st, end - st, cols[i].end_rods_[r].dist_);
418 if (!cols[i].keep_inside_line_.is_empty ())
420 spacer.add_rod (i - st, end - st, cols[i].keep_inside_line_[RIGHT]);
421 spacer.add_rod (0, i - st, -cols[i].keep_inside_line_[LEFT]);
424 spacer.solve ((b == 0) ? line_len - indent : line_len, ragged);
425 force[b * breaks.size () + c] = spacer.force_penalty (ragged);
430 force[b * breaks.size () + c] = -200000;
432 force[b * breaks.size () + c] = infinity_f;
435 if (end < cols.size () && cols[end].break_permission_ == force_break)
443 get_line_configuration (vector<Grob*> const &columns,
448 vector<Column_description> cols;
449 Simple_spacer spacer;
450 Column_x_positions ret;
452 ret.cols_.push_back (dynamic_cast<Item*> (columns[0])->find_prebroken_piece (RIGHT));
453 for (vsize i = 1; i + 1 < columns.size (); i++)
455 if (is_loose (columns[i]))
456 ret.loose_cols_.push_back (columns[i]);
458 ret.cols_.push_back (columns[i]);
460 ret.cols_.push_back (dynamic_cast<Item*> (columns.back ())->find_prebroken_piece (LEFT));
462 /* since we've already put our line-ending column in the column list, we can ignore
463 the end_XXX_ fields of our column_description */
464 for (vsize i = 0; i + 1 < ret.cols_.size (); i++)
466 cols.push_back (get_column_description (ret.cols_, i, i == 0));
467 spacer.add_spring (cols[i].spring_);
469 for (vsize i = 0; i < cols.size (); i++)
471 for (vsize r = 0; r < cols[i].rods_.size (); r++)
472 spacer.add_rod (i, cols[i].rods_[r].r_, cols[i].rods_[r].dist_);
474 if (!cols[i].keep_inside_line_.is_empty ())
476 spacer.add_rod (i, cols.size (), cols[i].keep_inside_line_[RIGHT]);
477 spacer.add_rod (0, i, -cols[i].keep_inside_line_[LEFT]);
481 spacer.solve (line_len, ragged);
482 ret.force_ = spacer.force_penalty (ragged);
484 ret.config_ = spacer.spring_positions ();
485 for (vsize i = 0; i < ret.config_.size (); i++)
486 ret.config_[i] += indent;
488 ret.satisfies_constraints_ = spacer.fits ();
491 Check if breaking constraints are met.
493 for (vsize i = 1; i + 1 < ret.cols_.size (); i++)
495 SCM p = ret.cols_[i]->get_property ("line-break-permission");
496 if (p == ly_symbol2scm ("force"))
497 ret.satisfies_constraints_ = false;