2 simple-spacer.cc -- implement Simple_spacer
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 - add support for different stretch/shrink constants?
16 #include "libc-extension.hh" // isinf
17 #include "simple-spacer.hh"
18 #include "paper-column.hh"
21 #include "column-x-positions.hh"
22 #include "spaceable-grob.hh"
23 #include "dimensions.hh"
26 A simple spacing constraint solver. The approach:
28 Stretch the line uniformly until none of the constraints (rods)
29 block. It then is very wide.
31 Compress until the next constraint blocks,
33 Mark the springs over the constrained part to be non-active.
35 Repeat with the smaller set of non-active constraints, until all
36 constraints blocked, or until the line is as short as desired.
38 This is much simpler, and much much faster than full scale
39 Constrained QP. On the other hand, a situation like this will not
40 be typeset as dense as possible, because
43 veryveryverylongsyllable2 veryveryverylongsyllable2
44 " "4 veryveryverylongsyllable2 syllable4
47 can be further compressed to
51 veryveryverylongsyllable2 veryveryverylongsyllable2
52 " "4 veryveryverylongsyllable2 syllable4
55 Perhaps this is not a bad thing, because the 1st looks better anyway. */
58 positive force = expanding, negative force = compressing.
61 Simple_spacer::Simple_spacer ()
64 Give an extra penalty for compression. Needed to avoid compressing
70 default_space_ = 20 PT;
74 Simple_spacer::add_rod (int l, int r, Real dist)
76 if (isinf (dist) || isnan (dist))
78 programming_error ("ignoring weird minimum distance");
82 Real c = range_stiffness (l, r);
86 If a spring is fixed, we have to do something here:
87 we let the rod override the spring.
90 for (int i = l; i < r; i++)
91 total_dist += springs_[i].ideal_;
93 if (total_dist < dist)
94 for (int i = l; i < r; i++)
95 springs_[i].ideal_ *= dist / total_dist;
100 Real d = range_ideal_len (l, r);
101 Real block_stretch = dist - d;
103 Real block_force = c * block_stretch;
104 force_ = max (force_, block_force);
106 for (int i = l; i < r; i++)
107 springs_[i].block_force_ = max (block_force, springs_[i].block_force_);
111 Simple_spacer::range_ideal_len (int l, int r) const
114 for (int i = l; i < r; i++)
115 d += springs_[i].ideal_;
120 Simple_spacer::range_stiffness (int l, int r) const
123 for (int i = l; i < r; i++)
125 if (springs_[i].is_active_)
126 den += 1 * springs_[i].inverse_hooke_;
133 Simple_spacer::active_blocking_force () const
135 Real bf = -infinity_f;
136 for (int i = 0; i < springs_.size (); i++)
137 if (springs_[i].is_active_)
138 bf = max (bf, springs_[i].block_force_);
143 Simple_spacer::active_springs_stiffness () const
145 Real stiff = range_stiffness (0, springs_.size ());
149 all springs are inactive. Take the stiffness of the
150 latest spring to block.
153 Real max_block_force = -infinity_f;
155 for (int i = 0; i < springs_.size (); i++)
157 if (springs_[i].block_force_ > max_block_force)
160 max_block_force = springs_[i].block_force_;
164 stiff = 1/springs_[max_i].inverse_hooke_;
170 Simple_spacer::set_active_states ()
172 /* float comparison is safe, since force is only copied. */
173 for (int i = 0; i < springs_.size (); i++)
174 if (springs_[i].is_active_
175 && springs_[i].block_force_ >= force_)
177 springs_[i].is_active_ = false;
183 Simple_spacer::configuration_length () const
186 for (int i = 0; i < springs_.size (); i++)
187 l += springs_[i].length (force_);
193 Simple_spacer::is_active () const
195 return active_count_;
199 Simple_spacer::my_solve_linelen ()
203 force_ = active_blocking_force ();
204 Real conf = configuration_length ();
206 if (conf < line_len_)
208 force_ += (line_len_ - conf) * active_springs_stiffness ();
212 set_active_states ();
217 Simple_spacer::my_solve_natural_len ()
219 Real line_len_force = 0.0;
223 force_ = max (active_blocking_force (), 0.0);
224 Real conf = configuration_length ();
226 if (conf < line_len_)
228 line_len_force = force_
230 * active_springs_stiffness ();
233 if (force_ < 1e-8) // ugh.,
236 set_active_states ();
239 force_ = line_len_force;
242 /****************************************************************/
244 Spring_description::Spring_description ()
247 inverse_hooke_ = 0.0;
253 Spring_description::is_sane () const
255 return (inverse_hooke_ >= 0)
257 && !isinf (ideal_) && !isnan (ideal_);
261 Spring_description::length (Real f) const
265 return ideal_ + f * inverse_hooke_;
267 /****************************************************************/
270 TODO: should a add penalty for widely varying spring forces (caused
279 The ## forces the notes apart; we shouldn't allow the O's to touch
283 Simple_spacer_wrapper::solve (Column_x_positions *positions, bool ragged)
286 spacer_->my_solve_natural_len ();
288 spacer_->my_solve_linelen ();
290 positions->force_ = spacer_->force_;
293 We used to have a penalty for compression, no matter what, but that
294 fucked up wtk1-fugue2 (taking 3 full pages.)
296 positions->config_.push (spacer_->indent_);
297 for (int i = 0; i < spacer_->springs_.size (); i++)
299 Real l = spacer_->springs_[i].length ((ragged) ? 0.0 : spacer_->force_);
300 positions->config_.push (positions->config_.top () + l);
302 we have l>= 0 here, up to rounding errors
307 For raggedright, we must have a measure of music density: this is
308 to prevent lots of short lines (which all have force = 0).
312 positions->satisfies_constraints_
313 = positions->config_.top () < spacer_->line_len_;
316 positions->satisfies_constraints_ = spacer_->is_active ();
318 positions->cols_ = spaced_cols_;
319 positions->loose_cols_ = loose_cols_;
322 Check if breaking constraints are met.
324 bool break_satisfy = true;
325 int sz = positions->cols_.size ();
326 for (int i = sz; i--;)
328 SCM p = positions->cols_[i]->get_property ("penalty");
329 if (scm_is_number (p))
331 if (scm_to_double (p) < -9999)
332 break_satisfy = break_satisfy && (i == 0 || i == sz -1);
333 if (scm_to_double (p) > 9999)
334 break_satisfy = break_satisfy && ! (i == 0 || i == sz -1);
338 positions->satisfies_constraints_
339 = positions->satisfies_constraints_ && break_satisfy;
343 Simple_spacer::add_spring (Real ideal, Real inverse_hooke)
345 Spring_description desc;
348 desc.inverse_hooke_ = inverse_hooke;
349 if (!desc.is_sane ())
351 programming_error ("insane spring found, setting to unit");
353 desc.inverse_hooke_ = 1.0;
358 desc.is_active_ = false;
364 desc.block_force_ = -desc.ideal_ / desc.inverse_hooke_;
365 // block at distance 0
369 springs_.push (desc);
373 compare_paper_column_rank (Grob *const &a,
376 return Paper_column::get_rank (a) - Paper_column::get_rank (b);
380 Simple_spacer_wrapper::add_columns (Link_array<Grob> const &icols)
382 Link_array<Grob> cols (icols);
385 for (int i = 0; i < icols.size (); i++)
386 if (scm_is_pair (icols[i]->get_object ("between-cols")))
387 loose_cols_.push (icols[i]);
389 cols.push (icols[i]);
392 for (int i = 0; i < cols.size () - 1; i++)
394 Spring_smob *spring = 0;
396 for (SCM s = cols[i]->get_object ("ideal-distances");
397 !spring && scm_is_pair (s);
400 Spring_smob *sp = unsmob_spring (scm_car (s));
402 if (sp->other_ == cols[i + 1])
407 programming_error (_f ("No spring between column %d and next one",
408 Paper_column::get_rank (cols[i])));
410 Real ideal = (spring) ? spring->distance_ : spacer_->default_space_;
411 Real inverse_hooke = (spring) ? spring->inverse_strength_ : 1.0;
413 spacer_->add_spring (ideal, inverse_hooke);
416 for (int i = 0; i < cols.size () - 1; i++)
418 for (SCM s = Spaceable_grob::get_minimum_distances (cols[i]);
419 scm_is_pair (s); s = scm_cdr (s))
421 Grob *other = unsmob_grob (scm_caar (s));
422 int j = binsearch_links (cols, other, &compare_paper_column_rank);
423 if (j >= 0 && cols[j] == other)
424 spacer_->add_rod (i, j, scm_to_double (scm_cdar (s)));
428 && to_boolean (cols[i]->get_property ("keep-inside-line")))
430 Interval e = cols[i]->extent (cols[i], X_AXIS);
433 spacer_->add_rod (i, cols.size () - 1, e[RIGHT]);
434 spacer_->add_rod (0, i, e[LEFT]);
440 Simple_spacer_wrapper::Simple_spacer_wrapper ()
442 spacer_ = new Simple_spacer ();
445 Simple_spacer_wrapper::~Simple_spacer_wrapper ()
450 Simple_spacer_wrapper::Simple_spacer_wrapper (Simple_spacer_wrapper const &)