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, Link_array<Grob> const &all)
29 SCM preset_shortest = me->get_property ("common-shortest-duration");
30 Rational global_shortest;
31 if (unsmob_moment (preset_shortest))
32 global_shortest = unsmob_moment (preset_shortest)->main_part_;
35 global_shortest = Spacing_spanner::find_shortest (me, all);
36 if (be_verbose_global)
37 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
40 return global_shortest;
43 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
45 Spacing_spanner::set_springs (SCM smob)
47 Grob *me = unsmob_grob (smob);
50 can't use get_system() ? --hwn.
52 Link_array<Grob> all (me->pscore_->root_system ()->columns ());
54 set_explicit_neighbor_columns (all);
56 Spacing_options options;
57 options.init_from_grob (me);
58 options.global_shortest_ = effective_shortest_duration (me, all);
60 prune_loose_columns (me, &all, &options);
61 set_implicit_neighbor_columns (all);
62 generate_springs (me, all, &options);
64 return SCM_UNSPECIFIED;
68 We want the shortest note that is also "common" in the piece, so we
69 find the shortest in each measure, and take the most frequently
72 This probably gives weird effects with modern music, where every
73 note has a different duration, but hey, don't write that kind of
77 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
82 Array<Rational> durations;
85 Rational shortest_in_measure;
86 shortest_in_measure.set_infinite (1);
88 for (int i = 0; i < cols.size (); i++)
90 if (Paper_column::is_musical (cols[i]))
92 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
95 ignore grace notes for shortest notes.
97 if (when && when->grace_part_)
100 SCM st = cols[i]->get_property ("shortest-starter-duration");
101 Moment this_shortest = *unsmob_moment (st);
102 assert (this_shortest.to_bool ());
103 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
105 else if (!shortest_in_measure.is_infinity ()
106 && Item::is_breakable (cols[i]))
109 for (; j < durations.size (); j++)
111 if (durations[j] > shortest_in_measure)
113 counts.insert (1, j);
114 durations.insert (shortest_in_measure, j);
117 else if (durations[j] == shortest_in_measure)
124 if (durations.size () == j)
126 durations.push (shortest_in_measure);
130 shortest_in_measure.set_infinite (1);
136 for (int i = durations.size (); i--;)
138 if (counts[i] >= max_count)
141 max_count = counts[i];
144 // printf ("duration %d/%d, count %d\n",
145 // durations[i].num (), durations[i].den (), counts[i]);
148 SCM bsd = me->get_property ("base-shortest-duration");
149 Rational d = Rational (1, 8);
150 if (Moment *m = unsmob_moment (bsd))
154 d = min (d, durations[max_idx]);
160 Spacing_spanner::generate_pair_spacing (Grob *me,
161 Paper_column *left_col, Paper_column *right_col,
162 Paper_column *after_right_col,
163 Spacing_options const *options)
165 if (Paper_column::is_musical (left_col))
167 bool skip_unbroken_right = false;
169 if (!Paper_column::is_musical (right_col)
170 && options->float_nonmusical_columns_
172 && Paper_column::is_musical (after_right_col))
173 skip_unbroken_right = true;
175 if (skip_unbroken_right)
178 TODO: should generate rods to prevent collisions.
180 musical_column_spacing (me, left_col, after_right_col, options);
181 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
182 after_right_col->self_scm ()));
185 musical_column_spacing (me, left_col, right_col, options);
187 if (Item *rb = right_col->find_prebroken_piece (LEFT))
188 musical_column_spacing (me, left_col, rb, options);
193 The case that the right part is broken as well is rather
194 rare, but it is possible, eg. with a single empty measure,
195 or if one staff finishes a tad earlier than the rest.
197 Item *lb = left_col->find_prebroken_piece (RIGHT);
198 Item *rb = right_col->find_prebroken_piece (LEFT);
200 if (left_col && right_col)
201 breakable_column_spacing (me, left_col, right_col, options);
204 breakable_column_spacing (me, lb, right_col, options);
207 breakable_column_spacing (me, left_col, rb, options);
210 breakable_column_spacing (me, lb, rb, options);
215 Spacing_spanner::generate_springs (Grob *me,
216 Link_array<Grob> const &cols,
217 Spacing_options const *options)
219 Paper_column *next = 0;
220 Paper_column *next_next = 0;
221 for (int i = cols.size (); i--;)
223 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
225 generate_pair_spacing (me, col, next, next_next, options);
233 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
234 spacing parameters INCR and SHORTEST.
237 Spacing_spanner::musical_column_spacing (Grob *me,
240 Spacing_options const *options)
242 bool expand_only = false;
243 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
245 Real compound_note_space = 0.0;
246 Real compound_fixed_note_space = 0.0;
248 if (options->stretch_uniformly_)
249 compound_note_space = base_note_space;
254 extract_grob_set (left_col, "right-neighbors", neighbors);
257 We adjust the space following a note only if the next note
258 happens after the current note (this is set in the grob
259 property SPACING-SEQUENCE.
261 for (int i = 0; i < neighbors.size (); i++)
263 Grob *wish = neighbors[i];
265 Item *wish_rcol = Note_spacing::right_column (wish);
266 if (Note_spacing::left_column (wish) != left_col
267 || (wish_rcol != right_col && wish_rcol != right_col->original_))
271 This is probably a waste of time in the case of polyphonic
273 if (Note_spacing::has_interface (wish))
278 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
280 compound_note_space = compound_note_space + space;
281 compound_fixed_note_space = compound_fixed_note_space + fixed;
286 if (Paper_column::when_mom (right_col).grace_part_
287 && !Paper_column::when_mom (left_col).grace_part_)
290 Ugh. 0.8 is arbitrary.
292 compound_note_space *= 0.8;
295 if (compound_note_space < 0 || wish_count == 0)
297 compound_note_space = base_note_space;
298 compound_fixed_note_space = options->increment_;
302 compound_note_space /= wish_count;
303 compound_fixed_note_space /= wish_count;
307 Whatever we do, the fixed space is smaller than the real
310 TODO: this criterion is discontinuous in the derivative.
311 Maybe it should be continuous?
313 compound_fixed_note_space = min (compound_fixed_note_space,
314 compound_note_space);
317 Real inverse_strength = 1.0;
321 TODO: make sure that the space doesn't exceed the right margin.
323 if (options->packed_)
326 In packed mode, pack notes as tight as possible. This makes
327 sense mostly in combination with raggedright mode: the notes
328 are then printed at minimum distance. This is mostly useful
329 for ancient notation, but may also be useful for some flavours
330 of contemporary music. If not in raggedright mode, lily will
331 pack as much bars of music as possible into a line, but the
332 line will then be stretched to fill the whole linewidth.
334 inverse_strength = 1.0;
335 distance = compound_fixed_note_space;
339 inverse_strength = (compound_note_space - compound_fixed_note_space);
340 distance = compound_note_space;
343 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
347 Read hints from L and generate springs.
350 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
351 Spacing_options const *options)
353 Real compound_fixed = 0.0;
354 Real compound_space = 0.0;
357 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
359 if (dt == Moment (0, 0))
361 extract_grob_set (l, "spacing-wishes", wishes);
363 for (int i = 0; i < wishes.size (); i++)
365 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
367 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
374 column for the left one settings should be ok due automatic
378 assert (spacing_grob->get_column () == l);
380 Staff_spacing::get_spacing_params (spacing_grob,
381 &space, &fixed_space);
383 if (Paper_column::when_mom (r).grace_part_)
386 Correct for grace notes.
388 Ugh. The 0.8 is arbitrary.
393 compound_space += space;
394 compound_fixed += fixed_space;
399 if (compound_space <= 0.0 || !wish_count)
401 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
407 compound_space /= wish_count;
408 compound_fixed /= wish_count;
411 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
412 compound_fixed = 0.0;
414 assert (!isinf (compound_space));
415 compound_space = max (compound_space, compound_fixed);
418 There used to be code that changed spacing depending on
419 raggedright setting. Ugh.
421 Do it more cleanly, or rename the property.
424 Real inverse_strength = (compound_space - compound_fixed);
425 Real distance = compound_space;
426 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
429 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
430 "The space taken by a note is dependent on its duration. Doubling a\n"
431 "duration adds spacing-increment to the space. The most common shortest\n"
432 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
433 "spaced proportonial to their duration.\n"
435 "Typically, the increment is the width of a black note head. In a\n"
436 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
437 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
438 "head width) A 16th note is followed by 0.5 note head width. The\n"
439 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
441 "grace-space-factor spacing-increment base-shortest-duration strict-note-spacing "
442 "shortest-duration-space common-shortest-duration uniform-stretching "
445 ADD_INTERFACE (Spacing_interface, "spacing-interface",
446 "Something to do with line breaking and spacing. "
447 "Kill this one after determining line breaks.",