2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
10 #include "spacing-spanner.hh"
11 #include "paper-column.hh"
12 #include "dimensions.hh"
13 #include "paper-def.hh"
15 #include "paper-score.hh"
16 #include "line-of-score.hh"
20 Spacing_spanner::set_interface (Grob*me)
22 me->set_extent_callback (SCM_EOL, X_AXIS);
23 me->set_extent_callback (SCM_EOL, Y_AXIS) ;
28 The algorithm is partly taken from :
30 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
31 OSU-CISRC-10/87-TR35, Department of Computer and Information
32 Science, The Ohio State University, 1987.
40 Spacing_spanner::do_measure (Grob*me, Link_array<Grob> const & cols)
46 space as if this duration is present.
48 Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
49 shortest.set_infinite (1);
52 for (int i =0 ; i < cols.size (); i++)
54 if (Paper_column::musical_b (cols[i]))
56 Moment *when = unsmob_moment (cols[i]->get_grob_property ("when"));
59 ignore grace notes for shortest notes.
61 if (when && when->grace_part_)
64 SCM st = cols[i]->get_grob_property ("shortest-starter-duration");
65 Moment this_shortest = *unsmob_moment (st);
66 shortest = shortest <? this_shortest;
67 if (!mean_shortest.main_part_.infty_b ())
70 mean_shortest += this_shortest;
76 Array<Spring> springs;
77 for (int i= 0; i < cols.size () - 1; i++)
79 Item * l = dynamic_cast<Item*> (cols[i]);
80 Item * r = dynamic_cast<Item*> (cols[i+1]);
81 Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
82 Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
84 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
88 left refers to the space that is associated with items of the left column, so you have
90 LC <- left_space -><- right_space -> RC
94 typically, right_space is non-zero when there are
98 for (int j=0; j < 4; j++)
100 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
101 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
106 s.item_l_drul_[LEFT] = lc;
107 s.item_l_drul_[RIGHT] = rc;
109 SCM hint = lc->get_grob_property ("extra-space");
110 SCM next_hint = rc->get_grob_property ("extra-space");
111 SCM stretch_hint = lc->get_grob_property ("stretch-distance");
112 SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");
114 Real left_distance = 0;
115 if (gh_pair_p (hint))
117 left_distance = gh_scm2double (ly_cdr (hint));
119 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
120 else if (!Paper_column::musical_b (lc) && i+1 < cols.size ())
122 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
124 else if (Paper_column::musical_b ( lc))
126 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
129 programming_error ("uninitialised left_distance");
131 s.distance_f_ = left_distance;
134 Only do tight spaces *after* barlines (breakable columns),
137 We want the space before barline to be like the note
138 spacing in the measure.
140 SCM sfac =lc->get_grob_property ("space-factor");
141 if (gh_number_p (lc->get_grob_property ("column-space-strength"))
142 && (Item::breakable_b (lc) || lc->original_l_))
145 gh_scm2double (lc->get_grob_property ("column-space-strength"));
147 else if (gh_number_p (sfac))
148 left_distance *= gh_scm2double (sfac);
151 Real right_dist = 0.0;
152 if (gh_pair_p (next_hint))
154 right_dist += - gh_scm2double (ly_car (next_hint));
158 Interval ext (rc->extent (rc, X_AXIS));
159 right_dist = ext.empty_b () ? 0.0 : - ext [LEFT];
163 don't want to create too much extra space for accidentals
165 if (Paper_column::musical_b (rc))
167 if (to_boolean (rc->get_grob_property ("contains-grace")))
168 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
170 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
173 s.distance_f_ = left_distance + right_dist;
175 Real stretch_dist = 0.;
176 if (gh_number_p (stretch_hint))
177 stretch_dist += gh_scm2double (stretch_hint);
179 stretch_dist += left_distance;
181 if (gh_pair_p (next_stretch_hint))
182 // see regtest spacing-tight
183 stretch_dist += - gh_scm2double (ly_car (next_stretch_hint));
185 stretch_dist += right_dist;
187 if (s.distance_f_ <0)
189 programming_error ("Negative dist, setting to 1.0 PT");
192 if (stretch_dist == 0.0)
195 \bar "". We give it 0 space, with high strength.
197 s.strength_f_ = 20.0;
200 s.strength_f_ /= stretch_dist;
206 Spacing_spanner::stretch_to_regularity (me, &springs, cols);
207 for (int i=springs.size (); i --;)
208 springs[i].add_to_cols ();
212 Look at COLS, searching for columns that have 'regular-distance-to
213 set. A sequence of columns that have this property set should have
214 an equal distance (an equispaced run). Extract the projected
215 distance from SPRINGS, and scale SPRINGS for the equispaced run, to the
216 widest space necessary.
221 -- inefficient code; maybe it is easier to twiddle with the springs
222 after they've become grob properties (ie. have their
223 minimum-distances set)
225 -- does not adjust strength field of the springs very well: result
226 awkward spacing at the start of a line. (?)
228 -- will be confused when there are multiple equispaced runs in a measure.
230 -- dealing with springs for line breaks is a little tricky; in any
231 case, we will only space per measure.
233 -- we scale to actual distances, not to optical effects. Eg. if the
234 equispaced run contains optical corrections, then the scaling will
237 -- Regular_spacing_engraver doesn't mark the first column of the
238 next bar, making the space before a barline too short, in this case
242 x(8th) x(8th) <- equispaced run.
247 Spacing_spanner::stretch_to_regularity (Grob *me,
248 Array<Spring> * springs,
249 Link_array<Grob> const & cols)
252 Find the starting column of the run. REGULAR-DISTANCE-TO points
253 back to a previous column, so we look ahead to find a column
254 pointing back to the first one.
257 Grob * first_regular_spaced_col = 0;
258 for (int i = 0 ; i < cols.size () && !first_regular_spaced_col; i++)
260 SCM rdt = cols[i]->get_grob_property ("regular-distance-to");
261 if (cols.find_l (unsmob_grob (rdt)))
262 first_regular_spaced_col = unsmob_grob (rdt);
264 for (int i = springs->size (); i-- ;)
265 springs->elem (i).set_to_cols ();
268 for (i = 0; i < springs->size ()
269 && springs->elem (i).item_l_drul_[RIGHT] != first_regular_spaced_col;
274 if (i==springs->size ())
279 Grob *last_col = first_regular_spaced_col;
280 Grob *last_regular_spaced_col = first_regular_spaced_col;
284 find the max distance for this run.
286 for (int j = i; j < springs->size (); j++)
288 Spring *s = &(springs->elem_ref (j));
289 if (s->item_l_drul_[LEFT] != last_col)
292 dist += s->distance_f_;
294 last_col = s->item_l_drul_[RIGHT];
295 SCM rdt = last_col->get_grob_property ("regular-distance-to");
296 if (unsmob_grob (rdt) == last_regular_spaced_col)
298 maxdist = maxdist >? dist;
300 last_regular_spaced_col = last_col;
309 last_col = first_regular_spaced_col;
310 last_regular_spaced_col = first_regular_spaced_col;
311 for (int j = i; j < springs->size (); j++)
313 Spring *s = &springs->elem_ref (j);
314 if (s->item_l_drul_[LEFT] != last_col)
316 dist += s->distance_f_;
318 last_col = s->item_l_drul_[RIGHT];
319 SCM rdt = last_col->get_grob_property ("regular-distance-to");
320 if (unsmob_grob (rdt) == last_regular_spaced_col)
323 springs->elem_ref (i).distance_f_ *= maxdist / dist;
324 springs->elem_ref (i).strength_f_ *= dist / maxdist;
326 last_regular_spaced_col = last_col;
333 Do something if breakable column has no spacing hints set.
336 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
339 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
340 Real durational_distance = 0;
341 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
344 ugh should use shortest_playing distance
346 if (delta_t.to_bool ())
348 durational_distance = get_duration_space (me, delta_t, shortest);
351 return symbol_distance >? durational_distance;
356 Get the measure wide ant for arithmetic spacing.
359 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
360 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
361 The Ohio State University, 1987.
365 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
367 Real log = log_2 (shortest.main_part_);
368 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
371 Rational compdur = d.main_part_ + d.grace_part_ / Rational (3);
372 return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
377 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
380 Moment shortest_playing_len = 0;
381 SCM s = lc->get_grob_property ("shortest-playing-duration");
383 // SCM s = lc->get_grob_property ("mean-playing-duration");
384 if (unsmob_moment (s))
385 shortest_playing_len = *unsmob_moment (s);
387 if (! shortest_playing_len.to_bool ())
389 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
390 shortest_playing_len = 1;
393 if (! shortest.to_bool ())
395 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
398 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
399 Real dist = get_duration_space (me, shortest_playing_len, shortest);
403 ugh: 0.1 is an arbitrary distance.
405 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
406 + 0.1 * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
414 if (delta_t > Moment (Rational (1,32)))
415 dist += stem_dir_correction (me, lc,rc);
418 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
419 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
423 if (lm->grace_part_ && rm->grace_part_)
425 else if (!rm->grace_part_ && lm->grace_part_)
435 Correct for optical illusions. See [Wanske] p. 138. The combination
436 up-stem + down-stem should get extra space, the combination
437 down-stem + up-stem less.
439 This should be more advanced, since relative heights of the note
440 heads also influence required correction.
442 Also might not work correctly in case of multi voices or staff
445 TODO: lookup correction distances? More advanced correction?
446 Possibly turn this off?
448 TODO: have to check wether the stems are in the same staff.
450 This routine reads the DIR-LIST property of both its L and R arguments. */
452 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
454 SCM dl = l->get_grob_property ("dir-list");
455 SCM dr = r->get_grob_property ("dir-list");
457 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
463 assert (gh_number_p (dl) && gh_number_p (dr));
464 int d1 = gh_scm2int (dl);
465 int d2 = gh_scm2int (dr);
471 Real correction = 0.0;
472 Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
474 if (d1 && d2 && d1 * d2 == -1)
476 correction = d1 * ssc;
479 programming_error ("Stem directions not set correctly for optical correction");
484 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
486 Spacing_spanner::set_springs (SCM smob)
488 Grob *me = unsmob_grob (smob);
489 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
493 for (int i = 1; i < all.size (); i++)
496 if (Item::breakable_b (sc))
498 Link_array<Grob> measure (all.slice (j, i+1));
499 do_measure (me, measure);
505 farewell, cruel world
508 return SCM_UNSPECIFIED;
514 maximum-duration-for-spacing
515 From: bf250@freenet.carleton.ca (John Sankey)
516 To: gnu-music-discuss@gnu.org
517 Subject: note spacing suggestion
518 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
520 Currently, Lily spaces notes by starting with a basic distance,
521 arithmetic_multiplier, which it applies to the minimum duration note
522 of the bar. Then she adds a logarithmic increment, scaled from
523 arithmetic_basicspace, for longer notes. (Then, columns are aligned
524 and justified.) Fundamentally, this matches visual spacing to musical
525 weight and works well.
527 A lot of the time in music, I see a section basically in melodic
528 notes that occasionally has a rapid ornamental run (scale). So, there
529 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
530 a return to long notes. Currently, Lily gives the same horizontal
531 space to the 1/32nd notes in their bar (even if set in small size as
532 is commonly done for cadenzii) as she gives to 1/4 notes in bars
533 where 1/4 note is the minimum duration. The resulting visual weight
534 does not match the musical weight over the page.
536 Looking at the music I am typesetting, I feel that Lily's spacing
537 could be significantly improved if, with no change in the basic
538 method used, arithmetic_multiplier could be applied referred to the
539 same duration throughout a piece. Of course, the current method
540 should be retained for those who have already set music in it, so I
541 suggest a property called something like arithmetic_base=16 to fix
542 1/16 duration as the reference for arithmetic_multiplier; the default
543 would be a dynamic base is it is now.
545 Does anyone else feel that this would be a useful improvement for
546 their music? (Of course, if arithmetic_multiplier became a regular
547 property, this could be used to achieve a similar result by