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 (Score_element*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 (Score_element*me, Link_array<Score_element> cols)
46 space as if this duration is present.
48 Moment base_shortest_duration = *unsmob_moment (me->get_elt_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_elt_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_elt_property ("extra-space");
102 SCM next_hint = rc->get_elt_property ("extra-space");
103 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
104 SCM next_stretch_hint = rc->get_elt_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_elt_property ("space-factor");
131 if (Item::breakable_b (lc) || lc->original_l_)
134 gh_scm2double (lc->get_elt_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_elt_property ("contains-grace")))
157 right_dist *= gh_scm2double (rc->get_elt_property ("before-grace-spacing-factor")); // fixme.
159 right_dist *= gh_scm2double (lc->get_elt_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 (Score_element*me, Score_element *lc, Score_element *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 (Score_element*me, Moment d, Moment shortest)
232 Real log = log_2 (shortest);
233 Real k = gh_scm2double (me->get_elt_property ("arithmetic-basicspace"))
236 return (log_2 (d) + k) * gh_scm2double (me->get_elt_property ("arithmetic-multiplier")) * me->paper_l ()->get_var ("staffspace");
241 Spacing_spanner::note_spacing (Score_element*me, Score_element *lc, Score_element *rc,
244 Moment shortest_playing_len = 0;
245 SCM s = lc->get_elt_property ("shortest-playing-duration");
247 // SCM s = lc->get_elt_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 (Score_element*me, Score_element*l, Score_element*r)
296 SCM dl = l->get_elt_property ("dir-list");
297 SCM dr = r->get_elt_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_elt_property("stem-spacing-correction"));
316 ssc *= me->paper_l ()->get_var ("staffspace");
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 Score_element *me = unsmob_element (smob);
333 Link_array<Score_element> all (me->pscore_l_->line_l_->column_l_arr ()) ;
337 for (int i = 1; i < all.size (); i++)
339 Score_element *sc = all[i];
340 if (Item::breakable_b (sc))
342 Link_array<Score_element> 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