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) ;
36 column_compare (Grob *const &t1, Grob *const &t2)
38 return Moment::compare (Paper_column::when_mom (t1),
39 Paper_column::when_mom (t2));
44 run_length (Moment dt, int i, Array<Moment> const &moms,
45 Link_array<Note_run> runs)
53 Moment next = moms[i] + dt;
54 while (i < moms.size () && moms[i] < next)
56 if (i == moms.size () || moms[i] != next)
63 Moment dur = idxes.size ()
67 find_runs (Grob*me, Link_array<Grob> cols)
69 Link_array<Grob> filter_cols;
70 Array<Moment> col_moments;
71 for (int i = 0; i < cols.size (); i++)
73 Moment w = Paper_column::when_mom (cols[i]);
75 if (!w.grace_part_ && Paper_column::musical_b (cols[i]))
77 filter_cols.push (cols[i]);
82 Moment end_mom = col_moments.top ();
83 for (int i = 0; i < col_moments.size () ; i++)
85 for (int j = i+1; j < col_moments.size (); j++)
87 Moment dt = Paper_column::col_momentsfilter_cols
95 The algorithm is partly taken from :
97 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
98 OSU-CISRC-10/87-TR35, Department of Computer and Information
99 Science, The Ohio State University, 1987.
107 Spacing_spanner::do_measure (Grob*me, Link_array<Grob> const & cols)
110 Moment mean_shortest;
113 space as if this duration is present.
115 Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
116 shortest.set_infinite (1);
119 for (int i =0 ; i < cols.size (); i++)
121 if (Paper_column::musical_b (cols[i]))
123 Moment *when = unsmob_moment (cols[i]->get_grob_property ("when"));
126 ignore grace notes for shortest notes.
128 if (when && when->grace_part_)
131 SCM st = cols[i]->get_grob_property ("shortest-starter-duration");
132 Moment this_shortest = *unsmob_moment (st);
133 shortest = shortest <? this_shortest;
134 if (!mean_shortest.main_part_.infty_b ())
137 mean_shortest += this_shortest;
143 Array<Spring> springs;
144 for (int i= 0; i < cols.size () - 1; i++)
146 Item * l = dynamic_cast<Item*> (cols[i]);
147 Item * r = dynamic_cast<Item*> (cols[i+1]);
148 Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
149 Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
151 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
155 left refers to the space that is associated with items of the left column, so you have
157 LC <- left_space -><- right_space -> RC
161 typically, right_space is non-zero when there are
165 for (int j=0; j < 4; j++)
167 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
168 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
173 s.item_l_drul_[LEFT] = lc;
174 s.item_l_drul_[RIGHT] = rc;
176 SCM hint = lc->get_grob_property ("extra-space");
177 SCM next_hint = rc->get_grob_property ("extra-space");
178 SCM stretch_hint = lc->get_grob_property ("stretch-distance");
179 SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");
181 Real left_distance = 0;
182 if (gh_pair_p (hint))
184 left_distance = gh_scm2double (gh_cdr (hint));
186 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
187 else if (!Paper_column::musical_b (lc) && i+1 < cols.size ())
189 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
191 else if (Paper_column::musical_b ( lc))
193 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
196 programming_error ("uninitialised left_distance");
198 s.distance_f_ = left_distance;
201 Only do tight spaces *after* barlines (breakable columns),
204 We want the space before barline to be like the note
205 spacing in the measure.
207 SCM sfac =lc->get_grob_property ("space-factor");
208 if (gh_number_p (lc->get_grob_property ("column-space-strength"))
209 && (Item::breakable_b (lc) || lc->original_l_))
212 gh_scm2double (lc->get_grob_property ("column-space-strength"));
214 else if (gh_number_p (sfac))
215 left_distance *= gh_scm2double (sfac);
218 Real right_dist = 0.0;
219 if (gh_pair_p (next_hint))
221 right_dist += - gh_scm2double (gh_car (next_hint));
225 Interval ext (rc->extent (rc, X_AXIS));
226 right_dist = ext.empty_b () ? 0.0 : - ext [LEFT];
230 don't want to create too much extra space for accidentals
232 if (Paper_column::musical_b (rc))
234 if (to_boolean (rc->get_grob_property ("contains-grace")))
235 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
237 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
240 s.distance_f_ = left_distance + right_dist;
242 Real stretch_dist = 0.;
243 if (gh_number_p (stretch_hint))
244 stretch_dist += gh_scm2double (stretch_hint);
246 stretch_dist += left_distance;
248 if (gh_pair_p (next_stretch_hint))
249 // see regtest spacing-tight
250 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
252 stretch_dist += right_dist;
254 if (s.distance_f_ <0)
256 programming_error ("Negative dist, setting to 1.0 PT");
259 if (stretch_dist == 0.0)
262 \bar "". We give it 0 space, with high strength.
264 s.strength_f_ = 20.0;
267 s.strength_f_ /= stretch_dist;
273 Spacing_spanner::stretch_to_regularity (me, &springs, cols);
274 for (int i=springs.size (); i --;)
275 springs[i].add_to_cols ();
279 Look at COLS, searching for columns that have 'regular-distance-to
280 set. A sequence of columns that have this property set should have
281 an equal distance (an equispaced run). Extract the projected
282 distance from SPRINGS, and scale SPRINGS for the equispaced run, to the
283 widest space necessary.
288 -- inefficient code; maybe it is easier to twiddle with the springs
289 after they've become grob properties (ie. have their
290 minimum-distances set)
292 -- does not adjust strength field of the springs very well: result
293 awkward spacing at the start of a line. (?)
295 -- will be confused when there are multiple equispaced runs in a measure.
297 -- dealing with springs for line breaks is a little tricky; in any
298 case, we will only space per measure.
300 -- we scale to actual distances, not to optical effects. Eg. if the
301 equispaced run contains optical corrections, then the scaling will
304 -- Regular_spacing_engraver doesn't mark the first column of the
305 next bar, making the space before a barline too short, in this case
309 x(8th) x(8th) <- equispaced run.
314 Spacing_spanner::stretch_to_regularity (Grob *me,
315 Array<Spring> * springs,
316 Link_array<Grob> const & cols)
319 Find the starting column of the run. REGULAR-DISTANCE-TO points
320 back to a previous column, so we look ahead to find a column
321 pointing back to the first one.
324 Grob * first_regular_spaced_col = 0;
325 for (int i = 0 ; i < cols.size () && !first_regular_spaced_col; i++)
327 SCM rdt = cols[i]->get_grob_property ("regular-distance-to");
328 if (cols.find_l (unsmob_grob (rdt)))
329 first_regular_spaced_col = unsmob_grob (rdt);
331 for (int i = springs->size (); i-- ;)
332 springs->elem (i).set_to_cols ();
335 for (i = 0; i < springs->size ()
336 && springs->elem (i).item_l_drul_[RIGHT] != first_regular_spaced_col;
341 if (i==springs->size ())
346 Grob *last_col = first_regular_spaced_col;
347 Grob *last_regular_spaced_col = first_regular_spaced_col;
351 find the max distance for this run.
353 for (int j = i; j < springs->size (); j++)
355 Spring *s = &(springs->elem_ref (j));
356 if (s->item_l_drul_[LEFT] != last_col)
359 dist += s->distance_f_;
361 last_col = s->item_l_drul_[RIGHT];
362 SCM rdt = last_col->get_grob_property ("regular-distance-to");
363 if (unsmob_grob (rdt) == last_regular_spaced_col)
365 maxdist = maxdist >? dist;
367 last_regular_spaced_col = last_col;
376 last_col = first_regular_spaced_col;
377 last_regular_spaced_col = first_regular_spaced_col;
378 for (int j = i; j < springs->size (); j++)
380 Spring *s = &springs->elem_ref (j);
381 if (s->item_l_drul_[LEFT] != last_col)
383 dist += s->distance_f_;
385 last_col = s->item_l_drul_[RIGHT];
386 SCM rdt = last_col->get_grob_property ("regular-distance-to");
387 if (unsmob_grob (rdt) == last_regular_spaced_col)
390 springs->elem_ref (i).distance_f_ *= maxdist / dist;
391 springs->elem_ref (i).strength_f_ *= dist / maxdist;
393 last_regular_spaced_col = last_col;
400 Do something if breakable column has no spacing hints set.
403 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
406 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
407 Real durational_distance = 0;
408 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
411 ugh should use shortest_playing distance
415 durational_distance = get_duration_space (me, delta_t, shortest);
418 return symbol_distance >? durational_distance;
423 Get the measure wide ant for arithmetic spacing.
426 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
427 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
428 The Ohio State University, 1987.
432 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
434 Real log = log_2 (shortest);
435 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
438 return (log_2 (d) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
443 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
446 Moment shortest_playing_len = 0;
447 SCM s = lc->get_grob_property ("shortest-playing-duration");
449 // SCM s = lc->get_grob_property ("mean-playing-duration");
450 if (unsmob_moment (s))
451 shortest_playing_len = *unsmob_moment (s);
453 if (! shortest_playing_len)
455 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
456 shortest_playing_len = 1;
461 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
464 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
465 Real dist = get_duration_space (me, shortest_playing_len, shortest);
469 ugh: 0.1 is an arbitrary distance.
471 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
472 + 0.1 * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
480 if (delta_t > Moment (1,32))
481 dist += stem_dir_correction (me, lc,rc);
484 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
485 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
489 if (lm->grace_part_ && rm->grace_part_)
491 else if (!rm->grace_part_ && lm->grace_part_)
501 Correct for optical illusions. See [Wanske] p. 138. The combination
502 up-stem + down-stem should get extra space, the combination
503 down-stem + up-stem less.
505 This should be more advanced, since relative heights of the note
506 heads also influence required correction.
508 Also might not work correctly in case of multi voices or staff
511 TODO: lookup correction distances? More advanced correction?
512 Possibly turn this off?
514 TODO: have to check wether the stems are in the same staff.
516 This routine reads the DIR-LIST property of both its L and R arguments. */
518 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
520 SCM dl = l->get_grob_property ("dir-list");
521 SCM dr = r->get_grob_property ("dir-list");
523 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
529 assert (gh_number_p (dl) && gh_number_p (dr));
530 int d1 = gh_scm2int (dl);
531 int d2 = gh_scm2int (dr);
537 Real correction = 0.0;
538 Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
540 if (d1 && d2 && d1 * d2 == -1)
542 correction = d1 * ssc;
545 programming_error ("Stem directions not set correctly for optical correction");
550 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
552 Spacing_spanner::set_springs (SCM smob)
554 Grob *me = unsmob_grob (smob);
555 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
559 for (int i = 1; i < all.size (); i++)
562 if (Item::breakable_b (sc))
564 Link_array<Grob> measure (all.slice (j, i+1));
565 do_measure (me, measure);
571 farewell, cruel world
574 return SCM_UNSPECIFIED;
580 maximum-duration-for-spacing
581 From: bf250@freenet.carleton.ca (John Sankey)
582 To: gnu-music-discuss@gnu.org
583 Subject: note spacing suggestion
584 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
586 Currently, Lily spaces notes by starting with a basic distance,
587 arithmetic_multiplier, which it applies to the minimum duration note
588 of the bar. Then she adds a logarithmic increment, scaled from
589 arithmetic_basicspace, for longer notes. (Then, columns are aligned
590 and justified.) Fundamentally, this matches visual spacing to musical
591 weight and works well.
593 A lot of the time in music, I see a section basically in melodic
594 notes that occasionally has a rapid ornamental run (scale). So, there
595 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
596 a return to long notes. Currently, Lily gives the same horizontal
597 space to the 1/32nd notes in their bar (even if set in small size as
598 is commonly done for cadenzii) as she gives to 1/4 notes in bars
599 where 1/4 note is the minimum duration. The resulting visual weight
600 does not match the musical weight over the page.
602 Looking at the music I am typesetting, I feel that Lily's spacing
603 could be significantly improved if, with no change in the basic
604 method used, arithmetic_multiplier could be applied referred to the
605 same duration throughout a piece. Of course, the current method
606 should be retained for those who have already set music in it, so I
607 suggest a property called something like arithmetic_base=16 to fix
608 1/16 duration as the reference for arithmetic_multiplier; the default
609 would be a dynamic base is it is now.
611 Does anyone else feel that this would be a useful improvement for
612 their music? (Of course, if arithmetic_multiplier became a regular
613 property, this could be used to achieve a similar result by