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;
68 Real non_musical_space_strength = me->paper_l ()->get_var ("breakable_column_space_strength");
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_)
132 s.strength_f_ = non_musical_space_strength;
133 else if (gh_number_p (sfac))
134 left_distance *= gh_scm2double (sfac);
137 Real right_dist = 0.0;
138 if (gh_pair_p (next_hint))
140 right_dist += - gh_scm2double (gh_car (next_hint));
144 Interval ext (rc->extent (rc, X_AXIS));
145 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
149 don't want to create too much extra space for accidentals
151 if (rc->musical_b ())
153 if (to_boolean (rc->get_elt_property ("contains-grace")))
154 right_dist *= me->paper_l ()->get_var ("before_grace_spacing_factor"); // fixme.
156 right_dist *= gh_scm2double (lc->get_elt_property ("before-musical-spacing-factor"));
159 s.distance_f_ = left_distance + right_dist;
161 Real stretch_dist = 0.;
162 if (gh_number_p (stretch_hint))
163 stretch_dist += gh_scm2double (stretch_hint);
165 stretch_dist += left_distance;
167 if (gh_pair_p (next_stretch_hint))
168 // see regtest spacing-tight
169 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
171 stretch_dist += right_dist;
173 if (s.distance_f_ <0)
175 programming_error("Negative dist, setting to 1.0 PT");
178 if (stretch_dist == 0.0)
181 \bar "". We give it 0 space, with high strength.
183 s.strength_f_ = 20.0;
186 s.strength_f_ /= stretch_dist;
195 Do something if breakable column has no spacing hints set.
198 Spacing_spanner::default_bar_spacing (Score_element*me, Score_element *lc, Score_element *rc,
201 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
202 Real durational_distance = 0;
203 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
206 ugh should use shortest_playing distance
210 durational_distance = get_duration_space (me, delta_t, shortest);
213 return symbol_distance >? durational_distance;
218 Get the measure wide ant for arithmetic spacing.
221 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
222 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
223 The Ohio State University, 1987.
227 Spacing_spanner::get_duration_space (Score_element*me, Moment d, Moment shortest)
229 Real log = log_2 (shortest);
230 Real k= me->paper_l ()->get_var ("arithmetic_basicspace")
233 return (log_2 (d) + k) * me->paper_l ()->get_var ("arithmetic_multiplier");
238 Spacing_spanner::note_spacing (Score_element*me, Score_element *lc, Score_element *rc,
241 Moment shortest_playing_len = 0;
242 SCM s = lc->get_elt_property ("shortest-playing-duration");
244 // SCM s = lc->get_elt_property ("mean-playing-duration");
245 if (unsmob_moment (s))
246 shortest_playing_len = *unsmob_moment(s);
248 if (! shortest_playing_len)
250 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
251 shortest_playing_len = 1;
256 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
259 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
260 Real dist = get_duration_space (me, shortest_playing_len, shortest);
261 dist *= (double)(delta_t / shortest_playing_len);
267 if (delta_t > Moment (1,32))
268 dist += stem_dir_correction (me, lc,rc);
274 Correct for optical illusions. See [Wanske] p. 138. The combination
275 up-stem + down-stem should get extra space, the combination
276 down-stem + up-stem less.
278 This should be more advanced, since relative heights of the note
279 heads also influence required correction.
281 Also might not work correctly in case of multi voices or staff
284 TODO: lookup correction distances? More advanced correction?
285 Possibly turn this off?
287 TODO: have to check wether the stems are in the same staff.
289 This routine reads the DIR-LIST property of both its L and R arguments. */
291 Spacing_spanner::stem_dir_correction (Score_element*me, Score_element*l, Score_element*r)
293 SCM dl = l->get_elt_property ("dir-list");
294 SCM dr = r->get_elt_property ("dir-list");
296 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
302 assert (gh_number_p (dl) && gh_number_p(dr));
303 int d1 = gh_scm2int (dl);
304 int d2 = gh_scm2int (dr);
310 Real correction = 0.0;
311 Real ssc = me->paper_l ()->get_var("stemSpacingCorrection");
314 if (d1 && d2 && d1 * d2 == -1)
316 correction = d1 * ssc;
319 programming_error ("Stem directions not set correctly for optical correction");
324 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs,1);
326 Spacing_spanner::set_springs (SCM smob)
328 Score_element *me = unsmob_element (smob);
329 Link_array<Score_element> all (me->pscore_l_->line_l_->column_l_arr ()) ;
333 for (int i = 1; i < all.size (); i++)
335 Score_element *sc = all[i];
336 if (Item::breakable_b (sc))
338 Link_array<Score_element> measure (all.slice (j, i+1));
339 do_measure (me, measure);
345 farewell, cruel world
348 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