2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "spacing-spanner.hh"
14 #include "spacing-options.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"
30 Spacing_spanner::get_columns (Grob *me_grob)
32 Spanner *me = dynamic_cast<Spanner*> (me_grob);
33 vector<Grob*> all (get_root_system (me)->used_columns ());
34 vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
35 &Paper_column::less_than);
36 vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
37 &Paper_column::less_than);
39 all = vector<Grob*>::vector<Grob*> (all.begin () + start,
40 all.begin () + end + 1);
44 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
46 Spacing_spanner::set_springs (SCM smob)
48 Spanner *me = unsmob_spanner (smob);
51 can't use get_system () ? --hwn.
53 Spacing_options options;
54 options.init_from_grob (me);
55 vector<Grob*> cols = Spacing_spanner::get_columns (me);
56 set_explicit_neighbor_columns (cols);
58 prune_loose_columns (me, &cols, &options);
59 set_implicit_neighbor_columns (cols);
60 generate_springs (me, cols, &options);
62 return SCM_UNSPECIFIED;
66 We want the shortest note that is also "common" in the piece, so we
67 find the shortest in each measure, and take the most frequently
70 This probably gives weird effects with modern music, where every
71 note has a different duration, but hey, don't write that kind of
75 MAKE_SCHEME_CALLBACK (Spacing_spanner, calc_common_shortest_duration, 1);
77 Spacing_spanner::calc_common_shortest_duration (SCM grob)
79 Spanner *me = unsmob_spanner (grob);
81 vector<Grob*> cols (get_columns (me));
86 vector<Rational> durations;
89 Rational shortest_in_measure;
90 shortest_in_measure.set_infinite (1);
92 for (vsize i = 0; i < cols.size (); i++)
94 if (Paper_column::is_musical (cols[i]))
96 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
99 ignore grace notes for shortest notes.
101 if (when && when->grace_part_)
104 SCM st = cols[i]->get_property ("shortest-starter-duration");
105 Moment this_shortest = *unsmob_moment (st);
106 assert (this_shortest.to_bool ());
107 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
109 else if (!shortest_in_measure.is_infinity ()
110 && Paper_column::is_breakable (cols[i]))
113 for (; j < durations.size (); j++)
115 if (durations[j] > shortest_in_measure)
117 counts.insert (counts.begin () + j, 1);
118 durations.insert (durations.begin () + j, shortest_in_measure);
121 else if (durations[j] == shortest_in_measure)
128 if (durations.size () == j)
130 durations.push_back (shortest_in_measure);
131 counts.push_back (1);
134 shortest_in_measure.set_infinite (1);
140 for (vsize i = durations.size (); i--;)
142 if (counts[i] >= max_count)
145 max_count = 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]);
157 return Moment (d).smobbed_copy ();
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 if (!Paper_column::is_musical (right_col)
169 && options->float_nonmusical_columns_
171 && Paper_column::is_musical (after_right_col))
174 TODO: should generate rods to prevent collisions.
176 musical_column_spacing (me, left_col, after_right_col, options);
177 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
178 after_right_col->self_scm ()));
181 musical_column_spacing (me, left_col, right_col, options);
183 if (Item *rb = right_col->find_prebroken_piece (LEFT))
184 musical_column_spacing (me, left_col, rb, options);
189 The case that the right part is broken as well is rather
190 rare, but it is possible, eg. with a single empty measure,
191 or if one staff finishes a tad earlier than the rest.
193 Item *lb = left_col->find_prebroken_piece (RIGHT);
194 Item *rb = right_col->find_prebroken_piece (LEFT);
196 if (left_col && right_col)
197 breakable_column_spacing (me, left_col, right_col, options);
200 breakable_column_spacing (me, lb, right_col, options);
203 breakable_column_spacing (me, left_col, rb, options);
206 breakable_column_spacing (me, lb, rb, options);
211 Spacing_spanner::generate_springs (Grob *me,
212 vector<Grob*> const &cols,
213 Spacing_options const *options)
215 Paper_column *prev = 0;
216 for (vsize i = 0; i < cols.size (); i++)
218 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
219 Paper_column *next = (i + 1 < cols.size ()) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
222 generate_pair_spacing (me, prev, col, next, options);
229 Generate the space between two musical columns LEFT_COL and RIGHT_COL.
232 Spacing_spanner::musical_column_spacing (Grob *me,
235 Spacing_options const *options)
237 Real base_note_space = note_spacing (me, left_col, right_col, options);
240 if (options->stretch_uniformly_)
241 spring = Spring (base_note_space, 0.0);
244 vector<Spring> springs;
245 extract_grob_set (left_col, "right-neighbors", neighbors);
247 for (vsize i = 0; i < neighbors.size (); i++)
249 Grob *wish = neighbors[i];
251 Item *wish_rcol = Spacing_interface::right_column (wish);
252 if (Spacing_interface::left_column (wish) != left_col
253 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
257 This is probably a waste of time in the case of polyphonic
259 if (Note_spacing::has_interface (wish))
260 springs.push_back (Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_));
263 if (springs.empty ())
266 if (!Paper_column::is_musical (right_col))
269 There used to be code that examined left_col->extent
270 (X_AXIS), but this is resulted in unexpected wide
271 spacing, because the width of s^"text" output is also
272 taken into account here.
274 spring = Spring (max (base_note_space, options->increment_),
275 options->increment_);
280 Fixed should be 0.0. If there are no spacing wishes, we're
281 likely dealing with polyphonic spacing of hemiolas.
283 We used to have min_distance_ = options->increment_
285 but this can lead to numeric instability problems when we
288 inverse_strength = (distance_ - min_distance_)
291 spring = Spring (base_note_space, 0.0);
295 spring = merge_springs (springs);
298 if (Paper_column::when_mom (right_col).grace_part_
299 && !Paper_column::when_mom (left_col).grace_part_)
302 Ugh. 0.8 is arbitrary.
308 TODO: make sure that the space doesn't exceed the right margin.
310 if (options->packed_)
313 In packed mode, pack notes as tight as possible. This makes
314 sense mostly in combination with raggedright mode: the notes
315 are then printed at minimum distance. This is mostly useful
316 for ancient notation, but may also be useful for some flavours
317 of contemporary music. If not in raggedright mode, lily will
318 pack as much bars of music as possible into a line, but the
319 line will then be stretched to fill the whole linewidth.
321 spring.set_distance (spring.min_distance ());
322 spring.set_inverse_stretch_strength (1.0);
325 Spaceable_grob::add_spring (left_col, right_col, spring);
329 Check if COL fills the whole measure.
332 Spacing_spanner::fills_measure (Grob *me, Item *left, Item *col)
334 System *sys = get_root_system (me);
335 Item *next = sys->column (col->get_column ()->get_rank () + 1);
339 if (Paper_column::is_musical (next)
340 || Paper_column::is_musical (left)
341 || !Paper_column::is_musical (col)
342 || !Paper_column::is_used (next))
346 Paper_column::when_mom (next) - Paper_column::when_mom (col);
348 Moment *len = unsmob_moment (left->get_property ("measure-length"));
353 Don't check for exact measure length, since ending measures are
354 often shortened due to pickups.
356 if (dt.main_part_ > len->main_part_ / Rational (2)
357 && (next->is_broken ()
358 || next->break_status_dir ()))
365 Read hints from L and generate springs.
368 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
369 Spacing_options const *options)
371 vector<Spring> springs;
374 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
376 if (dt == Moment (0, 0))
378 extract_grob_set (l, "spacing-wishes", wishes);
380 for (vsize i = 0; i < wishes.size (); i++)
382 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
384 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
388 column for the left one settings should be ok due automatic
391 assert (spacing_grob->get_column () == l);
393 springs.push_back (Staff_spacing::get_spacing (spacing_grob, r));
397 if (springs.empty ())
398 spring = standard_breakable_column_spacing (me, l, r, options);
400 spring = merge_springs (springs);
402 if (Paper_column::when_mom (r).grace_part_)
405 Correct for grace notes.
407 Ugh. The 0.8 is arbitrary.
412 if (Paper_column::is_musical (r)
413 && l->break_status_dir () == CENTER
414 && fills_measure (me, l, r))
416 spring.set_distance (spring.distance () + 1.0);
417 spring.set_default_strength ();
420 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
422 spring.set_min_distance (0.0);
423 spring.set_default_strength ();
426 Spaceable_grob::add_spring (l, r, spring);
429 ADD_INTERFACE (Spacing_spanner,
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",
442 "average-spacing-wishes "
443 "base-shortest-duration "
444 "common-shortest-duration "
446 "shortest-duration-space "
448 "strict-grace-spacing "
449 "strict-note-spacing "
450 "uniform-stretching "