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"
37 Spacing_spanner::effective_shortest_duration (Grob *me,
38 vector<Grob*> const &all)
40 SCM preset_shortest = me->get_property ("common-shortest-duration");
41 Rational global_shortest;
42 if (unsmob_moment (preset_shortest))
43 global_shortest = unsmob_moment (preset_shortest)->main_part_;
46 global_shortest = Spacing_spanner::find_shortest (me, all);
47 if (be_verbose_global)
48 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
51 return global_shortest;
55 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
57 Spacing_spanner::set_springs (SCM smob)
59 Spanner *me = unsmob_spanner (smob);
62 can't use get_system() ? --hwn.
64 vector<Grob*> all (get_root_system (me)->columns ());
65 vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
66 &Paper_column::compare);
67 vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
68 &Paper_column::compare);
70 all = vector<Grob*>::vector<Grob*> (all.begin () + start,
71 all.begin () + end + 1);
73 set_explicit_neighbor_columns (all);
75 Spacing_options options;
76 options.init_from_grob (me);
77 options.global_shortest_ = effective_shortest_duration (me, all);
79 prune_loose_columns (me, &all, &options);
80 set_implicit_neighbor_columns (all);
81 generate_springs (me, all, &options);
83 return SCM_UNSPECIFIED;
87 We want the shortest note that is also "common" in the piece, so we
88 find the shortest in each measure, and take the most frequently
91 This probably gives weird effects with modern music, where every
92 note has a different duration, but hey, don't write that kind of
96 Spacing_spanner::find_shortest (Grob *me, vector<Grob*> const &cols)
101 vector<Rational> durations;
104 Rational shortest_in_measure;
105 shortest_in_measure.set_infinite (1);
107 for (vsize i = 0; i < cols.size (); i++)
109 if (Paper_column::is_musical (cols[i]))
111 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
114 ignore grace notes for shortest notes.
116 if (when && when->grace_part_)
119 SCM st = cols[i]->get_property ("shortest-starter-duration");
120 Moment this_shortest = *unsmob_moment (st);
121 assert (this_shortest.to_bool ());
122 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
124 else if (!shortest_in_measure.is_infinity ()
125 && Paper_column::is_breakable (cols[i]))
128 for (; j < durations.size (); j++)
130 if (durations[j] > shortest_in_measure)
132 counts.insert (counts.begin () + j, 1);
133 durations.insert (durations.begin () + j, shortest_in_measure);
136 else if (durations[j] == shortest_in_measure)
143 if (durations.size () == j)
145 durations.push_back (shortest_in_measure);
146 counts.push_back (1);
149 shortest_in_measure.set_infinite (1);
155 for (vsize i = durations.size (); i--;)
157 if (counts[i] >= max_count)
160 max_count = counts[i];
163 // printf ("duration %d/%d, count %d\n",
164 // durations[i].num (), durations[i].den (), counts[i]);
167 SCM bsd = me->get_property ("base-shortest-duration");
168 Rational d = Rational (1, 8);
169 if (Moment *m = unsmob_moment (bsd))
173 d = min (d, durations[max_idx]);
179 Spacing_spanner::generate_pair_spacing (Grob *me,
180 Paper_column *left_col, Paper_column *right_col,
181 Paper_column *after_right_col,
182 Spacing_options const *options)
184 if (Paper_column::is_musical (left_col))
186 bool skip_unbroken_right = false;
188 if (!Paper_column::is_musical (right_col)
189 && options->float_nonmusical_columns_
191 && Paper_column::is_musical (after_right_col))
192 skip_unbroken_right = true;
194 if (skip_unbroken_right)
197 TODO: should generate rods to prevent collisions.
199 musical_column_spacing (me, left_col, after_right_col, options);
200 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
201 after_right_col->self_scm ()));
204 musical_column_spacing (me, left_col, right_col, options);
206 if (Item *rb = right_col->find_prebroken_piece (LEFT))
207 musical_column_spacing (me, left_col, rb, options);
212 The case that the right part is broken as well is rather
213 rare, but it is possible, eg. with a single empty measure,
214 or if one staff finishes a tad earlier than the rest.
216 Item *lb = left_col->find_prebroken_piece (RIGHT);
217 Item *rb = right_col->find_prebroken_piece (LEFT);
219 if (left_col && right_col)
220 breakable_column_spacing (me, left_col, right_col, options);
223 breakable_column_spacing (me, lb, right_col, options);
226 breakable_column_spacing (me, left_col, rb, options);
229 breakable_column_spacing (me, lb, rb, options);
234 Spacing_spanner::generate_springs (Grob *me,
235 vector<Grob*> const &cols,
236 Spacing_options const *options)
238 Paper_column *prev = 0;
239 for (vsize i = 0; i < cols.size (); i++)
241 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
242 Paper_column *next = (i < cols.size()-1) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
245 generate_pair_spacing (me, prev, col, next, options);
252 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
253 spacing parameters INCR and SHORTEST.
256 Spacing_spanner::musical_column_spacing (Grob *me,
259 Spacing_options const *options)
261 bool expand_only = false;
262 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
266 Real compound_note_space = 0.0;
267 Real compound_fixed_note_space = 0.0;
269 if (options->stretch_uniformly_)
271 compound_note_space = base_note_space;
273 if (!Paper_column::is_musical (right_col))
276 Crude fix for notes that lead up to barlines and time sigs.
278 Interval lext = right_col->extent (right_col, X_AXIS);
279 if (!lext.is_empty ())
280 compound_note_space += -lext[LEFT];
288 extract_grob_set (left_col, "right-neighbors", neighbors);
291 We adjust the space following a note only if the next note
292 happens after the current note (this is set in the grob
293 property SPACING-SEQUENCE.
295 for (vsize i = 0; i < neighbors.size (); i++)
297 Grob *wish = neighbors[i];
299 Item *wish_rcol = Note_spacing::right_column (wish);
300 if (Note_spacing::left_column (wish) != left_col
301 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
305 This is probably a waste of time in the case of polyphonic
307 if (Note_spacing::has_interface (wish))
312 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
315 max_space = max (max_space, space);
316 max_fixed = max (max_fixed, fixed);
318 compound_note_space += space;
319 compound_fixed_note_space += fixed;
324 if (Paper_column::when_mom (right_col).grace_part_
325 && !Paper_column::when_mom (left_col).grace_part_)
328 Ugh. 0.8 is arbitrary.
330 compound_note_space *= 0.8;
333 if (compound_note_space < 0 || wish_count == 0)
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;
351 else if (to_boolean (me->get_property ("average-spacing-wishes")))
353 compound_note_space /= wish_count;
354 compound_fixed_note_space /= wish_count;
358 compound_fixed_note_space = max_fixed;
359 compound_note_space = max_space;
363 Whatever we do, the fixed space is smaller than the real
366 TODO: this criterion is discontinuous in the derivative.
367 Maybe it should be continuous?
369 compound_fixed_note_space = min (compound_fixed_note_space,
370 compound_note_space);
373 Real inverse_strength = 1.0;
377 TODO: make sure that the space doesn't exceed the right margin.
379 if (options->packed_)
382 In packed mode, pack notes as tight as possible. This makes
383 sense mostly in combination with raggedright mode: the notes
384 are then printed at minimum distance. This is mostly useful
385 for ancient notation, but may also be useful for some flavours
386 of contemporary music. If not in raggedright mode, lily will
387 pack as much bars of music as possible into a line, but the
388 line will then be stretched to fill the whole linewidth.
390 inverse_strength = 1.0;
391 distance = compound_fixed_note_space;
395 inverse_strength = (compound_note_space - compound_fixed_note_space);
396 distance = compound_note_space;
399 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
403 Read hints from L and generate springs.
406 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
407 Spacing_options const *options)
409 Real compound_fixed = 0.0;
410 Real compound_space = 0.0;
411 Real max_fixed = 0.0;
412 Real max_space = 0.0;
416 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
418 if (dt == Moment (0, 0))
420 extract_grob_set (l, "spacing-wishes", wishes);
422 for (vsize i = 0; i < wishes.size (); i++)
424 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
426 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
433 column for the left one settings should be ok due automatic
437 assert (spacing_grob->get_column () == l);
439 Staff_spacing::get_spacing_params (spacing_grob,
440 &space, &fixed_space);
442 if (Paper_column::when_mom (r).grace_part_)
445 Correct for grace notes.
447 Ugh. The 0.8 is arbitrary.
452 max_space = max (max_space, space);
453 max_fixed = max (max_fixed, fixed_space);
455 compound_space += space;
456 compound_fixed += fixed_space;
461 if (compound_space <= 0.0 || !wish_count)
463 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
469 if (to_boolean (me->get_property ("average-spacing-wishes")))
471 compound_space /= wish_count;
472 compound_fixed /= wish_count;
476 compound_fixed = max_fixed;
477 compound_space = max_space;
482 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
483 compound_fixed = 0.0;
485 assert (!isinf (compound_space));
486 compound_space = max (compound_space, compound_fixed);
489 There used to be code that changed spacing depending on
490 raggedright setting. Ugh.
492 Do it more cleanly, or rename the property.
495 Real inverse_strength = (compound_space - compound_fixed);
496 Real distance = compound_space;
497 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
500 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
501 "The space taken by a note is dependent on its duration. Doubling a\n"
502 "duration adds spacing-increment to the space. The most common shortest\n"
503 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
504 "spaced proportonial to their duration.\n"
506 "Typically, the increment is the width of a black note head. In a\n"
507 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
508 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
509 "head width) A 16th note is followed by 0.5 note head width. The\n"
510 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
513 "average-spacing-wishes "
514 "base-shortest-duration "
515 "common-shortest-duration "
516 "grace-space-factor "
518 "shortest-duration-space "
520 "strict-note-spacing "
521 "uniform-stretching "
525 ADD_INTERFACE (Spacing_interface, "spacing-interface",
526 "Something to do with line breaking and spacing. "
527 "Kill this one after determining line breaks.",