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);
243 Real compound_note_space = 0.0;
244 Real compound_fixed_note_space = 0.0;
246 if (options->stretch_uniformly_)
248 compound_note_space = base_note_space;
250 if (!Paper_column::is_musical (right_col))
253 Crude fix for notes that lead up to barlines and time sigs.
255 Interval lext = right_col->extent (right_col, X_AXIS);
256 if (!lext.is_empty ())
257 compound_note_space += -lext[LEFT];
264 extract_grob_set (left_col, "right-neighbors", neighbors);
267 We adjust the space following a note only if the next note
268 happens after the current note (this is set in the grob
269 property SPACING-SEQUENCE.
271 for (vsize i = 0; i < neighbors.size (); i++)
273 Grob *wish = neighbors[i];
275 Item *wish_rcol = Note_spacing::right_column (wish);
276 if (Note_spacing::left_column (wish) != left_col
277 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
281 This is probably a waste of time in the case of polyphonic
283 if (Note_spacing::has_interface (wish))
288 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
291 max_space = max (max_space, space);
292 max_fixed = max (max_fixed, fixed);
294 compound_note_space += space;
295 compound_fixed_note_space += fixed;
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.
306 compound_note_space *= 0.8;
309 if (compound_note_space < 0 || wish_count == 0)
312 if (!Paper_column::is_musical (right_col))
315 There used to be code that examined left_col->extent
316 (X_AXIS), but this is resulted in unexpected wide
317 spacing, because the width of s^"text" output is also
318 taken into account here.
320 compound_fixed_note_space = options->increment_;
321 compound_note_space = max (base_note_space,
322 options->increment_);
327 Fixed should be 0.0. If there are no spacing wishes, we're
328 likely dealing with polyphonic spacing of hemiolas.
330 We used to have compound_fixed_note_space = options->increment_
332 but this can lead to numeric instability problems when we
335 inverse_strength = (compound_note_space - compound_fixed_note_space)
339 compound_note_space = base_note_space;
340 compound_fixed_note_space = 0.0;
343 else if (to_boolean (me->get_property ("average-spacing-wishes")))
345 compound_note_space /= wish_count;
346 compound_fixed_note_space /= wish_count;
350 compound_fixed_note_space = max_fixed;
351 compound_note_space = max_space;
355 Whatever we do, the fixed space is smaller than the real
358 TODO: this criterion is discontinuous in the derivative.
359 Maybe it should be continuous?
361 compound_fixed_note_space = min (compound_fixed_note_space,
362 compound_note_space);
365 Real inverse_strength = 1.0;
369 TODO: make sure that the space doesn't exceed the right margin.
371 if (options->packed_)
374 In packed mode, pack notes as tight as possible. This makes
375 sense mostly in combination with raggedright mode: the notes
376 are then printed at minimum distance. This is mostly useful
377 for ancient notation, but may also be useful for some flavours
378 of contemporary music. If not in raggedright mode, lily will
379 pack as much bars of music as possible into a line, but the
380 line will then be stretched to fill the whole linewidth.
382 inverse_strength = 1.0;
383 distance = compound_fixed_note_space;
387 inverse_strength = (compound_note_space - compound_fixed_note_space);
388 distance = compound_note_space;
391 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
395 Check if COL fills the whole measure.
398 Spacing_spanner::fills_measure (Grob *me, Item *left, Item *col)
400 System *sys = get_root_system (me);
401 Item *next = sys->column (col->get_column ()->get_rank () + 1);
405 if (Paper_column::is_musical (next)
406 || Paper_column::is_musical (left)
407 || !Paper_column::is_musical (col)
408 || !Paper_column::is_used (next))
412 Paper_column::when_mom (next) - Paper_column::when_mom (col);
414 Moment *len = unsmob_moment (left->get_property ("measure-length"));
419 Don't check for exact measure length, since ending measures are
420 often shortened due to pickups.
422 if (dt.main_part_ > len->main_part_ / Rational (2)
423 && (next->is_broken ()
424 || next->break_status_dir ()))
431 Read hints from L and generate springs.
434 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
435 Spacing_options const *options)
437 Real compound_fixed = 0.0;
438 Real compound_space = 0.0;
439 Real max_fixed = 0.0;
440 Real max_space = 0.0;
444 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
446 if (dt == Moment (0, 0))
448 extract_grob_set (l, "spacing-wishes", wishes);
450 for (vsize i = 0; i < wishes.size (); i++)
452 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
454 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
458 column for the left one settings should be ok due automatic
461 assert (spacing_grob->get_column () == l);
463 Spring sp = Staff_spacing::get_spacing_params (spacing_grob);
464 Real space = sp.distance_;
465 Real fixed = sp.distance_ - sp.inverse_compress_strength_;
467 if (Paper_column::when_mom (r).grace_part_)
470 Correct for grace notes.
472 Ugh. The 0.8 is arbitrary.
477 max_space = max (max_space, space);
478 max_fixed = max (max_fixed, fixed);
480 compound_space += space;
481 compound_fixed += fixed;
486 if (compound_space <= 0.0 || !wish_count)
488 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
494 if (to_boolean (me->get_property ("average-spacing-wishes")))
496 compound_space /= wish_count;
497 compound_fixed /= wish_count;
501 compound_fixed = max_fixed;
502 compound_space = max_space;
507 if (Paper_column::is_musical (r)
508 && l->break_status_dir () == CENTER
509 && fills_measure (me, l, r))
511 compound_space += 1.0;
514 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
515 compound_fixed = 0.0;
517 assert (!isinf (compound_space));
518 compound_space = max (compound_space, compound_fixed);
520 Real inverse_strength = (compound_space - compound_fixed);
521 Real distance = compound_space;
522 message (_f ("adding spring of length %f", distance));
523 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
526 ADD_INTERFACE (Spacing_spanner,
527 "The space taken by a note is dependent on its duration. Doubling a\n"
528 "duration adds spacing-increment to the space. The most common shortest\n"
529 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
530 "spaced proportonial to their duration.\n"
532 "Typically, the increment is the width of a black note head. In a\n"
533 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
534 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
535 "head width) A 16th note is followed by 0.5 note head width. The\n"
536 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
539 "average-spacing-wishes "
540 "base-shortest-duration "
541 "common-shortest-duration "
543 "shortest-duration-space "
545 "strict-grace-spacing "
546 "strict-note-spacing "
547 "uniform-stretching "