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 Real compound_fixed = 0.0;
374 Real compound_space = 0.0;
375 Real max_fixed = 0.0;
376 Real max_space = 0.0;
380 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
382 if (dt == Moment (0, 0))
384 extract_grob_set (l, "spacing-wishes", wishes);
386 for (vsize i = 0; i < wishes.size (); i++)
388 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
390 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
394 column for the left one settings should be ok due automatic
397 assert (spacing_grob->get_column () == l);
399 Spring sp = Staff_spacing::get_spacing (spacing_grob);
400 Real space = sp.distance ();
401 Real fixed = sp.min_distance ();
403 if (Paper_column::when_mom (r).grace_part_)
406 Correct for grace notes.
408 Ugh. The 0.8 is arbitrary.
413 max_space = max (max_space, space);
414 max_fixed = max (max_fixed, fixed);
416 compound_space += space;
417 compound_fixed += fixed;
422 if (compound_space <= 0.0 || !wish_count)
424 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
430 if (to_boolean (me->get_property ("average-spacing-wishes")))
432 compound_space /= wish_count;
433 compound_fixed /= wish_count;
437 compound_fixed = max_fixed;
438 compound_space = max_space;
443 if (Paper_column::is_musical (r)
444 && l->break_status_dir () == CENTER
445 && fills_measure (me, l, r))
447 compound_space += 1.0;
450 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
451 compound_fixed = 0.0;
453 assert (!isinf (compound_space));
454 compound_space = max (compound_space, compound_fixed);
456 Real inverse_strength = (compound_space - compound_fixed);
457 Real distance = compound_space;
459 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
462 ADD_INTERFACE (Spacing_spanner,
463 "The space taken by a note is dependent on its duration. Doubling a\n"
464 "duration adds spacing-increment to the space. The most common shortest\n"
465 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
466 "spaced proportonial to their duration.\n"
468 "Typically, the increment is the width of a black note head. In a\n"
469 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
470 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
471 "head width) A 16th note is followed by 0.5 note head width. The\n"
472 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
475 "average-spacing-wishes "
476 "base-shortest-duration "
477 "common-shortest-duration "
479 "shortest-duration-space "
481 "strict-grace-spacing "
482 "strict-note-spacing "
483 "uniform-stretching "