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