2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2002 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))
166 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
168 s.distance_f_ = left_distance + right_dist;
170 Real stretch_dist = 0.;
171 if (gh_number_p (stretch_hint))
172 stretch_dist += gh_scm2double (stretch_hint);
174 stretch_dist += left_distance;
176 if (gh_pair_p (next_stretch_hint))
177 // see regtest spacing-tight
178 stretch_dist += - gh_scm2double (ly_car (next_stretch_hint));
180 stretch_dist += right_dist;
182 if (s.distance_f_ <0)
184 programming_error ("Negative dist, setting to 1.0 PT");
187 if (stretch_dist == 0.0)
190 \bar "". We give it 0 space, with high strength.
192 s.strength_f_ = 20.0;
195 s.strength_f_ /= stretch_dist;
201 Spacing_spanner::stretch_to_regularity (me, &springs, cols);
202 for (int i=springs.size (); i --;)
203 springs[i].add_to_cols ();
207 Look at COLS, searching for columns that have 'regular-distance-to
208 set. A sequence of columns that have this property set should have
209 an equal distance (an equispaced run). Extract the projected
210 distance from SPRINGS, and scale SPRINGS for the equispaced run, to the
211 widest space necessary.
216 -- inefficient code; maybe it is easier to twiddle with the springs
217 after they've become grob properties (ie. have their
218 minimum-distances set)
220 -- does not adjust strength field of the springs very well: result
221 awkward spacing at the start of a line. (?)
223 -- will be confused when there are multiple equispaced runs in a measure.
225 -- dealing with springs for line breaks is a little tricky; in any
226 case, we will only space per measure.
228 -- we scale to actual distances, not to optical effects. Eg. if the
229 equispaced run contains optical corrections, then the scaling will
232 -- Regular_spacing_engraver doesn't mark the first column of the
233 next bar, making the space before a barline too short, in this case
237 x(8th) x(8th) <- equispaced run.
242 Spacing_spanner::stretch_to_regularity (Grob *me,
243 Array<Spring> * springs,
244 Link_array<Grob> const & cols)
247 Find the starting column of the run. REGULAR-DISTANCE-TO points
248 back to a previous column, so we look ahead to find a column
249 pointing back to the first one.
252 Grob * first_regular_spaced_col = 0;
253 for (int i = 0 ; i < cols.size () && !first_regular_spaced_col; i++)
255 SCM rdt = cols[i]->get_grob_property ("regular-distance-to");
256 if (cols.find_l (unsmob_grob (rdt)))
257 first_regular_spaced_col = unsmob_grob (rdt);
259 for (int i = springs->size (); i-- ;)
260 springs->elem (i).set_to_cols ();
263 for (i = 0; i < springs->size ()
264 && springs->elem (i).item_l_drul_[RIGHT] != first_regular_spaced_col;
269 if (i==springs->size ())
274 Grob *last_col = first_regular_spaced_col;
275 Grob *last_regular_spaced_col = first_regular_spaced_col;
279 find the max distance for this run.
281 for (int j = i; j < springs->size (); j++)
283 Spring *s = &(springs->elem_ref (j));
284 if (s->item_l_drul_[LEFT] != last_col)
287 dist += s->distance_f_;
289 last_col = s->item_l_drul_[RIGHT];
290 SCM rdt = last_col->get_grob_property ("regular-distance-to");
291 if (unsmob_grob (rdt) == last_regular_spaced_col)
293 maxdist = maxdist >? dist;
295 last_regular_spaced_col = last_col;
304 last_col = first_regular_spaced_col;
305 last_regular_spaced_col = first_regular_spaced_col;
306 for (int j = i; j < springs->size (); j++)
308 Spring *s = &springs->elem_ref (j);
309 if (s->item_l_drul_[LEFT] != last_col)
311 dist += s->distance_f_;
313 last_col = s->item_l_drul_[RIGHT];
314 SCM rdt = last_col->get_grob_property ("regular-distance-to");
315 if (unsmob_grob (rdt) == last_regular_spaced_col)
318 springs->elem_ref (i).distance_f_ *= maxdist / dist;
319 springs->elem_ref (i).strength_f_ *= dist / maxdist;
321 last_regular_spaced_col = last_col;
328 Do something if breakable column has no spacing hints set.
331 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
334 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
335 Real durational_distance = 0;
336 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
339 ugh should use shortest_playing distance
341 if (delta_t.to_bool ())
343 durational_distance = get_duration_space (me, delta_t, shortest);
346 return symbol_distance >? durational_distance;
351 Get the measure wide ant for arithmetic spacing.
354 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
355 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
356 The Ohio State University, 1987.
360 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
362 Real log = log_2 (shortest.main_part_);
363 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
366 Rational compdur = d.main_part_ + d.grace_part_ / Rational (3);
367 return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
372 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
375 Moment shortest_playing_len = 0;
376 SCM s = lc->get_grob_property ("shortest-playing-duration");
378 // SCM s = lc->get_grob_property ("mean-playing-duration");
379 if (unsmob_moment (s))
380 shortest_playing_len = *unsmob_moment (s);
382 if (! shortest_playing_len.to_bool ())
384 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
385 shortest_playing_len = 1;
388 if (! shortest.to_bool ())
390 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
393 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
394 Real dist = get_duration_space (me, shortest_playing_len, shortest);
398 ugh: 0.1 is an arbitrary distance.
400 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
401 + 0.1 * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
405 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
406 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
410 if (lm->grace_part_ && rm->grace_part_)
412 else if (!rm->grace_part_ && lm->grace_part_)
420 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
422 Spacing_spanner::set_springs (SCM smob)
424 Grob *me = unsmob_grob (smob);
425 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
429 for (int i = 1; i < all.size (); i++)
432 if (Item::breakable_b (sc))
434 Link_array<Grob> measure (all.slice (j, i+1));
435 do_measure (me, measure);
441 farewell, cruel world
444 return SCM_UNSPECIFIED;
450 maximum-duration-for-spacing
451 From: bf250@freenet.carleton.ca (John Sankey)
452 To: gnu-music-discuss@gnu.org
453 Subject: note spacing suggestion
454 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
456 Currently, Lily spaces notes by starting with a basic distance,
457 arithmetic_multiplier, which it applies to the minimum duration note
458 of the bar. Then she adds a logarithmic increment, scaled from
459 arithmetic_basicspace, for longer notes. (Then, columns are aligned
460 and justified.) Fundamentally, this matches visual spacing to musical
461 weight and works well.
463 A lot of the time in music, I see a section basically in melodic
464 notes that occasionally has a rapid ornamental run (scale). So, there
465 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
466 a return to long notes. Currently, Lily gives the same horizontal
467 space to the 1/32nd notes in their bar (even if set in small size as
468 is commonly done for cadenzii) as she gives to 1/4 notes in bars
469 where 1/4 note is the minimum duration. The resulting visual weight
470 does not match the musical weight over the page.
472 Looking at the music I am typesetting, I feel that Lily's spacing
473 could be significantly improved if, with no change in the basic
474 method used, arithmetic_multiplier could be applied referred to the
475 same duration throughout a piece. Of course, the current method
476 should be retained for those who have already set music in it, so I
477 suggest a property called something like arithmetic_base=16 to fix
478 1/16 duration as the reference for arithmetic_multiplier; the default
479 would be a dynamic base is it is now.
481 Does anyone else feel that this would be a useful improvement for
482 their music? (Of course, if arithmetic_multiplier became a regular
483 property, this could be used to achieve a similar result by