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_mom_ && 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> 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_mom_)
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 for (int i= 0; i < cols.size () - 1; i++)
145 Item * l = dynamic_cast<Item*> (cols[i]);
146 Item * r = dynamic_cast<Item*> (cols[i+1]);
147 Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
148 Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
150 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
154 left refers to the space that is associated with items of the left column, so you have
156 LC <- left_space -><- right_space -> RC
160 typically, right_space is non-zero when there are
164 for (int j=0; j < 4; j++)
166 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
167 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
172 s.item_l_drul_[LEFT] = lc;
173 s.item_l_drul_[RIGHT] = rc;
175 SCM hint = lc->get_grob_property ("extra-space");
176 SCM next_hint = rc->get_grob_property ("extra-space");
177 SCM stretch_hint = lc->get_grob_property ("stretch-distance");
178 SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");
180 Real left_distance = 0;
181 if (gh_pair_p (hint))
183 left_distance = gh_scm2double (gh_cdr (hint));
185 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
186 else if (!Paper_column::musical_b (lc) && i+1 < cols.size ())
188 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
190 else if (Paper_column::musical_b ( lc))
192 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
195 programming_error ("uninitialised left_distance");
197 s.distance_f_ = left_distance;
200 Only do tight spaces *after* barlines (breakable columns),
203 We want the space before barline to be like the note
204 spacing in the measure.
206 SCM sfac =lc->get_grob_property ("space-factor");
207 if (gh_number_p (lc->get_grob_property ("column-space-strength"))
208 && (Item::breakable_b (lc) || lc->original_l_))
211 gh_scm2double (lc->get_grob_property ("column-space-strength"));
213 else if (gh_number_p (sfac))
214 left_distance *= gh_scm2double (sfac);
217 Real right_dist = 0.0;
218 if (gh_pair_p (next_hint))
220 right_dist += - gh_scm2double (gh_car (next_hint));
224 Interval ext (rc->extent (rc, X_AXIS));
225 right_dist = ext.empty_b () ? 0.0 : - ext [LEFT];
229 don't want to create too much extra space for accidentals
231 if (Paper_column::musical_b (rc))
233 if (to_boolean (rc->get_grob_property ("contains-grace")))
234 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
236 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
239 s.distance_f_ = left_distance + right_dist;
241 Real stretch_dist = 0.;
242 if (gh_number_p (stretch_hint))
243 stretch_dist += gh_scm2double (stretch_hint);
245 stretch_dist += left_distance;
247 if (gh_pair_p (next_stretch_hint))
248 // see regtest spacing-tight
249 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
251 stretch_dist += right_dist;
253 if (s.distance_f_ <0)
255 programming_error ("Negative dist, setting to 1.0 PT");
258 if (stretch_dist == 0.0)
261 \bar "". We give it 0 space, with high strength.
263 s.strength_f_ = 20.0;
266 s.strength_f_ /= stretch_dist;
275 Do something if breakable column has no spacing hints set.
278 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
281 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
282 Real durational_distance = 0;
283 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
286 ugh should use shortest_playing distance
290 durational_distance = get_duration_space (me, delta_t, shortest);
293 return symbol_distance >? durational_distance;
298 Get the measure wide ant for arithmetic spacing.
301 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
302 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
303 The Ohio State University, 1987.
307 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
309 Real log = log_2 (shortest);
310 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
313 return (log_2 (d) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
318 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
321 Moment shortest_playing_len = 0;
322 SCM s = lc->get_grob_property ("shortest-playing-duration");
324 // SCM s = lc->get_grob_property ("mean-playing-duration");
325 if (unsmob_moment (s))
326 shortest_playing_len = *unsmob_moment (s);
328 if (! shortest_playing_len)
330 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
331 shortest_playing_len = 1;
336 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
339 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
340 Real dist = get_duration_space (me, shortest_playing_len, shortest);
344 ugh: 0.1 is an arbitrary distance.
346 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
347 + 0.1 * (double) (delta_t.grace_mom_ / shortest_playing_len.main_part_);
355 if (delta_t > Moment (1,32))
356 dist += stem_dir_correction (me, lc,rc);
359 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
360 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
364 if (lm->grace_mom_ && rm->grace_mom_)
366 else if (!rm->grace_mom_ && lm->grace_mom_)
376 Correct for optical illusions. See [Wanske] p. 138. The combination
377 up-stem + down-stem should get extra space, the combination
378 down-stem + up-stem less.
380 This should be more advanced, since relative heights of the note
381 heads also influence required correction.
383 Also might not work correctly in case of multi voices or staff
386 TODO: lookup correction distances? More advanced correction?
387 Possibly turn this off?
389 TODO: have to check wether the stems are in the same staff.
391 This routine reads the DIR-LIST property of both its L and R arguments. */
393 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
395 SCM dl = l->get_grob_property ("dir-list");
396 SCM dr = r->get_grob_property ("dir-list");
398 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
404 assert (gh_number_p (dl) && gh_number_p (dr));
405 int d1 = gh_scm2int (dl);
406 int d2 = gh_scm2int (dr);
412 Real correction = 0.0;
413 Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
415 if (d1 && d2 && d1 * d2 == -1)
417 correction = d1 * ssc;
420 programming_error ("Stem directions not set correctly for optical correction");
425 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
427 Spacing_spanner::set_springs (SCM smob)
429 Grob *me = unsmob_grob (smob);
430 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
434 for (int i = 1; i < all.size (); i++)
437 if (Item::breakable_b (sc))
439 Link_array<Grob> measure (all.slice (j, i+1));
440 do_measure (me, measure);
446 farewell, cruel world
449 return SCM_UNSPECIFIED;
455 maximum-duration-for-spacing
456 From: bf250@freenet.carleton.ca (John Sankey)
457 To: gnu-music-discuss@gnu.org
458 Subject: note spacing suggestion
459 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
461 Currently, Lily spaces notes by starting with a basic distance,
462 arithmetic_multiplier, which it applies to the minimum duration note
463 of the bar. Then she adds a logarithmic increment, scaled from
464 arithmetic_basicspace, for longer notes. (Then, columns are aligned
465 and justified.) Fundamentally, this matches visual spacing to musical
466 weight and works well.
468 A lot of the time in music, I see a section basically in melodic
469 notes that occasionally has a rapid ornamental run (scale). So, there
470 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
471 a return to long notes. Currently, Lily gives the same horizontal
472 space to the 1/32nd notes in their bar (even if set in small size as
473 is commonly done for cadenzii) as she gives to 1/4 notes in bars
474 where 1/4 note is the minimum duration. The resulting visual weight
475 does not match the musical weight over the page.
477 Looking at the music I am typesetting, I feel that Lily's spacing
478 could be significantly improved if, with no change in the basic
479 method used, arithmetic_multiplier could be applied referred to the
480 same duration throughout a piece. Of course, the current method
481 should be retained for those who have already set music in it, so I
482 suggest a property called something like arithmetic_base=16 to fix
483 1/16 duration as the reference for arithmetic_multiplier; the default
484 would be a dynamic base is it is now.
486 Does anyone else feel that this would be a useful improvement for
487 their music? (Of course, if arithmetic_multiplier became a regular
488 property, this could be used to achieve a similar result by