lilypond-1.3.80
[lilypond.git] / lily / spacing-spanner.cc
blob5ad98d494ecf7f6b5227e2a1e0726d2240dfbdd9
1 /*
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>
8 */
10 #include "spacing-spanner.hh"
11 #include "paper-column.hh"
12 #include "dimensions.hh"
13 #include "paper-def.hh"
14 #include "warn.hh"
15 #include "paper-score.hh"
16 #include "line-of-score.hh"
17 #include "misc.hh"
19 void
20 Spacing_spanner::set_interface (Score_element*me)
22 me->set_extent_callback (0, X_AXIS);
23 me->set_extent_callback (0, Y_AXIS);
28 The algorithm is partly taken from :
30 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
31 OSU-CISRC-10/87-TR35, Department of Computer and Information
32 Science, The Ohio State University, 1987.
34 TOO HAIRY.
36 TODO: write comments
39 void
40 Spacing_spanner::do_measure (Score_element*me, Link_array<Score_element> cols)
42 Moment shortest;
43 Moment mean_shortest;
46 space as if this duration is present.
48 Moment base_shortest_duration = *unsmob_moment (me->get_elt_property ("maximum-duration-for-spacing"));
49 shortest.set_infinite (1);
51 int n = 0;
52 for (int i =0 ; i < cols.size (); i++)
54 if (dynamic_cast<Paper_column*> (cols[i])->musical_b ())
56 SCM st = cols[i]->get_elt_property ("shortest-starter-duration");
57 Moment this_shortest = *unsmob_moment(st);
58 shortest = shortest <? this_shortest;
59 if (!mean_shortest.infty_b ())
61 n++;
62 mean_shortest += this_shortest;
66 mean_shortest /= n;
68 Real non_musical_space_strength = me->paper_l ()->get_var ("breakable_column_space_strength");
69 for (int i= 0; i < cols.size () - 1; i++)
71 Item * l = dynamic_cast<Item*> (cols[i]);
72 Item * r = dynamic_cast<Item*> (cols[i+1]);
73 Item * lb = dynamic_cast<Item*> ( l->find_prebroken_piece (RIGHT));
74 Item * rb = dynamic_cast<Item*> ( r->find_prebroken_piece (LEFT));
76 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
78 for (int j=0; j < 4; j++)
80 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
81 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
82 if (!lc || !rc)
83 continue;
85 Spring s;
86 s.item_l_drul_[LEFT] = lc;
87 s.item_l_drul_[RIGHT] = rc;
89 SCM hint = lc->get_elt_property ("extra-space");
90 SCM next_hint = rc->get_elt_property ("extra-space");
91 SCM stretch_hint = lc->get_elt_property ("stretch-distance");
92 SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");
94 Real left_distance;
95 if (gh_pair_p (hint))
97 left_distance = gh_scm2double (gh_cdr (hint));
99 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
100 else if (!lc->musical_b() && i+1 < cols.size ())
102 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
104 else if (lc->musical_b())
106 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
109 s.distance_f_ = left_distance;
112 Only do tight spaces *after* barlines (breakable columns),
113 not before.
115 We want the space before barline to be like the note
116 spacing in the measure.
118 if (Item::breakable_b (lc) || lc->original_l_)
119 s.strength_f_ = non_musical_space_strength;
120 else if (!lc->musical_b ())
121 left_distance *= me->paper_l ()->get_var ("decrease_nonmus_spacing_factor");
124 Real right_dist = 0.0;
125 if (gh_pair_p (next_hint))
127 right_dist += - gh_scm2double (gh_car (next_hint));
129 else
131 Interval ext (rc->extent (X_AXIS));
132 right_dist = ext.empty_b() ? 0.0 : - ext [LEFT];
136 don't want to create too much extra space for accidentals
138 if (lc->musical_b () && rc->musical_b ())
140 if (!to_boolean (rc->get_elt_property ("contains-grace")))
141 right_dist *= me->paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
144 if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
145 right_dist *= me->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);
152 else
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));
158 else
159 stretch_dist += right_dist;
161 if (s.distance_f_ <0)
163 programming_error("Negative dist, setting to 1.0 PT");
164 s.distance_f_ = 1.0;
166 if (stretch_dist == 0.0)
169 \bar "". We give it 0 space, with high strength.
171 s.strength_f_ = 20.0;
173 else
174 s.strength_f_ /= stretch_dist;
176 s.add_to_cols ();
183 Do something if breakable column has no spacing hints set.
185 Real
186 Spacing_spanner::default_bar_spacing (Score_element*me, Score_element *lc, Score_element *rc,
187 Moment shortest)
189 Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
190 Real durational_distance = 0;
191 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
194 ugh should use shortest_playing distance
196 if (delta_t)
198 durational_distance = get_duration_space (me, delta_t, shortest);
201 return symbol_distance >? durational_distance;
206 Get the measure wide ant for arithmetic spacing.
208 @see
209 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
210 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
211 The Ohio State University, 1987.
214 Real
215 Spacing_spanner::get_duration_space (Score_element*me, Moment d, Moment shortest)
217 Real log = log_2 (shortest);
218 Real k= me->paper_l ()->get_var ("arithmetic_basicspace")
219 - log;
221 return (log_2 (d) + k) * me->paper_l ()->get_var ("arithmetic_multiplier");
225 Real
226 Spacing_spanner::note_spacing (Score_element*me, Score_element *lc, Score_element *rc,
227 Moment shortest)
229 Moment shortest_playing_len = 0;
230 SCM s = lc->get_elt_property ("shortest-playing-duration");
232 // SCM s = lc->get_elt_property ("mean-playing-duration");
233 if (unsmob_moment (s))
234 shortest_playing_len = *unsmob_moment(s);
236 if (! shortest_playing_len)
238 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
239 shortest_playing_len = 1;
242 if (! shortest)
244 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
245 shortest = 1;
247 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
248 Real dist = get_duration_space (me, shortest_playing_len, shortest);
249 dist *= (double)(delta_t / shortest_playing_len);
252 UGH: KLUDGE!
255 if (delta_t > Moment (1,32))
256 dist += stem_dir_correction (me, lc,rc);
257 return dist;
262 Correct for optical illusions. See [Wanske] p. 138. The combination
263 up-stem + down-stem should get extra space, the combination
264 down-stem + up-stem less.
266 This should be more advanced, since relative heights of the note
267 heads also influence required correction.
269 Also might not work correctly in case of multi voices or staff
270 changing voices
272 TODO: lookup correction distances? More advanced correction?
273 Possibly turn this off?
275 TODO: have to check wether the stems are in the same staff.
277 This routine reads the DIR-LIST property of both its L and R arguments. */
278 Real
279 Spacing_spanner::stem_dir_correction (Score_element*me, Score_element*l, Score_element*r)
281 SCM dl = l->get_elt_property ("dir-list");
282 SCM dr = r->get_elt_property ("dir-list");
284 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
285 return 0.;
287 dl = gh_car (dl);
288 dr = gh_car (dr);
290 assert (gh_number_p (dl) && gh_number_p(dr));
291 int d1 = gh_scm2int (dl);
292 int d2 = gh_scm2int (dr);
294 if (d1 == d2)
295 return 0.0;
298 Real correction = 0.0;
299 Real ssc = me->paper_l ()->get_var("stemSpacingCorrection");
302 if (d1 && d2 && d1 * d2 == -1)
304 correction = d1 * ssc;
306 else
307 programming_error ("Stem directions not set correctly for optical correction");
308 return correction;
312 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs);
314 Spacing_spanner::set_springs (SCM smob)
316 Score_element *me = unsmob_element (smob);
317 Link_array<Score_element> all (me->pscore_l_->line_l_->column_l_arr ()) ;
319 int j = 0;
321 for (int i = 1; i < all.size (); i++)
323 Score_element *sc = all[i];
324 if (Item::breakable_b (sc))
326 Link_array<Score_element> measure (all.slice (j, i+1));
327 do_measure (me, measure);
328 j = i;
333 farewell, cruel world
335 me->suicide ();
336 return SCM_UNSPECIFIED;