Fix InstrumentSwitch grob definition.
[lilypond.git] / lily / spacing-spanner.cc
blobba5d40e51879ffe7f5b7709c51cbfed96579ed41
1 /*
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>
7 */
9 #include "spacing-spanner.hh"
11 #include <math.h>
12 #include <cstdio>
14 #include "spacing-options.hh"
15 #include "international.hh"
16 #include "main.hh"
17 #include "moment.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 "separation-item.hh"
24 #include "skyline-pair.hh"
25 #include "spaceable-grob.hh"
26 #include "spacing-interface.hh"
27 #include "staff-spacing.hh"
28 #include "system.hh"
29 #include "warn.hh"
31 vector<Grob*>
32 Spacing_spanner::get_columns (Grob *me_grob)
34 Spanner *me = dynamic_cast<Spanner*> (me_grob);
35 vector<Grob*> all (get_root_system (me)->used_columns ());
36 vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
37 &Paper_column::less_than);
38 vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
39 &Paper_column::less_than);
41 all = vector<Grob*>::vector<Grob*> (all.begin () + start,
42 all.begin () + end + 1);
43 return all;
46 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
47 SCM
48 Spacing_spanner::set_springs (SCM smob)
50 Spanner *me = unsmob_spanner (smob);
53 can't use get_system () ? --hwn.
55 Spacing_options options;
56 options.init_from_grob (me);
57 vector<Grob*> cols = Spacing_spanner::get_columns (me);
58 set_explicit_neighbor_columns (cols);
60 prune_loose_columns (me, &cols, &options);
61 set_implicit_neighbor_columns (cols);
62 generate_springs (me, cols, &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
70 found duration.
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
74 stuff, then.
77 MAKE_SCHEME_CALLBACK (Spacing_spanner, calc_common_shortest_duration, 1);
78 SCM
79 Spacing_spanner::calc_common_shortest_duration (SCM grob)
81 Spanner *me = unsmob_spanner (grob);
83 vector<Grob*> cols (get_columns (me));
86 ascending in duration
88 vector<Rational> durations;
89 vector<int> counts;
91 Rational shortest_in_measure;
92 shortest_in_measure.set_infinite (1);
94 for (vsize i = 0; i < cols.size (); i++)
96 if (Paper_column::is_musical (cols[i]))
98 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
101 ignore grace notes for shortest notes.
103 if (when && when->grace_part_)
104 continue;
106 SCM st = cols[i]->get_property ("shortest-starter-duration");
107 Moment this_shortest = *unsmob_moment (st);
108 assert (this_shortest.to_bool ());
109 shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
111 else if (!shortest_in_measure.is_infinity ()
112 && Paper_column::is_breakable (cols[i]))
114 vsize j = 0;
115 for (; j < durations.size (); j++)
117 if (durations[j] > shortest_in_measure)
119 counts.insert (counts.begin () + j, 1);
120 durations.insert (durations.begin () + j, shortest_in_measure);
121 break;
123 else if (durations[j] == shortest_in_measure)
125 counts[j]++;
126 break;
130 if (durations.size () == j)
132 durations.push_back (shortest_in_measure);
133 counts.push_back (1);
136 shortest_in_measure.set_infinite (1);
140 vsize max_idx = VPOS;
141 int max_count = 0;
142 for (vsize i = durations.size (); i--;)
144 if (counts[i] >= max_count)
146 max_idx = i;
147 max_count = counts[i];
151 SCM bsd = me->get_property ("base-shortest-duration");
152 Rational d = Rational (1, 8);
153 if (Moment *m = unsmob_moment (bsd))
154 d = m->main_part_;
156 if (max_idx != VPOS)
157 d = min (d, durations[max_idx]);
159 return Moment (d).smobbed_copy ();
162 void
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 if (!Paper_column::is_musical (right_col)
171 && options->float_nonmusical_columns_
172 && after_right_col
173 && Paper_column::is_musical (after_right_col))
176 TODO: should generate rods to prevent collisions.
178 musical_column_spacing (me, left_col, after_right_col, options);
179 right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
180 after_right_col->self_scm ()));
182 else
183 musical_column_spacing (me, left_col, right_col, options);
185 if (Item *rb = right_col->find_prebroken_piece (LEFT))
186 musical_column_spacing (me, left_col, rb, options);
188 else
191 The case that the right part is broken as well is rather
192 rare, but it is possible, eg. with a single empty measure,
193 or if one staff finishes a tad earlier than the rest.
195 Item *lb = left_col->find_prebroken_piece (RIGHT);
196 Item *rb = right_col->find_prebroken_piece (LEFT);
198 if (left_col && right_col)
199 breakable_column_spacing (me, left_col, right_col, options);
201 if (lb && right_col)
202 breakable_column_spacing (me, lb, right_col, options);
204 if (left_col && rb)
205 breakable_column_spacing (me, left_col, rb, options);
207 if (lb && rb)
208 breakable_column_spacing (me, lb, rb, options);
212 static void
213 set_column_rods (vector<Grob*> const &cols, Real padding)
215 /* distances[i] will be the minimum distance between column i and column i+1 */
216 vector<Real> distances;
218 for (vsize i = 1; i < cols.size (); i++)
220 assert (distances.size () == i-1);
222 Item *r = dynamic_cast<Item*> (cols[i]);
223 Item *rb = r->find_prebroken_piece (LEFT);
225 if (Separation_item::is_empty (r) && (!rb || Separation_item::is_empty (rb)))
227 distances.push_back (0);
228 continue;
231 Skyline_pair *skys = Skyline_pair::unsmob (r->get_property ("horizontal-skylines"));
232 Real right_stickout = skys ? (*skys)[LEFT].max_height () : 0.0;
234 /* min rather than max because right-stickout will be negative if the right-hand column
235 sticks out a lot to the left */
236 right_stickout = min (right_stickout,
237 Separation_item::conditional_skyline (r, cols[i-1]).max_height ());
239 Drul_array<Item*> r_cols (r, rb);
240 Drul_array<Real> cur_dist (0.0, 0.0);
242 /* This is an inner loop and hence it is potentially quadratic. However, we only continue
243 as long as there is a rod to insert. Therefore, this loop will usually only execute
244 a constant number of times per iteration of the outer loop. */
245 for (vsize j = i; j--;)
247 Item *l = dynamic_cast<Item*> (cols[j]);
248 Item *lb = l->find_prebroken_piece (RIGHT);
249 Skyline_pair *skys = Skyline_pair::unsmob (l->get_property ("horizontal-skylines"));
250 Real left_stickout = skys ? (*skys)[RIGHT].max_height () : 0.0;
251 bool done = true;
253 Direction d = LEFT;
256 if (j < i-1)
257 cur_dist[d] += distances[j];
259 Item *r_col = r_cols[d];
260 bool touches = right_stickout - left_stickout + cur_dist[d] < 0.0;
261 Real dist = 0.0;
263 /* we set a distance for the line-starter column even if it's non-broken counterpart
264 doesn't touch the right column. */
265 if (lb)
266 Separation_item::set_distance (lb, r_col, padding);
268 if (touches || j == i-1)
269 dist = Separation_item::set_distance (l, r_col, padding);
271 if (j == i-1 && d == LEFT)
272 distances.push_back (dist);
274 if (j == i-1)
275 cur_dist[d] = distances[j];
277 done = done && !touches;
279 while (flip (&d) != LEFT && rb);
281 /* we need the empty check for gregorian notation, where there are a lot of
282 extraneous paper-columns that we need to skip over */
283 if (done && !Separation_item::is_empty (l))
284 break;
290 void
291 Spacing_spanner::generate_springs (Grob *me,
292 vector<Grob*> const &cols,
293 Spacing_options const *options)
295 Paper_column *prev = dynamic_cast<Paper_column*> (cols[0]);
296 for (vsize i = 1; i < cols.size (); i++)
298 Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
299 Paper_column *next = (i + 1 < cols.size ()) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
301 generate_pair_spacing (me, prev, col, next, options);
303 prev = col;
306 set_column_rods (cols, 0.1); // FIXME: padding
310 Generate the space between two musical columns LEFT_COL and RIGHT_COL.
312 void
313 Spacing_spanner::musical_column_spacing (Grob *me,
314 Item *left_col,
315 Item *right_col,
316 Spacing_options const *options)
318 Real base_note_space = note_spacing (me, left_col, right_col, options);
319 Spring spring;
321 if (options->stretch_uniformly_)
322 spring = Spring (base_note_space, 0.0);
323 else
325 vector<Spring> springs;
326 extract_grob_set (left_col, "right-neighbors", neighbors);
328 for (vsize i = 0; i < neighbors.size (); i++)
330 Grob *wish = neighbors[i];
332 Item *wish_rcol = Spacing_interface::right_column (wish);
333 if (Spacing_interface::left_column (wish) != left_col
334 || (wish_rcol != right_col && wish_rcol != right_col->original ()))
335 continue;
338 This is probably a waste of time in the case of polyphonic
339 music. */
340 if (Note_spacing::has_interface (wish))
342 Real inc = options->increment_;
343 Grob *gsp = unsmob_grob (left_col->get_object ("grace-spacing"));
344 if (gsp && Paper_column::when_mom (left_col).grace_part_)
346 Spacing_options grace_opts;
347 grace_opts.init_from_grob (gsp);
348 inc = grace_opts.increment_;
350 springs.push_back (Note_spacing::get_spacing (wish, right_col, base_note_space, inc));
354 if (springs.empty ())
357 if (!Paper_column::is_musical (right_col))
360 There used to be code that examined left_col->extent
361 (X_AXIS), but this is resulted in unexpected wide
362 spacing, because the width of s^"text" output is also
363 taken into account here.
365 spring = Spring (max (base_note_space, options->increment_),
366 options->increment_);
368 else
371 Min distance should be 0.0. If there are no spacing
372 wishes, we're probably dealing with polyphonic spacing
373 of hemiolas.
375 spring = Spring (base_note_space, 0.0);
378 else
379 spring = merge_springs (springs);
382 if (Paper_column::when_mom (right_col).grace_part_
383 && !Paper_column::when_mom (left_col).grace_part_)
386 Ugh. 0.8 is arbitrary.
388 spring *= 0.8;
392 TODO: make sure that the space doesn't exceed the right margin.
394 if (options->packed_)
397 In packed mode, pack notes as tight as possible. This makes
398 sense mostly in combination with ragged-right mode: the notes
399 are then printed at minimum distance. This is mostly useful
400 for ancient notation, but may also be useful for some flavours
401 of contemporary music. If not in ragged-right mode, lily will
402 pack as many bars of music as possible into a line, but the
403 line will then be stretched to fill the whole linewidth.
405 Note that we don't actually pack things as tightly as possible:
406 we don't allow the next column to begin before this one ends.
408 spring.set_distance (max (left_col->extent (left_col, X_AXIS)[RIGHT],
409 spring.min_distance ()));
410 spring.set_inverse_stretch_strength (1.0);
413 Spaceable_grob::add_spring (left_col, right_col, spring);
417 Check if COL fills the whole measure.
419 bool
420 Spacing_spanner::fills_measure (Grob *me, Item *left, Item *col)
422 System *sys = get_root_system (me);
423 Item *next = sys->column (col->get_column ()->get_rank () + 1);
424 if (!next)
425 return false;
427 if (Paper_column::is_musical (next)
428 || Paper_column::is_musical (left)
429 || !Paper_column::is_musical (col)
430 || !Paper_column::is_used (next))
431 return false;
433 Moment dt =
434 Paper_column::when_mom (next) - Paper_column::when_mom (col);
436 Moment *len = unsmob_moment (left->get_property ("measure-length"));
437 if (!len)
438 return false;
441 Don't check for exact measure length, since ending measures are
442 often shortened due to pickups.
444 if (dt.main_part_ > len->main_part_ / Rational (2)
445 && (next->is_broken ()
446 || next->break_status_dir ()))
447 return true;
449 return false;
453 Read hints from L and generate springs.
455 void
456 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
457 Spacing_options const *options)
459 vector<Spring> springs;
460 Spring spring;
462 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
464 if (dt == Moment (0, 0))
466 extract_grob_set (l, "spacing-wishes", wishes);
468 for (vsize i = 0; i < wishes.size (); i++)
470 Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
472 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
473 continue;
476 column for the left one settings should be ok due automatic
477 pointer munging.
479 assert (spacing_grob->get_column () == l);
481 springs.push_back (Staff_spacing::get_spacing (spacing_grob, r));
485 if (springs.empty ())
486 spring = standard_breakable_column_spacing (me, l, r, options);
487 else
488 spring = merge_springs (springs);
490 if (Paper_column::when_mom (r).grace_part_)
493 Correct for grace notes.
495 Ugh. The 0.8 is arbitrary.
497 spring *= 0.8;
500 if (Paper_column::is_musical (r)
501 && l->break_status_dir () == CENTER
502 && fills_measure (me, l, r))
504 spring.set_distance (spring.distance () + 1.0);
505 spring.set_default_strength ();
508 if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
510 spring.set_min_distance (0.0);
511 spring.set_default_strength ();
514 Spaceable_grob::add_spring (l, r, spring);
517 ADD_INTERFACE (Spacing_spanner,
518 "The space taken by a note is dependent on its duration."
519 " Doubling a duration adds @code{spacing-increment} to the"
520 " space. The most common shortest note gets"
521 " @code{shortest-duration-space}. Notes that are even shorter"
522 " are spaced proportonial to their duration.\n"
523 "\n"
524 "Typically, the increment is the width of a black note head."
525 " In a piece with lots of 8th notes, and some 16th notes, the"
526 " eighth note gets a 2@tie{}note heads width (i.e., the space"
527 " following a note is a 1@tie{}note head width). A 16th note"
528 " is followed by 0.5 note head width. The quarter note is"
529 " followed by 3@tie{}NHW, the half by 4@tie{}NHW, etc.",
531 /* properties */
532 "average-spacing-wishes "
533 "base-shortest-duration "
534 "common-shortest-duration "
535 "packed-spacing "
536 "shortest-duration-space "
537 "spacing-increment "
538 "strict-grace-spacing "
539 "strict-note-spacing "
540 "uniform-stretching "