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 "score-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<Score_column> cols) const
43 for (int i =0 ; i < cols.size (); i++)
45 cols[i]->preprocess ();
50 shortest.set_infinite (1);
51 for (int i =0 ; i < cols.size (); i++)
53 if (cols[i]->musical_b ())
55 shortest = shortest <? cols[i]->shortest_starter_mom_;
59 Array<Spring> meas_springs;
61 Real non_musical_space_strength = paper_l ()->get_var ("breakable_column_space_strength");
62 for (int i= 0; i < cols.size (); i++)
66 Item * lb = l->find_broken_piece (RIGHT);
67 Item * rb = r->find_broken_piece (LEFT);
69 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
71 for (int j=0; j < 4; j++)
73 Score_column * lc = dynamic_cast<Score_column*> (combinations[j][0]);
74 Score_column *rc = dynamic_cast<Score_column*> (combinations[j][1]);
79 s.item_l_drul_[LEFT] = lc;
80 s.item_l_drul_[RIGHT] = rc;
82 SCM hint = lc->get_elt_property ("extra-space");
83 SCM next_hint = rc->get_elt_property ("extra-space");
84 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
85 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
88 if (hint != SCM_UNDEFINED)
90 left_distance = gh_scm2double (gh_cdr (hint));
92 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
93 else if (!lc->musical_b() && i+1 < cols.size ())
95 left_distance= default_bar_spacing (lc,rc,shortest);
97 else if (lc->musical_b())
99 left_distance = note_spacing (lc, rc, shortest);
102 s.distance_f_ = left_distance;
105 Only do tight spaces *after* barlines (breakable columns),
108 We want the space before barline to be like the note
109 spacing in the measure.
111 if (lc->breakable_b () || lc->original_l_)
112 s.strength_f_ = non_musical_space_strength;
113 else if (!lc->musical_b ())
114 left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
117 Real right_dist = 0.0;
118 if (next_hint != SCM_UNDEFINED)
120 right_dist += - gh_scm2double (gh_car (next_hint));
124 Interval ext (rc->extent (X_AXIS));
125 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
129 don't want to create too much extra space for accidentals
131 if (lc->musical_b () && rc->musical_b ())
133 if (rc->get_elt_property ("contains-grace") == SCM_UNDEFINED)
134 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
137 if (rc->musical_b () && rc->get_elt_property ("contains-grace") != SCM_UNDEFINED)
138 right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
141 s.distance_f_ = left_distance + right_dist;
143 Real stretch_dist = 0.;
144 if (gh_number_p (stretch_hint))
145 stretch_dist += gh_scm2double (stretch_hint);
147 stretch_dist += left_distance;
149 if (next_stretch_hint != SCM_UNDEFINED)
150 // see regtest spacing-tight
151 stretch_dist += - gh_scm2double (SCM_CAR (next_stretch_hint));
153 stretch_dist += right_dist;
155 if (s.distance_f_ <0)
156 programming_error("negative dist");
158 if (stretch_dist == 0.0)
161 \bar "". We give it 0 space, with high strength.
163 s.strength_f_ = 20.0;
166 s.strength_f_ /= stretch_dist;
168 meas_springs.push (s);
176 Do something if breakable column has no spacing hints set.
179 Spacing_spanner::default_bar_spacing (Score_column *lc, Score_column *rc,
180 Moment shortest) const
182 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
183 Real durational_distance = 0;
184 Moment delta_t = rc->when_mom () - lc->when_mom () ;
187 ugh should use shortest_playing distance
191 Real k= paper_l()->arithmetic_constant (shortest);
192 durational_distance = paper_l()->length_mom_to_dist (delta_t,k);
195 return symbol_distance >? durational_distance;
200 Spacing_spanner::note_spacing (Score_column *lc, Score_column *rc, Moment shortest) const
202 Moment shortest_playing_len = lc->shortest_playing_mom_;
203 if (! shortest_playing_len)
205 programming_error ("Can't find a ruling note at " + lc->when_mom ().str ());
206 shortest_playing_len = 1;
211 programming_error ("no minimum in measure at " + lc->when_mom ().str ());
214 Moment delta_t = rc->when_mom () - lc->when_mom ();
215 Real k= paper_l()->arithmetic_constant(shortest);
216 Real dist = paper_l()->length_mom_to_dist (shortest_playing_len, k);
217 dist *= (double)(delta_t / shortest_playing_len);
219 dist += stem_dir_correction (lc,rc);
225 Correct for optical illusions. See [Wanske] p. 138. The combination
226 up-stem + down-stem should get extra space, the combination
227 down-stem + up-stem less.
229 This should be more advanced, since relative heights of the note
230 heads also influence required correction.
232 Also might not work correctly ico. multi voices or staff changing voices
234 TODO: lookup correction distances? More advanced correction?
235 Possibly turn this off?
237 This routine reads the DIR_LIST property of both its L and R arguments.
240 Spacing_spanner::stem_dir_correction (Score_column*l, Score_column*r) const
242 SCM dl = l->get_elt_property ("dir-list");
243 SCM dr = r->get_elt_property ("dir-list");
244 if (dl == SCM_UNDEFINED || dr == SCM_UNDEFINED)
248 if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
254 assert (gh_number_p (dl) && gh_number_p(dr));
255 int d1 = gh_scm2int (dl);
256 int d2 = gh_scm2int (dr);
262 Real correction = 0.0;
263 Real ssc = paper_l ()->get_var("stemSpacingCorrection");
268 if (d1 == 1 && d2 == -1)
270 else if (d1 == -1 && d2 == 1)
280 programming_error ("Stem directions not set correctly for optical correction");
286 Spacing_spanner::get_springs () const
288 Array<Spring> springs;
290 SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
291 Link_array<Score_column> measure;
292 for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
294 SCM elt = gh_car (s);
295 Score_column* sc = dynamic_cast<Score_column*> (SMOB_TO_TYPE (Score_column, elt));
297 if (sc->breakable_b ())
300 springs.concat (do_measure (measure));