lilypond-1.5.9
[lilypond.git] / lily / spacing-spanner.cc
blob472fa6732bd82528b5ec2584caaeab60fe99c6b2
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_part_ && 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> const & 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_part_)
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 Array<Spring> springs;
144 for (int i= 0; i < cols.size () - 1; i++)
146 Item * l = dynamic_cast<Item*> (cols[i]);
147 Item * r = dynamic_cast<Item*> (cols[i+1]);
148 Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
149 Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
151 Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
155 left refers to the space that is associated with items of the left column, so you have
157 LC <- left_space -><- right_space -> RC
158 <- total space ->
161 typically, right_space is non-zero when there are
162 accidentals in RC
165 for (int j=0; j < 4; j++)
167 Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
168 Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
169 if (!lc || !rc)
170 continue;
172 Spring s;
173 s.item_l_drul_[LEFT] = lc;
174 s.item_l_drul_[RIGHT] = rc;
176 SCM hint = lc->get_grob_property ("extra-space");
177 SCM next_hint = rc->get_grob_property ("extra-space");
178 SCM stretch_hint = lc->get_grob_property ("stretch-distance");
179 SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");
181 Real left_distance = 0;
182 if (gh_pair_p (hint))
184 left_distance = gh_scm2double (gh_cdr (hint));
186 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
187 else if (!Paper_column::musical_b (lc) && i+1 < cols.size ())
189 left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
191 else if (Paper_column::musical_b ( lc))
193 left_distance = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
195 else
196 programming_error ("uninitialised left_distance");
198 s.distance_f_ = left_distance;
201 Only do tight spaces *after* barlines (breakable columns),
202 not before.
204 We want the space before barline to be like the note
205 spacing in the measure.
207 SCM sfac =lc->get_grob_property ("space-factor");
208 if (gh_number_p (lc->get_grob_property ("column-space-strength"))
209 && (Item::breakable_b (lc) || lc->original_l_))
211 s.strength_f_ =
212 gh_scm2double (lc->get_grob_property ("column-space-strength"));
214 else if (gh_number_p (sfac))
215 left_distance *= gh_scm2double (sfac);
218 Real right_dist = 0.0;
219 if (gh_pair_p (next_hint))
221 right_dist += - gh_scm2double (gh_car (next_hint));
223 else
225 Interval ext (rc->extent (rc, X_AXIS));
226 right_dist = ext.empty_b () ? 0.0 : - ext [LEFT];
230 don't want to create too much extra space for accidentals
232 if (Paper_column::musical_b (rc))
234 if (to_boolean (rc->get_grob_property ("contains-grace")))
235 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
236 else
237 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
240 s.distance_f_ = left_distance + right_dist;
242 Real stretch_dist = 0.;
243 if (gh_number_p (stretch_hint))
244 stretch_dist += gh_scm2double (stretch_hint);
245 else
246 stretch_dist += left_distance;
248 if (gh_pair_p (next_stretch_hint))
249 // see regtest spacing-tight
250 stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
251 else
252 stretch_dist += right_dist;
254 if (s.distance_f_ <0)
256 programming_error ("Negative dist, setting to 1.0 PT");
257 s.distance_f_ = 1.0;
259 if (stretch_dist == 0.0)
262 \bar "". We give it 0 space, with high strength.
264 s.strength_f_ = 20.0;
266 else
267 s.strength_f_ /= stretch_dist;
269 springs.push (s);
273 Spacing_spanner::stretch_to_regularity (me, &springs, cols);
274 for (int i=springs.size (); i --;)
275 springs[i].add_to_cols ();
279 Look at COLS, searching for columns that have 'regular-distance-to
280 set. A sequence of columns that have this property set should have
281 an equal distance (an equispaced run). Extract the projected
282 distance from SPRINGS, and scale SPRINGS for the equispaced run, to the
283 widest space necessary.
286 TODO:
288 -- inefficient code; maybe it is easier to twiddle with the springs
289 after they've become grob properties (ie. have their
290 minimum-distances set)
292 -- does not adjust strength field of the springs very well: result
293 awkward spacing at the start of a line. (?)
295 -- will be confused when there are multiple equispaced runs in a measure.
297 -- dealing with springs for line breaks is a little tricky; in any
298 case, we will only space per measure.
300 -- we scale to actual distances, not to optical effects. Eg. if the
301 equispaced run contains optical corrections, then the scaling will
302 cancel those.
304 -- Regular_spacing_engraver doesn't mark the first column of the
305 next bar, making the space before a barline too short, in this case
308 x<- 16ths--> x(8th)
309 x(8th) x(8th) <- equispaced run.
313 void
314 Spacing_spanner::stretch_to_regularity (Grob *me,
315 Array<Spring> * springs,
316 Link_array<Grob> const & cols)
319 Find the starting column of the run. REGULAR-DISTANCE-TO points
320 back to a previous column, so we look ahead to find a column
321 pointing back to the first one.
324 Grob * first_regular_spaced_col = 0;
325 for (int i = 0 ; i < cols.size () && !first_regular_spaced_col; i++)
327 SCM rdt = cols[i]->get_grob_property ("regular-distance-to");
328 if (cols.find_l (unsmob_grob (rdt)))
329 first_regular_spaced_col = unsmob_grob (rdt);
331 for (int i = springs->size (); i-- ;)
332 springs->elem (i).set_to_cols ();
334 int i;
335 for (i = 0; i < springs->size ()
336 && springs->elem (i).item_l_drul_[RIGHT] != first_regular_spaced_col;
337 i++)
341 if (i==springs->size ())
342 return ;
344 Real maxdist = 0.0;
345 Real dist =0.0;
346 Grob *last_col = first_regular_spaced_col;
347 Grob *last_regular_spaced_col = first_regular_spaced_col;
351 find the max distance for this run.
353 for (int j = i; j < springs->size (); j++)
355 Spring *s = &(springs->elem_ref (j));
356 if (s->item_l_drul_[LEFT] != last_col)
357 continue;
359 dist += s->distance_f_;
361 last_col = s->item_l_drul_[RIGHT];
362 SCM rdt = last_col->get_grob_property ("regular-distance-to");
363 if (unsmob_grob (rdt) == last_regular_spaced_col)
365 maxdist = maxdist >? dist;
366 dist = 0.0;
367 last_regular_spaced_col = last_col;
373 Scale the springs
375 dist =0.0;
376 last_col = first_regular_spaced_col;
377 last_regular_spaced_col = first_regular_spaced_col;
378 for (int j = i; j < springs->size (); j++)
380 Spring *s = &springs->elem_ref (j);
381 if (s->item_l_drul_[LEFT] != last_col)
382 continue;
383 dist += s->distance_f_;
385 last_col = s->item_l_drul_[RIGHT];
386 SCM rdt = last_col->get_grob_property ("regular-distance-to");
387 if (unsmob_grob (rdt) == last_regular_spaced_col)
389 do {
390 springs->elem_ref (i).distance_f_ *= maxdist / dist;
391 springs->elem_ref (i).strength_f_ *= dist / maxdist;
392 } while (i++ < j);
393 last_regular_spaced_col = last_col;
394 dist =0.0;
400 Do something if breakable column has no spacing hints set.
402 Real
403 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
404 Moment shortest)
406 Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
407 Real durational_distance = 0;
408 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
411 ugh should use shortest_playing distance
413 if (delta_t.to_bool ())
415 durational_distance = get_duration_space (me, delta_t, shortest);
418 return symbol_distance >? durational_distance;
423 Get the measure wide ant for arithmetic spacing.
425 @see
426 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
427 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
428 The Ohio State University, 1987.
431 Real
432 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
434 Real log = log_2 (shortest.main_part_);
435 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
436 - log;
438 Rational compdur = d.main_part_ + d.grace_part_ / Rational (3);
439 return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
443 Real
444 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
445 Moment shortest)
447 Moment shortest_playing_len = 0;
448 SCM s = lc->get_grob_property ("shortest-playing-duration");
450 // SCM s = lc->get_grob_property ("mean-playing-duration");
451 if (unsmob_moment (s))
452 shortest_playing_len = *unsmob_moment (s);
454 if (! shortest_playing_len.to_bool ())
456 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
457 shortest_playing_len = 1;
460 if (! shortest.to_bool ())
462 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
463 shortest = 1;
465 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
466 Real dist = get_duration_space (me, shortest_playing_len, shortest);
470 ugh: 0.1 is an arbitrary distance.
472 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
473 + 0.1 * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
478 UGH: KLUDGE!
481 if (delta_t > Moment (Rational (1,32)))
482 dist += stem_dir_correction (me, lc,rc);
485 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
486 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
488 if (lm && rm)
490 if (lm->grace_part_ && rm->grace_part_)
491 dist *= 0.5;
492 else if (!rm->grace_part_ && lm->grace_part_)
493 dist *= 0.7;
497 return dist;
502 Correct for optical illusions. See [Wanske] p. 138. The combination
503 up-stem + down-stem should get extra space, the combination
504 down-stem + up-stem less.
506 This should be more advanced, since relative heights of the note
507 heads also influence required correction.
509 Also might not work correctly in case of multi voices or staff
510 changing voices
512 TODO: lookup correction distances? More advanced correction?
513 Possibly turn this off?
515 TODO: have to check wether the stems are in the same staff.
517 This routine reads the DIR-LIST property of both its L and R arguments. */
518 Real
519 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
521 SCM dl = l->get_grob_property ("dir-list");
522 SCM dr = r->get_grob_property ("dir-list");
524 if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
525 return 0.;
527 dl = gh_car (dl);
528 dr = gh_car (dr);
530 assert (gh_number_p (dl) && gh_number_p (dr));
531 int d1 = gh_scm2int (dl);
532 int d2 = gh_scm2int (dr);
534 if (d1 == d2)
535 return 0.0;
538 Real correction = 0.0;
539 Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
541 if (d1 && d2 && d1 * d2 == -1)
543 correction = d1 * ssc;
545 else
546 programming_error ("Stem directions not set correctly for optical correction");
547 return correction;
551 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
553 Spacing_spanner::set_springs (SCM smob)
555 Grob *me = unsmob_grob (smob);
556 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
558 int j = 0;
560 for (int i = 1; i < all.size (); i++)
562 Grob *sc = all[i];
563 if (Item::breakable_b (sc))
565 Link_array<Grob> measure (all.slice (j, i+1));
566 do_measure (me, measure);
567 j = i;
572 farewell, cruel world
574 me->suicide ();
575 return SCM_UNSPECIFIED;
581 maximum-duration-for-spacing
582 From: bf250@freenet.carleton.ca (John Sankey)
583 To: gnu-music-discuss@gnu.org
584 Subject: note spacing suggestion
585 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
587 Currently, Lily spaces notes by starting with a basic distance,
588 arithmetic_multiplier, which it applies to the minimum duration note
589 of the bar. Then she adds a logarithmic increment, scaled from
590 arithmetic_basicspace, for longer notes. (Then, columns are aligned
591 and justified.) Fundamentally, this matches visual spacing to musical
592 weight and works well.
594 A lot of the time in music, I see a section basically in melodic
595 notes that occasionally has a rapid ornamental run (scale). So, there
596 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
597 a return to long notes. Currently, Lily gives the same horizontal
598 space to the 1/32nd notes in their bar (even if set in small size as
599 is commonly done for cadenzii) as she gives to 1/4 notes in bars
600 where 1/4 note is the minimum duration. The resulting visual weight
601 does not match the musical weight over the page.
603 Looking at the music I am typesetting, I feel that Lily's spacing
604 could be significantly improved if, with no change in the basic
605 method used, arithmetic_multiplier could be applied referred to the
606 same duration throughout a piece. Of course, the current method
607 should be retained for those who have already set music in it, so I
608 suggest a property called something like arithmetic_base=16 to fix
609 1/16 duration as the reference for arithmetic_multiplier; the default
610 would be a dynamic base is it is now.
612 Does anyone else feel that this would be a useful improvement for
613 their music? (Of course, if arithmetic_multiplier became a regular
614 property, this could be used to achieve a similar result by
615 tweaking.)