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"
19 Spacing_spanner::Spacing_spanner (SCM s)
22 set_extent_callback (0, X_AXIS);
23 set_extent_callback (0, Y_AXIS);
24 set_elt_property ("transparent", SCM_BOOL_T);
29 The algorithm is partly taken from :
31 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
32 OSU-CISRC-10/87-TR35, Department of Computer and Information
33 Science, The Ohio State University, 1987.
41 Spacing_spanner::do_measure (Link_array<Paper_column> cols) const
45 shortest.set_infinite (1);
48 for (int i =0 ; i < cols.size (); i++)
50 if (cols[i]->musical_b ())
52 SCM st = cols[i]->get_elt_property ("shortest-starter-duration");
53 Moment this_shortest = (*SMOB_TO_TYPE(Moment, st));
54 shortest = shortest <? this_shortest;
55 if (!mean_shortest.infty_b ())
58 mean_shortest += this_shortest;
64 Array<Spring> meas_springs;
66 Real non_musical_space_strength = paper_l ()->get_var ("breakable_column_space_strength");
67 for (int i= 0; i < cols.size () - 1; i++)
71 Item * lb = l->find_prebroken_piece (RIGHT);
72 Item * rb = r->find_prebroken_piece (LEFT);
74 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
76 for (int j=0; j < 4; j++)
78 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
79 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
84 s.item_l_drul_[LEFT] = lc;
85 s.item_l_drul_[RIGHT] = rc;
87 SCM hint = lc->get_elt_property ("extra-space");
88 SCM next_hint = rc->get_elt_property ("extra-space");
89 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
90 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
95 left_distance = gh_scm2double (gh_cdr (hint));
97 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
98 else if (!lc->musical_b() && i+1 < cols.size ())
100 left_distance= default_bar_spacing (lc,rc,shortest);
102 else if (lc->musical_b())
104 left_distance = note_spacing (lc, rc, shortest);
107 s.distance_f_ = left_distance;
110 Only do tight spaces *after* barlines (breakable columns),
113 We want the space before barline to be like the note
114 spacing in the measure.
116 if (lc->breakable_b () || lc->original_l_)
117 s.strength_f_ = non_musical_space_strength;
118 else if (!lc->musical_b ())
119 left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
122 Real right_dist = 0.0;
123 if (gh_pair_p (next_hint))
125 right_dist += - gh_scm2double (gh_car (next_hint));
129 Interval ext (rc->extent (X_AXIS));
130 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
134 don't want to create too much extra space for accidentals
136 if (lc->musical_b () && rc->musical_b ())
138 if (!to_boolean (rc->get_elt_property ("contains-grace")))
139 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
142 if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
143 right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
145 s.distance_f_ = left_distance + right_dist;
147 Real stretch_dist = 0.;
148 if (gh_number_p (stretch_hint))
149 stretch_dist += gh_scm2double (stretch_hint);
151 stretch_dist += left_distance;
153 if (gh_pair_p (next_stretch_hint))
154 // see regtest spacing-tight
155 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
157 stretch_dist += right_dist;
159 if (s.distance_f_ <0)
161 programming_error("Negative dist, setting to 1.0 PT");
164 if (stretch_dist == 0.0)
167 \bar "". We give it 0 space, with high strength.
169 s.strength_f_ = 20.0;
172 s.strength_f_ /= stretch_dist;
174 meas_springs.push (s);
182 Do something if breakable column has no spacing hints set.
185 Spacing_spanner::default_bar_spacing (Paper_column *lc, Paper_column *rc,
186 Moment shortest) const
188 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
189 Real durational_distance = 0;
190 Moment delta_t = rc->when_mom () - lc->when_mom () ;
193 ugh should use shortest_playing distance
197 durational_distance = get_duration_space (delta_t, shortest);
200 return symbol_distance >? durational_distance;
205 Get the measure wide constant for arithmetic spacing.
208 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
209 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
210 The Ohio State University, 1987.
214 Spacing_spanner::get_duration_space (Moment d, Moment shortest) const
216 Real log = log_2 (Moment (1,8) <? shortest);
217 Real k= paper_l ()->get_var ("arithmetic_basicspace")
220 return (log_2 (d) + k) * paper_l ()->get_var ("arithmetic_multiplier");
225 Spacing_spanner::note_spacing (Paper_column *lc, Paper_column *rc, Moment shortest) const
227 Moment shortest_playing_len = 0;
228 SCM s = lc->get_elt_property ("shortest-playing-duration");
229 // SCM s = lc->get_elt_property ("mean-playing-duration");
230 if (SMOB_IS_TYPE_B(Moment, s))
231 shortest_playing_len = *SMOB_TO_TYPE (Moment, s);
234 if (! shortest_playing_len)
236 programming_error ("can't find a ruling note at " + lc->when_mom ().str ());
237 shortest_playing_len = 1;
242 programming_error ("no minimum in measure at " + lc->when_mom ().str ());
245 Moment delta_t = rc->when_mom () - lc->when_mom ();
246 Real dist = get_duration_space (shortest_playing_len, shortest);
247 dist *= (double)(delta_t / shortest_playing_len);
249 dist += stem_dir_correction (lc,rc);
255 Correct for optical illusions. See [Wanske] p. 138. The combination
256 up-stem + down-stem should get extra space, the combination
257 down-stem + up-stem less.
259 This should be more advanced, since relative heights of the note
260 heads also influence required correction.
262 Also might not work correctly in case of multi voices or staff
265 TODO: lookup correction distances? More advanced correction?
266 Possibly turn this off?
268 This routine reads the DIR-LIST property of both its L and R arguments. */
270 Spacing_spanner::stem_dir_correction (Paper_column*l, Paper_column*r) const
272 SCM dl = l->get_elt_property ("dir-list");
273 SCM dr = r->get_elt_property ("dir-list");
274 if (dl == SCM_UNDEFINED || dr == SCM_UNDEFINED)
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 = paper_l ()->get_var("stemSpacingCorrection");
298 if (d1 == 1 && d2 == -1)
300 else if (d1 == -1 && d2 == 1)
310 programming_error ("Stem directions not set correctly for optical correction");
316 Spacing_spanner::get_springs () const
318 Array<Spring> springs;
320 Link_array<Paper_column> all (pscore_l_->line_l_->column_l_arr ()) ;
324 for (int i = 1; i < all.size (); i++)
326 Paper_column* sc = dynamic_cast<Paper_column*> (all[i]);
327 if (sc->breakable_b ())
329 Link_array<Paper_column> measure (all.slice (j, i+1));
330 springs.concat (do_measure (measure));
336 farewell, cruel world
338 ((Spacing_spanner*)this)->suicide ();