new file. (Thanks Hendrik Maryns)
[lilypond.git] / lily / spacing-spanner.cc
blobfdc5bacad4a0c78c87c63f6b75f6d370c8a5f7b6
1 /*
2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
8 */
10 #include <math.h>
11 #include <stdio.h>
13 #include "main.hh"
14 #include "system.hh"
15 #include "warn.hh"
16 #include "output-def.hh"
17 #include "paper-score.hh"
18 #include "paper-column.hh"
19 #include "item.hh"
20 #include "moment.hh"
21 #include "note-spacing.hh"
22 #include "misc.hh"
23 #include "warn.hh"
24 #include "staff-spacing.hh"
25 #include "spring.hh"
26 #include "paper-column.hh"
27 #include "spaceable-grob.hh"
28 #include "break-align-interface.hh"
29 #include "spacing-interface.hh"
33 TODO: this file/class is too complex. Should figure out how to chop
34 this up even more.
38 class Spacing_spanner
40 public:
41 static void standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
42 Real * fixed, Real * space, Moment);
45 static Real default_bar_spacing (Grob*,Grob*,Grob*,Moment);
46 static Real note_spacing (Grob*,Grob*,Grob*,Moment, bool*);
47 static Real get_duration_space (Grob*,Moment dur, Rational shortest, bool*);
48 static Rational find_shortest (Grob *, Link_array<Grob> const &);
49 static void breakable_column_spacing (Grob*, Item* l, Item *r, Moment);
50 static void find_loose_columns () {}
51 static void prune_loose_columns (Grob*,Link_array<Grob> *cols, Rational);
52 static void find_loose_columns (Link_array<Grob> cols);
53 static void set_explicit_neighbor_columns (Link_array<Grob> cols);
54 static void set_implicit_neighbor_columns (Link_array<Grob> cols);
55 static void do_measure (Rational, Grob*me,Link_array<Grob> *cols);
56 static void musical_column_spacing (Grob*,Item*,Item*, Real, Rational);
57 DECLARE_SCHEME_CALLBACK (set_springs, (SCM ));
58 static bool has_interface (Grob*);
62 Return whether COL is fixed to its neighbors by some kind of spacing
63 constraint.
66 If in doubt, then we're not loose; the spacing engine should space
67 for it, risking suboptimal spacing.
69 (Otherwise, we might risk core dumps, and other weird stuff.)
72 static bool
73 loose_column (Grob *l, Grob *c, Grob *r)
75 SCM rns = c->get_property ("right-neighbors");
76 SCM lns = c->get_property ("left-neighbors");
79 If this column doesn't have a proper neighbor, we should really
80 make it loose, but spacing it correctly is more than we can
81 currently can handle.
83 (this happens in the following situation:
86 | clef G
89 | | ||
90 | | ||
91 O O ||
94 the column containing the clef is really loose, and should be
95 attached right to the first column, but that is a lot of work for
96 such a borderline case.)
98 */
99 if (!ly_c_pair_p (lns) || !ly_c_pair_p (rns))
100 return false;
102 Item * l_neighbor = dynamic_cast<Item*> (unsmob_grob (ly_car (lns)));
103 Item * r_neighbor = dynamic_cast<Item*> (unsmob_grob (ly_car (rns)));
105 if (!l_neighbor || !r_neighbor)
106 return false;
108 l_neighbor = l_neighbor->get_column ();
109 r_neighbor = dynamic_cast<Item*> (Note_spacing::right_column (r_neighbor));
111 if (l == l_neighbor && r == r_neighbor)
112 return false;
114 if (!l_neighbor || !r_neighbor)
115 return false;
120 Only declare loose if the bounds make a little sense. This means
121 some cases (two isolated, consecutive clef changes) won't be
122 nicely folded, but hey, then don't do that.
124 if (! ((Paper_column::is_musical (l_neighbor) || Item::is_breakable (l_neighbor))
125 && (Paper_column::is_musical (r_neighbor) || Item::is_breakable (r_neighbor))) )
127 return false;
132 A rather hairy check, but we really only want to move around clefs. (anything else?)
134 in any case, we don't want to move bar lines.
136 for (SCM e = c->get_property ("elements"); ly_c_pair_p (e); e = ly_cdr (e))
138 Grob * g = unsmob_grob (ly_car (e));
139 if (g && Break_align_interface::has_interface (g))
141 for (SCM s = g->get_property ("elements"); ly_c_pair_p (s);
142 s = ly_cdr (s))
144 Grob *h = unsmob_grob (ly_car (s));
147 ugh. -- fix staff-bar name?
149 if (h && h->get_property ("break-align-symbol") == ly_symbol2scm ("staff-bar"))
150 return false;
155 return true;
159 Remove columns that are not tightly fitting from COLS. In the
160 removed columns, set 'between-cols to the columns where it is in
161 between.
163 void
164 Spacing_spanner::prune_loose_columns (Grob*me,Link_array<Grob> *cols, Rational shortest)
166 Link_array<Grob> newcols;
167 Real increment = robust_scm2double (me->get_property ("spacing-increment"), 1.2);
168 for (int i=0; i < cols->size (); i++)
170 if (Item::is_breakable (cols->elem (i)) || Paper_column::is_musical (cols->elem (i)))
172 newcols.push (cols->elem (i));
173 continue;
176 Grob *c = cols->elem (i);
177 if (loose_column (cols->elem (i-1), c, cols->elem (i+1)))
179 SCM lns = c->get_property ("left-neighbors");
180 lns = ly_c_pair_p (lns) ? ly_car (lns) : SCM_BOOL_F;
182 SCM rns = c->get_property ("right-neighbors");
183 rns = ly_c_pair_p (rns) ? ly_car (rns) : SCM_BOOL_F;
186 Either object can be non existent, if the score ends
187 prematurely.
189 rns = ly_car (unsmob_grob (rns)->get_property ("right-items"));
190 c->set_property ("between-cols", scm_cons (lns,
191 rns));
194 Set distance constraints for loose columns
196 Drul_array<Grob*> next_door;
197 next_door[LEFT] =cols->elem (i - 1);
198 next_door[RIGHT] =cols->elem (i + 1);
199 Direction d = LEFT;
200 Drul_array<Real> dists (0,0);
204 dists[d] = 0.0;
205 Item *lc = dynamic_cast<Item*> ((d == LEFT) ? next_door[LEFT] : c);
206 Item *rc = dynamic_cast<Item*> (d == LEFT ? c : next_door[RIGHT]);
208 for (SCM s = lc->get_property ("spacing-wishes");
209 ly_c_pair_p (s); s = ly_cdr (s))
211 Grob *sp = unsmob_grob (ly_car (s));
212 if (Note_spacing::left_column (sp) != lc
213 || Note_spacing::right_column (sp) != rc)
214 continue;
216 Real space, fixed;
217 fixed = 0.0;
218 bool dummy;
220 if (d == LEFT)
223 The note spacing should be taken from the musical
224 columns.
227 Real base = note_spacing (me, lc, rc, shortest, &dummy);
228 Note_spacing::get_spacing (sp, rc, base, increment, &space, &fixed);
230 space -= increment;
232 dists[d] = dists[d] >? space;
234 else
236 Real space, fixed_space;
237 Staff_spacing::get_spacing_params (sp,
238 &space, &fixed_space);
240 dists[d] = dists[d] >? fixed_space;
245 while (flip (&d) != LEFT);
247 Rod r;
248 r.distance_ = dists[LEFT] + dists[RIGHT];
249 r.item_l_drul_[LEFT] = dynamic_cast<Item*> (cols->elem (i-1));
250 r.item_l_drul_[RIGHT] = dynamic_cast<Item*> (cols->elem (i+1));
252 r.add_to_cols ();
254 else
256 newcols.push (c);
260 *cols = newcols;
264 Set neighboring columns determined by the spacing-wishes grob property.
266 void
267 Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
269 for (int i=0; i < cols.size (); i++)
271 SCM right_neighbors = SCM_EOL;
272 int min_rank = 100000; // inf.
275 SCM wishes= cols[i]->get_property ("spacing-wishes");
276 for (SCM s =wishes; ly_c_pair_p (s); s = ly_cdr (s))
278 Item * wish = dynamic_cast<Item*> (unsmob_grob (ly_car (s)));
280 Item * lc = wish->get_column ();
281 Grob * right = Note_spacing::right_column (wish);
283 if (!right)
284 continue;
286 Item * rc = dynamic_cast<Item*> (right);
288 int right_rank = Paper_column::get_rank (rc);
289 int left_rank = Paper_column::get_rank (lc);
292 update the left column.
294 if (right_rank <= min_rank)
296 if (right_rank < min_rank)
297 right_neighbors =SCM_EOL;
299 min_rank = right_rank;
300 right_neighbors = scm_cons (wish->self_scm (), right_neighbors);
304 update the right column of the wish.
306 int maxrank = 0;
307 SCM left_neighs = rc->get_property ("left-neighbors");
308 if (ly_c_pair_p (left_neighs)
309 && unsmob_grob (ly_car (left_neighs)))
311 Item * it = dynamic_cast<Item*> (unsmob_grob (ly_car (left_neighs)));
312 maxrank = Paper_column::get_rank (it->get_column ());
315 if (left_rank >= maxrank)
317 if (left_rank > maxrank)
318 left_neighs = SCM_EOL;
320 left_neighs = scm_cons (wish->self_scm (), left_neighs);
321 rc->set_property ("left-neighbors", right_neighbors);
325 if (ly_c_pair_p (right_neighbors))
327 cols[i]->set_property ("right-neighbors", right_neighbors);
333 Set neighboring columns that have no left/right-neighbor set
334 yet. Only do breakable non-musical columns, and musical columns.
336 void
337 Spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> cols)
339 for (int i = 0; i < cols.size (); i++)
341 Item * it = dynamic_cast<Item*>(cols[i]);
342 if (!Item::is_breakable (it) && !Paper_column::is_musical (it))
343 continue;
345 // it->breakable || it->musical
348 sloppy with typnig left/right-neighbors should take list, but paper-column found instead.
350 SCM ln = cols[i] ->get_property ("left-neighbors");
351 if (!ly_c_pair_p (ln) && i )
353 cols[i]->set_property ("left-neighbors", scm_cons (cols[i-1]->self_scm (), SCM_EOL));
356 SCM rn = cols[i] ->get_property ("right-neighbors");
357 if (!ly_c_pair_p (rn) && i < cols.size () - 1)
359 cols[i]->set_property ("right-neighbors", scm_cons (cols[i + 1]->self_scm (), SCM_EOL));
365 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
367 Spacing_spanner::set_springs (SCM smob)
369 Grob *me = unsmob_grob (smob);
372 can't use get_system() ? --hwn.
374 Link_array<Grob> all (me->pscore_->system_->columns ());
376 set_explicit_neighbor_columns (all);
378 SCM preset_shortest = me->get_property ("common-shortest-duration");
379 Rational global_shortest;
380 if (unsmob_moment (preset_shortest))
382 global_shortest = unsmob_moment (preset_shortest)->main_part_;
384 else
386 global_shortest = find_shortest (me, all);
387 if (verbose_global_b)
388 progress_indication (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
390 prune_loose_columns (me, &all, global_shortest);
391 set_implicit_neighbor_columns (all);
394 int j = 0;
395 for (int i = 1; i < all.size (); i++)
397 Grob *sc = all[i];
398 if (Item::is_breakable (sc))
400 Link_array<Grob> measure (all.slice (j, i+1));
401 do_measure (global_shortest, me, &measure);
402 j = i;
406 return SCM_UNSPECIFIED;
411 We want the shortest note that is also "common" in the piece, so we
412 find the shortest in each measure, and take the most frequently
413 found duration.
415 This probably gives weird effects with modern music, where every
416 note has a different duration, but hey, don't write that kind of
417 stuff, then.
420 Rational
421 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
424 ascending in duration
426 Array<Rational> durations;
427 Array<int> counts;
429 Rational shortest_in_measure;
430 shortest_in_measure.set_infinite (1);
432 for (int i =0 ; i < cols.size (); i++)
434 if (Paper_column::is_musical (cols[i]))
436 Moment *when = unsmob_moment (cols[i]->get_property ("when"));
439 ignore grace notes for shortest notes.
441 if (when && when->grace_part_)
442 continue;
444 SCM st = cols[i]->get_property ("shortest-starter-duration");
445 Moment this_shortest = *unsmob_moment (st);
446 assert (this_shortest.to_bool ());
447 shortest_in_measure = shortest_in_measure <? this_shortest.main_part_;
449 else if (!shortest_in_measure.is_infinity ()
450 && Item::is_breakable (cols[i]))
452 int j = 0;
453 for (; j < durations.size (); j++)
455 if (durations[j] > shortest_in_measure)
457 counts.insert (1, j);
458 durations.insert (shortest_in_measure, j);
459 break;
461 else if (durations[j] == shortest_in_measure)
463 counts[j]++;
464 break;
468 if (durations.size () == j)
470 durations.push (shortest_in_measure);
471 counts.push (1);
474 shortest_in_measure.set_infinite (1);
478 int max_idx = -1;
479 int max_count = 0;
480 for (int i =durations.size (); i--;)
482 if (counts[i] >= max_count)
484 max_idx = i;
485 max_count = counts[i];
488 // printf ("duration %d/%d, count %d\n", durations[i].num (), durations[i].den (), counts[i]);
491 SCM bsd = me->get_property ("base-shortest-duration");
492 Rational d = Rational (1,8);
493 if (Moment *m = unsmob_moment (bsd))
494 d = m->main_part_;
496 if (max_idx >= 0)
497 d = d <? durations[max_idx] ;
499 return d;
503 Generate spacing for a single measure. We used to have code that did
504 per-measure spacing. Now we have piecewise spacing. We should fix
505 this to support "spacing-regions": some regions have different notes
506 (different time sigs) than others, and should be spaced differently.
508 void
509 Spacing_spanner::do_measure (Rational global_shortest, Grob*me, Link_array<Grob> *cols)
512 Real headwid = robust_scm2double (me->get_property ("spacing-increment"), 1);
513 for (int i= 0; i < cols->size () - 1; i++)
515 Item * l = dynamic_cast<Item*> (cols->elem (i));
516 Item * r = dynamic_cast<Item*> (cols->elem (i+1));
518 Paper_column * lc = dynamic_cast<Paper_column*> (l);
519 Paper_column * rc = dynamic_cast<Paper_column*> (r);
521 if (!Paper_column::is_musical (l))
523 breakable_column_spacing (me, l, r, global_shortest);
527 The case that the right part is broken as well is rather
528 rare, but it is possible, eg. with a single empty measure,
529 or if one staff finishes a tad earlier than the rest.
532 Item *lb = l->find_prebroken_piece (RIGHT);
533 Item *rb = r->find_prebroken_piece (LEFT);
535 if (lb)
536 breakable_column_spacing (me, lb,r, global_shortest);
538 if (rb)
539 breakable_column_spacing (me, l, rb, global_shortest);
540 if (lb && rb)
541 breakable_column_spacing (me, lb, rb, global_shortest);
543 continue ;
547 musical_column_spacing (me, lc, rc, headwid, global_shortest);
548 if (Item *rb = r->find_prebroken_piece (LEFT))
549 musical_column_spacing (me, lc, rb, headwid, global_shortest);
555 Generate the space between two musical columns LC and RC, given
556 spacing parameters INCR and SHORTEST.
558 void
559 Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real increment, Rational global_shortest)
561 bool expand_only = false;
562 Real base_note_space = note_spacing (me, lc, rc, global_shortest, &expand_only);
564 Real compound_note_space = 0.0;
565 Real compound_fixed_note_space = 0.0;
566 int wish_count = 0;
568 SCM seq = lc->get_property ("right-neighbors");
571 We adjust the space following a note only if the next note
572 happens after the current note (this is set in the grob
573 property SPACING-SEQUENCE.
575 for (SCM s = seq; ly_c_pair_p (s); s = ly_cdr (s))
577 Grob * wish = unsmob_grob (ly_car (s));
579 Item *wish_rcol = Note_spacing::right_column (wish);
580 if (Note_spacing::left_column (wish) != lc
581 || (wish_rcol != rc && wish_rcol != rc->original_))
582 continue;
585 This is probably a waste of time in the case of polyphonic
586 music. */
587 if (Note_spacing::has_interface (wish))
589 Real space =0.0;
590 Real fixed =0.0;
592 Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed);
595 compound_note_space = compound_note_space + space;
596 compound_fixed_note_space = compound_fixed_note_space + fixed;
597 wish_count ++;
602 if (Paper_column::when_mom (rc).grace_part_ &&
603 !Paper_column::when_mom (lc).grace_part_)
606 Ugh. 0.8 is arbitrary.
608 compound_note_space *= 0.8;
611 if (compound_note_space < 0 || wish_count == 0)
613 compound_note_space = base_note_space;
614 compound_fixed_note_space = increment;
616 else
618 compound_note_space /= wish_count;
619 compound_fixed_note_space /= wish_count;
623 Whatever we do, the fixed space is smaller than the real
624 space.
626 TODO: this criterion is discontinuous in the derivative.
627 Maybe it should be continuous?
629 compound_fixed_note_space = compound_fixed_note_space <? compound_note_space;
631 bool packed = to_boolean (me->get_paper ()->c_variable ("packed"));
632 Real strength, distance;
635 TODO: make sure that the space doesn't exceed the right margin.
637 if (packed)
640 In packed mode, pack notes as tight as possible. This makes
641 sense mostly in combination with raggedright mode: the notes
642 are then printed at minimum distance. This is mostly useful
643 for ancient notation, but may also be useful for some flavours
644 of contemporary music. If not in raggedright mode, lily will
645 pack as much bars of music as possible into a line, but the
646 line will then be stretched to fill the whole linewidth.
648 strength = 1.0;
649 distance = compound_fixed_note_space;
651 else
653 strength = 1 / (compound_note_space - compound_fixed_note_space);
654 distance = compound_note_space;
657 // Spaceable_grob::add_spring (lc, rc, distance, strength, expand_only);
659 Spaceable_grob::add_spring (lc, rc, distance, strength, false);
664 The one-size-fits all spacing. It doesn't take into account
665 different spacing wishes from one to the next column.
667 void
668 Spacing_spanner::standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
669 Real * fixed, Real * space,
670 Moment shortest)
672 *fixed = 0.0;
673 Direction d = LEFT;
674 Drul_array<Item*> cols (l,r);
678 if (!Paper_column::is_musical (cols[d]))
681 Tied accidentals over barlines cause problems, so lets see
682 what happens if we do this for non musical columns only.
684 Interval lext = cols[d]->extent (cols [d], X_AXIS);
685 if (!lext.is_empty ())
686 *fixed += -d * lext[-d];
689 while (flip (&d) != LEFT);
692 if (l->is_breakable (l) && r->is_breakable (r))
694 Moment *dt = unsmob_moment (l->get_property ("measure-length"));
695 Moment mlen (1);
696 if (dt)
697 mlen = *dt;
699 Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
701 *space = *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8;
703 else
705 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
707 if (dt == Moment (0,0))
710 In this case, Staff_spacing should handle the job,
711 using dt when it is 0 is silly.
713 *space = *fixed + 0.5;
715 else
717 bool dummy;
718 *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
725 Read hints from L and generate springs.
727 void
728 Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shortest)
730 Real compound_fixed = 0.0;
731 Real compound_space = 0.0;
732 int wish_count = 0;
734 Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
736 if (dt == Moment (0,0))
738 for (SCM s = l->get_property ("spacing-wishes");
739 ly_c_pair_p (s); s = ly_cdr (s))
741 Item * spacing_grob = dynamic_cast<Item*> (unsmob_grob (ly_car (s)));
743 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
744 continue;
746 Real space;
747 Real fixed_space;
750 column for the left one settings should be ok due automatic
751 pointer munging.
754 assert (spacing_grob-> get_column () == l);
756 Staff_spacing::get_spacing_params (spacing_grob,
757 &space, &fixed_space);
759 if (Paper_column::when_mom (r).grace_part_)
762 Correct for grace notes.
764 Ugh. The 0.8 is arbitrary.
766 space *= 0.8;
770 compound_space += space;
771 compound_fixed += fixed_space;
772 wish_count ++ ;
776 if (compound_space <= 0.0 || !wish_count)
778 standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space ,
779 shortest);
780 wish_count = 1;
782 else
784 compound_space /= wish_count;
785 compound_fixed /= wish_count;
788 assert (!isinf (compound_space));
789 compound_space = compound_space >? compound_fixed;
793 Hmm. we do 1/0 in the next thing. Perhaps we should check if this
794 works on all architectures.
798 There used to be code that changed spacing depending on
799 raggedright setting. Ugh.
801 Do it more cleanly, or rename the property.
804 Real strength = 1 / (compound_space - compound_fixed);
805 Real distance = compound_space;
806 Spaceable_grob::add_spring (l, r, distance, strength, false);
811 Get the measure wide ant for arithmetic spacing.
813 Real
814 Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool * expand_only)
816 Real k = robust_scm2double (me->get_property ("shortest-duration-space"), 1);
817 Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
819 if (d < shortest)
822 We don't space really short notes using the log of the
823 duration, since it would disproportionally stretches the long
824 notes in a piece. In stead, we use geometric spacing with constant 0.5
825 (i.e. linear.)
827 This should probably be tunable, to use other base numbers.
829 In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
830 3.6 mm (total). head-width = 2.4, so we 1.2mm for 16th, 1.5
831 mm for 8th. (white space), suggesting that we use
833 (1.2 / 1.5)^{-log2(duration ratio)}
837 Rational ratio = d.main_part_ / shortest;
839 return ((k-1) + double (ratio)) * incr;
841 else
844 John S. Gourlay. ``Spacing a Line of Music,'' Technical
845 Report OSU-CISRC-10/87-TR35, Department of Computer and
846 Information Science, The Ohio State University, 1987.
848 Real log = log_2 (shortest);
849 k -= log;
850 Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
851 *expand_only = false;
853 return (log_2 (compdur) + k) * incr;
857 Real
858 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
859 Moment shortest, bool * expand_only)
861 Moment shortest_playing_len = 0;
862 SCM s = lc->get_property ("shortest-playing-duration");
864 if (unsmob_moment (s))
865 shortest_playing_len = *unsmob_moment (s);
867 if (! shortest_playing_len.to_bool ())
869 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ());
870 shortest_playing_len = 1;
873 Moment lwhen = Paper_column::when_mom (lc);
874 Moment rwhen = Paper_column::when_mom (rc);
876 Moment delta_t = rwhen - lwhen;
877 if (!Paper_column::is_musical (rc))
880 when toying with mmrests, it is possible to have musical
881 column on the left and non-musical on the right, spanning
882 several measures.
885 Moment *dt = unsmob_moment (rc->get_property ("measure-length"));
886 if (dt)
888 delta_t = delta_t <? *dt;
891 The following is an extra safety measure, such that
892 the length of a mmrest event doesn't cause havoc.
894 shortest_playing_len = shortest_playing_len <? *dt;
897 Real dist = 0.0;
900 In normal situations, the next column is at most
901 SHORTEST_PLAYING_LEN away. However chord-tremolos do funky faking stuff
902 with durations, invalidating this assumption. Here we kludge
903 around to get chord tremolos to behave properly.
906 shortest_playing_len = shortest_playing_len >? delta_t;
907 if (delta_t.main_part_ && !lwhen.grace_part_)
909 dist = get_duration_space (me, shortest_playing_len, shortest.main_part_, expand_only);
910 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
912 else if (delta_t.grace_part_)
915 TODO: figure out how to space grace notes.
917 dist = get_duration_space (me, shortest, shortest.main_part_, expand_only);
919 Real grace_fact
920 = robust_scm2double (me->get_property ("grace-space-factor"), 1);
922 dist *= grace_fact;
926 return dist;
931 ADD_INTERFACE (Spacing_spanner,"spacing-spanner-interface",
932 "The space taken by a note is dependent on its duration. Doubling a\n"
933 "duration adds spacing-increment to the space. The most common shortest\n"
934 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
935 "spaced proportonial to their duration.\n"
936 "\n"
937 "Typically, the increment is the width of a black note head. In a\n"
938 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
939 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
940 "head width) A 16th note is followed by 0.5 note head width. The\n"
941 "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
942 "grace-space-factor spacing-increment base-shortest-duration shortest-duration-space common-shortest-duration");
946 ADD_INTERFACE (Spacing_interface,"spacing-interface",
947 "Something to do with line breaking and spacing. Kill this one after determining line breaks.",
948 "");