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 Link_array<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 Link_array<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, Link_array<Grob> const &cols)
94 std::vector<Rational> durations;
95 std::vector<int> counts;
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 && Item::is_breakable (cols[i]))
121 for (; j < durations.size (); j++)
123 if (durations[j] > shortest_in_measure)
125 counts.insert (1, j);
126 durations.insert (shortest_in_measure, j);
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 Link_array<Grob> const &cols,
229 Spacing_options const *options)
231 Paper_column *next = 0;
232 Paper_column *next_next = 0;
233 for (vsize i = cols.size (); i--;)
235 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
237 generate_pair_spacing (me, col, next, next_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_)
263 compound_note_space = base_note_space;
268 extract_grob_set (left_col, "right-neighbors", neighbors);
271 We adjust the space following a note only if the next note
272 happens after the current note (this is set in the grob
273 property SPACING-SEQUENCE.
275 for (vsize i = 0; i < neighbors.size (); i++)
277 Grob *wish = neighbors[i];
279 Item *wish_rcol = Note_spacing::right_column (wish);
280 if (Note_spacing::left_column (wish) != left_col
281 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
285 This is probably a waste of time in the case of polyphonic
287 if (Note_spacing::has_interface (wish))
292 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
295 max_space = max (max_space, space);
296 max_fixed = max (max_fixed, fixed);
298 compound_note_space += space;
299 compound_fixed_note_space += fixed;
304 if (Paper_column::when_mom (right_col).grace_part_
305 && !Paper_column::when_mom (left_col).grace_part_)
308 Ugh. 0.8 is arbitrary.
310 compound_note_space *= 0.8;
313 if (compound_note_space < 0 || wish_count == 0)
315 compound_note_space = base_note_space;
316 compound_fixed_note_space = options->increment_;
318 else if (to_boolean (me->get_property ("average-spacing-wishes")))
320 compound_note_space /= wish_count;
321 compound_fixed_note_space /= wish_count;
325 compound_fixed_note_space = max_fixed;
326 compound_note_space = max_space;
330 Whatever we do, the fixed space is smaller than the real
333 TODO: this criterion is discontinuous in the derivative.
334 Maybe it should be continuous?
336 compound_fixed_note_space = min (compound_fixed_note_space,
337 compound_note_space);
340 Real inverse_strength = 1.0;
344 TODO: make sure that the space doesn't exceed the right margin.
346 if (options->packed_)
349 In packed mode, pack notes as tight as possible. This makes
350 sense mostly in combination with raggedright mode: the notes
351 are then printed at minimum distance. This is mostly useful
352 for ancient notation, but may also be useful for some flavours
353 of contemporary music. If not in raggedright mode, lily will
354 pack as much bars of music as possible into a line, but the
355 line will then be stretched to fill the whole linewidth.
357 inverse_strength = 1.0;
358 distance = compound_fixed_note_space;
362 inverse_strength = (compound_note_space - compound_fixed_note_space);
363 distance = compound_note_space;
366 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
370 Read hints from L and generate springs.
373 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
374 Spacing_options const *options)
376 Real compound_fixed = 0.0;
377 Real compound_space = 0.0;
378 Real max_fixed = 0.0;
379 Real max_space = 0.0;
383 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
385 if (dt == Moment (0, 0))
387 extract_grob_set (l, "spacing-wishes", wishes);
389 for (vsize i = 0; i < wishes.size (); i++)
391 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
393 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
400 column for the left one settings should be ok due automatic
404 assert (spacing_grob->get_column () == l);
406 Staff_spacing::get_spacing_params (spacing_grob,
407 &space, &fixed_space);
409 if (Paper_column::when_mom (r).grace_part_)
412 Correct for grace notes.
414 Ugh. The 0.8 is arbitrary.
419 max_space = max (max_space, space);
420 max_fixed = max (max_fixed, fixed_space);
422 compound_space += space;
423 compound_fixed += fixed_space;
428 if (compound_space <= 0.0 || !wish_count)
430 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
436 if (to_boolean (me->get_property ("average-spacing-wishes")))
438 compound_space /= wish_count;
439 compound_fixed /= wish_count;
443 compound_fixed = max_fixed;
444 compound_space = max_space;
449 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
450 compound_fixed = 0.0;
452 assert (!isinf (compound_space));
453 compound_space = max (compound_space, compound_fixed);
456 There used to be code that changed spacing depending on
457 raggedright setting. Ugh.
459 Do it more cleanly, or rename the property.
462 Real inverse_strength = (compound_space - compound_fixed);
463 Real distance = compound_space;
464 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
467 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
468 "The space taken by a note is dependent on its duration. Doubling a\n"
469 "duration adds spacing-increment to the space. The most common shortest\n"
470 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
471 "spaced proportonial to their duration.\n"
473 "Typically, the increment is the width of a black note head. In a\n"
474 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
475 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
476 "head width) A 16th note is followed by 0.5 note head width. The\n"
477 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
479 "average-spacing-wishes "
480 "grace-space-factor "
482 "base-shortest-duration "
483 "strict-note-spacing "
484 "shortest-duration-space "
485 "common-shortest-duration "
486 "uniform-stretching "
490 ADD_INTERFACE (Spacing_interface, "spacing-interface",
491 "Something to do with line breaking and spacing. "
492 "Kill this one after determining line breaks.",