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 ()
21 set_extent_callback (0, X_AXIS);
22 set_extent_callback (0, Y_AXIS);
23 set_elt_property ("transparent", SCM_BOOL_T);
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 (Link_array<Paper_column> cols) const
44 shortest.set_infinite (1);
47 for (int i =0 ; i < cols.size (); i++)
49 if (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 Array<Spring> meas_springs;
65 Real non_musical_space_strength = paper_l ()->get_var ("breakable_column_space_strength");
66 for (int i= 0; i < cols.size () - 1; i++)
70 Item * lb = l->find_broken_piece (RIGHT);
71 Item * rb = r->find_broken_piece (LEFT);
73 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
75 for (int j=0; j < 4; j++)
77 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
78 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
83 s.item_l_drul_[LEFT] = lc;
84 s.item_l_drul_[RIGHT] = rc;
86 SCM hint = lc->get_elt_property ("extra-space");
87 SCM next_hint = rc->get_elt_property ("extra-space");
88 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
89 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
94 left_distance = gh_scm2double (gh_cdr (hint));
96 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
97 else if (!lc->musical_b() && i+1 < cols.size ())
99 left_distance= default_bar_spacing (lc,rc,shortest);
101 else if (lc->musical_b())
103 left_distance = note_spacing (lc, rc, shortest);
106 s.distance_f_ = left_distance;
109 Only do tight spaces *after* barlines (breakable columns),
112 We want the space before barline to be like the note
113 spacing in the measure.
115 if (lc->breakable_b () || lc->original_l_)
116 s.strength_f_ = non_musical_space_strength;
117 else if (!lc->musical_b ())
118 left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
121 Real right_dist = 0.0;
122 if (gh_pair_p (next_hint))
124 right_dist += - gh_scm2double (gh_car (next_hint));
128 Interval ext (rc->extent (X_AXIS));
129 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
133 don't want to create too much extra space for accidentals
135 if (lc->musical_b () && rc->musical_b ())
137 if (!to_boolean (rc->get_elt_property ("contains-grace")))
138 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
141 if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
142 right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
144 s.distance_f_ = left_distance + right_dist;
146 Real stretch_dist = 0.;
147 if (gh_number_p (stretch_hint))
148 stretch_dist += gh_scm2double (stretch_hint);
150 stretch_dist += left_distance;
152 if (gh_pair_p (next_stretch_hint))
153 // see regtest spacing-tight
154 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
156 stretch_dist += right_dist;
158 if (s.distance_f_ <0)
159 programming_error("negative dist");
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;
171 meas_springs.push (s);
179 Do something if breakable column has no spacing hints set.
182 Spacing_spanner::default_bar_spacing (Paper_column *lc, Paper_column *rc,
183 Moment shortest) const
185 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
186 Real durational_distance = 0;
187 Moment delta_t = rc->when_mom () - lc->when_mom () ;
190 ugh should use shortest_playing distance
194 durational_distance = get_duration_space (delta_t, shortest);
197 return symbol_distance >? durational_distance;
202 Get the measure wide constant for arithmetic spacing.
205 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
206 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
207 The Ohio State University, 1987.
211 Spacing_spanner::get_duration_space (Moment d, Moment shortest) const
213 Real log = log_2 (Moment (1,8) <? shortest);
214 Real k= paper_l ()->get_var ("arithmetic_basicspace")
217 return (log_2 (d) + k) * paper_l ()->get_var ("arithmetic_multiplier");
222 Spacing_spanner::note_spacing (Paper_column *lc, Paper_column *rc, Moment shortest) const
224 Moment shortest_playing_len = 0;
225 SCM s = lc->get_elt_property ("shortest-playing-duration");
226 // SCM s = lc->get_elt_property ("mean-playing-duration");
227 if (SMOB_IS_TYPE_B(Moment, s))
228 shortest_playing_len = *SMOB_TO_TYPE (Moment, s);
231 if (! shortest_playing_len)
233 programming_error ("can't find a ruling note at " + lc->when_mom ().str ());
234 shortest_playing_len = 1;
239 programming_error ("no minimum in measure at " + lc->when_mom ().str ());
242 Moment delta_t = rc->when_mom () - lc->when_mom ();
243 Real dist = get_duration_space (shortest_playing_len, shortest);
244 dist *= (double)(delta_t / shortest_playing_len);
246 dist += stem_dir_correction (lc,rc);
252 Correct for optical illusions. See [Wanske] p. 138. The combination
253 up-stem + down-stem should get extra space, the combination
254 down-stem + up-stem less.
256 This should be more advanced, since relative heights of the note
257 heads also influence required correction.
259 Also might not work correctly in case of multi voices or staff
262 TODO: lookup correction distances? More advanced correction?
263 Possibly turn this off?
265 This routine reads the DIR-LIST property of both its L and R arguments. */
267 Spacing_spanner::stem_dir_correction (Paper_column*l, Paper_column*r) const
269 SCM dl = l->get_elt_property ("dir-list");
270 SCM dr = r->get_elt_property ("dir-list");
271 if (dl == SCM_UNDEFINED || dr == SCM_UNDEFINED)
275 if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
281 assert (gh_number_p (dl) && gh_number_p(dr));
282 int d1 = gh_scm2int (dl);
283 int d2 = gh_scm2int (dr);
289 Real correction = 0.0;
290 Real ssc = paper_l ()->get_var("stemSpacingCorrection");
295 if (d1 == 1 && d2 == -1)
297 else if (d1 == -1 && d2 == 1)
307 programming_error ("Stem directions not set correctly for optical correction");
313 Spacing_spanner::get_springs () const
315 Array<Spring> springs;
317 SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
318 Link_array<Paper_column> measure;
319 for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
321 Score_element * elt = unsmob_element (gh_car (s));
322 Paper_column* sc = dynamic_cast<Paper_column*> (elt);
324 if (sc->breakable_b ())
327 springs.concat (do_measure (measure));