]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
* Documentation/user/basic-notation.itely (Tuplets): add note
[lilypond.git] / lily / spacing-spanner.cc
1 /*
2   spacing-spanner.cc -- implement Spacing_spanner
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1999--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include <math.h>
10 #include <cstdio>
11
12 #include "main.hh"
13 #include "system.hh"
14 #include "warn.hh"
15 #include "output-def.hh"
16 #include "paper-score.hh"
17 #include "paper-column.hh"
18 #include "moment.hh"
19 #include "note-spacing.hh"
20 #include "misc.hh"
21 #include "warn.hh"
22 #include "staff-spacing.hh"
23 #include "spring.hh"
24 #include "paper-column.hh"
25 #include "spaceable-grob.hh"
26 #include "break-align-interface.hh"
27 #include "spacing-interface.hh"
28 #include "pointer-group-interface.hh"
29 #include "grob-array.hh"
30
31 /*
32   TODO: this file/class is too complex. Should figure out how to chop
33   this up even more.
34 */
35
36 class Spacing_spanner
37 {
38 public:
39   static void standard_breakable_column_spacing (Grob *me, Item *l, Item *r,
40                                                  Real *fixed, Real *space, Moment);
41
42   static Real default_bar_spacing (Grob *, Grob *, Grob *, Moment);
43   static Real note_spacing (Grob *, Grob *, Grob *, Moment, bool *);
44   static Real get_duration_space (Grob *, Moment dur, Rational shortest, bool *);
45   static Rational find_shortest (Grob *, Link_array<Grob> const &);
46   static void breakable_column_spacing (Grob *, Item *l, Item *r, Moment);
47   static void prune_loose_columns (Grob *, Link_array<Grob> *cols, Rational);
48   static void set_explicit_neighbor_columns (Link_array<Grob> const &cols);
49   static void set_implicit_neighbor_columns (Link_array<Grob> const &cols);
50   static void do_measure (Rational, Grob *me, Link_array<Grob> *cols);
51   static void musical_column_spacing (Grob *, Item *, Item *, Real, Rational);
52   DECLARE_SCHEME_CALLBACK (set_springs, (SCM));
53   static bool has_interface (Grob *);
54 };
55
56 /*
57   Return whether COL is fixed to its neighbors by some kind of spacing
58   constraint.
59
60
61   If in doubt, then we're not loose; the spacing engine should space
62   for it, risking suboptimal spacing.
63
64   (Otherwise, we might risk core dumps, and other weird stuff.)
65 */
66 static bool
67 loose_column (Grob *l, Grob *c, Grob *r)
68 {
69   extract_grob_set (c, "right-neighbors", rns);
70   extract_grob_set (c, "left-neighbors", lns);
71   
72   /*
73     If this column doesn't have a proper neighbor, we should really
74     make it loose, but spacing it correctly is more than we can
75     currently can handle.
76
77     (this happens in the following situation:
78
79     |
80     |    clef G
81     *
82
83     |               |      ||
84     |               |      ||
85     O               O       ||
86
87
88     the column containing the clef is really loose, and should be
89     attached right to the first column, but that is a lot of work for
90     such a borderline case.)
91
92   */
93   if (lns.is_empty () || rns.is_empty ())
94     return false;
95
96   Item *l_neighbor = dynamic_cast<Item *> (lns[0]);
97   Item *r_neighbor = dynamic_cast<Item *> (rns[0]);
98
99   if (!l_neighbor || !r_neighbor)
100     return false;
101
102   l_neighbor = l_neighbor->get_column ();
103   r_neighbor = dynamic_cast<Item *> (Note_spacing::right_column (r_neighbor));
104
105   if (l == l_neighbor && r == r_neighbor)
106     return false;
107
108   if (!l_neighbor || !r_neighbor)
109     return false;
110
111   /*
112     Only declare loose if the bounds make a little sense.  This means
113     some cases (two isolated, consecutive clef changes) won't be
114     nicely folded, but hey, then don't do that.
115   */
116   if (! ((Paper_column::is_musical (l_neighbor) || Item::is_breakable (l_neighbor))
117          && (Paper_column::is_musical (r_neighbor) || Item::is_breakable (r_neighbor))))
118     {
119       return false;
120     }
121
122   /*
123     A rather hairy check, but we really only want to move around
124     clefs. (anything else?)
125
126     in any case, we don't want to move bar lines.
127   */
128   extract_grob_set (c, "elements", elts);
129   for (int i = elts.size (); i--; )
130     {
131       Grob *g = elts[i];
132       if (g && Break_align_interface::has_interface (g))
133         {
134           extract_grob_set (g, "elements", gelts);
135           for (int j = gelts.size (); j--; )
136             {
137               Grob *h = gelts[j];
138
139               /*
140                 ugh. -- fix staff-bar name?
141               */
142               if (h && h->get_property ("break-align-symbol") == ly_symbol2scm ("staff-bar"))
143                 return false;
144             }
145         }
146     }
147
148   return true;
149 }
150
151 /*
152   Remove columns that are not tightly fitting from COLS. In the
153   removed columns, set 'between-cols to the columns where it is in
154   between.
155 */
156 void
157 Spacing_spanner::prune_loose_columns (Grob *me, Link_array<Grob> *cols, Rational shortest)
158 {
159   Link_array<Grob> newcols;
160   Real increment = robust_scm2double (me->get_property ("spacing-increment"), 1.2);
161   for (int i = 0; i < cols->size (); i++)
162     {
163       if (Item::is_breakable (cols->elem (i))
164           || Paper_column::is_musical (cols->elem (i)))
165         {
166           newcols.push (cols->elem (i));
167           continue;
168         }
169
170       Grob *c = cols->elem (i);
171       if (loose_column (cols->elem (i - 1), c, cols->elem (i + 1)))
172         {
173           extract_grob_set (c, "right-neighbors", rns_arr);
174           extract_grob_set (c, "left-neighbors", lns_arr);
175           
176           SCM lns = lns_arr.size () ? lns_arr.top()->self_scm () : SCM_BOOL_F;
177           SCM rns = rns_arr.size () ? rns_arr.top()->self_scm () : SCM_BOOL_F;
178           
179           /*
180             Either object can be non existent, if the score ends
181             prematurely.
182           */
183
184           extract_grob_set (unsmob_grob (rns), "right-items", right_items);
185           c->set_object ("between-cols", scm_cons (lns,
186                                                    right_items[0]->self_scm ()));
187
188           /*
189             Set distance constraints for loose columns
190           */
191           Drul_array<Grob *> next_door;
192           next_door[LEFT] = cols->elem (i - 1);
193           next_door[RIGHT] = cols->elem (i + 1);
194           Direction d = LEFT;
195           Drul_array<Real> dists (0, 0);
196
197           do
198             {
199               dists[d] = 0.0;
200               Item *lc = dynamic_cast<Item *> ((d == LEFT) ? next_door[LEFT] : c);
201               Item *rc = dynamic_cast<Item *> (d == LEFT ? c : next_door[RIGHT]);
202
203
204               extract_grob_set (lc, "spacing-wishes", wishes);
205               for (int k = wishes.size(); k--;)
206                 {
207                   Grob *sp = wishes[k];
208                   if (Note_spacing::left_column (sp) != lc
209                       || Note_spacing::right_column (sp) != rc)
210                     continue;
211
212                   Real space, fixed;
213                   fixed = 0.0;
214                   bool dummy;
215
216                   if (d == LEFT)
217                     {
218                       /*
219                         The note spacing should be taken from the musical
220                         columns.
221
222                       */
223                       Real base = note_spacing (me, lc, rc, shortest, &dummy);
224                       Note_spacing::get_spacing (sp, rc, base, increment, &space, &fixed);
225
226                       space -= increment;
227
228                       dists[d] = max (dists[d], space);
229                     }
230                   else
231                     {
232                       Real space, fixed_space;
233                       Staff_spacing::get_spacing_params (sp,
234                                                          &space, &fixed_space);
235
236                       dists[d] = max (dists[d], fixed_space);
237                     }
238                 }
239             }
240           while (flip (&d) != LEFT);
241
242           Rod r;
243           r.distance_ = dists[LEFT] + dists[RIGHT];
244           r.item_drul_[LEFT] = dynamic_cast<Item *> (cols->elem (i - 1));
245           r.item_drul_[RIGHT] = dynamic_cast<Item *> (cols->elem (i + 1));
246
247           r.add_to_cols ();
248         }
249       else
250         {
251           newcols.push (c);
252         }
253     }
254
255   *cols = newcols;
256 }
257
258 /*
259   Set neighboring columns determined by the spacing-wishes grob property.
260 */
261 void
262 Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> const &cols)
263 {
264   for (int i = 0; i < cols.size (); i++)
265     {
266       SCM right_neighbors = Grob_array::make_array ();
267       Grob_array *rn_arr = unsmob_grob_array (right_neighbors);
268       int min_rank = 100000;    // inf.
269
270       extract_grob_set (cols[i], "spacing-wishes", wishes);
271       for (int k = wishes.size(); k--;)
272         {
273           Item *wish = dynamic_cast<Item *> ( wishes[k]);
274
275           Item *lc = wish->get_column ();
276           Grob *right = Note_spacing::right_column (wish);
277
278           if (!right)
279             continue;
280
281           Item *rc = dynamic_cast<Item *> (right);
282
283           int right_rank = Paper_column::get_rank (rc);
284           int left_rank = Paper_column::get_rank (lc);
285
286           /*
287             update the left column.
288           */
289           if (right_rank <= min_rank)
290             {
291               if (right_rank < min_rank)
292                 rn_arr->clear ();
293
294               min_rank = right_rank;
295               rn_arr->add (wish);
296             }
297
298           /*
299             update the right column of the wish.
300           */
301           int maxrank = 0;
302
303           extract_grob_set (rc, "left-neighbors", lns_arr);
304           if (lns_arr.size ())
305             {
306               Item *it = dynamic_cast<Item *> (lns_arr.top());
307               maxrank = Paper_column::get_rank (it->get_column ());
308             }
309
310           if (left_rank >= maxrank)
311             {
312               
313               if (left_rank > maxrank)
314                 {
315                   Grob_array *ga = unsmob_grob_array (rc->get_object ("left-neighbors"));
316                   if (ga)
317                     ga->clear ();
318                 }
319
320               Pointer_group_interface::add_grob (rc, ly_symbol2scm ("left-neighbors"), wish);
321             }
322         }
323
324       if (rn_arr->size ())
325         {
326           cols[i]->set_object ("right-neighbors", right_neighbors);
327         }
328     }
329 }
330
331 /*
332   Set neighboring columns that have no left/right-neighbor set
333   yet. Only do breakable non-musical columns, and musical columns.
334 */
335 void
336 Spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> const &cols)
337 {
338   for (int i = 0; i < cols.size (); i++)
339     {
340       Item *it = dynamic_cast<Item *> (cols[i]);
341       if (!Item::is_breakable (it) && !Paper_column::is_musical (it))
342         continue;
343
344       // it->breakable || it->musical
345
346       /*
347         sloppy with typing left/right-neighbors should take list, but paper-column found instead.
348       */
349       extract_grob_set (cols[i], "left-neighbors", lns);
350       if (lns.is_empty () && i )
351         {
352           SCM ga_scm = Grob_array::make_array();
353           Grob_array *ga = unsmob_grob_array (ga_scm);
354           ga->add (cols[i-1]);
355           cols[i]->set_object ("left-neighbors", ga_scm);
356         }
357       extract_grob_set (cols[i], "right-neighbors", rns);
358       if (rns.is_empty () && i < cols.size () - 1)
359         {
360           SCM ga_scm = Grob_array::make_array();
361           Grob_array *ga = unsmob_grob_array (ga_scm);
362           ga->add (cols[i+1]);
363           cols[i]->set_object ("right-neighbors", ga_scm);
364         }
365     }
366 }
367
368 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
369 SCM
370 Spacing_spanner::set_springs (SCM smob)
371 {
372   Grob *me = unsmob_grob (smob);
373
374   /*
375     can't use get_system() ? --hwn.
376   */
377   Link_array<Grob> all (me->pscore_->root_system ()->columns ());
378
379   set_explicit_neighbor_columns (all);
380
381   SCM preset_shortest = me->get_property ("common-shortest-duration");
382   Rational global_shortest;
383   if (unsmob_moment (preset_shortest))
384     {
385       global_shortest = unsmob_moment (preset_shortest)->main_part_;
386     }
387   else
388     {
389       global_shortest = find_shortest (me, all);
390       if (be_verbose_global)
391         message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
392     }
393   prune_loose_columns (me, &all, global_shortest);
394   set_implicit_neighbor_columns (all);
395
396   int j = 0;
397   for (int i = 1; i < all.size (); i++)
398     {
399       Grob *sc = all[i];
400       if (Item::is_breakable (sc))
401         {
402           Link_array<Grob> measure (all.slice (j, i + 1));
403           do_measure (global_shortest, me, &measure);
404           j = i;
405         }
406     }
407
408   return SCM_UNSPECIFIED;
409 }
410
411 /*
412   We want the shortest note that is also "common" in the piece, so we
413   find the shortest in each measure, and take the most frequently
414   found duration.
415
416   This probably gives weird effects with modern music, where every
417   note has a different duration, but hey, don't write that kind of
418   stuff, then.
419 */
420 Rational
421 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
422 {
423   /*
424     ascending in duration
425   */
426   Array<Rational> durations;
427   Array<int> counts;
428
429   Rational shortest_in_measure;
430   shortest_in_measure.set_infinite (1);
431
432   for (int i = 0; i < cols.size (); i++)
433     {
434       if (Paper_column::is_musical (cols[i]))
435         {
436           Moment *when = unsmob_moment (cols[i]->get_property ("when"));
437
438           /*
439             ignore grace notes for shortest notes.
440           */
441           if (when && when->grace_part_)
442             continue;
443
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 = min (shortest_in_measure, this_shortest.main_part_);
448         }
449       else if (!shortest_in_measure.is_infinity ()
450                && Item::is_breakable (cols[i]))
451         {
452           int j = 0;
453           for (; j < durations.size (); j++)
454             {
455               if (durations[j] > shortest_in_measure)
456                 {
457                   counts.insert (1, j);
458                   durations.insert (shortest_in_measure, j);
459                   break;
460                 }
461               else if (durations[j] == shortest_in_measure)
462                 {
463                   counts[j]++;
464                   break;
465                 }
466             }
467
468           if (durations.size () == j)
469             {
470               durations.push (shortest_in_measure);
471               counts.push (1);
472             }
473
474           shortest_in_measure.set_infinite (1);
475         }
476     }
477
478   int max_idx = -1;
479   int max_count = 0;
480   for (int i = durations.size (); i--;)
481     {
482       if (counts[i] >= max_count)
483         {
484           max_idx = i;
485           max_count = counts[i];
486         }
487
488       //      printf ("duration %d/%d, count %d\n", durations[i].num (), durations[i].den (), counts[i]);
489     }
490
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_;
495
496   if (max_idx >= 0)
497     d = min (d, durations[max_idx]);
498
499   return d;
500 }
501
502 /*
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.
507 */
508 void
509 Spacing_spanner::do_measure (Rational global_shortest, Grob *me,
510                              Link_array<Grob> *cols)
511 {
512   Real headwid = robust_scm2double (me->get_property ("spacing-increment"), 1);
513   for (int i = 0; i < cols->size () - 1; i++)
514     {
515       Item *l = dynamic_cast<Item *> (cols->elem (i));
516       Item *r = dynamic_cast<Item *> (cols->elem (i + 1));
517
518       Paper_column *lc = dynamic_cast<Paper_column *> (l);
519       Paper_column *rc = dynamic_cast<Paper_column *> (r);
520
521       if (Paper_column::is_musical (l))
522         {
523           musical_column_spacing (me, lc, rc, headwid, global_shortest);
524           if (Item *rb = r->find_prebroken_piece (LEFT))
525             musical_column_spacing (me, lc, rb, headwid, global_shortest);
526         }
527       else
528         {
529           /*
530             The case that the right part is broken as well is rather
531             rare, but it is possible, eg. with a single empty measure,
532             or if one staff finishes a tad earlier than the rest.
533           */
534           Item *lb = l->find_prebroken_piece (RIGHT);
535           Item *rb = r->find_prebroken_piece (LEFT);
536
537           if (i == 0 && Paper_column::get_rank (l) == 0)
538             l = 0;
539
540           if (l && r)
541             breakable_column_spacing (me, l, r, global_shortest);
542           
543           if (lb && r)
544             breakable_column_spacing (me, lb, r, global_shortest);
545
546           if (l && rb)
547             breakable_column_spacing (me, l, rb, global_shortest);
548
549           if (lb && rb)
550             breakable_column_spacing (me, lb, rb, global_shortest);
551         }
552     }
553 }
554
555 /*
556   Generate the space between two musical columns LC and RC, given
557   spacing parameters INCR and SHORTEST.
558 */
559 void
560 Spacing_spanner::musical_column_spacing (Grob *me, Item *lc, Item *rc, Real increment, Rational global_shortest)
561 {
562   bool expand_only = false;
563   Real base_note_space = note_spacing (me, lc, rc, global_shortest, &expand_only);
564
565   Real compound_note_space = 0.0;
566   Real compound_fixed_note_space = 0.0;
567   int wish_count = 0;
568
569   extract_grob_set (lc, "right-neighbors", neighbors);
570
571   /*
572     We adjust the space following a note only if the next note
573     happens after the current note (this is set in the grob
574     property SPACING-SEQUENCE.
575   */
576   for (int i = 0; i < neighbors.size (); i++)
577     {
578       Grob *wish = neighbors[i];
579
580       Item *wish_rcol = Note_spacing::right_column (wish);
581       if (Note_spacing::left_column (wish) != lc
582           || (wish_rcol != rc && wish_rcol != rc->original_))
583         continue;
584
585       /*
586         This is probably a waste of time in the case of polyphonic
587         music.  */
588       if (Note_spacing::has_interface (wish))
589         {
590           Real space = 0.0;
591           Real fixed = 0.0;
592
593           Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed);
594
595           compound_note_space = compound_note_space + space;
596           compound_fixed_note_space = compound_fixed_note_space + fixed;
597           wish_count++;
598         }
599     }
600
601   if (Paper_column::when_mom (rc).grace_part_
602       && !Paper_column::when_mom (lc).grace_part_)
603     {
604       /*
605         Ugh. 0.8 is arbitrary.
606       */
607       compound_note_space *= 0.8;
608     }
609
610   if (compound_note_space < 0 || wish_count == 0)
611     {
612       compound_note_space = base_note_space;
613       compound_fixed_note_space = increment;
614     }
615   else
616     {
617       compound_note_space /= wish_count;
618       compound_fixed_note_space /= wish_count;
619     }
620
621   /*
622     Whatever we do, the fixed space is smaller than the real
623     space.
624
625     TODO: this criterion is discontinuous in the derivative.
626     Maybe it should be continuous?
627   */
628   compound_fixed_note_space = min (compound_fixed_note_space, compound_note_space);
629
630   bool packed = to_boolean (me->get_layout ()->c_variable ("packed"));
631   Real inverse_strength = 1.0;
632   Real distance = 1.0;
633
634   /*
635     TODO: make sure that the space doesn't exceed the right margin.
636   */
637   if (packed)
638     {
639       /*
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.
647       */
648       inverse_strength = 1.0;
649       distance = compound_fixed_note_space;
650     }
651   else
652     {
653       inverse_strength = (compound_note_space - compound_fixed_note_space);
654       distance = compound_note_space;
655     }
656
657   Spaceable_grob::add_spring (lc, rc, distance, inverse_strength);
658 }
659
660 /*
661   The one-size-fits all spacing. It doesn't take into account
662   different spacing wishes from one to the next column.
663 */
664 void
665 Spacing_spanner::standard_breakable_column_spacing (Grob *me, Item *l, Item *r,
666                                                     Real *fixed, Real *space,
667                                                     Moment shortest)
668 {
669   *fixed = 0.0;
670   Direction d = LEFT;
671   Drul_array<Item *> cols (l, r);
672
673   do
674     {
675       if (!Paper_column::is_musical (cols[d]))
676         {
677           /*
678             Tied accidentals over barlines cause problems, so lets see
679             what happens if we do this for non musical columns only.
680           */
681           Interval lext = cols[d]->extent (cols [d], X_AXIS);
682           if (!lext.is_empty ())
683             *fixed += -d * lext[-d];
684         }
685     }
686   while (flip (&d) != LEFT);
687
688   if (l->is_breakable (l) && r->is_breakable (r))
689     {
690       Moment *dt = unsmob_moment (l->get_property ("measure-length"));
691       Moment mlen (1);
692       if (dt)
693         mlen = *dt;
694
695       Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
696
697       *space = *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8;
698     }
699   else
700     {
701       Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
702
703       if (dt == Moment (0, 0))
704         {
705           /*
706             In this case, Staff_spacing should handle the job,
707             using dt when it is 0 is silly.
708           */
709           *space = *fixed + 0.5;
710         }
711       else
712         {
713           bool dummy;
714           *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
715         }
716     }
717 }
718
719 /*
720   Read hints from L and generate springs.
721 */
722 void
723 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r, Moment shortest)
724 {
725   Real compound_fixed = 0.0;
726   Real compound_space = 0.0;
727   int wish_count = 0;
728
729   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
730
731   if (dt == Moment (0, 0))
732     {
733       extract_grob_set (l, "spacing-wishes", wishes);
734
735       for (int i = 0; i < wishes.size (); i++)
736         {
737           Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
738
739           if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
740             continue;
741
742           Real space;
743           Real fixed_space;
744
745           /*
746             column for the left one settings should be ok due automatic
747             pointer munging.
748
749           */
750           assert (spacing_grob->get_column () == l);
751
752           Staff_spacing::get_spacing_params (spacing_grob,
753                                              &space, &fixed_space);
754
755           if (Paper_column::when_mom (r).grace_part_)
756             {
757               /*
758                 Correct for grace notes.
759
760                 Ugh. The 0.8 is arbitrary.
761               */
762               space *= 0.8;
763             }
764
765           compound_space += space;
766           compound_fixed += fixed_space;
767           wish_count++;
768         }
769     }
770
771   if (compound_space <= 0.0 || !wish_count)
772     {
773       standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
774                                          shortest);
775       wish_count = 1;
776     }
777   else
778     {
779       compound_space /= wish_count;
780       compound_fixed /= wish_count;
781     }
782
783   assert (!isinf (compound_space));
784   compound_space = max (compound_space, compound_fixed);
785
786   /*
787     There used to be code that changed spacing depending on
788     raggedright setting.  Ugh.
789
790     Do it more cleanly, or rename the property.
791
792   */
793   Real inverse_strength = (compound_space - compound_fixed);
794   Real distance = compound_space;
795   Spaceable_grob::add_spring (l, r, distance, inverse_strength);
796 }
797
798 /**
799    Get the measure wide ant for arithmetic spacing.
800 */
801 Real
802 Spacing_spanner::get_duration_space (Grob *me, Moment d, Rational shortest, bool *expand_only)
803 {
804   Real k = robust_scm2double (me->get_property ("shortest-duration-space"), 1);
805   Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
806
807   if (d < shortest)
808     {
809       /*
810         We don't space really short notes using the log of the
811         duration, since it would disproportionally stretches the long
812         notes in a piece. In stead, we use geometric spacing with constant 0.5
813         (i.e. linear.)
814
815         This should probably be tunable, to use other base numbers.
816
817         In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
818         3.6 mm (total).  head-width = 2.4, so we 1.2mm for 16th, 1.5
819         mm for 8th. (white space), suggesting that we use
820
821         (1.2 / 1.5)^{-log2(duration ratio)}
822
823
824       */
825       Rational ratio = d.main_part_ / shortest;
826
827       return ((k - 1) + double (ratio)) * incr;
828     }
829   else
830     {
831       /*
832         John S. Gourlay. ``Spacing a Line of Music, '' Technical
833         Report OSU-CISRC-10/87-TR35, Department of Computer and
834         Information Science, The Ohio State University, 1987.
835       */
836       Real log = log_2 (shortest);
837       k -= log;
838       Rational compdur = d.main_part_ + d.grace_part_ / Rational (3);
839       *expand_only = false;
840
841       return (log_2 (compdur) + k) * incr;
842     }
843 }
844
845 Real
846 Spacing_spanner::note_spacing (Grob *me, Grob *lc, Grob *rc,
847                                Moment shortest, bool *expand_only)
848 {
849   Moment shortest_playing_len = 0;
850   SCM s = lc->get_property ("shortest-playing-duration");
851
852   if (unsmob_moment (s))
853     shortest_playing_len = *unsmob_moment (s);
854
855   if (! shortest_playing_len.to_bool ())
856     {
857       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ());
858       shortest_playing_len = 1;
859     }
860
861   Moment lwhen = Paper_column::when_mom (lc);
862   Moment rwhen = Paper_column::when_mom (rc);
863
864   Moment delta_t = rwhen - lwhen;
865   if (!Paper_column::is_musical (rc))
866     {
867       /*
868         when toying with mmrests, it is possible to have musical
869         column on the left and non-musical on the right, spanning
870         several measures.
871       */
872
873       Moment *dt = unsmob_moment (rc->get_property ("measure-length"));
874       if (dt)
875         {
876           delta_t = min (delta_t, *dt);
877
878           /*
879             The following is an extra safety measure, such that
880             the length of a mmrest event doesn't cause havoc.
881           */
882           shortest_playing_len = min (shortest_playing_len, *dt);
883         }
884     }
885   Real dist = 0.0;
886
887   /*
888     In normal situations, the next column is at most
889     SHORTEST_PLAYING_LEN away. However chord-tremolos do funky faking stuff
890     with durations, invalidating this assumption. Here we kludge
891     around to get chord tremolos to behave properly.
892
893   */
894   shortest_playing_len = max (shortest_playing_len, delta_t);
895   if (delta_t.main_part_ && !lwhen.grace_part_)
896     {
897       dist = get_duration_space (me, shortest_playing_len,
898                                  shortest.main_part_, expand_only);
899       dist *= double (delta_t.main_part_ / shortest_playing_len.main_part_);
900     }
901   else if (delta_t.grace_part_)
902     {
903       /*
904         TODO: figure out how to space grace notes.
905       */
906       dist = get_duration_space (me, shortest, shortest.main_part_, expand_only);
907
908       Real grace_fact
909         = robust_scm2double (me->get_property ("grace-space-factor"), 1);
910
911       dist *= grace_fact;
912     }
913
914   return dist;
915 }
916
917 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
918                "The space taken by a note is dependent on its duration. Doubling a\n"
919                "duration adds spacing-increment to the space. The most common shortest\n"
920                "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
921                "spaced proportonial to their duration.\n"
922                "\n"
923                "Typically, the increment is the width of a black note head.  In a\n"
924                "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
925                "gets 2 note heads width (i.e. the space following a note is 1 note\n"
926                "head width) A 16th note is followed by 0.5 note head width. The\n"
927                "quarter note is followed by  3 NHW, the half by 4 NHW, etc.\n",
928                
929                "grace-space-factor spacing-increment base-shortest-duration "
930                "shortest-duration-space common-shortest-duration"
931
932                );
933
934 ADD_INTERFACE (Spacing_interface, "spacing-interface",
935                "Something to do with line breaking and spacing. "
936                "Kill this one after determining line breaks.",
937                "");
938