2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "spacing-spanner.hh"
16 #include "spacing-options.hh"
17 #include "international.hh"
20 #include "note-spacing.hh"
21 #include "output-def.hh"
22 #include "paper-column.hh"
23 #include "paper-score.hh"
24 #include "pointer-group-interface.hh"
25 #include "spaceable-grob.hh"
26 #include "spacing-interface.hh"
27 #include "staff-spacing.hh"
32 Spacing_spanner::get_columns (Grob *me_grob)
34 Spanner *me = dynamic_cast<Spanner*> (me_grob);
35 vector<Grob*> all (get_root_system (me)->used_columns ());
36 vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
37 &Paper_column::less_than);
38 vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
39 &Paper_column::less_than);
41 all = vector<Grob*>::vector<Grob*> (all.begin () + start,
42 all.begin () + end + 1);
46 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
48 Spacing_spanner::set_springs (SCM smob)
50 Spanner *me = unsmob_spanner (smob);
53 can't use get_system () ? --hwn.
55 Spacing_options options;
56 options.init_from_grob (me);
57 vector<Grob*> cols = Spacing_spanner::get_columns (me);
58 set_explicit_neighbor_columns (cols);
60 prune_loose_columns (me, &cols, &options);
61 set_implicit_neighbor_columns (cols);
62 generate_springs (me, cols, &options);
64 return SCM_UNSPECIFIED;
68 We want the shortest note that is also "common" in the piece, so we
69 find the shortest in each measure, and take the most frequently
72 This probably gives weird effects with modern music, where every
73 note has a different duration, but hey, don't write that kind of
77 MAKE_SCHEME_CALLBACK (Spacing_spanner, calc_common_shortest_duration, 1);
79 Spacing_spanner::calc_common_shortest_duration (SCM grob)
81 Spanner *me = unsmob_spanner (grob);
83 vector<Grob*> cols (get_columns (me));
88 vector<Rational> durations;
91 Rational shortest_in_measure;
92 shortest_in_measure.set_infinite (1);
94 for (vsize i = 0; i < cols.size (); i++)
96 if (Paper_column::is_musical (cols[i]))
98 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
101 ignore grace notes for shortest notes.
103 if (when && when->grace_part_)
106 SCM st = cols[i]->get_property ("shortest-starter-duration");
107 Moment this_shortest = *unsmob_moment (st);
108 assert (this_shortest.to_bool ());
109 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
111 else if (!shortest_in_measure.is_infinity ()
112 && Paper_column::is_breakable (cols[i]))
115 for (; j < durations.size (); j++)
117 if (durations[j] > shortest_in_measure)
119 counts.insert (counts.begin () + j, 1);
120 durations.insert (durations.begin () + j, shortest_in_measure);
123 else if (durations[j] == shortest_in_measure)
130 if (durations.size () == j)
132 durations.push_back (shortest_in_measure);
133 counts.push_back (1);
136 shortest_in_measure.set_infinite (1);
142 for (vsize i = durations.size (); i--;)
144 if (counts[i] >= max_count)
147 max_count = counts[i];
151 SCM bsd = me->get_property ("base-shortest-duration");
152 Rational d = Rational (1, 8);
153 if (Moment *m = unsmob_moment (bsd))
157 d = min (d, durations[max_idx]);
159 return Moment (d).smobbed_copy ();
163 Spacing_spanner::generate_pair_spacing (Grob *me,
164 Paper_column *left_col, Paper_column *right_col,
165 Paper_column *after_right_col,
166 Spacing_options const *options)
168 if (Paper_column::is_musical (left_col))
170 if (!Paper_column::is_musical (right_col)
171 && options->float_nonmusical_columns_
173 && Paper_column::is_musical (after_right_col))
176 TODO: should generate rods to prevent collisions.
178 musical_column_spacing (me, left_col, after_right_col, options);
179 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
180 after_right_col->self_scm ()));
183 musical_column_spacing (me, left_col, right_col, options);
185 if (Item *rb = right_col->find_prebroken_piece (LEFT))
186 musical_column_spacing (me, left_col, rb, options);
191 The case that the right part is broken as well is rather
192 rare, but it is possible, eg. with a single empty measure,
193 or if one staff finishes a tad earlier than the rest.
195 Item *lb = left_col->find_prebroken_piece (RIGHT);
196 Item *rb = right_col->find_prebroken_piece (LEFT);
198 if (left_col && right_col)
199 breakable_column_spacing (me, left_col, right_col, options);
202 breakable_column_spacing (me, lb, right_col, options);
205 breakable_column_spacing (me, left_col, rb, options);
208 breakable_column_spacing (me, lb, rb, options);
213 Spacing_spanner::generate_springs (Grob *me,
214 vector<Grob*> const &cols,
215 Spacing_options const *options)
217 Paper_column *prev = 0;
218 for (vsize i = 0; i < cols.size (); i++)
220 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
221 Paper_column *next = (i + 1 < cols.size ()) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
224 generate_pair_spacing (me, prev, col, next, options);
231 Generate the space between two musical columns LEFT_COL and RIGHT_COL.
234 Spacing_spanner::musical_column_spacing (Grob *me,
237 Spacing_options const *options)
239 Real base_note_space = note_spacing (me, left_col, right_col, options);
242 if (options->stretch_uniformly_)
243 spring = Spring (base_note_space, 0.0);
246 vector<Spring> springs;
247 extract_grob_set (left_col, "right-neighbors", neighbors);
249 for (vsize i = 0; i < neighbors.size (); i++)
251 Grob *wish = neighbors[i];
253 Item *wish_rcol = Spacing_interface::right_column (wish);
254 if (Spacing_interface::left_column (wish) != left_col
255 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
259 This is probably a waste of time in the case of polyphonic
261 if (Note_spacing::has_interface (wish))
262 springs.push_back (Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_));
265 if (springs.empty ())
268 if (!Paper_column::is_musical (right_col))
271 There used to be code that examined left_col->extent
272 (X_AXIS), but this is resulted in unexpected wide
273 spacing, because the width of s^"text" output is also
274 taken into account here.
276 spring = Spring (max (base_note_space, options->increment_),
277 options->increment_);
282 Fixed should be 0.0. If there are no spacing wishes, we're
283 likely dealing with polyphonic spacing of hemiolas.
285 We used to have min_distance_ = options->increment_
287 but this can lead to numeric instability problems when we
290 inverse_strength = (distance_ - min_distance_)
293 spring = Spring (base_note_space, 0.0);
297 spring = merge_springs (springs);
300 if (Paper_column::when_mom (right_col).grace_part_
301 && !Paper_column::when_mom (left_col).grace_part_)
304 Ugh. 0.8 is arbitrary.
310 TODO: make sure that the space doesn't exceed the right margin.
312 if (options->packed_)
315 In packed mode, pack notes as tight as possible. This makes
316 sense mostly in combination with raggedright mode: the notes
317 are then printed at minimum distance. This is mostly useful
318 for ancient notation, but may also be useful for some flavours
319 of contemporary music. If not in raggedright mode, lily will
320 pack as much bars of music as possible into a line, but the
321 line will then be stretched to fill the whole linewidth.
323 spring.set_distance (spring.min_distance ());
324 spring.set_inverse_stretch_strength (1.0);
327 Spaceable_grob::add_spring (left_col, right_col, spring);
331 Check if COL fills the whole measure.
334 Spacing_spanner::fills_measure (Grob *me, Item *left, Item *col)
336 System *sys = get_root_system (me);
337 Item *next = sys->column (col->get_column ()->get_rank () + 1);
341 if (Paper_column::is_musical (next)
342 || Paper_column::is_musical (left)
343 || !Paper_column::is_musical (col)
344 || !Paper_column::is_used (next))
348 Paper_column::when_mom (next) - Paper_column::when_mom (col);
350 Moment *len = unsmob_moment (left->get_property ("measure-length"));
355 Don't check for exact measure length, since ending measures are
356 often shortened due to pickups.
358 if (dt.main_part_ > len->main_part_ / Rational (2)
359 && (next->is_broken ()
360 || next->break_status_dir ()))
367 Read hints from L and generate springs.
370 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
371 Spacing_options const *options)
373 vector<Spring> springs;
376 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
378 if (dt == Moment (0, 0))
380 extract_grob_set (l, "spacing-wishes", wishes);
382 for (vsize i = 0; i < wishes.size (); i++)
384 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
386 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
390 column for the left one settings should be ok due automatic
393 assert (spacing_grob->get_column () == l);
395 springs.push_back (Staff_spacing::get_spacing (spacing_grob));
399 if (springs.empty ())
400 spring = standard_breakable_column_spacing (me, l, r, options);
402 spring = merge_springs (springs);
404 if (Paper_column::when_mom (r).grace_part_)
407 Correct for grace notes.
409 Ugh. The 0.8 is arbitrary.
414 if (Paper_column::is_musical (r)
415 && l->break_status_dir () == CENTER
416 && fills_measure (me, l, r))
418 spring.set_distance (spring.distance () + 1.0);
419 spring.set_default_strength ();
422 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
424 spring.set_min_distance (0.0);
425 spring.set_default_strength ();
428 Spaceable_grob::add_spring (l, r, spring);
431 ADD_INTERFACE (Spacing_spanner,
432 "The space taken by a note is dependent on its duration. Doubling a\n"
433 "duration adds spacing-increment to the space. The most common shortest\n"
434 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
435 "spaced proportonial to their duration.\n"
437 "Typically, the increment is the width of a black note head. In a\n"
438 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
439 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
440 "head width) A 16th note is followed by 0.5 note head width. The\n"
441 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
444 "average-spacing-wishes "
445 "base-shortest-duration "
446 "common-shortest-duration "
448 "shortest-duration-space "
450 "strict-grace-spacing "
451 "strict-note-spacing "
452 "uniform-stretching "