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 (0, X_AXIS);
23 me->set_extent_callback (0, 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)
44 shortest.set_infinite (1);
47 for (int i =0 ; i < cols.size (); i++)
49 if (dynamic_cast<Paper_column*> (cols[i])->musical_b ())
51 SCM st = cols[i]->get_elt_property ("shortest-starter-duration");
52 Moment this_shortest = (*SMOB_TO_TYPE(Moment, st));
53 shortest = shortest <? this_shortest;
54 if (!mean_shortest.infty_b ())
57 mean_shortest += this_shortest;
63 Real non_musical_space_strength = me->paper_l ()->get_var ("breakable_column_space_strength");
64 for (int i= 0; i < cols.size () - 1; i++)
66 Item * l = dynamic_cast<Item*> (cols[i]);
67 Item * r = dynamic_cast<Item*> (cols[i+1]);
68 Item * lb = dynamic_cast<Item*> ( l->find_prebroken_piece (RIGHT));
69 Item * rb = dynamic_cast<Item*> ( r->find_prebroken_piece (LEFT));
71 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
73 for (int j=0; j < 4; j++)
75 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
76 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
81 s.item_l_drul_[LEFT] = lc;
82 s.item_l_drul_[RIGHT] = rc;
84 SCM hint = lc->get_elt_property ("extra-space");
85 SCM next_hint = rc->get_elt_property ("extra-space");
86 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
87 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
92 left_distance = gh_scm2double (gh_cdr (hint));
94 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
95 else if (!lc->musical_b() && i+1 < cols.size ())
97 left_distance= default_bar_spacing (me,lc,rc,shortest);
99 else if (lc->musical_b())
101 left_distance = note_spacing (me,lc, rc, shortest);
104 s.distance_f_ = left_distance;
107 Only do tight spaces *after* barlines (breakable columns),
110 We want the space before barline to be like the note
111 spacing in the measure.
113 if (Item::breakable_b (lc) || lc->original_l_)
114 s.strength_f_ = non_musical_space_strength;
115 else if (!lc->musical_b ())
116 left_distance *= me->paper_l ()->get_var ("decrease_nonmus_spacing_factor");
119 Real right_dist = 0.0;
120 if (gh_pair_p (next_hint))
122 right_dist += - gh_scm2double (gh_car (next_hint));
126 Interval ext (rc->extent (X_AXIS));
127 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
131 don't want to create too much extra space for accidentals
133 if (lc->musical_b () && rc->musical_b ())
135 if (!to_boolean (rc->get_elt_property ("contains-grace")))
136 right_dist *= me->paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
139 if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
140 right_dist *= me->paper_l ()->get_var ("before_grace_spacing_factor");
142 s.distance_f_ = left_distance + right_dist;
144 Real stretch_dist = 0.;
145 if (gh_number_p (stretch_hint))
146 stretch_dist += gh_scm2double (stretch_hint);
148 stretch_dist += left_distance;
150 if (gh_pair_p (next_stretch_hint))
151 // see regtest spacing-tight
152 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
154 stretch_dist += right_dist;
156 if (s.distance_f_ <0)
158 programming_error("Negative dist, setting to 1.0 PT");
161 if (stretch_dist == 0.0)
164 \bar "". We give it 0 space, with high strength.
166 s.strength_f_ = 20.0;
169 s.strength_f_ /= stretch_dist;
178 Do something if breakable column has no spacing hints set.
181 Spacing_spanner::default_bar_spacing (Score_element*me, Score_element *lc, Score_element *rc,
184 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
185 Real durational_distance = 0;
186 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
189 ugh should use shortest_playing distance
193 durational_distance = get_duration_space (me, delta_t, shortest);
196 return symbol_distance >? durational_distance;
201 Get the measure wide ant for arithmetic spacing.
204 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
205 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
206 The Ohio State University, 1987.
210 Spacing_spanner::get_duration_space (Score_element*me, Moment d, Moment shortest)
212 Real log = log_2 (Moment (1,8) <? shortest);
213 Real k= me->paper_l ()->get_var ("arithmetic_basicspace")
216 return (log_2 (d) + k) * me->paper_l ()->get_var ("arithmetic_multiplier");
221 Spacing_spanner::note_spacing (Score_element*me, Score_element *lc, Score_element *rc, Moment shortest)
223 Moment shortest_playing_len = 0;
224 SCM s = lc->get_elt_property ("shortest-playing-duration");
226 // SCM s = lc->get_elt_property ("mean-playing-duration");
227 if (unsmob_moment (s))
228 shortest_playing_len = *unsmob_moment(s);
230 if (! shortest_playing_len)
232 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
233 shortest_playing_len = 1;
238 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
241 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
242 Real dist = get_duration_space (me, shortest_playing_len, shortest);
243 dist *= (double)(delta_t / shortest_playing_len);
249 if (delta_t > Moment (1,32))
250 dist += stem_dir_correction (me, lc,rc);
256 Correct for optical illusions. See [Wanske] p. 138. The combination
257 up-stem + down-stem should get extra space, the combination
258 down-stem + up-stem less.
260 This should be more advanced, since relative heights of the note
261 heads also influence required correction.
263 Also might not work correctly in case of multi voices or staff
266 TODO: lookup correction distances? More advanced correction?
267 Possibly turn this off?
269 TODO: have to check wether the stems are in the same staff.
271 This routine reads the DIR-LIST property of both its L and R arguments. */
273 Spacing_spanner::stem_dir_correction (Score_element*me, Score_element*l, Score_element*r)
275 SCM dl = l->get_elt_property ("dir-list");
276 SCM dr = r->get_elt_property ("dir-list");
278 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
284 assert (gh_number_p (dl) && gh_number_p(dr));
285 int d1 = gh_scm2int (dl);
286 int d2 = gh_scm2int (dr);
292 Real correction = 0.0;
293 Real ssc = me->paper_l ()->get_var("stemSpacingCorrection");
296 if (d1 && d2 && d1 * d2 == -1)
298 correction = d1 * ssc;
301 programming_error ("Stem directions not set correctly for optical correction");
306 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs);
308 Spacing_spanner::set_springs (SCM smob)
310 Score_element *me = unsmob_element (smob);
311 Link_array<Score_element> all (me->pscore_l_->line_l_->column_l_arr ()) ;
315 for (int i = 1; i < all.size (); i++)
317 Score_element *sc = all[i];
318 if (Item::breakable_b (sc))
320 Link_array<Score_element> measure (all.slice (j, i+1));
321 do_measure (me, measure);
327 farewell, cruel world
330 return SCM_UNDEFINED;