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_options::init (Grob*me)
31 packed_ = to_boolean (me->get_layout ()->c_variable ("packed"));
32 uniform_ = to_boolean (me->get_property ("uniform-stretching"));
37 Spacing_spanner::effective_shortest_duration (Grob *me, Link_array<Grob> const &all)
39 SCM preset_shortest = me->get_property ("common-shortest-duration");
40 Rational global_shortest;
41 if (unsmob_moment (preset_shortest))
43 global_shortest = unsmob_moment (preset_shortest)->main_part_;
47 global_shortest = Spacing_spanner::find_shortest (me, all);
48 if (be_verbose_global)
49 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
52 return global_shortest;
56 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
58 Spacing_spanner::set_springs (SCM smob)
60 Grob *me = unsmob_grob (smob);
63 can't use get_system() ? --hwn.
65 Link_array<Grob> all (me->pscore_->root_system ()->columns ());
67 set_explicit_neighbor_columns (all);
69 Spacing_options options;
71 options.global_shortest_ = effective_shortest_duration (me, all);
73 prune_loose_columns (me, &all, &options);
74 set_implicit_neighbor_columns (all);
77 for (int i = 1; i < all.size (); i++)
80 if (Item::is_breakable (sc))
82 Link_array<Grob> measure (all.slice (j, i + 1));
83 do_measure (me, &measure, &options);
88 return SCM_UNSPECIFIED;
92 We want the shortest note that is also "common" in the piece, so we
93 find the shortest in each measure, and take the most frequently
96 This probably gives weird effects with modern music, where every
97 note has a different duration, but hey, don't write that kind of
101 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
104 ascending in duration
106 Array<Rational> durations;
109 Rational shortest_in_measure;
110 shortest_in_measure.set_infinite (1);
112 for (int i = 0; i < cols.size (); i++)
114 if (Paper_column::is_musical (cols[i]))
116 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
119 ignore grace notes for shortest notes.
121 if (when && when->grace_part_)
124 SCM st = cols[i]->get_property ("shortest-starter-duration");
125 Moment this_shortest = *unsmob_moment (st);
126 assert (this_shortest.to_bool ());
127 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
129 else if (!shortest_in_measure.is_infinity ()
130 && Item::is_breakable (cols[i]))
133 for (; j < durations.size (); j++)
135 if (durations[j] > shortest_in_measure)
137 counts.insert (1, j);
138 durations.insert (shortest_in_measure, j);
141 else if (durations[j] == shortest_in_measure)
148 if (durations.size () == j)
150 durations.push (shortest_in_measure);
154 shortest_in_measure.set_infinite (1);
160 for (int i = durations.size (); i--;)
162 if (counts[i] >= max_count)
165 max_count = counts[i];
168 // printf ("duration %d/%d, count %d\n", durations[i].num (), durations[i].den (), counts[i]);
171 SCM bsd = me->get_property ("base-shortest-duration");
172 Rational d = Rational (1, 8);
173 if (Moment *m = unsmob_moment (bsd))
177 d = min (d, durations[max_idx]);
183 Generate spacing for a single measure. We used to have code that did
184 per-measure spacing. Now we have piecewise spacing. We should fix
185 this to support "spacing-regions": some regions have different notes
186 (different time sigs) than others, and should be spaced differently.
189 Spacing_spanner::do_measure (Grob *me,
190 Link_array<Grob> *cols,
191 Spacing_options const *options
194 Real headwid = robust_scm2double (me->get_property ("spacing-increment"), 1);
195 for (int i = 0; i < cols->size () - 1; i++)
197 Item *l = dynamic_cast<Item *> (cols->elem (i));
198 Item *r = dynamic_cast<Item *> (cols->elem (i + 1));
200 Paper_column *lc = dynamic_cast<Paper_column *> (l);
201 Paper_column *rc = dynamic_cast<Paper_column *> (r);
203 if (Paper_column::is_musical (l))
205 musical_column_spacing (me, lc, rc, headwid, options);
206 if (Item *rb = r->find_prebroken_piece (LEFT))
207 musical_column_spacing (me, lc, rb, headwid, 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 = l->find_prebroken_piece (RIGHT);
217 Item *rb = r->find_prebroken_piece (LEFT);
219 if (i == 0 && Paper_column::get_rank (l) == 0)
223 breakable_column_spacing (me, l, r, options);
226 breakable_column_spacing (me, lb, r, options);
229 breakable_column_spacing (me, l, rb, options);
232 breakable_column_spacing (me, lb, rb, options);
238 Generate the space between two musical columns LC and RC, given
239 spacing parameters INCR and SHORTEST.
242 Spacing_spanner::musical_column_spacing (Grob *me, Item *lc, Item *rc,
244 Spacing_options const *options)
246 bool expand_only = false;
247 Real base_note_space = note_spacing (me, lc, rc, options, &expand_only);
249 Real compound_note_space = 0.0;
250 Real compound_fixed_note_space = 0.0;
252 if (options->uniform_)
254 compound_note_space = base_note_space;
260 extract_grob_set (lc, "right-neighbors", neighbors);
263 We adjust the space following a note only if the next note
264 happens after the current note (this is set in the grob
265 property SPACING-SEQUENCE.
267 for (int i = 0; i < neighbors.size (); i++)
269 Grob *wish = neighbors[i];
271 Item *wish_rcol = Note_spacing::right_column (wish);
272 if (Note_spacing::left_column (wish) != lc
273 || (wish_rcol != rc && wish_rcol != rc->original_))
277 This is probably a waste of time in the case of polyphonic
279 if (Note_spacing::has_interface (wish))
284 Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed);
286 compound_note_space = compound_note_space + space;
287 compound_fixed_note_space = compound_fixed_note_space + fixed;
292 if (Paper_column::when_mom (rc).grace_part_
293 && !Paper_column::when_mom (lc).grace_part_)
296 Ugh. 0.8 is arbitrary.
298 compound_note_space *= 0.8;
301 if (compound_note_space < 0 || wish_count == 0)
303 compound_note_space = base_note_space;
304 compound_fixed_note_space = increment;
308 compound_note_space /= wish_count;
309 compound_fixed_note_space /= wish_count;
313 Whatever we do, the fixed space is smaller than the real
316 TODO: this criterion is discontinuous in the derivative.
317 Maybe it should be continuous?
319 compound_fixed_note_space = min (compound_fixed_note_space,
320 compound_note_space);
323 Real inverse_strength = 1.0;
327 TODO: make sure that the space doesn't exceed the right margin.
329 if (options->packed_)
332 In packed mode, pack notes as tight as possible. This makes
333 sense mostly in combination with raggedright mode: the notes
334 are then printed at minimum distance. This is mostly useful
335 for ancient notation, but may also be useful for some flavours
336 of contemporary music. If not in raggedright mode, lily will
337 pack as much bars of music as possible into a line, but the
338 line will then be stretched to fill the whole linewidth.
340 inverse_strength = 1.0;
341 distance = compound_fixed_note_space;
345 inverse_strength = (compound_note_space - compound_fixed_note_space);
346 distance = compound_note_space;
349 Spaceable_grob::add_spring (lc, rc, distance, inverse_strength);
353 Read hints from L and generate springs.
356 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
357 Spacing_options const *options)
359 Real compound_fixed = 0.0;
360 Real compound_space = 0.0;
363 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
365 if (dt == Moment (0, 0))
367 extract_grob_set (l, "spacing-wishes", wishes);
369 for (int i = 0; i < wishes.size (); i++)
371 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
373 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
380 column for the left one settings should be ok due automatic
384 assert (spacing_grob->get_column () == l);
386 Staff_spacing::get_spacing_params (spacing_grob,
387 &space, &fixed_space);
389 if (Paper_column::when_mom (r).grace_part_)
392 Correct for grace notes.
394 Ugh. The 0.8 is arbitrary.
399 compound_space += space;
400 compound_fixed += fixed_space;
405 if (compound_space <= 0.0 || !wish_count)
407 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
413 compound_space /= wish_count;
414 compound_fixed /= wish_count;
417 assert (!isinf (compound_space));
418 compound_space = max (compound_space, compound_fixed);
421 There used to be code that changed spacing depending on
422 raggedright setting. Ugh.
424 Do it more cleanly, or rename the property.
427 Real inverse_strength = (compound_space - compound_fixed);
428 Real distance = compound_space;
429 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
432 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
433 "The space taken by a note is dependent on its duration. Doubling a\n"
434 "duration adds spacing-increment to the space. The most common shortest\n"
435 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
436 "spaced proportonial to their duration.\n"
438 "Typically, the increment is the width of a black note head. In a\n"
439 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
440 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
441 "head width) A 16th note is followed by 0.5 note head width. The\n"
442 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
444 "grace-space-factor spacing-increment base-shortest-duration "
445 "shortest-duration-space common-shortest-duration uniform-stretching"
449 ADD_INTERFACE (Spacing_interface, "spacing-interface",
450 "Something to do with line breaking and spacing. "
451 "Kill this one after determining line breaks.",