2 simple-spacer.cc -- implement Simple_spacer
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 - add support for different stretch/shrink constants?
14 #include <libc-extension.hh> // isinf
16 #include "simple-spacer.hh"
17 #include "paper-column.hh"
21 #include "column-x-positions.hh"
22 #include "spaceable-grob.hh"
23 #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. */
59 Simple_spacer::Simple_spacer ()
62 Give an extra penalty for compression. Needed to avoid compressing
65 compression_penalty_b_ = false;
69 default_space_f_ = 20 PT;
73 Simple_spacer::add_rod (int l, int r, Real dist)
75 if (isinf (dist) || isnan (dist))
77 programming_error ("Weird minimum distance. Ignoring");
82 Real c = range_stiffness (l,r);
83 Real d = range_ideal_len (l,r);
84 Real block_stretch = dist - d;
86 Real block_force = c * block_stretch;
87 force_f_ = force_f_ >? block_force;
89 for (int i=l; i < r; i++)
90 springs_[i].block_force_f_ = block_force >?
91 springs_[i].block_force_f_ ;
95 Simple_spacer::range_ideal_len (int l, int r) const
98 for (int i=l; i < r; i++)
99 d += springs_[i].ideal_f_;
104 Simple_spacer::range_stiffness (int l, int r) const
107 for (int i=l; i < r; i++)
109 if (springs_[i].active_b_)
110 den += 1 / springs_[i].hooke_f_;
117 Simple_spacer::active_blocking_force () const
119 Real bf = - infinity_f;
120 for (int i=0; i < springs_.size (); i++)
121 if (springs_[i].active_b_)
123 bf = bf >? springs_[i].block_force_f_;
129 Simple_spacer::active_springs_stiffness () const
132 for (int i=0; i < springs_.size (); i++)
133 if (springs_[i].active_b_)
135 den += 1 / springs_[i].hooke_f_;
141 Simple_spacer::set_active_states ()
143 /* float comparison is safe, since force is only copied. */
144 for (int i=0 ; i <springs_.size (); i++)
145 if (springs_[i].active_b_
146 && springs_[i].block_force_f_ >= force_f_)
148 springs_[i].active_b_ = false;
154 Simple_spacer::configuration_length () const
157 for (int i=0; i < springs_.size (); i++)
158 l += springs_[i].length (force_f_);
164 Simple_spacer::active_b () const
166 return active_count_;
170 Simple_spacer::my_solve_linelen ()
174 force_f_ = active_blocking_force ();
175 Real conf = configuration_length ();
177 if (conf < line_len_f_)
179 force_f_ += (line_len_f_ - conf) * active_springs_stiffness ();
183 set_active_states ();
189 Simple_spacer::my_solve_natural_len ()
193 force_f_ = active_blocking_force () >? 0.0;
195 if (force_f_ < 1e-8) // ugh.,
198 set_active_states ();
203 Simple_spacer::add_columns (Link_array<Grob> cols)
205 for (int i = cols.size (); i--;)
206 if (gh_pair_p (cols[i]->get_grob_property ("between-cols")))
208 loose_cols_.push (cols[i]);
213 for (int i=0; i < cols.size () - 1; i++)
215 Spring_smob *spring = 0;
217 for (SCM s = cols[i]->get_grob_property ("ideal-distances");
218 !spring && gh_pair_p (s);
221 Spring_smob *sp = unsmob_spring (ly_car (s));
224 if (sp->other_ == cols[i+1])
228 Spring_description desc;
231 desc.ideal_f_ = spring->distance_f_;
232 desc.hooke_f_ = spring->strength_f_;
236 programming_error (_f("No spring between column %d and next one",
237 Paper_column::rank_i (cols[i])
240 desc.ideal_f_ = default_space_f_;
247 programming_error ("Insane spring found. Setting to unit spring.");
249 cout << "columns " << Paper_column::rank_i (cols[i])
250 << " " << Paper_column::rank_i (cols[i+1]) << endl;
255 if (isinf (desc.hooke_f_))
257 desc.active_b_ = false;
258 springs_.push (desc);
262 desc.block_force_f_ = - desc.hooke_f_ * desc.ideal_f_; // block at distance 0
263 springs_.push (desc);
268 if (spring->expand_only_b_)
270 compression_penalty_b_ = true;
275 for (int i=0; i < cols.size () - 1; i++)
277 for (SCM s = Spaceable_grob::get_minimum_distances (cols[i]);
278 gh_pair_p (s); s = ly_cdr (s))
280 Grob * other = unsmob_grob (ly_caar (s));
281 int oi = cols.find_i (other);
284 add_rod (i, oi, gh_scm2double (ly_cdar (s)));
290 TODO: should support natural length on only the last line.
293 my_solve_natural_len ();
301 Simple_spacer::solve (Column_x_positions *positions) const
303 positions->force_f_ = force_f_;
304 if (compression_penalty_b_ && (force_f_ < 0))
307 positions->force_f_ *= 2; // hmm.
310 positions->config_.push (indent_f_);
311 for (int i=0; i <springs_.size (); i++)
313 positions->config_.push (positions->config_.top () + springs_[i].length (force_f_));
315 positions->cols_ = spaced_cols_;
316 positions->loose_cols_ = loose_cols_;
318 positions->satisfies_constraints_b_ = (line_len_f_ < 0) || active_b ();
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_grob_property( "penalty");
331 if (gh_scm2double (p) < -9999)
332 break_satisfy = break_satisfy && (i == 0 || i == sz -1);
333 if (gh_scm2double (p) > 9999)
334 break_satisfy = break_satisfy && !(i == 0 || i == sz -1);
339 positions->satisfies_constraints_b_ =
340 positions->satisfies_constraints_b_ && break_satisfy;
343 /****************************************************************/
345 Spring_description::Spring_description ()
350 block_force_f_ = 0.0;
355 Spring_description::sane_b () const
357 return (hooke_f_ > 0) && !isinf (ideal_f_) && !isnan (ideal_f_);
361 Spring_description::length (Real f) const
365 return ideal_f_ + f / hooke_f_ ;