2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2000 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 SCM st = cols[i]->get_grob_property ("shortest-starter-duration");
57 Moment this_shortest = *unsmob_moment(st);
58 shortest = shortest <? this_shortest;
59 if (!mean_shortest.infty_b ())
62 mean_shortest += this_shortest;
69 for (int i= 0; i < cols.size () - 1; i++)
71 Item * l = dynamic_cast<Item*> (cols[i]);
72 Item * r = dynamic_cast<Item*> (cols[i+1]);
73 Item * lb = dynamic_cast<Item*> ( l->find_prebroken_piece (RIGHT));
74 Item * rb = dynamic_cast<Item*> ( r->find_prebroken_piece (LEFT));
76 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
80 left refers to the space that is associated with items of the left column, so you have
82 LC <- left_space -><- right_space -> RC
86 typically, right_space is non-zero when there are
90 for (int j=0; j < 4; j++)
92 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
93 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
98 s.item_l_drul_[LEFT] = lc;
99 s.item_l_drul_[RIGHT] = rc;
101 SCM hint = lc->get_grob_property ("extra-space");
102 SCM next_hint = rc->get_grob_property ("extra-space");
103 SCM stretch_hint = lc->get_grob_property ("stretch-distance");
104 SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");
106 Real left_distance = 0;
107 if (gh_pair_p (hint))
109 left_distance = gh_scm2double (gh_cdr (hint));
111 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
112 else if (!lc->musical_b() && i+1 < cols.size ())
114 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
116 else if (lc->musical_b())
118 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
121 programming_error ("uninitialised left_distance");
123 s.distance_f_ = left_distance;
126 Only do tight spaces *after* barlines (breakable columns),
129 We want the space before barline to be like the note
130 spacing in the measure.
132 SCM sfac =lc->get_grob_property ("space-factor");
133 if (Item::breakable_b (lc) || lc->original_l_)
136 gh_scm2double (lc->get_grob_property ("column-space-strength"));
138 else if (gh_number_p (sfac))
139 left_distance *= gh_scm2double (sfac);
142 Real right_dist = 0.0;
143 if (gh_pair_p (next_hint))
145 right_dist += - gh_scm2double (gh_car (next_hint));
149 Interval ext (rc->extent (rc, X_AXIS));
150 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
154 don't want to create too much extra space for accidentals
156 if (rc->musical_b ())
158 if (to_boolean (rc->get_grob_property ("contains-grace")))
159 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
161 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
164 s.distance_f_ = left_distance + right_dist;
166 Real stretch_dist = 0.;
167 if (gh_number_p (stretch_hint))
168 stretch_dist += gh_scm2double (stretch_hint);
170 stretch_dist += left_distance;
172 if (gh_pair_p (next_stretch_hint))
173 // see regtest spacing-tight
174 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
176 stretch_dist += right_dist;
178 if (s.distance_f_ <0)
180 programming_error("Negative dist, setting to 1.0 PT");
183 if (stretch_dist == 0.0)
186 \bar "". We give it 0 space, with high strength.
188 s.strength_f_ = 20.0;
191 s.strength_f_ /= stretch_dist;
200 Do something if breakable column has no spacing hints set.
203 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
206 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
207 Real durational_distance = 0;
208 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
211 ugh should use shortest_playing distance
215 durational_distance = get_duration_space (me, delta_t, shortest);
218 return symbol_distance >? durational_distance;
223 Get the measure wide ant for arithmetic spacing.
226 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
227 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
228 The Ohio State University, 1987.
232 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
234 Real log = log_2 (shortest);
235 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
238 return (log_2 (d) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
243 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
246 Moment shortest_playing_len = 0;
247 SCM s = lc->get_grob_property ("shortest-playing-duration");
249 // SCM s = lc->get_grob_property ("mean-playing-duration");
250 if (unsmob_moment (s))
251 shortest_playing_len = *unsmob_moment(s);
253 if (! shortest_playing_len)
255 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
256 shortest_playing_len = 1;
261 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
264 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
265 Real dist = get_duration_space (me, shortest_playing_len, shortest);
266 dist *= (double)(delta_t / shortest_playing_len);
272 if (delta_t > Moment (1,32))
273 dist += stem_dir_correction (me, lc,rc);
279 Correct for optical illusions. See [Wanske] p. 138. The combination
280 up-stem + down-stem should get extra space, the combination
281 down-stem + up-stem less.
283 This should be more advanced, since relative heights of the note
284 heads also influence required correction.
286 Also might not work correctly in case of multi voices or staff
289 TODO: lookup correction distances? More advanced correction?
290 Possibly turn this off?
292 TODO: have to check wether the stems are in the same staff.
294 This routine reads the DIR-LIST property of both its L and R arguments. */
296 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
298 SCM dl = l->get_grob_property ("dir-list");
299 SCM dr = r->get_grob_property ("dir-list");
301 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
307 assert (gh_number_p (dl) && gh_number_p(dr));
308 int d1 = gh_scm2int (dl);
309 int d2 = gh_scm2int (dr);
315 Real correction = 0.0;
316 Real ssc = gh_scm2double (me->get_grob_property("stem-spacing-correction"));
318 if (d1 && d2 && d1 * d2 == -1)
320 correction = d1 * ssc;
323 programming_error ("Stem directions not set correctly for optical correction");
328 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs,1);
330 Spacing_spanner::set_springs (SCM smob)
332 Grob *me = unsmob_grob (smob);
333 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
337 for (int i = 1; i < all.size (); i++)
340 if (Item::breakable_b (sc))
342 Link_array<Grob> measure (all.slice (j, i+1));
343 do_measure (me, measure);
349 farewell, cruel world
352 return SCM_UNSPECIFIED;
358 maximum-duration-for-spacing
359 From: bf250@freenet.carleton.ca (John Sankey)
360 To: gnu-music-discuss@gnu.org
361 Subject: note spacing suggestion
362 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
364 Currently, Lily spaces notes by starting with a basic distance,
365 arithmetic_multiplier, which it applies to the minimum duration note
366 of the bar. Then she adds a logarithmic increment, scaled from
367 arithmetic_basicspace, for longer notes. (Then, columns are aligned
368 and justified.) Fundamentally, this matches visual spacing to musical
369 weight and works well.
371 A lot of the time in music, I see a section basically in melodic
372 notes that occasionally has a rapid ornamental run (scale). So, there
373 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
374 a return to long notes. Currently, Lily gives the same horizontal
375 space to the 1/32nd notes in their bar (even if set in small size as
376 is commonly done for cadenzii) as she gives to 1/4 notes in bars
377 where 1/4 note is the minimum duration. The resulting visual weight
378 does not match the musical weight over the page.
380 Looking at the music I am typesetting, I feel that Lily's spacing
381 could be significantly improved if, with no change in the basic
382 method used, arithmetic_multiplier could be applied referred to the
383 same duration throughout a piece. Of course, the current method
384 should be retained for those who have already set music in it, so I
385 suggest a property called something like arithmetic_base=16 to fix
386 1/16 duration as the reference for arithmetic_multiplier; the default
387 would be a dynamic base is it is now.
389 Does anyone else feel that this would be a useful improvement for
390 their music? (Of course, if arithmetic_multiplier became a regular
391 property, this could be used to achieve a similar result by