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 Grob *me = unsmob_grob (smob);
62 can't use get_system() ? --hwn.
64 vector<Grob*> all (get_root_system (me)->columns ());
66 set_explicit_neighbor_columns (all);
68 Spacing_options options;
69 options.init_from_grob (me);
70 options.global_shortest_ = effective_shortest_duration (me, all);
72 prune_loose_columns (me, &all, &options);
73 set_implicit_neighbor_columns (all);
74 generate_springs (me, all, &options);
76 return SCM_UNSPECIFIED;
80 We want the shortest note that is also "common" in the piece, so we
81 find the shortest in each measure, and take the most frequently
84 This probably gives weird effects with modern music, where every
85 note has a different duration, but hey, don't write that kind of
89 Spacing_spanner::find_shortest (Grob *me, vector<Grob*> const &cols)
94 vector<Rational> durations;
97 Rational shortest_in_measure;
98 shortest_in_measure.set_infinite (1);
100 for (vsize i = 0; i < cols.size (); i++)
102 if (Paper_column::is_musical (cols[i]))
104 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
107 ignore grace notes for shortest notes.
109 if (when && when->grace_part_)
112 SCM st = cols[i]->get_property ("shortest-starter-duration");
113 Moment this_shortest = *unsmob_moment (st);
114 assert (this_shortest.to_bool ());
115 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
117 else if (!shortest_in_measure.is_infinity ()
118 && Paper_column::is_breakable (cols[i]))
121 for (; j < durations.size (); j++)
123 if (durations[j] > shortest_in_measure)
125 counts.insert (counts.begin () + j, 1);
126 durations.insert (durations.begin () + j, shortest_in_measure);
129 else if (durations[j] == shortest_in_measure)
136 if (durations.size () == j)
138 durations.push_back (shortest_in_measure);
139 counts.push_back (1);
142 shortest_in_measure.set_infinite (1);
148 for (vsize i = durations.size (); i--;)
150 if (counts[i] >= max_count)
153 max_count = counts[i];
156 // printf ("duration %d/%d, count %d\n",
157 // durations[i].num (), durations[i].den (), counts[i]);
160 SCM bsd = me->get_property ("base-shortest-duration");
161 Rational d = Rational (1, 8);
162 if (Moment *m = unsmob_moment (bsd))
166 d = min (d, durations[max_idx]);
172 Spacing_spanner::generate_pair_spacing (Grob *me,
173 Paper_column *left_col, Paper_column *right_col,
174 Paper_column *after_right_col,
175 Spacing_options const *options)
177 if (Paper_column::is_musical (left_col))
179 bool skip_unbroken_right = false;
181 if (!Paper_column::is_musical (right_col)
182 && options->float_nonmusical_columns_
184 && Paper_column::is_musical (after_right_col))
185 skip_unbroken_right = true;
187 if (skip_unbroken_right)
190 TODO: should generate rods to prevent collisions.
192 musical_column_spacing (me, left_col, after_right_col, options);
193 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
194 after_right_col->self_scm ()));
197 musical_column_spacing (me, left_col, right_col, options);
199 if (Item *rb = right_col->find_prebroken_piece (LEFT))
200 musical_column_spacing (me, left_col, rb, options);
205 The case that the right part is broken as well is rather
206 rare, but it is possible, eg. with a single empty measure,
207 or if one staff finishes a tad earlier than the rest.
209 Item *lb = left_col->find_prebroken_piece (RIGHT);
210 Item *rb = right_col->find_prebroken_piece (LEFT);
212 if (left_col && right_col)
213 breakable_column_spacing (me, left_col, right_col, options);
216 breakable_column_spacing (me, lb, right_col, options);
219 breakable_column_spacing (me, left_col, rb, options);
222 breakable_column_spacing (me, lb, rb, options);
227 Spacing_spanner::generate_springs (Grob *me,
228 vector<Grob*> const &cols,
229 Spacing_options const *options)
231 Paper_column *prev = 0;
232 for (vsize i = 0; i < cols.size (); i++)
234 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
235 Paper_column *next = (i < cols.size()-1) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
238 generate_pair_spacing (me, prev, col, next, options);
245 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
246 spacing parameters INCR and SHORTEST.
249 Spacing_spanner::musical_column_spacing (Grob *me,
252 Spacing_options const *options)
254 bool expand_only = false;
255 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
259 Real compound_note_space = 0.0;
260 Real compound_fixed_note_space = 0.0;
262 if (options->stretch_uniformly_)
264 compound_note_space = base_note_space;
266 if (!Paper_column::is_musical (right_col))
269 Crude fix for notes that lead up to barlines and time sigs.
271 Interval lext = right_col->extent (right_col, X_AXIS);
272 if (!lext.is_empty ())
273 compound_note_space += -lext[LEFT];
281 extract_grob_set (left_col, "right-neighbors", neighbors);
284 We adjust the space following a note only if the next note
285 happens after the current note (this is set in the grob
286 property SPACING-SEQUENCE.
288 for (vsize i = 0; i < neighbors.size (); i++)
290 Grob *wish = neighbors[i];
292 Item *wish_rcol = Note_spacing::right_column (wish);
293 if (Note_spacing::left_column (wish) != left_col
294 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
298 This is probably a waste of time in the case of polyphonic
300 if (Note_spacing::has_interface (wish))
305 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
308 max_space = max (max_space, space);
309 max_fixed = max (max_fixed, fixed);
311 compound_note_space += space;
312 compound_fixed_note_space += fixed;
317 if (Paper_column::when_mom (right_col).grace_part_
318 && !Paper_column::when_mom (left_col).grace_part_)
321 Ugh. 0.8 is arbitrary.
323 compound_note_space *= 0.8;
326 if (compound_note_space < 0 || wish_count == 0)
328 compound_note_space = base_note_space;
329 compound_fixed_note_space = options->increment_;
331 else if (to_boolean (me->get_property ("average-spacing-wishes")))
333 compound_note_space /= wish_count;
334 compound_fixed_note_space /= wish_count;
338 compound_fixed_note_space = max_fixed;
339 compound_note_space = max_space;
343 Whatever we do, the fixed space is smaller than the real
346 TODO: this criterion is discontinuous in the derivative.
347 Maybe it should be continuous?
349 compound_fixed_note_space = min (compound_fixed_note_space,
350 compound_note_space);
353 Real inverse_strength = 1.0;
357 TODO: make sure that the space doesn't exceed the right margin.
359 if (options->packed_)
362 In packed mode, pack notes as tight as possible. This makes
363 sense mostly in combination with raggedright mode: the notes
364 are then printed at minimum distance. This is mostly useful
365 for ancient notation, but may also be useful for some flavours
366 of contemporary music. If not in raggedright mode, lily will
367 pack as much bars of music as possible into a line, but the
368 line will then be stretched to fill the whole linewidth.
370 inverse_strength = 1.0;
371 distance = compound_fixed_note_space;
375 inverse_strength = (compound_note_space - compound_fixed_note_space);
376 distance = compound_note_space;
379 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
383 Read hints from L and generate springs.
386 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
387 Spacing_options const *options)
389 Real compound_fixed = 0.0;
390 Real compound_space = 0.0;
391 Real max_fixed = 0.0;
392 Real max_space = 0.0;
396 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
398 if (dt == Moment (0, 0))
400 extract_grob_set (l, "spacing-wishes", wishes);
402 for (vsize i = 0; i < wishes.size (); i++)
404 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
406 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
413 column for the left one settings should be ok due automatic
417 assert (spacing_grob->get_column () == l);
419 Staff_spacing::get_spacing_params (spacing_grob,
420 &space, &fixed_space);
422 if (Paper_column::when_mom (r).grace_part_)
425 Correct for grace notes.
427 Ugh. The 0.8 is arbitrary.
432 max_space = max (max_space, space);
433 max_fixed = max (max_fixed, fixed_space);
435 compound_space += space;
436 compound_fixed += fixed_space;
441 if (compound_space <= 0.0 || !wish_count)
443 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
449 if (to_boolean (me->get_property ("average-spacing-wishes")))
451 compound_space /= wish_count;
452 compound_fixed /= wish_count;
456 compound_fixed = max_fixed;
457 compound_space = max_space;
462 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
463 compound_fixed = 0.0;
465 assert (!isinf (compound_space));
466 compound_space = max (compound_space, compound_fixed);
469 There used to be code that changed spacing depending on
470 raggedright setting. Ugh.
472 Do it more cleanly, or rename the property.
475 Real inverse_strength = (compound_space - compound_fixed);
476 Real distance = compound_space;
477 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
480 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
481 "The space taken by a note is dependent on its duration. Doubling a\n"
482 "duration adds spacing-increment to the space. The most common shortest\n"
483 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
484 "spaced proportonial to their duration.\n"
486 "Typically, the increment is the width of a black note head. In a\n"
487 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
488 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
489 "head width) A 16th note is followed by 0.5 note head width. The\n"
490 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
492 "average-spacing-wishes "
493 "grace-space-factor "
495 "base-shortest-duration "
496 "strict-note-spacing "
497 "shortest-duration-space "
498 "common-shortest-duration "
499 "uniform-stretching "
503 ADD_INTERFACE (Spacing_interface, "spacing-interface",
504 "Something to do with line breaking and spacing. "
505 "Kill this one after determining line breaks.",