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"
19 Spacing_spanner::Spacing_spanner ()
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");
147 s.distance_f_ = left_distance + right_dist;
149 Real stretch_dist = 0.;
150 if (gh_number_p (stretch_hint))
151 stretch_dist += gh_scm2double (stretch_hint);
153 stretch_dist += left_distance;
155 if (gh_pair_p (next_stretch_hint))
156 // see regtest spacing-tight
157 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
159 stretch_dist += right_dist;
161 if (s.distance_f_ <0)
162 programming_error("negative dist");
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 ico. multi voices or staff changing voices
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.
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 SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
321 Link_array<Paper_column> measure;
322 for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
324 Score_element * elt = unsmob_element (gh_car (s));
325 Paper_column* sc = dynamic_cast<Paper_column*> (elt);
327 if (sc->breakable_b ())
330 springs.concat (do_measure (measure));