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");
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 s.distance_f_ = left_distance;
124 Only do tight spaces *after* barlines (breakable columns),
127 We want the space before barline to be like the note
128 spacing in the measure.
130 SCM sfac =lc->get_grob_property ("space-factor");
131 if (Item::breakable_b (lc) || lc->original_l_)
134 gh_scm2double (lc->get_grob_property ("column-space-strength"));
136 else if (gh_number_p (sfac))
137 left_distance *= gh_scm2double (sfac);
140 Real right_dist = 0.0;
141 if (gh_pair_p (next_hint))
143 right_dist += - gh_scm2double (gh_car (next_hint));
147 Interval ext (rc->extent (rc, X_AXIS));
148 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
152 don't want to create too much extra space for accidentals
154 if (rc->musical_b ())
156 if (to_boolean (rc->get_grob_property ("contains-grace")))
157 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
159 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
162 s.distance_f_ = left_distance + right_dist;
164 Real stretch_dist = 0.;
165 if (gh_number_p (stretch_hint))
166 stretch_dist += gh_scm2double (stretch_hint);
168 stretch_dist += left_distance;
170 if (gh_pair_p (next_stretch_hint))
171 // see regtest spacing-tight
172 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
174 stretch_dist += right_dist;
176 if (s.distance_f_ <0)
178 programming_error("Negative dist, setting to 1.0 PT");
181 if (stretch_dist == 0.0)
184 \bar "". We give it 0 space, with high strength.
186 s.strength_f_ = 20.0;
189 s.strength_f_ /= stretch_dist;
198 Do something if breakable column has no spacing hints set.
201 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
204 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
205 Real durational_distance = 0;
206 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
209 ugh should use shortest_playing distance
213 durational_distance = get_duration_space (me, delta_t, shortest);
216 return symbol_distance >? durational_distance;
221 Get the measure wide ant for arithmetic spacing.
224 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
225 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
226 The Ohio State University, 1987.
230 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
232 Real log = log_2 (shortest);
233 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
236 return (log_2 (d) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
241 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
244 Moment shortest_playing_len = 0;
245 SCM s = lc->get_grob_property ("shortest-playing-duration");
247 // SCM s = lc->get_grob_property ("mean-playing-duration");
248 if (unsmob_moment (s))
249 shortest_playing_len = *unsmob_moment(s);
251 if (! shortest_playing_len)
253 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
254 shortest_playing_len = 1;
259 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
262 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
263 Real dist = get_duration_space (me, shortest_playing_len, shortest);
264 dist *= (double)(delta_t / shortest_playing_len);
270 if (delta_t > Moment (1,32))
271 dist += stem_dir_correction (me, lc,rc);
277 Correct for optical illusions. See [Wanske] p. 138. The combination
278 up-stem + down-stem should get extra space, the combination
279 down-stem + up-stem less.
281 This should be more advanced, since relative heights of the note
282 heads also influence required correction.
284 Also might not work correctly in case of multi voices or staff
287 TODO: lookup correction distances? More advanced correction?
288 Possibly turn this off?
290 TODO: have to check wether the stems are in the same staff.
292 This routine reads the DIR-LIST property of both its L and R arguments. */
294 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
296 SCM dl = l->get_grob_property ("dir-list");
297 SCM dr = r->get_grob_property ("dir-list");
299 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
305 assert (gh_number_p (dl) && gh_number_p(dr));
306 int d1 = gh_scm2int (dl);
307 int d2 = gh_scm2int (dr);
313 Real correction = 0.0;
314 Real ssc = gh_scm2double (me->get_grob_property("stem-spacing-correction"));
316 if (d1 && d2 && d1 * d2 == -1)
318 correction = d1 * ssc;
321 programming_error ("Stem directions not set correctly for optical correction");
326 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs,1);
328 Spacing_spanner::set_springs (SCM smob)
330 Grob *me = unsmob_element (smob);
331 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
335 for (int i = 1; i < all.size (); i++)
338 if (Item::breakable_b (sc))
340 Link_array<Grob> measure (all.slice (j, i+1));
341 do_measure (me, measure);
347 farewell, cruel world
350 return SCM_UNSPECIFIED;
356 maximum-duration-for-spacing
357 From: bf250@freenet.carleton.ca (John Sankey)
358 To: gnu-music-discuss@gnu.org
359 Subject: note spacing suggestion
360 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
362 Currently, Lily spaces notes by starting with a basic distance,
363 arithmetic_multiplier, which it applies to the minimum duration note
364 of the bar. Then she adds a logarithmic increment, scaled from
365 arithmetic_basicspace, for longer notes. (Then, columns are aligned
366 and justified.) Fundamentally, this matches visual spacing to musical
367 weight and works well.
369 A lot of the time in music, I see a section basically in melodic
370 notes that occasionally has a rapid ornamental run (scale). So, there
371 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
372 a return to long notes. Currently, Lily gives the same horizontal
373 space to the 1/32nd notes in their bar (even if set in small size as
374 is commonly done for cadenzii) as she gives to 1/4 notes in bars
375 where 1/4 note is the minimum duration. The resulting visual weight
376 does not match the musical weight over the page.
378 Looking at the music I am typesetting, I feel that Lily's spacing
379 could be significantly improved if, with no change in the basic
380 method used, arithmetic_multiplier could be applied referred to the
381 same duration throughout a piece. Of course, the current method
382 should be retained for those who have already set music in it, so I
383 suggest a property called something like arithmetic_base=16 to fix
384 1/16 duration as the reference for arithmetic_multiplier; the default
385 would be a dynamic base is it is now.
387 Does anyone else feel that this would be a useful improvement for
388 their music? (Of course, if arithmetic_multiplier became a regular
389 property, this could be used to achieve a similar result by