lilypond-1.5.2
[lilypond.git] / lily / spacing-spanner.cc
blob1bff342744f9b8205f725f61857074a99d161fe9
1 /*
2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2001 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 (Grob*me)
22 me->set_extent_callback (SCM_EOL, X_AXIS);
23 me->set_extent_callback (SCM_EOL, Y_AXIS) ;
26 #if 0
27 struct Note_run
29 Array<int> idxes;
30 int start, end;
31 Moment duration;
32 int count;
35 int
36 column_compare (Grob *const &t1, Grob *const &t2)
38 return Moment::compare (Paper_column::when_mom (t1),
39 Paper_column::when_mom (t2));
43 Note_run
44 run_length (Moment dt, int i, Array<Moment> const &moms,
45 Link_array<Note_run> runs)
47 int k = 0;
48 Array<int> idxes;
50 idxes.push (i);
51 while (1)
53 Moment next = moms[i] + dt;
54 while (i < moms.size () && moms[i] < next)
55 i++;
56 if (i == moms.size () || moms[i] != next)
57 break;
59 idxes.push (i);
60 k++;
63 Moment dur = idxes.size ()
66 void
67 find_runs (Grob*me, Link_array<Grob> cols)
69 Link_array<Grob> filter_cols;
70 Array<Moment> col_moments;
71 for (int i = 0; i < cols.size (); i++)
73 Moment w = Paper_column::when_mom (cols[i]);
75 if (!w.grace_mom_ && Paper_column::musical_b (cols[i]))
77 filter_cols.push (cols[i]);
78 col_moments.push (w);
82 Moment end_mom = col_moments.top ();
83 for (int i = 0; i < col_moments.size () ; i++)
85 for (int j = i+1; j < col_moments.size (); j++)
87 Moment dt = Paper_column::col_momentsfilter_cols
91 #endif
95 The algorithm is partly taken from :
97 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
98 OSU-CISRC-10/87-TR35, Department of Computer and Information
99 Science, The Ohio State University, 1987.
101 TOO HAIRY.
103 TODO: write comments
106 void
107 Spacing_spanner::do_measure (Grob*me, Link_array<Grob> cols)
109 Moment shortest;
110 Moment mean_shortest;
113 space as if this duration is present.
115 Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
116 shortest.set_infinite (1);
118 int n = 0;
119 for (int i =0 ; i < cols.size (); i++)
121 if (Paper_column::musical_b (cols[i]))
123 Moment *when = unsmob_moment (cols[i]->get_grob_property ("when"));
126 ignore grace notes for shortest notes.
128 if (when && when->grace_mom_)
129 continue;
131 SCM st = cols[i]->get_grob_property ("shortest-starter-duration");
132 Moment this_shortest = *unsmob_moment (st);
133 shortest = shortest <? this_shortest;
134 if (!mean_shortest.main_part_.infty_b ())
136 n++;
137 mean_shortest += this_shortest;
141 mean_shortest /= n;
143 for (int i= 0; i < cols.size () - 1; i++)
145 Item * l = dynamic_cast<Item*> (cols[i]);
146 Item * r = dynamic_cast<Item*> (cols[i+1]);
147 Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
148 Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
150 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
154 left refers to the space that is associated with items of the left column, so you have
156 LC <- left_space -><- right_space -> RC
157 <- total space ->
160 typically, right_space is non-zero when there are
161 accidentals in RC
164 for (int j=0; j < 4; j++)
166 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
167 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
168 if (!lc || !rc)
169 continue;
171 Spring s;
172 s.item_l_drul_[LEFT] = lc;
173 s.item_l_drul_[RIGHT] = rc;
175 SCM hint = lc->get_grob_property ("extra-space");
176 SCM next_hint = rc->get_grob_property ("extra-space");
177 SCM stretch_hint = lc->get_grob_property ("stretch-distance");
178 SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");
180 Real left_distance = 0;
181 if (gh_pair_p (hint))
183 left_distance = gh_scm2double (gh_cdr (hint));
185 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
186 else if (!Paper_column::musical_b (lc) && i+1 < cols.size ())
188 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
190 else if (Paper_column::musical_b ( lc))
192 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
194 else
195 programming_error ("uninitialised left_distance");
197 s.distance_f_ = left_distance;
200 Only do tight spaces *after* barlines (breakable columns),
201 not before.
203 We want the space before barline to be like the note
204 spacing in the measure.
206 SCM sfac =lc->get_grob_property ("space-factor");
207 if (gh_number_p (lc->get_grob_property ("column-space-strength"))
208 && (Item::breakable_b (lc) || lc->original_l_))
210 s.strength_f_ =
211 gh_scm2double (lc->get_grob_property ("column-space-strength"));
213 else if (gh_number_p (sfac))
214 left_distance *= gh_scm2double (sfac);
217 Real right_dist = 0.0;
218 if (gh_pair_p (next_hint))
220 right_dist += - gh_scm2double (gh_car (next_hint));
222 else
224 Interval ext (rc->extent (rc, X_AXIS));
225 right_dist = ext.empty_b () ? 0.0 : - ext [LEFT];
229 don't want to create too much extra space for accidentals
231 if (Paper_column::musical_b (rc))
233 if (to_boolean (rc->get_grob_property ("contains-grace")))
234 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
235 else
236 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
239 s.distance_f_ = left_distance + right_dist;
241 Real stretch_dist = 0.;
242 if (gh_number_p (stretch_hint))
243 stretch_dist += gh_scm2double (stretch_hint);
244 else
245 stretch_dist += left_distance;
247 if (gh_pair_p (next_stretch_hint))
248 // see regtest spacing-tight
249 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
250 else
251 stretch_dist += right_dist;
253 if (s.distance_f_ <0)
255 programming_error ("Negative dist, setting to 1.0 PT");
256 s.distance_f_ = 1.0;
258 if (stretch_dist == 0.0)
261 \bar "". We give it 0 space, with high strength.
263 s.strength_f_ = 20.0;
265 else
266 s.strength_f_ /= stretch_dist;
268 s.add_to_cols ();
275 Do something if breakable column has no spacing hints set.
277 Real
278 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
279 Moment shortest)
281 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
282 Real durational_distance = 0;
283 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
286 ugh should use shortest_playing distance
288 if (delta_t)
290 durational_distance = get_duration_space (me, delta_t, shortest);
293 return symbol_distance >? durational_distance;
298 Get the measure wide ant for arithmetic spacing.
300 @see
301 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
302 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
303 The Ohio State University, 1987.
306 Real
307 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
309 Real log = log_2 (shortest);
310 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
311 - log;
313 return (log_2 (d) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
317 Real
318 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
319 Moment shortest)
321 Moment shortest_playing_len = 0;
322 SCM s = lc->get_grob_property ("shortest-playing-duration");
324 // SCM s = lc->get_grob_property ("mean-playing-duration");
325 if (unsmob_moment (s))
326 shortest_playing_len = *unsmob_moment (s);
328 if (! shortest_playing_len)
330 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
331 shortest_playing_len = 1;
334 if (! shortest)
336 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
337 shortest = 1;
339 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
340 Real dist = get_duration_space (me, shortest_playing_len, shortest);
344 ugh: 0.1 is an arbitrary distance.
346 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
347 + 0.1 * (double) (delta_t.grace_mom_ / shortest_playing_len.main_part_);
352 UGH: KLUDGE!
355 if (delta_t > Moment (1,32))
356 dist += stem_dir_correction (me, lc,rc);
359 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
360 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
362 if (lm && rm)
364 if (lm->grace_mom_ && rm->grace_mom_)
365 dist *= 0.5;
366 else if (!rm->grace_mom_ && lm->grace_mom_)
367 dist *= 0.7;
371 return dist;
376 Correct for optical illusions. See [Wanske] p. 138. The combination
377 up-stem + down-stem should get extra space, the combination
378 down-stem + up-stem less.
380 This should be more advanced, since relative heights of the note
381 heads also influence required correction.
383 Also might not work correctly in case of multi voices or staff
384 changing voices
386 TODO: lookup correction distances? More advanced correction?
387 Possibly turn this off?
389 TODO: have to check wether the stems are in the same staff.
391 This routine reads the DIR-LIST property of both its L and R arguments. */
392 Real
393 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
395 SCM dl = l->get_grob_property ("dir-list");
396 SCM dr = r->get_grob_property ("dir-list");
398 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
399 return 0.;
401 dl = gh_car (dl);
402 dr = gh_car (dr);
404 assert (gh_number_p (dl) && gh_number_p (dr));
405 int d1 = gh_scm2int (dl);
406 int d2 = gh_scm2int (dr);
408 if (d1 == d2)
409 return 0.0;
412 Real correction = 0.0;
413 Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
415 if (d1 && d2 && d1 * d2 == -1)
417 correction = d1 * ssc;
419 else
420 programming_error ("Stem directions not set correctly for optical correction");
421 return correction;
425 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
427 Spacing_spanner::set_springs (SCM smob)
429 Grob *me = unsmob_grob (smob);
430 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
432 int j = 0;
434 for (int i = 1; i < all.size (); i++)
436 Grob *sc = all[i];
437 if (Item::breakable_b (sc))
439 Link_array<Grob> measure (all.slice (j, i+1));
440 do_measure (me, measure);
441 j = i;
446 farewell, cruel world
448 me->suicide ();
449 return SCM_UNSPECIFIED;
455 maximum-duration-for-spacing
456 From: bf250@freenet.carleton.ca (John Sankey)
457 To: gnu-music-discuss@gnu.org
458 Subject: note spacing suggestion
459 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
461 Currently, Lily spaces notes by starting with a basic distance,
462 arithmetic_multiplier, which it applies to the minimum duration note
463 of the bar. Then she adds a logarithmic increment, scaled from
464 arithmetic_basicspace, for longer notes. (Then, columns are aligned
465 and justified.) Fundamentally, this matches visual spacing to musical
466 weight and works well.
468 A lot of the time in music, I see a section basically in melodic
469 notes that occasionally has a rapid ornamental run (scale). So, there
470 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
471 a return to long notes. Currently, Lily gives the same horizontal
472 space to the 1/32nd notes in their bar (even if set in small size as
473 is commonly done for cadenzii) as she gives to 1/4 notes in bars
474 where 1/4 note is the minimum duration. The resulting visual weight
475 does not match the musical weight over the page.
477 Looking at the music I am typesetting, I feel that Lily's spacing
478 could be significantly improved if, with no change in the basic
479 method used, arithmetic_multiplier could be applied referred to the
480 same duration throughout a piece. Of course, the current method
481 should be retained for those who have already set music in it, so I
482 suggest a property called something like arithmetic_base=16 to fix
483 1/16 duration as the reference for arithmetic_multiplier; the default
484 would be a dynamic base is it is now.
486 Does anyone else feel that this would be a useful improvement for
487 their music? (Of course, if arithmetic_multiplier became a regular
488 property, this could be used to achieve a similar result by
489 tweaking.)