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