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 "separation-item.hh"
24 #include "skyline-pair.hh"
25 #include "spaceable-grob.hh"
26 #include "spacing-interface.hh"
27 #include "staff-spacing.hh"
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);
46 MAKE_SCHEME_CALLBACK (Spacing_spanner
, set_springs
, 1);
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
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 MAKE_SCHEME_CALLBACK (Spacing_spanner
, calc_common_shortest_duration
, 1);
79 Spacing_spanner::calc_common_shortest_duration (SCM grob
)
81 Spanner
*me
= unsmob_spanner (grob
);
83 vector
<Grob
*> cols (get_columns (me
));
88 vector
<Rational
> durations
;
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_
)
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
]))
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
);
123 else if (durations
[j
] == shortest_in_measure
)
130 if (durations
.size () == j
)
132 durations
.push_back (shortest_in_measure
);
133 counts
.push_back (1);
136 shortest_in_measure
.set_infinite (1);
142 for (vsize i
= durations
.size (); i
--;)
144 if (counts
[i
] >= max_count
)
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
))
157 d
= min (d
, durations
[max_idx
]);
159 return Moment (d
).smobbed_copy ();
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_
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 ()));
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
);
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
);
202 breakable_column_spacing (me
, lb
, right_col
, options
);
205 breakable_column_spacing (me
, left_col
, rb
, options
);
208 breakable_column_spacing (me
, lb
, rb
, options
);
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);
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;
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;
263 /* we set a distance for the line-starter column even if it's non-broken counterpart
264 doesn't touch the right column. */
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
);
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
))
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
);
306 set_column_rods (cols
, 0.1); // FIXME: padding
310 Generate the space between two musical columns LEFT_COL and RIGHT_COL.
313 Spacing_spanner::musical_column_spacing (Grob
*me
,
316 Spacing_options
const *options
)
318 Real base_note_space
= note_spacing (me
, left_col
, right_col
, options
);
321 if (options
->stretch_uniformly_
)
322 spring
= Spring (base_note_space
, 0.0);
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 ()))
338 This is probably a waste of time in the case of polyphonic
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_
);
371 Fixed should be 0.0. If there are no spacing wishes, we're
372 likely dealing with polyphonic spacing of hemiolas.
374 We used to have min_distance_ = options->increment_
376 but this can lead to numeric instability problems when we
379 inverse_strength = (distance_ - min_distance_)
382 spring
= Spring (base_note_space
, 0.0);
386 spring
= merge_springs (springs
);
389 if (Paper_column::when_mom (right_col
).grace_part_
390 && !Paper_column::when_mom (left_col
).grace_part_
)
393 Ugh. 0.8 is arbitrary.
399 TODO: make sure that the space doesn't exceed the right margin.
401 if (options
->packed_
)
404 In packed mode, pack notes as tight as possible. This makes
405 sense mostly in combination with raggedright mode: the notes
406 are then printed at minimum distance. This is mostly useful
407 for ancient notation, but may also be useful for some flavours
408 of contemporary music. If not in raggedright mode, lily will
409 pack as much bars of music as possible into a line, but the
410 line will then be stretched to fill the whole linewidth.
412 spring
.set_distance (spring
.min_distance ());
413 spring
.set_inverse_stretch_strength (1.0);
416 Spaceable_grob::add_spring (left_col
, right_col
, spring
);
420 Check if COL fills the whole measure.
423 Spacing_spanner::fills_measure (Grob
*me
, Item
*left
, Item
*col
)
425 System
*sys
= get_root_system (me
);
426 Item
*next
= sys
->column (col
->get_column ()->get_rank () + 1);
430 if (Paper_column::is_musical (next
)
431 || Paper_column::is_musical (left
)
432 || !Paper_column::is_musical (col
)
433 || !Paper_column::is_used (next
))
437 Paper_column::when_mom (next
) - Paper_column::when_mom (col
);
439 Moment
*len
= unsmob_moment (left
->get_property ("measure-length"));
444 Don't check for exact measure length, since ending measures are
445 often shortened due to pickups.
447 if (dt
.main_part_
> len
->main_part_
/ Rational (2)
448 && (next
->is_broken ()
449 || next
->break_status_dir ()))
456 Read hints from L and generate springs.
459 Spacing_spanner::breakable_column_spacing (Grob
*me
, Item
*l
, Item
*r
,
460 Spacing_options
const *options
)
462 vector
<Spring
> springs
;
465 Moment dt
= Paper_column::when_mom (r
) - Paper_column::when_mom (l
);
467 if (dt
== Moment (0, 0))
469 extract_grob_set (l
, "spacing-wishes", wishes
);
471 for (vsize i
= 0; i
< wishes
.size (); i
++)
473 Item
*spacing_grob
= dynamic_cast<Item
*> (wishes
[i
]);
475 if (!spacing_grob
|| !Staff_spacing::has_interface (spacing_grob
))
479 column for the left one settings should be ok due automatic
482 assert (spacing_grob
->get_column () == l
);
484 springs
.push_back (Staff_spacing::get_spacing (spacing_grob
, r
));
488 if (springs
.empty ())
489 spring
= standard_breakable_column_spacing (me
, l
, r
, options
);
491 spring
= merge_springs (springs
);
493 if (Paper_column::when_mom (r
).grace_part_
)
496 Correct for grace notes.
498 Ugh. The 0.8 is arbitrary.
503 if (Paper_column::is_musical (r
)
504 && l
->break_status_dir () == CENTER
505 && fills_measure (me
, l
, r
))
507 spring
.set_distance (spring
.distance () + 1.0);
508 spring
.set_default_strength ();
511 if (options
->stretch_uniformly_
&& l
->break_status_dir () != RIGHT
)
513 spring
.set_min_distance (0.0);
514 spring
.set_default_strength ();
517 Spaceable_grob::add_spring (l
, r
, spring
);
520 ADD_INTERFACE (Spacing_spanner
,
521 "The space taken by a note is dependent on its duration."
522 " Doubling a duration adds @code{spacing-increment} to the"
523 " space. The most common shortest note gets"
524 " @code{shortest-duration-space}. Notes that are even shorter"
525 " are spaced proportonial to their duration.\n"
527 "Typically, the increment is the width of a black note head."
528 " In a piece with lots of 8th notes, and some 16th notes, the"
529 " eighth note gets a 2@tie{}note heads width (i.e., the space"
530 " following a note is a 1@tie{}note head width). A 16th note"
531 " is followed by 0.5 note head width. The quarter note is"
532 " followed by 3@tie{}NHW, the half by 4@tie{}NHW, etc.",
535 "average-spacing-wishes "
536 "base-shortest-duration "
537 "common-shortest-duration "
539 "shortest-duration-space "
541 "strict-grace-spacing "
542 "strict-note-spacing "
543 "uniform-stretching "