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