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}};
78 for (int j=0; j < 4; j++)
80 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
81 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
86 s.item_l_drul_[LEFT] = lc;
87 s.item_l_drul_[RIGHT] = rc;
89 SCM hint = lc->get_elt_property ("extra-space");
90 SCM next_hint = rc->get_elt_property ("extra-space");
91 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
92 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
97 left_distance = gh_scm2double (gh_cdr (hint));
99 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
100 else if (!lc->musical_b() && i+1 < cols.size ())
102 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
104 else if (lc->musical_b())
106 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
109 s.distance_f_ = left_distance;
112 Only do tight spaces *after* barlines (breakable columns),
115 We want the space before barline to be like the note
116 spacing in the measure.
118 if (Item::breakable_b (lc) || lc->original_l_)
119 s.strength_f_ = non_musical_space_strength;
120 else if (!lc->musical_b ())
121 left_distance *= me->paper_l ()->get_var ("decrease_nonmus_spacing_factor");
124 Real right_dist = 0.0;
125 if (gh_pair_p (next_hint))
127 right_dist += - gh_scm2double (gh_car (next_hint));
131 Interval ext (rc->extent (rc, X_AXIS));
132 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
136 don't want to create too much extra space for accidentals
138 if (lc->musical_b () && rc->musical_b ())
140 if (!to_boolean (rc->get_elt_property ("contains-grace")))
141 right_dist *= me->paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
144 if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
145 right_dist *= me->paper_l ()->get_var ("before_grace_spacing_factor");
147 s.distance_f_ = left_distance + right_dist;
149 Real stretch_dist = 0.;
150 if (gh_number_p (stretch_hint))
151 stretch_dist += gh_scm2double (stretch_hint);
153 stretch_dist += left_distance;
155 if (gh_pair_p (next_stretch_hint))
156 // see regtest spacing-tight
157 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
159 stretch_dist += right_dist;
161 if (s.distance_f_ <0)
163 programming_error("Negative dist, setting to 1.0 PT");
166 if (stretch_dist == 0.0)
169 \bar "". We give it 0 space, with high strength.
171 s.strength_f_ = 20.0;
174 s.strength_f_ /= stretch_dist;
183 Do something if breakable column has no spacing hints set.
186 Spacing_spanner::default_bar_spacing (Score_element*me, Score_element *lc, Score_element *rc,
189 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
190 Real durational_distance = 0;
191 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
194 ugh should use shortest_playing distance
198 durational_distance = get_duration_space (me, delta_t, shortest);
201 return symbol_distance >? durational_distance;
206 Get the measure wide ant for arithmetic spacing.
209 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
210 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
211 The Ohio State University, 1987.
215 Spacing_spanner::get_duration_space (Score_element*me, Moment d, Moment shortest)
217 Real log = log_2 (shortest);
218 Real k= me->paper_l ()->get_var ("arithmetic_basicspace")
221 return (log_2 (d) + k) * me->paper_l ()->get_var ("arithmetic_multiplier");
226 Spacing_spanner::note_spacing (Score_element*me, Score_element *lc, Score_element *rc,
229 Moment shortest_playing_len = 0;
230 SCM s = lc->get_elt_property ("shortest-playing-duration");
232 // SCM s = lc->get_elt_property ("mean-playing-duration");
233 if (unsmob_moment (s))
234 shortest_playing_len = *unsmob_moment(s);
236 if (! shortest_playing_len)
238 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
239 shortest_playing_len = 1;
244 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
247 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
248 Real dist = get_duration_space (me, shortest_playing_len, shortest);
249 dist *= (double)(delta_t / shortest_playing_len);
255 if (delta_t > Moment (1,32))
256 dist += stem_dir_correction (me, lc,rc);
262 Correct for optical illusions. See [Wanske] p. 138. The combination
263 up-stem + down-stem should get extra space, the combination
264 down-stem + up-stem less.
266 This should be more advanced, since relative heights of the note
267 heads also influence required correction.
269 Also might not work correctly in case of multi voices or staff
272 TODO: lookup correction distances? More advanced correction?
273 Possibly turn this off?
275 TODO: have to check wether the stems are in the same staff.
277 This routine reads the DIR-LIST property of both its L and R arguments. */
279 Spacing_spanner::stem_dir_correction (Score_element*me, Score_element*l, Score_element*r)
281 SCM dl = l->get_elt_property ("dir-list");
282 SCM dr = r->get_elt_property ("dir-list");
284 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
290 assert (gh_number_p (dl) && gh_number_p(dr));
291 int d1 = gh_scm2int (dl);
292 int d2 = gh_scm2int (dr);
298 Real correction = 0.0;
299 Real ssc = me->paper_l ()->get_var("stemSpacingCorrection");
302 if (d1 && d2 && d1 * d2 == -1)
304 correction = d1 * ssc;
307 programming_error ("Stem directions not set correctly for optical correction");
312 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs,1);
314 Spacing_spanner::set_springs (SCM smob)
316 Score_element *me = unsmob_element (smob);
317 Link_array<Score_element> all (me->pscore_l_->line_l_->column_l_arr ()) ;
321 for (int i = 1; i < all.size (); i++)
323 Score_element *sc = all[i];
324 if (Item::breakable_b (sc))
326 Link_array<Score_element> measure (all.slice (j, i+1));
327 do_measure (me, measure);
333 farewell, cruel world
336 return SCM_UNSPECIFIED;
344 maximum-duration-for-spacing
345 From: bf250@freenet.carleton.ca (John Sankey)
346 To: gnu-music-discuss@gnu.org
347 Subject: note spacing suggestion
348 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
350 Currently, Lily spaces notes by starting with a basic distance,
351 arithmetic_multiplier, which it applies to the minimum duration note
352 of the bar. Then she adds a logarithmic increment, scaled from
353 arithmetic_basicspace, for longer notes. (Then, columns are aligned
354 and justified.) Fundamentally, this matches visual spacing to musical
355 weight and works well.
357 A lot of the time in music, I see a section basically in melodic
358 notes that occasionally has a rapid ornamental run (scale). So, there
359 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
360 a return to long notes. Currently, Lily gives the same horizontal
361 space to the 1/32nd notes in their bar (even if set in small size as
362 is commonly done for cadenzii) as she gives to 1/4 notes in bars
363 where 1/4 note is the minimum duration. The resulting visual weight
364 does not match the musical weight over the page.
366 Looking at the music I am typesetting, I feel that Lily's spacing
367 could be significantly improved if, with no change in the basic
368 method used, arithmetic_multiplier could be applied referred to the
369 same duration throughout a piece. Of course, the current method
370 should be retained for those who have already set music in it, so I
371 suggest a property called something like arithmetic_base=16 to fix
372 1/16 duration as the reference for arithmetic_multiplier; the default
373 would be a dynamic base is it is now.
375 Does anyone else feel that this would be a useful improvement for
376 their music? (Of course, if arithmetic_multiplier became a regular
377 property, this could be used to achieve a similar result by