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> 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 (dynamic_cast<Paper_column*> (cols[i])->musical_b ())
56 Moment *when = unsmob_moment (cols[i]->get_grob_property ("when"));
59 ignore grace notes for shortest notes.
61 if (when && when->grace_mom_)
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 for (int i= 0; i < cols.size () - 1; i++)
78 Item * l = dynamic_cast<Item*> (cols[i]);
79 Item * r = dynamic_cast<Item*> (cols[i+1]);
80 Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
81 Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
83 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
87 left refers to the space that is associated with items of the left column, so you have
89 LC <- left_space -><- right_space -> RC
93 typically, right_space is non-zero when there are
97 for (int j=0; j < 4; j++)
99 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
100 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
105 s.item_l_drul_[LEFT] = lc;
106 s.item_l_drul_[RIGHT] = rc;
108 SCM hint = lc->get_grob_property ("extra-space");
109 SCM next_hint = rc->get_grob_property ("extra-space");
110 SCM stretch_hint = lc->get_grob_property ("stretch-distance");
111 SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");
113 Real left_distance = 0;
114 if (gh_pair_p (hint))
116 left_distance = gh_scm2double (gh_cdr (hint));
118 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
119 else if (!lc->musical_b () && i+1 < cols.size ())
121 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
123 else if (lc->musical_b ())
125 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
128 programming_error ("uninitialised left_distance");
130 s.distance_f_ = left_distance;
133 Only do tight spaces *after* barlines (breakable columns),
136 We want the space before barline to be like the note
137 spacing in the measure.
139 SCM sfac =lc->get_grob_property ("space-factor");
140 if (gh_number_p (lc->get_grob_property ("column-space-strength"))
141 && (Item::breakable_b (lc) || lc->original_l_))
144 gh_scm2double (lc->get_grob_property ("column-space-strength"));
146 else if (gh_number_p (sfac))
147 left_distance *= gh_scm2double (sfac);
150 Real right_dist = 0.0;
151 if (gh_pair_p (next_hint))
153 right_dist += - gh_scm2double (gh_car (next_hint));
157 Interval ext (rc->extent (rc, X_AXIS));
158 right_dist = ext.empty_b () ? 0.0 : - ext [LEFT];
162 don't want to create too much extra space for accidentals
164 if (rc->musical_b ())
166 if (to_boolean (rc->get_grob_property ("contains-grace")))
167 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
169 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
172 s.distance_f_ = left_distance + right_dist;
174 Real stretch_dist = 0.;
175 if (gh_number_p (stretch_hint))
176 stretch_dist += gh_scm2double (stretch_hint);
178 stretch_dist += left_distance;
180 if (gh_pair_p (next_stretch_hint))
181 // see regtest spacing-tight
182 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
184 stretch_dist += right_dist;
186 if (s.distance_f_ <0)
188 programming_error ("Negative dist, setting to 1.0 PT");
191 if (stretch_dist == 0.0)
194 \bar "". We give it 0 space, with high strength.
196 s.strength_f_ = 20.0;
199 s.strength_f_ /= stretch_dist;
208 Do something if breakable column has no spacing hints set.
211 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
214 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
215 Real durational_distance = 0;
216 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
219 ugh should use shortest_playing distance
223 durational_distance = get_duration_space (me, delta_t, shortest);
226 return symbol_distance >? durational_distance;
231 Get the measure wide ant for arithmetic spacing.
234 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
235 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
236 The Ohio State University, 1987.
240 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
242 Real log = log_2 (shortest);
243 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
246 return (log_2 (d) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
251 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
254 Moment shortest_playing_len = 0;
255 SCM s = lc->get_grob_property ("shortest-playing-duration");
257 // SCM s = lc->get_grob_property ("mean-playing-duration");
258 if (unsmob_moment (s))
259 shortest_playing_len = *unsmob_moment (s);
261 if (! shortest_playing_len)
263 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
264 shortest_playing_len = 1;
269 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
272 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
273 Real dist = get_duration_space (me, shortest_playing_len, shortest);
274 dist *= (double) (delta_t / shortest_playing_len);
282 if (delta_t > Moment (1,32))
283 dist += stem_dir_correction (me, lc,rc);
286 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
287 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
291 if (lm->grace_mom_ && rm->grace_mom_)
293 else if (!rm->grace_mom_ && lm->grace_mom_)
303 Correct for optical illusions. See [Wanske] p. 138. The combination
304 up-stem + down-stem should get extra space, the combination
305 down-stem + up-stem less.
307 This should be more advanced, since relative heights of the note
308 heads also influence required correction.
310 Also might not work correctly in case of multi voices or staff
313 TODO: lookup correction distances? More advanced correction?
314 Possibly turn this off?
316 TODO: have to check wether the stems are in the same staff.
318 This routine reads the DIR-LIST property of both its L and R arguments. */
320 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
322 SCM dl = l->get_grob_property ("dir-list");
323 SCM dr = r->get_grob_property ("dir-list");
325 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
331 assert (gh_number_p (dl) && gh_number_p (dr));
332 int d1 = gh_scm2int (dl);
333 int d2 = gh_scm2int (dr);
339 Real correction = 0.0;
340 Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
342 if (d1 && d2 && d1 * d2 == -1)
344 correction = d1 * ssc;
347 programming_error ("Stem directions not set correctly for optical correction");
352 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
354 Spacing_spanner::set_springs (SCM smob)
356 Grob *me = unsmob_grob (smob);
357 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
361 for (int i = 1; i < all.size (); i++)
364 if (Item::breakable_b (sc))
366 Link_array<Grob> measure (all.slice (j, i+1));
367 do_measure (me, measure);
373 farewell, cruel world
376 return SCM_UNSPECIFIED;
382 maximum-duration-for-spacing
383 From: bf250@freenet.carleton.ca (John Sankey)
384 To: gnu-music-discuss@gnu.org
385 Subject: note spacing suggestion
386 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
388 Currently, Lily spaces notes by starting with a basic distance,
389 arithmetic_multiplier, which it applies to the minimum duration note
390 of the bar. Then she adds a logarithmic increment, scaled from
391 arithmetic_basicspace, for longer notes. (Then, columns are aligned
392 and justified.) Fundamentally, this matches visual spacing to musical
393 weight and works well.
395 A lot of the time in music, I see a section basically in melodic
396 notes that occasionally has a rapid ornamental run (scale). So, there
397 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
398 a return to long notes. Currently, Lily gives the same horizontal
399 space to the 1/32nd notes in their bar (even if set in small size as
400 is commonly done for cadenzii) as she gives to 1/4 notes in bars
401 where 1/4 note is the minimum duration. The resulting visual weight
402 does not match the musical weight over the page.
404 Looking at the music I am typesetting, I feel that Lily's spacing
405 could be significantly improved if, with no change in the basic
406 method used, arithmetic_multiplier could be applied referred to the
407 same duration throughout a piece. Of course, the current method
408 should be retained for those who have already set music in it, so I
409 suggest a property called something like arithmetic_base=16 to fix
410 1/16 duration as the reference for arithmetic_multiplier; the default
411 would be a dynamic base is it is now.
413 Does anyone else feel that this would be a useful improvement for
414 their music? (Of course, if arithmetic_multiplier became a regular
415 property, this could be used to achieve a similar result by