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);
27 cut 'n paste from spring-spacer.cc
29 generate springs between columns.
32 The algorithm is partly taken from :
34 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
35 OSU-CISRC-10/87-TR35, Department of Computer and Information
36 Science, The Ohio State University, 1987.
42 Spacing_spanner::do_measure (Link_array<Paper_column> cols) const
46 shortest.set_infinite (1);
49 for (int i =0 ; i < cols.size (); i++)
51 if (cols[i]->musical_b ())
53 SCM st = cols[i]->get_elt_property ("shortest-starter-duration");
54 Moment this_shortest = (*SMOB_TO_TYPE(Moment, st));
55 shortest = shortest <? this_shortest;
56 if (!mean_shortest.infty_b ())
59 mean_shortest += this_shortest;
65 Array<Spring> meas_springs;
67 Real non_musical_space_strength = paper_l ()->get_var ("breakable_column_space_strength");
68 for (int i= 0; i < cols.size () - 1; i++)
72 Item * lb = l->find_broken_piece (RIGHT);
73 Item * rb = r->find_broken_piece (LEFT);
75 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
77 for (int j=0; j < 4; j++)
79 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
80 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
85 s.item_l_drul_[LEFT] = lc;
86 s.item_l_drul_[RIGHT] = rc;
88 SCM hint = lc->get_elt_property ("extra-space");
89 SCM next_hint = rc->get_elt_property ("extra-space");
90 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
91 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
96 left_distance = gh_scm2double (gh_cdr (hint));
98 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
99 else if (!lc->musical_b() && i+1 < cols.size ())
101 left_distance= default_bar_spacing (lc,rc,shortest);
103 else if (lc->musical_b())
105 left_distance = note_spacing (lc, rc, shortest);
108 s.distance_f_ = left_distance;
111 Only do tight spaces *after* barlines (breakable columns),
114 We want the space before barline to be like the note
115 spacing in the measure.
117 if (lc->breakable_b () || lc->original_l_)
118 s.strength_f_ = non_musical_space_strength;
119 else if (!lc->musical_b ())
120 left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
123 Real right_dist = 0.0;
124 if (gh_pair_p (next_hint))
126 right_dist += - gh_scm2double (gh_car (next_hint));
130 Interval ext (rc->extent (X_AXIS));
131 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
135 don't want to create too much extra space for accidentals
137 if (lc->musical_b () && rc->musical_b ())
139 if (!to_boolean (rc->get_elt_property ("contains-grace")))
140 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
143 if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
144 right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
146 s.distance_f_ = left_distance + right_dist;
148 Real stretch_dist = 0.;
149 if (gh_number_p (stretch_hint))
150 stretch_dist += gh_scm2double (stretch_hint);
152 stretch_dist += left_distance;
154 if (gh_pair_p (next_stretch_hint))
155 // see regtest spacing-tight
156 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
158 stretch_dist += right_dist;
160 if (s.distance_f_ <0)
161 programming_error("negative dist");
163 if (stretch_dist == 0.0)
166 \bar "". We give it 0 space, with high strength.
168 s.strength_f_ = 20.0;
171 s.strength_f_ /= stretch_dist;
173 meas_springs.push (s);
181 Do something if breakable column has no spacing hints set.
184 Spacing_spanner::default_bar_spacing (Paper_column *lc, Paper_column *rc,
185 Moment shortest) const
187 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
188 Real durational_distance = 0;
189 Moment delta_t = rc->when_mom () - lc->when_mom () ;
192 ugh should use shortest_playing distance
196 durational_distance = get_duration_space (delta_t, shortest);
199 return symbol_distance >? durational_distance;
204 Get the measure wide constant for arithmetic spacing.
207 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
208 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
209 The Ohio State University, 1987.
213 Spacing_spanner::get_duration_space (Moment d, Moment shortest) const
215 Real log = log_2 (Moment (1,8) <? shortest);
216 Real k= paper_l ()->get_var ("arithmetic_basicspace")
219 return (log_2 (d) + k) * paper_l ()->get_var ("arithmetic_multiplier");
224 Spacing_spanner::note_spacing (Paper_column *lc, Paper_column *rc, Moment shortest) const
226 Moment shortest_playing_len = 0;
227 SCM s = lc->get_elt_property ("shortest-playing-duration");
228 // SCM s = lc->get_elt_property ("mean-playing-duration");
229 if (SMOB_IS_TYPE_B(Moment, s))
230 shortest_playing_len = *SMOB_TO_TYPE (Moment, s);
233 if (! shortest_playing_len)
235 programming_error ("can't find a ruling note at " + lc->when_mom ().str ());
236 shortest_playing_len = 1;
241 programming_error ("no minimum in measure at " + lc->when_mom ().str ());
244 Moment delta_t = rc->when_mom () - lc->when_mom ();
245 Real dist = get_duration_space (shortest_playing_len, shortest);
246 dist *= (double)(delta_t / shortest_playing_len);
248 dist += stem_dir_correction (lc,rc);
254 Correct for optical illusions. See [Wanske] p. 138. The combination
255 up-stem + down-stem should get extra space, the combination
256 down-stem + up-stem less.
258 This should be more advanced, since relative heights of the note
259 heads also influence required correction.
261 Also might not work correctly in case of multi voices or staff
264 TODO: lookup correction distances? More advanced correction?
265 Possibly turn this off?
267 This routine reads the DIR-LIST property of both its L and R arguments. */
269 Spacing_spanner::stem_dir_correction (Paper_column*l, Paper_column*r) const
271 SCM dl = l->get_elt_property ("dir-list");
272 SCM dr = r->get_elt_property ("dir-list");
273 if (dl == SCM_UNDEFINED || dr == SCM_UNDEFINED)
277 if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
283 assert (gh_number_p (dl) && gh_number_p(dr));
284 int d1 = gh_scm2int (dl);
285 int d2 = gh_scm2int (dr);
291 Real correction = 0.0;
292 Real ssc = paper_l ()->get_var("stemSpacingCorrection");
297 if (d1 == 1 && d2 == -1)
299 else if (d1 == -1 && d2 == 1)
309 programming_error ("Stem directions not set correctly for optical correction");
315 Spacing_spanner::get_springs () const
317 Array<Spring> springs;
319 SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
320 Link_array<Paper_column> measure;
321 for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
323 Score_element * elt = unsmob_element (gh_car (s));
324 Paper_column* sc = dynamic_cast<Paper_column*> (elt);
326 if (sc->breakable_b ())
329 springs.concat (do_measure (measure));