Nitpick: ly:spanner-bound grob name slur -> spanner.
[lilypond.git] / lily / simple-spacer.cc
blob1d38d9a70fda59eab91069b691339bd6a7d6b98d
1 /*
2 simple-spacer.cc -- implement Simple_spacer
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
8 TODO:
9 - add support for different stretch/shrink constants?
12 #include <cstdio>
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"
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
41 c4 c4 c4 c4
42 veryveryverylongsyllable2 veryveryverylongsyllable2
43 " "4 veryveryverylongsyllable2 syllable4
46 can be further compressed to
49 c4 c4 c4 c4
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 ()
62 line_len_ = 0.0;
63 force_ = 0.0;
64 fits_ = true;
65 ragged_ = true;
68 Real
69 Simple_spacer::force () const
71 return force_;
74 bool
75 Simple_spacer::fits () const
77 return fits_;
80 Real
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 */
88 return 0;
89 return c * block_stretch;
92 void
93 Simple_spacer::add_rod (int l, int r, Real dist)
95 if (isinf (dist) || isnan (dist))
97 programming_error ("ignoring weird minimum distance");
98 return;
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++)
109 if (spring_dist)
110 springs_[i].set_distance (springs_[i].distance () * dist / spring_dist);
111 else
112 springs_[i].set_distance (dist / (r - l));
115 return;
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 ()));
122 Real
123 Simple_spacer::range_ideal_len (int l, int r) const
125 Real d = 0.;
126 for (int i = l; i < r; i++)
127 d += springs_[i].distance ();
128 return d;
131 Real
132 Simple_spacer::range_stiffness (int l, int r, bool stretch) const
134 Real den = 0.0;
135 for (int i = l; i < r; i++)
136 den += stretch ? springs_[i].inverse_stretch_strength ()
137 : springs_[i].inverse_compress_strength ();
139 return 1 / den;
142 Real
143 Simple_spacer::configuration_length (Real force) const
145 Real l = 0.;
146 for (vsize i = 0; i < springs_.size (); i++)
147 l += springs_[i].length (force);
149 return l;
152 void
153 Simple_spacer::solve (Real line_len, bool ragged)
155 Real conf = configuration_length (force_);
157 ragged_ = ragged;
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)
165 fits_ = false;
168 Real
169 Simple_spacer::expand_line ()
171 double inv_hooke = 0;
172 double cur_len = configuration_length (force_);
174 fits_ = true;
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_;
185 Real
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_)
199 cur_force = 0.0;
200 cur_len = configuration_length (0.0);
201 compressed = true;
204 fits_ = true;
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 ()))
221 break;
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;
227 cur_len = line_len_;
230 Paranoia check.
232 assert (fabs (configuration_length (cur_force) - cur_len) < 1e-6);
233 return cur_force;
236 cur_len -= block_dist;
237 inv_hooke -= sp.inverse_compress_strength ();
238 cur_force = sp.blocking_force ();
241 fits_ = false;
242 return cur_force;
245 void
246 Simple_spacer::add_spring (Spring const &sp)
248 force_ = max (force_, sp.blocking_force ());
249 springs_.push_back (sp);
252 vector<Real>
253 Simple_spacer::spring_positions () const
255 vector<Real> ret;
256 ret.push_back (0.);
258 for (vsize i = 0; i < springs_.size (); i++)
259 ret.push_back (ret.back () + springs_[i].length (ragged_ && force_ > 0 ? 0.0 : force_));
260 return ret;
263 Real
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
268 of the line. */
269 if (ragged)
270 return max (0.0, line_len_ - configuration_length (0.0));
272 /* Use a convex compression penalty. */
273 Real f = force_;
274 return f - (f < 0 ? f*f*f*f*2 : 0);
277 /****************************************************************/
279 struct Rod_description
281 vsize r_;
282 Real dist_;
284 bool operator< (const Rod_description r)
286 return r_ < r.r_;
289 Rod_description ()
291 r_ = 0;
292 dist_ = 0;
295 Rod_description (vsize r, Real d)
297 r_ = r;
298 dist_ = 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 */
306 Spring spring_;
307 Spring end_spring_;
309 SCM break_permission_;
310 Interval keep_inside_line_;
312 Column_description ()
314 break_permission_ = SCM_EOL;
318 static bool
319 is_loose (Grob *g)
321 return (scm_is_pair (g->get_object ("between-cols")));
324 static Grob*
325 maybe_find_prebroken_piece (Grob *g, Direction d)
327 Grob *ret = dynamic_cast<Item*> (g)->find_prebroken_piece (d);
328 if (ret)
329 return ret;
330 return g;
333 static Grob*
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]))
338 return list[i];
339 return 0;
342 static Column_description
343 get_column_description (vector<Grob*> const &cols, vsize col_index, bool line_starter)
345 Grob *col = cols[col_index];
346 if (line_starter)
347 col = maybe_find_prebroken_piece (col, RIGHT);
349 Column_description description;
350 Grob *next_col = next_spaceable_column (cols, col_index);
351 if (next_col)
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);
355 if (end_col)
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);
363 if (j != VPOS)
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");
376 return description;
379 vector<Real>
380 get_line_forces (vector<Grob*> const &columns,
381 Real line_len, Real indent, bool ragged)
383 vector<vsize> breaks;
384 vector<Real> force;
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]);
393 breaks.clear ();
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);
438 if (!spacer.fits ())
440 if (c == b + 1)
441 force[b * breaks.size () + c] = -200000;
442 else
443 force[b * breaks.size () + c] = infinity_f;
444 break;
446 if (end < cols.size () && cols[end].break_permission_ == force_break)
447 break;
450 return force;
453 Column_x_positions
454 get_line_configuration (vector<Grob*> const &columns,
455 Real line_len,
456 Real indent,
457 bool ragged)
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]);
468 else
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;
511 return ret;