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"
15 #include "international.hh"
18 #include "note-spacing.hh"
19 #include "output-def.hh"
20 #include "paper-column.hh"
21 #include "paper-score.hh"
22 #include "pointer-group-interface.hh"
23 #include "spaceable-grob.hh"
24 #include "spacing-interface.hh"
25 #include "staff-spacing.hh"
30 Spacing_spanner::get_columns (Spanner *me)
32 vector<Grob*> all (get_root_system (me)->columns ());
33 vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
34 &Paper_column::compare);
35 vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
36 &Paper_column::compare);
38 all = vector<Grob*>::vector<Grob*> (all.begin () + start,
39 all.begin () + end + 1);
43 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
45 Spacing_spanner::set_springs (SCM smob)
47 Spanner *me = unsmob_spanner (smob);
50 can't use get_system() ? --hwn.
52 vector<Grob*> all (get_columns (me));
53 set_explicit_neighbor_columns (all);
55 Spacing_options options;
56 options.init_from_grob (me);
57 options.global_shortest_ = robust_scm2moment (me->get_property ("common-shortest-duration"),
58 Moment (1,8)).main_part_;
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 < cols.size()-1) ? 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];
275 extract_grob_set (left_col, "right-neighbors", neighbors);
278 We adjust the space following a note only if the next note
279 happens after the current note (this is set in the grob
280 property SPACING-SEQUENCE.
282 for (vsize i = 0; i < neighbors.size (); i++)
284 Grob *wish = neighbors[i];
286 Item *wish_rcol = Note_spacing::right_column (wish);
287 if (Note_spacing::left_column (wish) != left_col
288 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
292 This is probably a waste of time in the case of polyphonic
294 if (Note_spacing::has_interface (wish))
299 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
302 max_space = max (max_space, space);
303 max_fixed = max (max_fixed, fixed);
305 compound_note_space += space;
306 compound_fixed_note_space += fixed;
311 if (Paper_column::when_mom (right_col).grace_part_
312 && !Paper_column::when_mom (left_col).grace_part_)
315 Ugh. 0.8 is arbitrary.
317 compound_note_space *= 0.8;
320 if (compound_note_space < 0 || wish_count == 0)
323 Fixed should be 0.0. If there are no spacing wishes, we're
324 likely dealing with polyphonic spacing of hemiolas.
326 We used to have compound_fixed_note_space = options->increment_
328 but this can lead to numeric instability problems when we
331 inverse_strength = (compound_note_space - compound_fixed_note_space)
335 compound_note_space = base_note_space;
336 compound_fixed_note_space = 0.0;
338 else if (to_boolean (me->get_property ("average-spacing-wishes")))
340 compound_note_space /= wish_count;
341 compound_fixed_note_space /= wish_count;
345 compound_fixed_note_space = max_fixed;
346 compound_note_space = max_space;
350 Whatever we do, the fixed space is smaller than the real
353 TODO: this criterion is discontinuous in the derivative.
354 Maybe it should be continuous?
356 compound_fixed_note_space = min (compound_fixed_note_space,
357 compound_note_space);
360 Real inverse_strength = 1.0;
364 TODO: make sure that the space doesn't exceed the right margin.
366 if (options->packed_)
369 In packed mode, pack notes as tight as possible. This makes
370 sense mostly in combination with raggedright mode: the notes
371 are then printed at minimum distance. This is mostly useful
372 for ancient notation, but may also be useful for some flavours
373 of contemporary music. If not in raggedright mode, lily will
374 pack as much bars of music as possible into a line, but the
375 line will then be stretched to fill the whole linewidth.
377 inverse_strength = 1.0;
378 distance = compound_fixed_note_space;
382 inverse_strength = (compound_note_space - compound_fixed_note_space);
383 distance = compound_note_space;
386 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
390 Read hints from L and generate springs.
393 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
394 Spacing_options const *options)
396 Real compound_fixed = 0.0;
397 Real compound_space = 0.0;
398 Real max_fixed = 0.0;
399 Real max_space = 0.0;
403 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
405 if (dt == Moment (0, 0))
407 extract_grob_set (l, "spacing-wishes", wishes);
409 for (vsize i = 0; i < wishes.size (); i++)
411 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
413 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
420 column for the left one settings should be ok due automatic
424 assert (spacing_grob->get_column () == l);
426 Staff_spacing::get_spacing_params (spacing_grob,
427 &space, &fixed_space);
429 if (Paper_column::when_mom (r).grace_part_)
432 Correct for grace notes.
434 Ugh. The 0.8 is arbitrary.
439 max_space = max (max_space, space);
440 max_fixed = max (max_fixed, fixed_space);
442 compound_space += space;
443 compound_fixed += fixed_space;
448 if (compound_space <= 0.0 || !wish_count)
450 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
456 if (to_boolean (me->get_property ("average-spacing-wishes")))
458 compound_space /= wish_count;
459 compound_fixed /= wish_count;
463 compound_fixed = max_fixed;
464 compound_space = max_space;
469 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
470 compound_fixed = 0.0;
472 assert (!isinf (compound_space));
473 compound_space = max (compound_space, compound_fixed);
476 There used to be code that changed spacing depending on
477 raggedright setting. Ugh.
479 Do it more cleanly, or rename the property.
482 Real inverse_strength = (compound_space - compound_fixed);
483 Real distance = compound_space;
484 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
487 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
488 "The space taken by a note is dependent on its duration. Doubling a\n"
489 "duration adds spacing-increment to the space. The most common shortest\n"
490 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
491 "spaced proportonial to their duration.\n"
493 "Typically, the increment is the width of a black note head. In a\n"
494 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
495 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
496 "head width) A 16th note is followed by 0.5 note head width. The\n"
497 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
500 "average-spacing-wishes "
501 "base-shortest-duration "
502 "common-shortest-duration "
503 "grace-space-factor "
505 "shortest-duration-space "
507 "strict-note-spacing "
508 "uniform-stretching "
512 ADD_INTERFACE (Spacing_interface, "spacing-interface",
513 "Something to do with line breaking and spacing. "
514 "Kill this one after determining line breaks.",