2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
12 #include "spacing-spanner.hh"
13 #include "paper-column.hh"
14 #include "output-def.hh"
15 #include "paper-score.hh"
18 #include "note-spacing.hh"
21 #include "pointer-group-interface.hh"
22 #include "spaceable-grob.hh"
23 #include "staff-spacing.hh"
24 #include "spacing-interface.hh"
29 Spacing_spanner::effective_shortest_duration (Grob *me, Link_array<Grob> const &all)
31 SCM preset_shortest = me->get_property ("common-shortest-duration");
32 Rational global_shortest;
33 if (unsmob_moment (preset_shortest))
35 global_shortest = unsmob_moment (preset_shortest)->main_part_;
39 global_shortest = Spacing_spanner::find_shortest (me, all);
40 if (be_verbose_global)
41 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
44 return global_shortest;
48 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
50 Spacing_spanner::set_springs (SCM smob)
52 Grob *me = unsmob_grob (smob);
55 can't use get_system() ? --hwn.
57 Link_array<Grob> all (me->pscore_->root_system ()->columns ());
59 set_explicit_neighbor_columns (all);
61 Spacing_options options;
62 options.init_from_grob (me);
63 options.global_shortest_ = effective_shortest_duration (me, all);
65 prune_loose_columns (me, &all, &options);
66 set_implicit_neighbor_columns (all);
67 generate_springs (me, all, &options);
69 return SCM_UNSPECIFIED;
73 We want the shortest note that is also "common" in the piece, so we
74 find the shortest in each measure, and take the most frequently
77 This probably gives weird effects with modern music, where every
78 note has a different duration, but hey, don't write that kind of
82 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
87 Array<Rational> durations;
90 Rational shortest_in_measure;
91 shortest_in_measure.set_infinite (1);
93 for (int i = 0; i < cols.size (); i++)
95 if (Paper_column::is_musical (cols[i]))
97 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
100 ignore grace notes for shortest notes.
102 if (when && when->grace_part_)
105 SCM st = cols[i]->get_property ("shortest-starter-duration");
106 Moment this_shortest = *unsmob_moment (st);
107 assert (this_shortest.to_bool ());
108 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
110 else if (!shortest_in_measure.is_infinity ()
111 && Item::is_breakable (cols[i]))
114 for (; j < durations.size (); j++)
116 if (durations[j] > shortest_in_measure)
118 counts.insert (1, j);
119 durations.insert (shortest_in_measure, j);
122 else if (durations[j] == shortest_in_measure)
129 if (durations.size () == j)
131 durations.push (shortest_in_measure);
135 shortest_in_measure.set_infinite (1);
141 for (int i = durations.size (); i--;)
143 if (counts[i] >= max_count)
146 max_count = counts[i];
149 // printf ("duration %d/%d, count %d\n",
150 // durations[i].num (), durations[i].den (), counts[i]);
153 SCM bsd = me->get_property ("base-shortest-duration");
154 Rational d = Rational (1, 8);
155 if (Moment *m = unsmob_moment (bsd))
159 d = min (d, durations[max_idx]);
165 Spacing_spanner::generate_pair_spacing (Grob *me,
166 Paper_column *left_col, Paper_column *right_col,
167 Paper_column *after_right_col,
168 Spacing_options const *options)
170 if (Paper_column::is_musical (left_col))
172 bool skip_unbroken_right = false;
174 if (!Paper_column::is_musical (right_col)
175 && options->float_nonmusical_columns_
177 && Paper_column::is_musical (after_right_col))
179 skip_unbroken_right = true;
182 if (skip_unbroken_right)
185 TODO: should generate rods to prevent collisions.
187 musical_column_spacing (me, left_col, after_right_col, options);
188 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
189 after_right_col->self_scm ()));
192 musical_column_spacing (me, left_col, right_col, options);
195 if (Item *rb = right_col->find_prebroken_piece (LEFT))
196 musical_column_spacing (me, left_col, rb, options);
201 The case that the right part is broken as well is rather
202 rare, but it is possible, eg. with a single empty measure,
203 or if one staff finishes a tad earlier than the rest.
205 Item *lb = left_col->find_prebroken_piece (RIGHT);
206 Item *rb = right_col->find_prebroken_piece (LEFT);
208 if (left_col && right_col)
209 breakable_column_spacing (me, left_col, right_col, options);
212 breakable_column_spacing (me, lb, right_col, options);
215 breakable_column_spacing (me, left_col, rb, options);
218 breakable_column_spacing (me, lb, rb, options);
223 Spacing_spanner::generate_springs (Grob *me,
224 Link_array<Grob> const &cols,
225 Spacing_options const *options)
227 Paper_column *next = 0;
228 Paper_column *next_next = 0;
229 for (int i = cols.size (); i--;)
231 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
233 generate_pair_spacing (me, col, next, next_next, options);
241 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
242 spacing parameters INCR and SHORTEST.
245 Spacing_spanner::musical_column_spacing (Grob *me,
248 Spacing_options const *options)
250 bool expand_only = false;
251 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;
264 extract_grob_set (left_col, "right-neighbors", neighbors);
267 We adjust the space following a note only if the next note
268 happens after the current note (this is set in the grob
269 property SPACING-SEQUENCE.
271 for (int i = 0; i < neighbors.size (); i++)
273 Grob *wish = neighbors[i];
275 Item *wish_rcol = Note_spacing::right_column (wish);
276 if (Note_spacing::left_column (wish) != left_col
277 || (wish_rcol != right_col && wish_rcol != right_col->original_))
281 This is probably a waste of time in the case of polyphonic
283 if (Note_spacing::has_interface (wish))
288 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
290 compound_note_space = compound_note_space + space;
291 compound_fixed_note_space = compound_fixed_note_space + fixed;
296 if (Paper_column::when_mom (right_col).grace_part_
297 && !Paper_column::when_mom (left_col).grace_part_)
300 Ugh. 0.8 is arbitrary.
302 compound_note_space *= 0.8;
305 if (compound_note_space < 0 || wish_count == 0)
307 compound_note_space = base_note_space;
308 compound_fixed_note_space = options->increment_;
312 compound_note_space /= wish_count;
313 compound_fixed_note_space /= wish_count;
317 Whatever we do, the fixed space is smaller than the real
320 TODO: this criterion is discontinuous in the derivative.
321 Maybe it should be continuous?
323 compound_fixed_note_space = min (compound_fixed_note_space,
324 compound_note_space);
327 Real inverse_strength = 1.0;
331 TODO: make sure that the space doesn't exceed the right margin.
333 if (options->packed_)
336 In packed mode, pack notes as tight as possible. This makes
337 sense mostly in combination with raggedright mode: the notes
338 are then printed at minimum distance. This is mostly useful
339 for ancient notation, but may also be useful for some flavours
340 of contemporary music. If not in raggedright mode, lily will
341 pack as much bars of music as possible into a line, but the
342 line will then be stretched to fill the whole linewidth.
344 inverse_strength = 1.0;
345 distance = compound_fixed_note_space;
349 inverse_strength = (compound_note_space - compound_fixed_note_space);
350 distance = compound_note_space;
353 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
357 Read hints from L and generate springs.
360 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
361 Spacing_options const *options)
363 Real compound_fixed = 0.0;
364 Real compound_space = 0.0;
367 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
369 if (dt == Moment (0, 0))
371 extract_grob_set (l, "spacing-wishes", wishes);
373 for (int i = 0; i < wishes.size (); i++)
375 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
377 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
384 column for the left one settings should be ok due automatic
388 assert (spacing_grob->get_column () == l);
390 Staff_spacing::get_spacing_params (spacing_grob,
391 &space, &fixed_space);
393 if (Paper_column::when_mom (r).grace_part_)
396 Correct for grace notes.
398 Ugh. The 0.8 is arbitrary.
403 compound_space += space;
404 compound_fixed += fixed_space;
409 if (compound_space <= 0.0 || !wish_count)
411 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
417 compound_space /= wish_count;
418 compound_fixed /= wish_count;
421 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
423 compound_fixed = 0.0;
426 assert (!isinf (compound_space));
427 compound_space = max (compound_space, compound_fixed);
430 There used to be code that changed spacing depending on
431 raggedright setting. Ugh.
433 Do it more cleanly, or rename the property.
436 Real inverse_strength = (compound_space - compound_fixed);
437 Real distance = compound_space;
438 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
441 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
442 "The space taken by a note is dependent on its duration. Doubling a\n"
443 "duration adds spacing-increment to the space. The most common shortest\n"
444 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
445 "spaced proportonial to their duration.\n"
447 "Typically, the increment is the width of a black note head. In a\n"
448 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
449 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
450 "head width) A 16th note is followed by 0.5 note head width. The\n"
451 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
453 "grace-space-factor spacing-increment base-shortest-duration strict-note-spacing "
454 "shortest-duration-space common-shortest-duration uniform-stretching "
459 ADD_INTERFACE (Spacing_interface, "spacing-interface",
460 "Something to do with line breaking and spacing. "
461 "Kill this one after determining line breaks.",