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"
28 Spacing_spanner::effective_shortest_duration (Grob *me, 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))
34 global_shortest = unsmob_moment (preset_shortest)->main_part_;
38 global_shortest = Spacing_spanner::find_shortest (me, all);
39 if (be_verbose_global)
40 message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
43 return global_shortest;
46 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
48 Spacing_spanner::set_springs (SCM smob)
50 Grob *me = unsmob_grob (smob);
53 can't use get_system() ? --hwn.
55 Link_array<Grob> all (me->pscore_->root_system ()->columns ());
57 set_explicit_neighbor_columns (all);
59 Spacing_options options;
60 options.init_from_grob (me);
61 options.global_shortest_ = effective_shortest_duration (me, all);
63 prune_loose_columns (me, &all, &options);
64 set_implicit_neighbor_columns (all);
65 generate_springs (me, all, &options);
67 return SCM_UNSPECIFIED;
71 We want the shortest note that is also "common" in the piece, so we
72 find the shortest in each measure, and take the most frequently
75 This probably gives weird effects with modern music, where every
76 note has a different duration, but hey, don't write that kind of
80 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
85 Array<Rational> durations;
88 Rational shortest_in_measure;
89 shortest_in_measure.set_infinite (1);
91 for (int i = 0; i < cols.size (); i++)
93 if (Paper_column::is_musical (cols[i]))
95 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
98 ignore grace notes for shortest notes.
100 if (when && when->grace_part_)
103 SCM st = cols[i]->get_property ("shortest-starter-duration");
104 Moment this_shortest = *unsmob_moment (st);
105 assert (this_shortest.to_bool ());
106 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
108 else if (!shortest_in_measure.is_infinity ()
109 && Item::is_breakable (cols[i]))
112 for (; j < durations.size (); j++)
114 if (durations[j] > shortest_in_measure)
116 counts.insert (1, j);
117 durations.insert (shortest_in_measure, j);
120 else if (durations[j] == shortest_in_measure)
127 if (durations.size () == j)
129 durations.push (shortest_in_measure);
133 shortest_in_measure.set_infinite (1);
139 for (int i = durations.size (); i--;)
141 if (counts[i] >= max_count)
144 max_count = counts[i];
147 // printf ("duration %d/%d, count %d\n",
148 // durations[i].num (), durations[i].den (), counts[i]);
151 SCM bsd = me->get_property ("base-shortest-duration");
152 Rational d = Rational (1, 8);
153 if (Moment *m = unsmob_moment (bsd))
157 d = min (d, durations[max_idx]);
163 Spacing_spanner::generate_pair_spacing (Grob *me,
164 Paper_column *left_col, Paper_column *right_col,
165 Paper_column *after_right_col,
166 Spacing_options const *options)
168 if (Paper_column::is_musical (left_col))
170 bool skip_unbroken_right = false;
172 if (!Paper_column::is_musical (right_col)
173 && options->float_nonmusical_columns_
175 && Paper_column::is_musical (after_right_col))
176 skip_unbroken_right = true;
178 if (skip_unbroken_right)
181 TODO: should generate rods to prevent collisions.
183 musical_column_spacing (me, left_col, after_right_col, options);
184 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
185 after_right_col->self_scm ()));
188 musical_column_spacing (me, left_col, right_col, options);
190 if (Item *rb = right_col->find_prebroken_piece (LEFT))
191 musical_column_spacing (me, left_col, rb, options);
196 The case that the right part is broken as well is rather
197 rare, but it is possible, eg. with a single empty measure,
198 or if one staff finishes a tad earlier than the rest.
200 Item *lb = left_col->find_prebroken_piece (RIGHT);
201 Item *rb = right_col->find_prebroken_piece (LEFT);
203 if (left_col && right_col)
204 breakable_column_spacing (me, left_col, right_col, options);
207 breakable_column_spacing (me, lb, right_col, options);
210 breakable_column_spacing (me, left_col, rb, options);
213 breakable_column_spacing (me, lb, rb, options);
218 Spacing_spanner::generate_springs (Grob *me,
219 Link_array<Grob> const &cols,
220 Spacing_options const *options)
222 Paper_column *next = 0;
223 Paper_column *next_next = 0;
224 for (int i = cols.size (); i--;)
226 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
228 generate_pair_spacing (me, col, next, next_next, options);
236 Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
237 spacing parameters INCR and SHORTEST.
240 Spacing_spanner::musical_column_spacing (Grob *me,
243 Spacing_options const *options)
245 bool expand_only = false;
246 Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
248 Real compound_note_space = 0.0;
249 Real compound_fixed_note_space = 0.0;
251 if (options->stretch_uniformly_)
253 compound_note_space = base_note_space;
259 extract_grob_set (left_col, "right-neighbors", neighbors);
262 We adjust the space following a note only if the next note
263 happens after the current note (this is set in the grob
264 property SPACING-SEQUENCE.
266 for (int i = 0; i < neighbors.size (); i++)
268 Grob *wish = neighbors[i];
270 Item *wish_rcol = Note_spacing::right_column (wish);
271 if (Note_spacing::left_column (wish) != left_col
272 || (wish_rcol != right_col && wish_rcol != right_col->original_))
276 This is probably a waste of time in the case of polyphonic
278 if (Note_spacing::has_interface (wish))
283 Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
285 compound_note_space = compound_note_space + space;
286 compound_fixed_note_space = compound_fixed_note_space + fixed;
291 if (Paper_column::when_mom (right_col).grace_part_
292 && !Paper_column::when_mom (left_col).grace_part_)
295 Ugh. 0.8 is arbitrary.
297 compound_note_space *= 0.8;
300 if (compound_note_space < 0 || wish_count == 0)
302 compound_note_space = base_note_space;
303 compound_fixed_note_space = options->increment_;
307 compound_note_space /= wish_count;
308 compound_fixed_note_space /= wish_count;
312 Whatever we do, the fixed space is smaller than the real
315 TODO: this criterion is discontinuous in the derivative.
316 Maybe it should be continuous?
318 compound_fixed_note_space = min (compound_fixed_note_space,
319 compound_note_space);
322 Real inverse_strength = 1.0;
326 TODO: make sure that the space doesn't exceed the right margin.
328 if (options->packed_)
331 In packed mode, pack notes as tight as possible. This makes
332 sense mostly in combination with raggedright mode: the notes
333 are then printed at minimum distance. This is mostly useful
334 for ancient notation, but may also be useful for some flavours
335 of contemporary music. If not in raggedright mode, lily will
336 pack as much bars of music as possible into a line, but the
337 line will then be stretched to fill the whole linewidth.
339 inverse_strength = 1.0;
340 distance = compound_fixed_note_space;
344 inverse_strength = (compound_note_space - compound_fixed_note_space);
345 distance = compound_note_space;
348 Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
352 Read hints from L and generate springs.
355 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
356 Spacing_options const *options)
358 Real compound_fixed = 0.0;
359 Real compound_space = 0.0;
362 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
364 if (dt == Moment (0, 0))
366 extract_grob_set (l, "spacing-wishes", wishes);
368 for (int i = 0; i < wishes.size (); i++)
370 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
372 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
379 column for the left one settings should be ok due automatic
383 assert (spacing_grob->get_column () == l);
385 Staff_spacing::get_spacing_params (spacing_grob,
386 &space, &fixed_space);
388 if (Paper_column::when_mom (r).grace_part_)
391 Correct for grace notes.
393 Ugh. The 0.8 is arbitrary.
398 compound_space += space;
399 compound_fixed += fixed_space;
404 if (compound_space <= 0.0 || !wish_count)
406 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
412 compound_space /= wish_count;
413 compound_fixed /= wish_count;
416 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
418 compound_fixed = 0.0;
421 assert (!isinf (compound_space));
422 compound_space = max (compound_space, compound_fixed);
425 There used to be code that changed spacing depending on
426 raggedright setting. Ugh.
428 Do it more cleanly, or rename the property.
431 Real inverse_strength = (compound_space - compound_fixed);
432 Real distance = compound_space;
433 Spaceable_grob::add_spring (l, r, distance, inverse_strength);
436 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
437 "The space taken by a note is dependent on its duration. Doubling a\n"
438 "duration adds spacing-increment to the space. The most common shortest\n"
439 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
440 "spaced proportonial to their duration.\n"
442 "Typically, the increment is the width of a black note head. In a\n"
443 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
444 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
445 "head width) A 16th note is followed by 0.5 note head width. The\n"
446 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
448 "grace-space-factor spacing-increment base-shortest-duration strict-note-spacing "
449 "shortest-duration-space common-shortest-duration 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.",