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?
17 #include "libc-extension.hh" // isinf
18 #include "simple-spacer.hh"
19 #include "paper-column.hh"
22 #include "column-x-positions.hh"
23 #include "spaceable-grob.hh"
24 #include "dimensions.hh"
27 A simple spacing constraint solver. The approach:
29 Stretch the line uniformly until none of the constraints (rods)
30 block. It then is very wide.
32 Compress until the next constraint blocks,
34 Mark the springs over the constrained part to be non-active.
36 Repeat with the smaller set of non-active constraints, until all
37 constraints blocked, or until the line is as short as desired.
39 This is much simpler, and much much faster than full scale
40 Constrained QP. On the other hand, a situation like this will not
41 be typeset as dense as possible, because
44 veryveryverylongsyllable2 veryveryverylongsyllable2
45 " "4 veryveryverylongsyllable2 syllable4
48 can be further compressed to
52 veryveryverylongsyllable2 veryveryverylongsyllable2
53 " "4 veryveryverylongsyllable2 syllable4
56 Perhaps this is not a bad thing, because the 1st looks better anyway. */
61 positive force = expanding, negative force = compressing.
65 Simple_spacer::Simple_spacer ()
68 Give an extra penalty for compression. Needed to avoid compressing
74 default_space_ = 20 PT;
78 Simple_spacer::add_rod (int l, int r, Real dist)
80 if (isinf (dist) || isnan (dist))
82 programming_error ("Weird minimum distance. Ignoring");
86 Real c = range_stiffness (l, r);
90 If a spring is fixed, we have to do something here:
91 we let the rod override the spring.
94 for (int i = l ; i < r; i++)
95 total_dist += springs_[i].ideal_;
97 if (total_dist < dist)
98 for (int i = l ; i < r; i++)
99 springs_[i].ideal_ *= dist/total_dist;
104 Real d = range_ideal_len (l, r);
105 Real block_stretch = dist - d;
107 Real block_force = c * block_stretch;
108 force_ = force_ >? block_force;
110 for (int i = l; i < r; i++)
111 springs_[i].block_force_ = block_force >?
112 springs_[i].block_force_ ;
116 Simple_spacer::range_ideal_len (int l, int r) const
119 for (int i = l; i < r; i++)
120 d += springs_[i].ideal_;
125 Simple_spacer::range_stiffness (int l, int r) const
128 for (int i = l; i < r; i++)
130 if (springs_[i].is_active_)
131 den += 1 / springs_[i].hooke_;
138 Simple_spacer::active_blocking_force () const
140 Real bf = - infinity_f;
141 for (int i = 0; i < springs_.size (); i++)
142 if (springs_[i].is_active_)
144 bf = bf >? springs_[i].block_force_;
150 Simple_spacer::active_springs_stiffness () const
152 Real stiff = range_stiffness (0, springs_.size ());
156 all springs are inactive. Take the stiffness of the
157 latest spring to block.
160 Real max_block_force = -infinity_f;
162 for (int i = 0; i < springs_.size (); i++)
164 if (springs_[i].block_force_ > max_block_force)
167 max_block_force = springs_[i].block_force_;
171 stiff = springs_[max_i].hooke_;
177 Simple_spacer::set_active_states ()
179 /* float comparison is safe, since force is only copied. */
180 for (int i = 0 ; i <springs_.size (); i++)
181 if (springs_[i].is_active_
182 && springs_[i].block_force_ >= force_)
184 springs_[i].is_active_ = false;
190 Simple_spacer::configuration_length () const
193 for (int i = 0; i < springs_.size (); i++)
194 l += springs_[i].length (force_);
200 Simple_spacer::is_active () const
202 return active_count_;
206 Simple_spacer::my_solve_linelen ()
210 force_ = active_blocking_force ();
211 Real conf = configuration_length ();
213 if (conf < line_len_)
215 force_ += (line_len_ - conf) * active_springs_stiffness ();
219 set_active_states ();
225 Simple_spacer::my_solve_natural_len ()
227 Real line_len_force = 0.0;
231 force_ = active_blocking_force () >? 0.0;
232 Real conf = configuration_length ();
234 if (conf < line_len_)
236 line_len_force = force_
238 * active_springs_stiffness();
241 if (force_ < 1e-8) // ugh.,
244 set_active_states ();
247 force_ = line_len_force;
252 /****************************************************************/
254 Spring_description::Spring_description ()
264 Spring_description::is_sane () const
268 && !isinf (ideal_) && !isnan (ideal_);
272 Spring_description::length (Real f) const
276 return ideal_ + f / hooke_ ;
278 /****************************************************************/
283 TODO: should a add penalty for widely varying spring forces (caused
292 The ## forces the notes apart; we shouldn't allow the O's to touch
297 Simple_spacer_wrapper::solve (Column_x_positions *positions, bool ragged)
300 spacer_->my_solve_natural_len ();
302 spacer_->my_solve_linelen ();
304 positions->force_ = spacer_->force_;
307 We used to have a penalty for compression, no matter what, but that
308 fucked up wtk1-fugue2 (taking 3 full pages.)
310 positions->config_.push (spacer_->indent_);
311 for (int i = 0; i < spacer_->springs_.size (); i++)
313 Real l = spacer_->springs_[i].length ((ragged) ? 0.0 : spacer_->force_);
314 positions->config_.push (positions->config_.top () + l);
316 we have l>= 0 here, up to rounding errors
321 For raggedright, we must have a measure of music density: this is
322 to prevent lots of short lines (which all have force = 0).
326 positions->satisfies_constraints_ =
327 positions->config_.top () < spacer_->line_len_ ;
330 positions->satisfies_constraints_ = spacer_->is_active ();
332 positions->cols_ = spaced_cols_;
333 positions->loose_cols_ = loose_cols_;
336 Check if breaking constraints are met.
338 bool break_satisfy = true;
339 int sz = positions->cols_.size ();
340 for (int i = sz; i--; )
342 SCM p = positions->cols_[i]->get_property ( "penalty");
343 if (scm_is_number (p))
345 if (scm_to_double (p) < -9999)
346 break_satisfy = break_satisfy && (i == 0 || i == sz -1);
347 if (scm_to_double (p) > 9999)
348 break_satisfy = break_satisfy && !(i == 0 || i == sz -1);
353 positions->satisfies_constraints_ =
354 positions->satisfies_constraints_ && break_satisfy;
358 Simple_spacer::add_spring (Real ideal, Real hooke)
360 Spring_description desc;
364 if (!desc.is_sane ())
366 programming_error ("Insane spring found. Setting to unit spring.");
374 desc.is_active_ = false;
381 desc.block_force_ = - desc.hooke_ * desc.ideal_; // block at distance 0
385 springs_.push (desc);
389 compare_paper_column_rank (Grob *const &a,
392 return Paper_column::get_rank (a) - Paper_column::get_rank (b);
396 Simple_spacer_wrapper::add_columns (Link_array<Grob> const &icols)
398 Link_array<Grob> cols (icols);
400 for (int i = cols.size (); i--;)
401 if (scm_is_pair (cols[i]->get_property ("between-cols")))
403 loose_cols_.push (cols[i]);
408 for (int i = 0; i < cols.size () - 1; i++)
410 Spring_smob *spring = 0;
412 for (SCM s = cols[i]->get_property ("ideal-distances");
413 !spring && scm_is_pair (s);
416 Spring_smob *sp = unsmob_spring (scm_car (s));
419 if (sp->other_ == cols[i+1])
424 programming_error (_f ("No spring between column %d and next one",
425 Paper_column::get_rank (cols[i])
428 Real ideal = (spring) ? spring->distance_ : spacer_->default_space_;
429 Real hooke = (spring) ? spring->strength_ : 1.0;
431 spacer_->add_spring (ideal, hooke);
434 for (int i = 0; i < cols.size () - 1; i++)
436 for (SCM s = Spaceable_grob::get_minimum_distances (cols[i]);
437 scm_is_pair (s); s = scm_cdr (s))
439 Grob * other = unsmob_grob (scm_caar (s));
440 int oi = binsearch_links (cols, other, &compare_paper_column_rank);
443 spacer_->add_rod (i, oi, scm_to_double (scm_cdar (s)));
448 && !to_boolean (cols[i]->get_property ("allow-outside-line")))
450 Interval e = cols[i]->extent (cols[i], X_AXIS);
453 spacer_->add_rod (i, cols.size()-1, e[RIGHT]);
454 spacer_->add_rod (0, i, e[LEFT]);
460 Simple_spacer_wrapper::Simple_spacer_wrapper ()
462 spacer_ = new Simple_spacer ();
465 Simple_spacer_wrapper::~Simple_spacer_wrapper ()
471 Simple_spacer_wrapper::Simple_spacer_wrapper (Simple_spacer_wrapper const&)