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>
13 #include "spacing-spanner.hh"
14 #include "paper-column.hh"
15 #include "output-def.hh"
16 #include "paper-score.hh"
19 #include "note-spacing.hh"
22 #include "pointer-group-interface.hh"
23 #include "spaceable-grob.hh"
24 #include "staff-spacing.hh"
25 #include "spacing-interface.hh"
28 Spacing_spanner::effective_shortest_duration (Grob *me,
29 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))
34 global_shortest = unsmob_moment (preset_shortest)->main_part_;
37 global_shortest = Spacing_spanner::find_shortest (me, all);
38 if (be_verbose_global)
39 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
42 return global_shortest;
45 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
47 Spacing_spanner::set_springs (SCM smob)
49 Grob *me = unsmob_grob (smob);
52 can't use get_system() ? --hwn.
54 Link_array<Grob> all (me->pscore_->root_system ()->columns ());
56 set_explicit_neighbor_columns (all);
58 Spacing_options options;
59 options.init_from_grob (me);
60 options.global_shortest_ = effective_shortest_duration (me, all);
62 prune_loose_columns (me, &all, &options);
63 set_implicit_neighbor_columns (all);
64 generate_springs (me, all, &options);
66 return SCM_UNSPECIFIED;
70 We want the shortest note that is also "common" in the piece, so we
71 find the shortest in each measure, and take the most frequently
74 This probably gives weird effects with modern music, where every
75 note has a different duration, but hey, don't write that kind of
79 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
84 Array<Rational> durations;
87 Rational shortest_in_measure;
88 shortest_in_measure.set_infinite (1);
90 for (int i = 0; i < cols.size (); i++)
92 if (Paper_column::is_musical (cols[i]))
94 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
97 ignore grace notes for shortest notes.
99 if (when && when->grace_part_)
102 SCM st = cols[i]->get_property ("shortest-starter-duration");
103 Moment this_shortest = *unsmob_moment (st);
104 assert (this_shortest.to_bool ());
105 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
107 else if (!shortest_in_measure.is_infinity ()
108 && Item::is_breakable (cols[i]))
111 for (; j < durations.size (); j++)
113 if (durations[j] > shortest_in_measure)
115 counts.insert (1, j);
116 durations.insert (shortest_in_measure, j);
119 else if (durations[j] == shortest_in_measure)
126 if (durations.size () == j)
128 durations.push (shortest_in_measure);
132 shortest_in_measure.set_infinite (1);
138 for (int i = durations.size (); i--;)
140 if (counts[i] >= max_count)
143 max_count = counts[i];
146 // printf ("duration %d/%d, count %d\n",
147 // durations[i].num (), durations[i].den (), counts[i]);
150 SCM bsd = me->get_property ("base-shortest-duration");
151 Rational d = Rational (1, 8);
152 if (Moment *m = unsmob_moment (bsd))
156 d = min (d, durations[max_idx]);
162 Spacing_spanner::generate_pair_spacing (Grob *me,
163 Paper_column *left_col, Paper_column *right_col,
164 Paper_column *after_right_col,
165 Spacing_options const *options)
167 if (Paper_column::is_musical (left_col))
169 bool skip_unbroken_right = false;
171 if (!Paper_column::is_musical (right_col)
172 && options->float_nonmusical_columns_
174 && Paper_column::is_musical (after_right_col))
175 skip_unbroken_right = true;
177 if (skip_unbroken_right)
180 TODO: should generate rods to prevent collisions.
182 musical_column_spacing (me, left_col, after_right_col, options);
183 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
184 after_right_col->self_scm ()));
187 musical_column_spacing (me, left_col, right_col, options);
189 if (Item *rb = right_col->find_prebroken_piece (LEFT))
190 musical_column_spacing (me, left_col, rb, options);
195 The case that the right part is broken as well is rather
196 rare, but it is possible, eg. with a single empty measure,
197 or if one staff finishes a tad earlier than the rest.
199 Item *lb = left_col->find_prebroken_piece (RIGHT);
200 Item *rb = right_col->find_prebroken_piece (LEFT);
202 if (left_col && right_col)
203 breakable_column_spacing (me, left_col, right_col, options);
206 breakable_column_spacing (me, lb, right_col, options);
209 breakable_column_spacing (me, left_col, rb, options);
212 breakable_column_spacing (me, lb, rb, options);
217 Spacing_spanner::generate_springs (Grob *me,
218 Link_array<Grob> const &cols,
219 Spacing_options const *options)
221 Paper_column *next = 0;
222 Paper_column *next_next = 0;
223 for (int i = cols.size (); i--;)
225 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
227 generate_pair_spacing (me, col, next, next_next, options);
235 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
236 spacing parameters INCR and SHORTEST.
239 Spacing_spanner::musical_column_spacing (Grob *me,
242 Spacing_options const *options)
244 bool expand_only = false;
245 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
247 Real compound_note_space = 0.0;
248 Real compound_fixed_note_space = 0.0;
250 if (options->stretch_uniformly_)
251 compound_note_space = base_note_space;
256 extract_grob_set (left_col, "right-neighbors", neighbors);
259 We adjust the space following a note only if the next note
260 happens after the current note (this is set in the grob
261 property SPACING-SEQUENCE.
263 for (int i = 0; i < neighbors.size (); i++)
265 Grob *wish = neighbors[i];
267 Item *wish_rcol = Note_spacing::right_column (wish);
268 if (Note_spacing::left_column (wish) != left_col
269 || (wish_rcol != right_col && wish_rcol != right_col->original_))
273 This is probably a waste of time in the case of polyphonic
275 if (Note_spacing::has_interface (wish))
280 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
282 compound_note_space = compound_note_space + space;
283 compound_fixed_note_space = compound_fixed_note_space + fixed;
288 if (Paper_column::when_mom (right_col).grace_part_
289 && !Paper_column::when_mom (left_col).grace_part_)
292 Ugh. 0.8 is arbitrary.
294 compound_note_space *= 0.8;
297 if (compound_note_space < 0 || wish_count == 0)
299 compound_note_space = base_note_space;
300 compound_fixed_note_space = options->increment_;
304 compound_note_space /= wish_count;
305 compound_fixed_note_space /= wish_count;
309 Whatever we do, the fixed space is smaller than the real
312 TODO: this criterion is discontinuous in the derivative.
313 Maybe it should be continuous?
315 compound_fixed_note_space = min (compound_fixed_note_space,
316 compound_note_space);
319 Real inverse_strength = 1.0;
323 TODO: make sure that the space doesn't exceed the right margin.
325 if (options->packed_)
328 In packed mode, pack notes as tight as possible. This makes
329 sense mostly in combination with raggedright mode: the notes
330 are then printed at minimum distance. This is mostly useful
331 for ancient notation, but may also be useful for some flavours
332 of contemporary music. If not in raggedright mode, lily will
333 pack as much bars of music as possible into a line, but the
334 line will then be stretched to fill the whole linewidth.
336 inverse_strength = 1.0;
337 distance = compound_fixed_note_space;
341 inverse_strength = (compound_note_space - compound_fixed_note_space);
342 distance = compound_note_space;
345 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
349 Read hints from L and generate springs.
352 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
353 Spacing_options const *options)
355 Real compound_fixed = 0.0;
356 Real compound_space = 0.0;
359 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
361 if (dt == Moment (0, 0))
363 extract_grob_set (l, "spacing-wishes", wishes);
365 for (int i = 0; i < wishes.size (); i++)
367 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
369 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
376 column for the left one settings should be ok due automatic
380 assert (spacing_grob->get_column () == l);
382 Staff_spacing::get_spacing_params (spacing_grob,
383 &space, &fixed_space);
385 if (Paper_column::when_mom (r).grace_part_)
388 Correct for grace notes.
390 Ugh. The 0.8 is arbitrary.
395 compound_space += space;
396 compound_fixed += fixed_space;
401 if (compound_space <= 0.0 || !wish_count)
403 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
409 compound_space /= wish_count;
410 compound_fixed /= wish_count;
413 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
414 compound_fixed = 0.0;
416 assert (!isinf (compound_space));
417 compound_space = max (compound_space, compound_fixed);
420 There used to be code that changed spacing depending on
421 raggedright setting. Ugh.
423 Do it more cleanly, or rename the property.
426 Real inverse_strength = (compound_space - compound_fixed);
427 Real distance = compound_space;
428 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
431 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
432 "The space taken by a note is dependent on its duration. Doubling a\n"
433 "duration adds spacing-increment to the space. The most common shortest\n"
434 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
435 "spaced proportonial to their duration.\n"
437 "Typically, the increment is the width of a black note head. In a\n"
438 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
439 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
440 "head width) A 16th note is followed by 0.5 note head width. The\n"
441 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
443 "grace-space-factor "
445 "base-shortest-duration "
446 "strict-note-spacing "
447 "shortest-duration-space "
448 "common-shortest-duration "
449 "uniform-stretching "
453 ADD_INTERFACE (Spacing_interface, "spacing-interface",
454 "Something to do with line breaking and spacing. "
455 "Kill this one after determining line breaks.",