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, d > dist);
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> ());
203 for (vsize i = 0; i < sorted_springs.size (); i++)
205 Spring sp = sorted_springs[i];
207 assert (sp.blocking_force () <= cur_force);
208 if (isinf (sp.blocking_force ()))
211 double block_dist = (cur_force - sp.blocking_force ()) * inv_hooke;
212 if (cur_len - block_dist < line_len_)
214 cur_force += (line_len_ - cur_len) / inv_hooke;
220 assert (fabs (configuration_length (cur_force) - cur_len) < 1e-6);
224 cur_len -= block_dist;
225 inv_hooke -= sp.inverse_compress_strength ();
226 cur_force = sp.blocking_force ();
234 Simple_spacer::add_spring (Spring const &sp)
236 force_ = max (force_, sp.blocking_force ());
237 springs_.push_back (sp);
241 Simple_spacer::spring_positions () const
246 for (vsize i = 0; i < springs_.size (); i++)
247 ret.push_back (ret.back () + springs_[i].length (ragged_ && force_ > 0 ? 0.0 : force_));
252 Simple_spacer::force_penalty (bool ragged) const
254 /* If we are ragged-right, we don't want to penalise according to the force,
255 but according to the amount of whitespace that is present after the end
258 return max (0.0, line_len_ - configuration_length (0.0));
260 /* Use a convex compression penalty. */
262 return f - (f < 0 ? f*f*f*f*4 : 0);
265 /****************************************************************/
268 TODO: should a add penalty for widely varying spring forces (caused
277 The ## forces the notes apart; we shouldn't allow the O's to touch
281 struct Rod_description
286 bool operator< (const Rod_description r)
297 Rod_description (vsize r, Real d)
304 struct Column_description
306 vector<Rod_description> rods_;
307 vector<Rod_description> end_rods_; /* use these if they end at the last column of the line */
311 SCM break_permission_;
312 Interval keep_inside_line_;
314 Column_description ()
316 break_permission_ = SCM_EOL;
323 return (scm_is_pair (g->get_object ("between-cols")));
327 maybe_find_prebroken_piece (Grob *g, Direction d)
329 Grob *ret = dynamic_cast<Item*> (g)->find_prebroken_piece (d);
336 next_spaceable_column (vector<Grob*> const &list, vsize starting)
338 for (vsize i = starting+1; i < list.size (); i++)
339 if (!is_loose (list[i]))
344 static Column_description
345 get_column_description (vector<Grob*> const &cols, vsize col_index, bool line_starter)
347 Grob *col = cols[col_index];
349 col = maybe_find_prebroken_piece (col, RIGHT);
351 Column_description description;
352 Grob *next_col = next_spaceable_column (cols, col_index);
354 description.spring_ = Spaceable_grob::get_spring (col, next_col);
356 Grob *end_col = dynamic_cast<Item*> (cols[col_index+1])->find_prebroken_piece (LEFT);
358 description.end_spring_ = Spaceable_grob::get_spring (col, end_col);
360 for (SCM s = Spaceable_grob::get_minimum_distances (col);
361 scm_is_pair (s); s = scm_cdr (s))
363 Grob *other = unsmob_grob (scm_caar (s));
364 vsize j = binary_search (cols, other, Paper_column::less_than, col_index);
367 if (cols[j] == other)
368 description.rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
369 else /* it must end at the LEFT prebroken_piece */
370 description.end_rods_.push_back (Rod_description (j, scm_to_double (scm_cdar (s))));
374 if (!line_starter && to_boolean (col->get_property ("keep-inside-line")))
375 description.keep_inside_line_ = col->extent (col, X_AXIS);
377 description.break_permission_ = col->get_property ("line-break-permission");
382 get_line_forces (vector<Grob*> const &columns,
383 Real line_len, Real indent, bool ragged)
385 vector<vsize> breaks;
387 vector<Grob*> non_loose;
388 vector<Column_description> cols;
389 SCM force_break = ly_symbol2scm ("force");
391 for (vsize i = 0; i < columns.size (); i++)
392 if (!is_loose (columns[i]) || Paper_column::is_breakable (columns[i]))
393 non_loose.push_back (columns[i]);
396 breaks.push_back (0);
397 cols.push_back (Column_description ());
398 for (vsize i = 1; i + 1 < non_loose.size (); i++)
400 if (Paper_column::is_breakable (non_loose[i]))
401 breaks.push_back (cols.size ());
403 cols.push_back (get_column_description (non_loose, i, false));
405 breaks.push_back (cols.size ());
406 force.resize (breaks.size () * breaks.size (), infinity_f);
408 for (vsize b = 0; b + 1 < breaks.size (); b++)
410 cols[breaks[b]] = get_column_description (non_loose, breaks[b], true);
411 vsize st = breaks[b];
413 for (vsize c = b+1; c < breaks.size (); c++)
415 vsize end = breaks[c];
416 Simple_spacer spacer;
418 for (vsize i = breaks[b]; i < end - 1; i++)
419 spacer.add_spring (cols[i].spring_);
420 spacer.add_spring (cols[end-1].end_spring_);
423 for (vsize i = breaks[b]; i < end; i++)
425 for (vsize r = 0; r < cols[i].rods_.size (); r++)
426 if (cols[i].rods_[r].r_ < end)
427 spacer.add_rod (i - st, cols[i].rods_[r].r_ - st, cols[i].rods_[r].dist_);
428 for (vsize r = 0; r < cols[i].end_rods_.size (); r++)
429 if (cols[i].end_rods_[r].r_ == end)
430 spacer.add_rod (i - st, end - st, cols[i].end_rods_[r].dist_);
431 if (!cols[i].keep_inside_line_.is_empty ())
433 spacer.add_rod (i - st, end - st, cols[i].keep_inside_line_[RIGHT]);
434 spacer.add_rod (0, i - st, -cols[i].keep_inside_line_[LEFT]);
437 spacer.solve ((b == 0) ? line_len - indent : line_len, ragged);
438 force[b * breaks.size () + c] = spacer.force_penalty (ragged);
443 force[b * breaks.size () + c] = -200000;
445 force[b * breaks.size () + c] = infinity_f;
448 if (end < cols.size () && cols[end].break_permission_ == force_break)
456 get_line_configuration (vector<Grob*> const &columns,
461 vector<Column_description> cols;
462 Simple_spacer spacer;
463 Column_x_positions ret;
465 ret.cols_.push_back (dynamic_cast<Item*> (columns[0])->find_prebroken_piece (RIGHT));
466 for (vsize i = 1; i + 1 < columns.size (); i++)
468 if (is_loose (columns[i]))
469 ret.loose_cols_.push_back (columns[i]);
471 ret.cols_.push_back (columns[i]);
473 ret.cols_.push_back (dynamic_cast<Item*> (columns.back ())->find_prebroken_piece (LEFT));
475 /* since we've already put our line-ending column in the column list, we can ignore
476 the end_XXX_ fields of our column_description */
477 for (vsize i = 0; i + 1 < ret.cols_.size (); i++)
479 cols.push_back (get_column_description (ret.cols_, i, i == 0));
480 spacer.add_spring (cols[i].spring_);
482 for (vsize i = 0; i < cols.size (); i++)
484 for (vsize r = 0; r < cols[i].rods_.size (); r++)
485 spacer.add_rod (i, cols[i].rods_[r].r_, cols[i].rods_[r].dist_);
487 if (!cols[i].keep_inside_line_.is_empty ())
489 spacer.add_rod (i, cols.size (), cols[i].keep_inside_line_[RIGHT]);
490 spacer.add_rod (0, i, -cols[i].keep_inside_line_[LEFT]);
494 spacer.solve (line_len, ragged);
495 ret.force_ = spacer.force_penalty (ragged);
497 ret.config_ = spacer.spring_positions ();
498 for (vsize i = 0; i < ret.config_.size (); i++)
499 ret.config_[i] += indent;
501 ret.satisfies_constraints_ = spacer.fits ();
504 Check if breaking constraints are met.
506 for (vsize i = 1; i + 1 < ret.cols_.size (); i++)
508 SCM p = ret.cols_[i]->get_property ("line-break-permission");
509 if (p == ly_symbol2scm ("force"))
510 ret.satisfies_constraints_ = false;