2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2006 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 (Spanner *me)
34 vector<Grob*> all (get_root_system (me)->columns ());
35 vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
36 &Paper_column::less_than);
37 vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
38 &Paper_column::less_than);
40 all = vector<Grob*>::vector<Grob*> (all.begin () + start,
41 all.begin () + end + 1);
45 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
47 Spacing_spanner::set_springs (SCM smob)
49 Spanner *me = unsmob_spanner (smob);
52 can't use get_system() ? --hwn.
54 vector<Grob*> all (get_columns (me));
55 set_explicit_neighbor_columns (all);
57 Spacing_options options;
58 options.init_from_grob (me);
60 prune_loose_columns (me, &all, &options);
61 set_implicit_neighbor_columns (all);
62 generate_springs (me, all, &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];
150 // printf ("duration %d/%d, count %d\n",
151 // durations[i].num (), durations[i].den (), counts[i]);
154 SCM bsd = me->get_property ("base-shortest-duration");
155 Rational d = Rational (1, 8);
156 if (Moment *m = unsmob_moment (bsd))
160 d = min (d, durations[max_idx]);
162 return Moment (d).smobbed_copy ();
166 Spacing_spanner::generate_pair_spacing (Grob *me,
167 Paper_column *left_col, Paper_column *right_col,
168 Paper_column *after_right_col,
169 Spacing_options const *options)
171 if (Paper_column::is_musical (left_col))
173 bool skip_unbroken_right = false;
175 if (!Paper_column::is_musical (right_col)
176 && options->float_nonmusical_columns_
178 && Paper_column::is_musical (after_right_col))
179 skip_unbroken_right = true;
181 if (skip_unbroken_right)
184 TODO: should generate rods to prevent collisions.
186 musical_column_spacing (me, left_col, after_right_col, options);
187 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
188 after_right_col->self_scm ()));
191 musical_column_spacing (me, left_col, right_col, options);
193 if (Item *rb = right_col->find_prebroken_piece (LEFT))
194 musical_column_spacing (me, left_col, rb, options);
199 The case that the right part is broken as well is rather
200 rare, but it is possible, eg. with a single empty measure,
201 or if one staff finishes a tad earlier than the rest.
203 Item *lb = left_col->find_prebroken_piece (RIGHT);
204 Item *rb = right_col->find_prebroken_piece (LEFT);
206 if (left_col && right_col)
207 breakable_column_spacing (me, left_col, right_col, options);
210 breakable_column_spacing (me, lb, right_col, options);
213 breakable_column_spacing (me, left_col, rb, options);
216 breakable_column_spacing (me, lb, rb, options);
221 Spacing_spanner::generate_springs (Grob *me,
222 vector<Grob*> const &cols,
223 Spacing_options const *options)
225 Paper_column *prev = 0;
226 for (vsize i = 0; i < cols.size (); i++)
228 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
229 Paper_column *next = (i + 1 < cols.size ()) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
232 generate_pair_spacing (me, prev, col, next, options);
239 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
240 spacing parameters INCR and SHORTEST.
243 Spacing_spanner::musical_column_spacing (Grob *me,
246 Spacing_options const *options)
248 bool expand_only = false;
249 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
253 Real compound_note_space = 0.0;
254 Real compound_fixed_note_space = 0.0;
256 if (options->stretch_uniformly_)
258 compound_note_space = base_note_space;
260 if (!Paper_column::is_musical (right_col))
263 Crude fix for notes that lead up to barlines and time sigs.
265 Interval lext = right_col->extent (right_col, X_AXIS);
266 if (!lext.is_empty ())
267 compound_note_space += -lext[LEFT];
274 extract_grob_set (left_col, "right-neighbors", neighbors);
277 We adjust the space following a note only if the next note
278 happens after the current note (this is set in the grob
279 property SPACING-SEQUENCE.
281 for (vsize i = 0; i < neighbors.size (); i++)
283 Grob *wish = neighbors[i];
285 Item *wish_rcol = Note_spacing::right_column (wish);
286 if (Note_spacing::left_column (wish) != left_col
287 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
291 This is probably a waste of time in the case of polyphonic
293 if (Note_spacing::has_interface (wish))
298 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
301 max_space = max (max_space, space);
302 max_fixed = max (max_fixed, fixed);
304 compound_note_space += space;
305 compound_fixed_note_space += fixed;
310 if (Paper_column::when_mom (right_col).grace_part_
311 && !Paper_column::when_mom (left_col).grace_part_)
314 Ugh. 0.8 is arbitrary.
316 compound_note_space *= 0.8;
319 if (compound_note_space < 0 || wish_count == 0)
322 if (!Paper_column::is_musical (right_col))
325 reconsider this: breaks with wide marks/tempos/etc.
327 Real left_col_stick_out = robust_relative_extent (left_col, left_col, X_AXIS)[RIGHT];
328 compound_fixed_note_space = max (left_col_stick_out, options->increment_);
330 compound_note_space = max (base_note_space,
331 base_note_space - options->increment_ + left_col_stick_out);
336 Fixed should be 0.0. If there are no spacing wishes, we're
337 likely dealing with polyphonic spacing of hemiolas.
339 We used to have compound_fixed_note_space = options->increment_
341 but this can lead to numeric instability problems when we
344 inverse_strength = (compound_note_space - compound_fixed_note_space)
348 compound_note_space = base_note_space;
349 compound_fixed_note_space = 0.0;
352 else if (to_boolean (me->get_property ("average-spacing-wishes")))
354 compound_note_space /= wish_count;
355 compound_fixed_note_space /= wish_count;
359 compound_fixed_note_space = max_fixed;
360 compound_note_space = max_space;
364 Whatever we do, the fixed space is smaller than the real
367 TODO: this criterion is discontinuous in the derivative.
368 Maybe it should be continuous?
370 compound_fixed_note_space = min (compound_fixed_note_space,
371 compound_note_space);
374 Real inverse_strength = 1.0;
378 TODO: make sure that the space doesn't exceed the right margin.
380 if (options->packed_)
383 In packed mode, pack notes as tight as possible. This makes
384 sense mostly in combination with raggedright mode: the notes
385 are then printed at minimum distance. This is mostly useful
386 for ancient notation, but may also be useful for some flavours
387 of contemporary music. If not in raggedright mode, lily will
388 pack as much bars of music as possible into a line, but the
389 line will then be stretched to fill the whole linewidth.
391 inverse_strength = 1.0;
392 distance = compound_fixed_note_space;
396 inverse_strength = (compound_note_space - compound_fixed_note_space);
397 distance = compound_note_space;
400 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
405 Read hints from L and generate springs.
408 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
409 Spacing_options const *options)
411 Real compound_fixed = 0.0;
412 Real compound_space = 0.0;
413 Real max_fixed = 0.0;
414 Real max_space = 0.0;
418 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
420 if (dt == Moment (0, 0))
422 extract_grob_set (l, "spacing-wishes", wishes);
424 for (vsize i = 0; i < wishes.size (); i++)
426 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
428 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
435 column for the left one settings should be ok due automatic
439 assert (spacing_grob->get_column () == l);
441 Staff_spacing::get_spacing_params (spacing_grob,
442 &space, &fixed_space);
444 if (Paper_column::when_mom (r).grace_part_)
447 Correct for grace notes.
449 Ugh. The 0.8 is arbitrary.
454 max_space = max (max_space, space);
455 max_fixed = max (max_fixed, fixed_space);
457 compound_space += space;
458 compound_fixed += fixed_space;
463 if (compound_space <= 0.0 || !wish_count)
465 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
471 if (to_boolean (me->get_property ("average-spacing-wishes")))
473 compound_space /= wish_count;
474 compound_fixed /= wish_count;
478 compound_fixed = max_fixed;
479 compound_space = max_space;
484 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
485 compound_fixed = 0.0;
487 assert (!isinf (compound_space));
488 compound_space = max (compound_space, compound_fixed);
491 There used to be code that changed spacing depending on
492 raggedright setting. Ugh.
494 Do it more cleanly, or rename the property.
497 Real inverse_strength = (compound_space - compound_fixed);
498 Real distance = compound_space;
499 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
502 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
503 "The space taken by a note is dependent on its duration. Doubling a\n"
504 "duration adds spacing-increment to the space. The most common shortest\n"
505 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
506 "spaced proportonial to their duration.\n"
508 "Typically, the increment is the width of a black note head. In a\n"
509 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
510 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
511 "head width) A 16th note is followed by 0.5 note head width. The\n"
512 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
515 "average-spacing-wishes "
516 "base-shortest-duration "
517 "common-shortest-duration "
519 "shortest-duration-space "
521 "strict-grace-spacing "
522 "strict-note-spacing "
523 "uniform-stretching "
527 ADD_INTERFACE (Spacing_interface, "spacing-interface",
528 "Something to do with line breaking and spacing. "
529 "Kill this one after determining line breaks.",