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"
27 Spacing_spanner::effective_shortest_duration (Grob *me,
28 Link_array<Grob> const &all)
30 SCM preset_shortest = me->get_property ("common-shortest-duration");
31 Rational global_shortest;
32 if (unsmob_moment (preset_shortest))
33 global_shortest = unsmob_moment (preset_shortest)->main_part_;
36 global_shortest = Spacing_spanner::find_shortest (me, all);
37 if (be_verbose_global)
38 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
41 return global_shortest;
44 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
46 Spacing_spanner::set_springs (SCM smob)
48 Grob *me = unsmob_grob (smob);
51 can't use get_system() ? --hwn.
53 Link_array<Grob> all (me->pscore_->root_system ()->columns ());
55 set_explicit_neighbor_columns (all);
57 Spacing_options options;
58 options.init_from_grob (me);
59 options.global_shortest_ = effective_shortest_duration (me, all);
61 prune_loose_columns (me, &all, &options);
62 set_implicit_neighbor_columns (all);
63 generate_springs (me, all, &options);
65 return SCM_UNSPECIFIED;
69 We want the shortest note that is also "common" in the piece, so we
70 find the shortest in each measure, and take the most frequently
73 This probably gives weird effects with modern music, where every
74 note has a different duration, but hey, don't write that kind of
78 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
83 Array<Rational> durations;
86 Rational shortest_in_measure;
87 shortest_in_measure.set_infinite (1);
89 for (int i = 0; i < cols.size (); i++)
91 if (Paper_column::is_musical (cols[i]))
93 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
96 ignore grace notes for shortest notes.
98 if (when && when->grace_part_)
101 SCM st = cols[i]->get_property ("shortest-starter-duration");
102 Moment this_shortest = *unsmob_moment (st);
103 assert (this_shortest.to_bool ());
104 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
106 else if (!shortest_in_measure.is_infinity ()
107 && Item::is_breakable (cols[i]))
110 for (; j < durations.size (); j++)
112 if (durations[j] > shortest_in_measure)
114 counts.insert (1, j);
115 durations.insert (shortest_in_measure, j);
118 else if (durations[j] == shortest_in_measure)
125 if (durations.size () == j)
127 durations.push (shortest_in_measure);
131 shortest_in_measure.set_infinite (1);
137 for (int i = durations.size (); i--;)
139 if (counts[i] >= max_count)
142 max_count = counts[i];
145 // printf ("duration %d/%d, count %d\n",
146 // durations[i].num (), durations[i].den (), counts[i]);
149 SCM bsd = me->get_property ("base-shortest-duration");
150 Rational d = Rational (1, 8);
151 if (Moment *m = unsmob_moment (bsd))
155 d = min (d, durations[max_idx]);
161 Spacing_spanner::generate_pair_spacing (Grob *me,
162 Paper_column *left_col, Paper_column *right_col,
163 Paper_column *after_right_col,
164 Spacing_options const *options)
166 if (Paper_column::is_musical (left_col))
168 bool skip_unbroken_right = false;
170 if (!Paper_column::is_musical (right_col)
171 && options->float_nonmusical_columns_
173 && Paper_column::is_musical (after_right_col))
174 skip_unbroken_right = true;
176 if (skip_unbroken_right)
179 TODO: should generate rods to prevent collisions.
181 musical_column_spacing (me, left_col, after_right_col, options);
182 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
183 after_right_col->self_scm ()));
186 musical_column_spacing (me, left_col, right_col, options);
188 if (Item *rb = right_col->find_prebroken_piece (LEFT))
189 musical_column_spacing (me, left_col, rb, options);
194 The case that the right part is broken as well is rather
195 rare, but it is possible, eg. with a single empty measure,
196 or if one staff finishes a tad earlier than the rest.
198 Item *lb = left_col->find_prebroken_piece (RIGHT);
199 Item *rb = right_col->find_prebroken_piece (LEFT);
201 if (left_col && right_col)
202 breakable_column_spacing (me, left_col, right_col, options);
205 breakable_column_spacing (me, lb, right_col, options);
208 breakable_column_spacing (me, left_col, rb, options);
211 breakable_column_spacing (me, lb, rb, options);
216 Spacing_spanner::generate_springs (Grob *me,
217 Link_array<Grob> const &cols,
218 Spacing_options const *options)
220 Paper_column *next = 0;
221 Paper_column *next_next = 0;
222 for (int i = cols.size (); i--;)
224 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
226 generate_pair_spacing (me, col, next, next_next, options);
234 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
235 spacing parameters INCR and SHORTEST.
238 Spacing_spanner::musical_column_spacing (Grob *me,
241 Spacing_options const *options)
243 bool expand_only = false;
244 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
246 Real compound_note_space = 0.0;
247 Real compound_fixed_note_space = 0.0;
249 if (options->stretch_uniformly_)
250 compound_note_space = base_note_space;
255 extract_grob_set (left_col, "right-neighbors", neighbors);
258 We adjust the space following a note only if the next note
259 happens after the current note (this is set in the grob
260 property SPACING-SEQUENCE.
262 for (int i = 0; i < neighbors.size (); i++)
264 Grob *wish = neighbors[i];
266 Item *wish_rcol = Note_spacing::right_column (wish);
267 if (Note_spacing::left_column (wish) != left_col
268 || (wish_rcol != right_col && wish_rcol != right_col->original_))
272 This is probably a waste of time in the case of polyphonic
274 if (Note_spacing::has_interface (wish))
279 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
281 compound_note_space = compound_note_space + space;
282 compound_fixed_note_space = compound_fixed_note_space + fixed;
287 if (Paper_column::when_mom (right_col).grace_part_
288 && !Paper_column::when_mom (left_col).grace_part_)
291 Ugh. 0.8 is arbitrary.
293 compound_note_space *= 0.8;
296 if (compound_note_space < 0 || wish_count == 0)
298 compound_note_space = base_note_space;
299 compound_fixed_note_space = options->increment_;
303 compound_note_space /= wish_count;
304 compound_fixed_note_space /= wish_count;
308 Whatever we do, the fixed space is smaller than the real
311 TODO: this criterion is discontinuous in the derivative.
312 Maybe it should be continuous?
314 compound_fixed_note_space = min (compound_fixed_note_space,
315 compound_note_space);
318 Real inverse_strength = 1.0;
322 TODO: make sure that the space doesn't exceed the right margin.
324 if (options->packed_)
327 In packed mode, pack notes as tight as possible. This makes
328 sense mostly in combination with raggedright mode: the notes
329 are then printed at minimum distance. This is mostly useful
330 for ancient notation, but may also be useful for some flavours
331 of contemporary music. If not in raggedright mode, lily will
332 pack as much bars of music as possible into a line, but the
333 line will then be stretched to fill the whole linewidth.
335 inverse_strength = 1.0;
336 distance = compound_fixed_note_space;
340 inverse_strength = (compound_note_space - compound_fixed_note_space);
341 distance = compound_note_space;
344 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
348 Read hints from L and generate springs.
351 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
352 Spacing_options const *options)
354 Real compound_fixed = 0.0;
355 Real compound_space = 0.0;
358 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
360 if (dt == Moment (0, 0))
362 extract_grob_set (l, "spacing-wishes", wishes);
364 for (int i = 0; i < wishes.size (); i++)
366 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
368 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
375 column for the left one settings should be ok due automatic
379 assert (spacing_grob->get_column () == l);
381 Staff_spacing::get_spacing_params (spacing_grob,
382 &space, &fixed_space);
384 if (Paper_column::when_mom (r).grace_part_)
387 Correct for grace notes.
389 Ugh. The 0.8 is arbitrary.
394 compound_space += space;
395 compound_fixed += fixed_space;
400 if (compound_space <= 0.0 || !wish_count)
402 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
408 compound_space /= wish_count;
409 compound_fixed /= wish_count;
412 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
413 compound_fixed = 0.0;
415 assert (!isinf (compound_space));
416 compound_space = max (compound_space, compound_fixed);
419 There used to be code that changed spacing depending on
420 raggedright setting. Ugh.
422 Do it more cleanly, or rename the property.
425 Real inverse_strength = (compound_space - compound_fixed);
426 Real distance = compound_space;
427 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
430 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
431 "The space taken by a note is dependent on its duration. Doubling a\n"
432 "duration adds spacing-increment to the space. The most common shortest\n"
433 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
434 "spaced proportonial to their duration.\n"
436 "Typically, the increment is the width of a black note head. In a\n"
437 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
438 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
439 "head width) A 16th note is followed by 0.5 note head width. The\n"
440 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
442 "grace-space-factor spacing-increment base-shortest-duration strict-note-spacing "
443 "shortest-duration-space common-shortest-duration uniform-stretching "
446 ADD_INTERFACE (Spacing_interface, "spacing-interface",
447 "Something to do with line breaking and spacing. "
448 "Kill this one after determining line breaks.",