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 increment_ = robust_scm2double (me->get_property ("spacing-increment"), 1);
32 packed_ = to_boolean (me->get_layout ()->c_variable ("packed"));
33 stretch_uniformly_ = to_boolean (me->get_property ("uniform-stretching"));
34 float_nonmusical_columns_
35 = to_boolean (me->get_property ("strict-note-spacing"));
40 Spacing_spanner::effective_shortest_duration (Grob *me, Link_array<Grob> const &all)
42 SCM preset_shortest = me->get_property ("common-shortest-duration");
43 Rational global_shortest;
44 if (unsmob_moment (preset_shortest))
46 global_shortest = unsmob_moment (preset_shortest)->main_part_;
50 global_shortest = Spacing_spanner::find_shortest (me, all);
51 if (be_verbose_global)
52 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
55 return global_shortest;
59 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
61 Spacing_spanner::set_springs (SCM smob)
63 Grob *me = unsmob_grob (smob);
66 can't use get_system() ? --hwn.
68 Link_array<Grob> all (me->pscore_->root_system ()->columns ());
70 set_explicit_neighbor_columns (all);
72 Spacing_options options;
74 options.global_shortest_ = effective_shortest_duration (me, all);
76 prune_loose_columns (me, &all, &options);
77 set_implicit_neighbor_columns (all);
78 generate_springs (me, all, &options);
80 return SCM_UNSPECIFIED;
84 We want the shortest note that is also "common" in the piece, so we
85 find the shortest in each measure, and take the most frequently
88 This probably gives weird effects with modern music, where every
89 note has a different duration, but hey, don't write that kind of
93 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
98 Array<Rational> durations;
101 Rational shortest_in_measure;
102 shortest_in_measure.set_infinite (1);
104 for (int i = 0; i < cols.size (); i++)
106 if (Paper_column::is_musical (cols[i]))
108 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
111 ignore grace notes for shortest notes.
113 if (when && when->grace_part_)
116 SCM st = cols[i]->get_property ("shortest-starter-duration");
117 Moment this_shortest = *unsmob_moment (st);
118 assert (this_shortest.to_bool ());
119 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
121 else if (!shortest_in_measure.is_infinity ()
122 && Item::is_breakable (cols[i]))
125 for (; j < durations.size (); j++)
127 if (durations[j] > shortest_in_measure)
129 counts.insert (1, j);
130 durations.insert (shortest_in_measure, j);
133 else if (durations[j] == shortest_in_measure)
140 if (durations.size () == j)
142 durations.push (shortest_in_measure);
146 shortest_in_measure.set_infinite (1);
152 for (int i = durations.size (); i--;)
154 if (counts[i] >= max_count)
157 max_count = counts[i];
160 // printf ("duration %d/%d, count %d\n",
161 // durations[i].num (), durations[i].den (), counts[i]);
164 SCM bsd = me->get_property ("base-shortest-duration");
165 Rational d = Rational (1, 8);
166 if (Moment *m = unsmob_moment (bsd))
170 d = min (d, durations[max_idx]);
176 Spacing_spanner::generate_pair_spacing (Grob *me,
177 Paper_column *left_col, Paper_column *right_col,
178 Paper_column *after_right_col,
179 Spacing_options const *options)
181 if (Paper_column::is_musical (left_col))
183 bool skip_unbroken_right = false;
185 if (!Paper_column::is_musical (right_col)
186 && options->float_nonmusical_columns_
188 && Paper_column::is_musical (after_right_col))
190 skip_unbroken_right = true;
193 if (skip_unbroken_right)
196 TODO: should generate rods to prevent collisions.
198 musical_column_spacing (me, left_col, after_right_col, options);
199 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
200 after_right_col->self_scm ()));
203 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 Link_array<Grob> const &cols,
236 Spacing_options const *options)
238 Paper_column *next = 0;
239 Paper_column *next_next = 0;
240 for (int i = cols.size (); i--;)
242 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
244 generate_pair_spacing (me, col, next, next_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);
264 Real compound_note_space = 0.0;
265 Real compound_fixed_note_space = 0.0;
267 if (options->stretch_uniformly_)
269 compound_note_space = base_note_space;
275 extract_grob_set (left_col, "right-neighbors", neighbors);
278 We adjust the space following a note only if the next note
279 happens after the current note (this is set in the grob
280 property SPACING-SEQUENCE.
282 for (int i = 0; i < neighbors.size (); i++)
284 Grob *wish = neighbors[i];
286 Item *wish_rcol = Note_spacing::right_column (wish);
287 if (Note_spacing::left_column (wish) != left_col
288 || (wish_rcol != right_col && wish_rcol != right_col->original_))
292 This is probably a waste of time in the case of polyphonic
294 if (Note_spacing::has_interface (wish))
299 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
301 compound_note_space = compound_note_space + space;
302 compound_fixed_note_space = compound_fixed_note_space + fixed;
307 if (Paper_column::when_mom (right_col).grace_part_
308 && !Paper_column::when_mom (left_col).grace_part_)
311 Ugh. 0.8 is arbitrary.
313 compound_note_space *= 0.8;
316 if (compound_note_space < 0 || wish_count == 0)
318 compound_note_space = base_note_space;
319 compound_fixed_note_space = options->increment_;
323 compound_note_space /= wish_count;
324 compound_fixed_note_space /= wish_count;
328 Whatever we do, the fixed space is smaller than the real
331 TODO: this criterion is discontinuous in the derivative.
332 Maybe it should be continuous?
334 compound_fixed_note_space = min (compound_fixed_note_space,
335 compound_note_space);
338 Real inverse_strength = 1.0;
342 TODO: make sure that the space doesn't exceed the right margin.
344 if (options->packed_)
347 In packed mode, pack notes as tight as possible. This makes
348 sense mostly in combination with raggedright mode: the notes
349 are then printed at minimum distance. This is mostly useful
350 for ancient notation, but may also be useful for some flavours
351 of contemporary music. If not in raggedright mode, lily will
352 pack as much bars of music as possible into a line, but the
353 line will then be stretched to fill the whole linewidth.
355 inverse_strength = 1.0;
356 distance = compound_fixed_note_space;
360 inverse_strength = (compound_note_space - compound_fixed_note_space);
361 distance = compound_note_space;
364 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
368 Read hints from L and generate springs.
371 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
372 Spacing_options const *options)
374 Real compound_fixed = 0.0;
375 Real compound_space = 0.0;
378 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
380 if (dt == Moment (0, 0))
382 extract_grob_set (l, "spacing-wishes", wishes);
384 for (int i = 0; i < wishes.size (); i++)
386 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
388 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
395 column for the left one settings should be ok due automatic
399 assert (spacing_grob->get_column () == l);
401 Staff_spacing::get_spacing_params (spacing_grob,
402 &space, &fixed_space);
404 if (Paper_column::when_mom (r).grace_part_)
407 Correct for grace notes.
409 Ugh. The 0.8 is arbitrary.
414 compound_space += space;
415 compound_fixed += fixed_space;
420 if (compound_space <= 0.0 || !wish_count)
422 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
428 compound_space /= wish_count;
429 compound_fixed /= wish_count;
432 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
434 compound_fixed = 0.0;
437 assert (!isinf (compound_space));
438 compound_space = max (compound_space, compound_fixed);
441 There used to be code that changed spacing depending on
442 raggedright setting. Ugh.
444 Do it more cleanly, or rename the property.
447 Real inverse_strength = (compound_space - compound_fixed);
448 Real distance = compound_space;
449 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
452 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
453 "The space taken by a note is dependent on its duration. Doubling a\n"
454 "duration adds spacing-increment to the space. The most common shortest\n"
455 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
456 "spaced proportonial to their duration.\n"
458 "Typically, the increment is the width of a black note head. In a\n"
459 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
460 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
461 "head width) A 16th note is followed by 0.5 note head width. The\n"
462 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
464 "grace-space-factor spacing-increment base-shortest-duration strict-note-spacing "
465 "shortest-duration-space common-shortest-duration uniform-stretching"
469 ADD_INTERFACE (Spacing_interface, "spacing-interface",
470 "Something to do with line breaking and spacing. "
471 "Kill this one after determining line breaks.",