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);
58 prune_loose_columns (me, &all, &options);
59 set_implicit_neighbor_columns (all);
60 generate_springs (me, all, &options);
62 return SCM_UNSPECIFIED;
66 We want the shortest note that is also "common" in the piece, so we
67 find the shortest in each measure, and take the most frequently
70 This probably gives weird effects with modern music, where every
71 note has a different duration, but hey, don't write that kind of
75 MAKE_SCHEME_CALLBACK (Spacing_spanner, calc_common_shortest_duration, 1);
77 Spacing_spanner::calc_common_shortest_duration (SCM grob)
79 Spanner *me = unsmob_spanner (grob);
81 vector<Grob*> cols (get_columns (me));
86 vector<Rational> durations;
89 Rational shortest_in_measure;
90 shortest_in_measure.set_infinite (1);
92 for (vsize i = 0; i < cols.size (); i++)
94 if (Paper_column::is_musical (cols[i]))
96 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
99 ignore grace notes for shortest notes.
101 if (when && when->grace_part_)
104 SCM st = cols[i]->get_property ("shortest-starter-duration");
105 Moment this_shortest = *unsmob_moment (st);
106 assert (this_shortest.to_bool ());
107 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
109 else if (!shortest_in_measure.is_infinity ()
110 && Paper_column::is_breakable (cols[i]))
113 for (; j < durations.size (); j++)
115 if (durations[j] > shortest_in_measure)
117 counts.insert (counts.begin () + j, 1);
118 durations.insert (durations.begin () + j, shortest_in_measure);
121 else if (durations[j] == shortest_in_measure)
128 if (durations.size () == j)
130 durations.push_back (shortest_in_measure);
131 counts.push_back (1);
134 shortest_in_measure.set_infinite (1);
140 for (vsize i = durations.size (); i--;)
142 if (counts[i] >= max_count)
145 max_count = counts[i];
148 // printf ("duration %d/%d, count %d\n",
149 // durations[i].num (), durations[i].den (), counts[i]);
152 SCM bsd = me->get_property ("base-shortest-duration");
153 Rational d = Rational (1, 8);
154 if (Moment *m = unsmob_moment (bsd))
158 d = min (d, durations[max_idx]);
160 return Moment (d).smobbed_copy ();
164 Spacing_spanner::generate_pair_spacing (Grob *me,
165 Paper_column *left_col, Paper_column *right_col,
166 Paper_column *after_right_col,
167 Spacing_options const *options)
169 if (Paper_column::is_musical (left_col))
171 bool skip_unbroken_right = false;
173 if (!Paper_column::is_musical (right_col)
174 && options->float_nonmusical_columns_
176 && Paper_column::is_musical (after_right_col))
177 skip_unbroken_right = true;
179 if (skip_unbroken_right)
182 TODO: should generate rods to prevent collisions.
184 musical_column_spacing (me, left_col, after_right_col, options);
185 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
186 after_right_col->self_scm ()));
189 musical_column_spacing (me, left_col, right_col, options);
191 if (Item *rb = right_col->find_prebroken_piece (LEFT))
192 musical_column_spacing (me, left_col, rb, options);
197 The case that the right part is broken as well is rather
198 rare, but it is possible, eg. with a single empty measure,
199 or if one staff finishes a tad earlier than the rest.
201 Item *lb = left_col->find_prebroken_piece (RIGHT);
202 Item *rb = right_col->find_prebroken_piece (LEFT);
204 if (left_col && right_col)
205 breakable_column_spacing (me, left_col, right_col, options);
208 breakable_column_spacing (me, lb, right_col, options);
211 breakable_column_spacing (me, left_col, rb, options);
214 breakable_column_spacing (me, lb, rb, options);
219 Spacing_spanner::generate_springs (Grob *me,
220 vector<Grob*> const &cols,
221 Spacing_options const *options)
223 Paper_column *prev = 0;
224 for (vsize i = 0; i < cols.size (); i++)
226 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
227 Paper_column *next = (i < cols.size()-1) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
230 generate_pair_spacing (me, prev, col, next, options);
237 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
238 spacing parameters INCR and SHORTEST.
241 Spacing_spanner::musical_column_spacing (Grob *me,
244 Spacing_options const *options)
246 bool expand_only = false;
247 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
251 Real compound_note_space = 0.0;
252 Real compound_fixed_note_space = 0.0;
254 if (options->stretch_uniformly_)
256 compound_note_space = base_note_space;
258 if (!Paper_column::is_musical (right_col))
261 Crude fix for notes that lead up to barlines and time sigs.
263 Interval lext = right_col->extent (right_col, X_AXIS);
264 if (!lext.is_empty ())
265 compound_note_space += -lext[LEFT];
272 extract_grob_set (left_col, "right-neighbors", neighbors);
275 We adjust the space following a note only if the next note
276 happens after the current note (this is set in the grob
277 property SPACING-SEQUENCE.
279 for (vsize i = 0; i < neighbors.size (); i++)
281 Grob *wish = neighbors[i];
283 Item *wish_rcol = Note_spacing::right_column (wish);
284 if (Note_spacing::left_column (wish) != left_col
285 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
289 This is probably a waste of time in the case of polyphonic
291 if (Note_spacing::has_interface (wish))
296 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
299 max_space = max (max_space, space);
300 max_fixed = max (max_fixed, fixed);
302 compound_note_space += space;
303 compound_fixed_note_space += fixed;
308 if (Paper_column::when_mom (right_col).grace_part_
309 && !Paper_column::when_mom (left_col).grace_part_)
312 Ugh. 0.8 is arbitrary.
314 compound_note_space *= 0.8;
317 if (compound_note_space < 0 || wish_count == 0)
320 Fixed should be 0.0. If there are no spacing wishes, we're
321 likely dealing with polyphonic spacing of hemiolas.
323 We used to have compound_fixed_note_space = options->increment_
325 but this can lead to numeric instability problems when we
328 inverse_strength = (compound_note_space - compound_fixed_note_space)
332 compound_note_space = base_note_space;
333 compound_fixed_note_space = 0.0;
335 else if (to_boolean (me->get_property ("average-spacing-wishes")))
337 compound_note_space /= wish_count;
338 compound_fixed_note_space /= wish_count;
342 compound_fixed_note_space = max_fixed;
343 compound_note_space = max_space;
347 Whatever we do, the fixed space is smaller than the real
350 TODO: this criterion is discontinuous in the derivative.
351 Maybe it should be continuous?
353 compound_fixed_note_space = min (compound_fixed_note_space,
354 compound_note_space);
357 Real inverse_strength = 1.0;
361 TODO: make sure that the space doesn't exceed the right margin.
363 if (options->packed_)
366 In packed mode, pack notes as tight as possible. This makes
367 sense mostly in combination with raggedright mode: the notes
368 are then printed at minimum distance. This is mostly useful
369 for ancient notation, but may also be useful for some flavours
370 of contemporary music. If not in raggedright mode, lily will
371 pack as much bars of music as possible into a line, but the
372 line will then be stretched to fill the whole linewidth.
374 inverse_strength = 1.0;
375 distance = compound_fixed_note_space;
379 inverse_strength = (compound_note_space - compound_fixed_note_space);
380 distance = compound_note_space;
383 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
387 Read hints from L and generate springs.
390 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
391 Spacing_options const *options)
393 Real compound_fixed = 0.0;
394 Real compound_space = 0.0;
395 Real max_fixed = 0.0;
396 Real max_space = 0.0;
400 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
402 if (dt == Moment (0, 0))
404 extract_grob_set (l, "spacing-wishes", wishes);
406 for (vsize i = 0; i < wishes.size (); i++)
408 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
410 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
417 column for the left one settings should be ok due automatic
421 assert (spacing_grob->get_column () == l);
423 Staff_spacing::get_spacing_params (spacing_grob,
424 &space, &fixed_space);
426 if (Paper_column::when_mom (r).grace_part_)
429 Correct for grace notes.
431 Ugh. The 0.8 is arbitrary.
436 max_space = max (max_space, space);
437 max_fixed = max (max_fixed, fixed_space);
439 compound_space += space;
440 compound_fixed += fixed_space;
445 if (compound_space <= 0.0 || !wish_count)
447 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
453 if (to_boolean (me->get_property ("average-spacing-wishes")))
455 compound_space /= wish_count;
456 compound_fixed /= wish_count;
460 compound_fixed = max_fixed;
461 compound_space = max_space;
466 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
467 compound_fixed = 0.0;
469 assert (!isinf (compound_space));
470 compound_space = max (compound_space, compound_fixed);
473 There used to be code that changed spacing depending on
474 raggedright setting. Ugh.
476 Do it more cleanly, or rename the property.
479 Real inverse_strength = (compound_space - compound_fixed);
480 Real distance = compound_space;
481 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
484 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
485 "The space taken by a note is dependent on its duration. Doubling a\n"
486 "duration adds spacing-increment to the space. The most common shortest\n"
487 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
488 "spaced proportonial to their duration.\n"
490 "Typically, the increment is the width of a black note head. In a\n"
491 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
492 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
493 "head width) A 16th note is followed by 0.5 note head width. The\n"
494 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
497 "average-spacing-wishes "
498 "base-shortest-duration "
499 "common-shortest-duration "
501 "shortest-duration-space "
503 "strict-grace-spacing "
504 "strict-note-spacing "
505 "uniform-stretching "
509 ADD_INTERFACE (Spacing_interface, "spacing-interface",
510 "Something to do with line breaking and spacing. "
511 "Kill this one after determining line breaks.",