2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999 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"
18 Spacing_spanner::Spacing_spanner ()
22 set_elt_property ("transparent", SCM_BOOL_T);
26 cut 'n paste from spring-spacer.cc
28 generate springs between columns.
31 The algorithm is partly taken from :
33 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
34 OSU-CISRC-10/87-TR35, Department of Computer and Information
35 Science, The Ohio State University, 1987.
41 Spacing_spanner::do_measure (Link_array<Paper_column> cols) const
44 shortest.set_infinite (1);
45 for (int i =0 ; i < cols.size (); i++)
47 if (cols[i]->musical_b ())
49 SCM st = cols[i]->get_elt_property ("shortest-starter");
51 shortest = shortest <? (*SMOB_TO_TYPE(Moment, st));
55 Array<Spring> meas_springs;
57 Real non_musical_space_strength = paper_l ()->get_var ("breakable_column_space_strength");
58 for (int i= 0; i < cols.size () - 1; i++)
62 Item * lb = l->find_broken_piece (RIGHT);
63 Item * rb = r->find_broken_piece (LEFT);
65 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
67 for (int j=0; j < 4; j++)
69 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
70 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
75 s.item_l_drul_[LEFT] = lc;
76 s.item_l_drul_[RIGHT] = rc;
78 SCM hint = lc->get_elt_property ("extra-space");
79 SCM next_hint = rc->get_elt_property ("extra-space");
80 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
81 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
86 left_distance = gh_scm2double (gh_cdr (hint));
88 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
89 else if (!lc->musical_b() && i+1 < cols.size ())
91 left_distance= default_bar_spacing (lc,rc,shortest);
93 else if (lc->musical_b())
95 left_distance = note_spacing (lc, rc, shortest);
98 s.distance_f_ = left_distance;
101 Only do tight spaces *after* barlines (breakable columns),
104 We want the space before barline to be like the note
105 spacing in the measure.
107 if (lc->breakable_b () || lc->original_l_)
108 s.strength_f_ = non_musical_space_strength;
109 else if (!lc->musical_b ())
110 left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
113 Real right_dist = 0.0;
114 if (gh_pair_p (next_hint))
116 right_dist += - gh_scm2double (gh_car (next_hint));
120 Interval ext (rc->extent (X_AXIS));
121 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
125 don't want to create too much extra space for accidentals
127 if (lc->musical_b () && rc->musical_b ())
129 if (!to_boolean (rc->get_elt_property ("contains-grace")))
130 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
133 if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
134 right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
137 s.distance_f_ = left_distance + right_dist;
139 Real stretch_dist = 0.;
140 if (gh_number_p (stretch_hint))
141 stretch_dist += gh_scm2double (stretch_hint);
143 stretch_dist += left_distance;
145 if (gh_pair_p (next_stretch_hint))
146 // see regtest spacing-tight
147 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
149 stretch_dist += right_dist;
151 if (s.distance_f_ <0)
152 programming_error("negative dist");
154 if (stretch_dist == 0.0)
157 \bar "". We give it 0 space, with high strength.
159 s.strength_f_ = 20.0;
162 s.strength_f_ /= stretch_dist;
164 meas_springs.push (s);
172 Do something if breakable column has no spacing hints set.
175 Spacing_spanner::default_bar_spacing (Paper_column *lc, Paper_column *rc,
176 Moment shortest) const
178 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
179 Real durational_distance = 0;
180 Moment delta_t = rc->when_mom () - lc->when_mom () ;
183 ugh should use shortest_playing distance
187 Real k= paper_l()->arithmetic_constant (shortest);
188 durational_distance = paper_l()->length_mom_to_dist (delta_t,k);
191 return symbol_distance >? durational_distance;
196 Spacing_spanner::note_spacing (Paper_column *lc, Paper_column *rc, Moment shortest) const
198 Moment shortest_playing_len = 0;
199 SCM s = lc->get_elt_property ("shortest-playing");
200 if (SMOB_IS_TYPE_B(Moment, s))
201 shortest_playing_len = *SMOB_TO_TYPE (Moment, s);
204 if (! shortest_playing_len)
206 programming_error ("Can't find a ruling note at " + lc->when_mom ().str ());
207 shortest_playing_len = 1;
212 programming_error ("no minimum in measure at " + lc->when_mom ().str ());
215 Moment delta_t = rc->when_mom () - lc->when_mom ();
216 Real k= paper_l()->arithmetic_constant(shortest);
217 Real dist = paper_l()->length_mom_to_dist (shortest_playing_len, k);
218 dist *= (double)(delta_t / shortest_playing_len);
220 dist += stem_dir_correction (lc,rc);
226 Correct for optical illusions. See [Wanske] p. 138. The combination
227 up-stem + down-stem should get extra space, the combination
228 down-stem + up-stem less.
230 This should be more advanced, since relative heights of the note
231 heads also influence required correction.
233 Also might not work correctly ico. multi voices or staff changing voices
235 TODO: lookup correction distances? More advanced correction?
236 Possibly turn this off?
238 This routine reads the DIR_LIST property of both its L and R arguments.
241 Spacing_spanner::stem_dir_correction (Paper_column*l, Paper_column*r) const
243 SCM dl = l->get_elt_property ("dir-list");
244 SCM dr = r->get_elt_property ("dir-list");
245 if (dl == SCM_UNDEFINED || dr == SCM_UNDEFINED)
249 if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
255 assert (gh_number_p (dl) && gh_number_p(dr));
256 int d1 = gh_scm2int (dl);
257 int d2 = gh_scm2int (dr);
263 Real correction = 0.0;
264 Real ssc = paper_l ()->get_var("stemSpacingCorrection");
269 if (d1 == 1 && d2 == -1)
271 else if (d1 == -1 && d2 == 1)
281 programming_error ("Stem directions not set correctly for optical correction");
287 Spacing_spanner::get_springs () const
289 Array<Spring> springs;
291 SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
292 Link_array<Paper_column> measure;
293 for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
295 Score_element * elt = unsmob_element (gh_car (s));
296 Paper_column* sc = dynamic_cast<Paper_column*> (elt);
298 if (sc->breakable_b ())
301 springs.concat (do_measure (measure));