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 ()
20 set_elt_property (break_helper_only_scm_sym, SCM_BOOL_T);
21 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
25 Spacing_spanner::col_count () const
27 return pscore_l_->line_l_->cols_.size ();
31 Spacing_spanner::scol (int i)const
33 return dynamic_cast<Score_column*> (pscore_l_->line_l_->cols_[i]);
37 cut 'n paste from spring-spacer.cc
39 generate springs between columns.
42 The algorithm is partly taken from :
44 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
45 OSU-CISRC-10/87-TR35, Department of Computer and Information
46 Science, The Ohio State University, 1987.
52 Spacing_spanner::do_measure (int col1, int col2) const
54 for (int i =col1; i < col2; i++)
56 scol (i)->preprocess ();
61 shortest.set_infinite (1);
62 for (int i =col1; i < col2; i++)
64 if (scol(i)->musical_b ())
66 shortest = shortest <? scol(i)->shortest_starter_mom_;
70 Array<Spring> meas_springs;
72 Real non_musical_space_strength = paper_l ()->get_var ("non_musical_space_strength");
73 for (int i= col1; i < col2; i++)
77 Item * lb = l->find_prebroken_piece (RIGHT);
78 Item * rb = r->find_prebroken_piece (LEFT);
80 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
82 for (int i=0; i < 4; i++)
84 Score_column * lc = dynamic_cast<Score_column*> (combinations[i][0]);
85 Score_column *rc = dynamic_cast<Score_column*> (combinations[i][1]);
90 s.item_l_drul_[LEFT] = lc;
91 s.item_l_drul_[RIGHT] = rc;
93 SCM hint = lc->get_elt_property (extra_space_scm_sym);
94 SCM next_hint = rc->get_elt_property (extra_space_scm_sym);
95 SCM stretch_hint = lc->get_elt_property (stretch_distance_scm_sym);
96 SCM next_stretch_hint = rc->get_elt_property (stretch_distance_scm_sym);
99 if (hint != SCM_BOOL_F)
101 hint = SCM_CDDR (hint);
103 left_distance = gh_scm2double (hint);
105 else if (!lc->musical_b() && i+1 < col_count())
107 left_distance= default_bar_spacing (lc,rc,shortest);
109 else if (lc->musical_b())
111 left_distance = note_spacing (lc, rc, shortest);
114 s.distance_f_ = left_distance;
115 if (!lc->musical_b () || !rc->musical_b ())
116 s.strength_f_ = non_musical_space_strength;
118 Real right_dist = 0.0;
119 if (next_hint != SCM_BOOL_F)
121 next_hint = SCM_CADR(next_hint);
122 right_dist += gh_scm2double (next_hint);
126 Interval ext (rc->extent (X_AXIS));
127 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
131 don't want to create too much extra space for accidentals
133 if (lc->musical_b () && rc->musical_b ())
136 s.distance_f_ = left_distance + right_dist;
138 Real stretch_dist = 0.;
139 if (stretch_hint != SCM_BOOL_F)
140 stretch_dist += gh_scm2double (SCM_CDDR (stretch_hint));
142 stretch_dist += left_distance;
144 if (next_stretch_hint != SCM_BOOL_F)
146 // see regtest spacing-tight
147 // stretch_dist += gh_scm2double (SCM_CADR (next_stretch_hint));
150 stretch_dist += right_dist;
152 if (stretch_dist == 0.0)
155 \bar "". We give it 0 space, with high strength.
157 s.strength_f_ = 20.0;
160 s.strength_f_ /= stretch_dist;
162 meas_springs.push (s);
170 Do something if breakable column has no spacing hints set.
173 Spacing_spanner::default_bar_spacing (Score_column *lc, Score_column *rc,
174 Moment shortest) const
176 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
177 Real durational_distance = 0;
178 Moment delta_t = rc->when_mom () - lc->when_mom () ;
181 ugh should use shortest_playing distance
185 Real k= paper_l()->arithmetic_constant (shortest);
186 durational_distance = paper_l()->length_mom_to_dist (delta_t,k);
189 return symbol_distance >? durational_distance;
194 Spacing_spanner::note_spacing (Score_column *lc, Score_column *rc, Moment shortest) const
196 Moment shortest_playing_len = lc->shortest_playing_mom_;
197 if (! shortest_playing_len)
199 warning (_f ("can't find a ruling note at %s",
200 lc->when_mom ().str ()));
201 shortest_playing_len = 1;
205 warning (_f ("no minimum in measure at %s",
206 lc->when_mom ().str ()));
209 Moment delta_t = rc->when_mom () - lc->when_mom ();
210 Real k= paper_l()->arithmetic_constant(shortest);
211 Real dist = paper_l()->length_mom_to_dist (shortest_playing_len, k);
212 dist *= (double)(delta_t / shortest_playing_len);
214 dist += stem_dir_correction (lc,rc);
220 Correct for optical illusions. See [Wanske] p. 138. The combination
221 up-stem + down-stem should get extra space, the combination
222 down-stem + up-stem less.
224 This should be more advanced, since relative heights of the note
225 heads also influence required correction.
227 Also might not work correctly ico. multi voices or staff changing voices
229 TODO: lookup correction distances? More advanced correction?
230 Possibly turn this off?
232 This routine reads the DIR_LIST property of both its L and R arguments.
235 Spacing_spanner::stem_dir_correction (Score_column*l, Score_column*r) const
237 SCM dl = l->get_elt_property (dir_list_scm_sym);
238 SCM dr = r->get_elt_property (dir_list_scm_sym);
239 if (dl == SCM_BOOL_F || dr == SCM_BOOL_F)
245 if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
251 assert (gh_number_p (dl) && gh_number_p(dr));
252 int d1 = gh_scm2int (dl);
253 int d2 = gh_scm2int (dr);
259 Real correction = 0.0;
260 Real ssc = paper_l ()->get_realvar(ly_symbol ("stemSpacingCorrection"));
265 if (d1 == 1 && d2 == -1)
267 else if (d1 == -1 && d2 == 1)
277 programming_error ("Stem directions not set correctly for optical correction");
283 Spacing_spanner::get_springs () const
285 Array<Spring> springs;
287 for (int i=1; i < col_count (); i++)
289 if (scol (i)->breakable_b ())
291 springs.concat (do_measure (last_break, i));