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