]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
* lily/spacing-spanner.cc (set_springs): add support for
[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   SCM preset_shortest = me->get_grob_property ("common-shortest-duration");
370   Rational global_shortest;
371   if (unsmob_moment (preset_shortest))
372     {
373       global_shortest = unsmob_moment (preset_shortest)->main_part_;
374     }
375   else
376     global_shortest = find_shortest (me, all);
377   
378   prune_loose_colunms (me, &all, global_shortest);
379   set_implicit_neighbor_columns (all);
380
381   
382   int j = 0;
383   for (int i = 1; i < all.size (); i++)
384     {
385       Grob *sc = all[i];
386       if (Item::breakable_b (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 /*
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 */
408 Rational
409 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
410 {
411   /*
412     ascending in duration
413    */
414   Array<Rational> durations; 
415   Array<int> counts;
416   
417   Rational shortest_in_measure;
418   shortest_in_measure.set_infinite (1);
419   
420   for (int i =0 ; i < cols.size (); i++)  
421     {
422       if (Paper_column::musical_b (cols[i]))
423         {
424           Moment *when = unsmob_moment (cols[i]->get_grob_property  ("when"));
425
426           /*
427             ignore grace notes for shortest notes.
428           */
429           if (when && when->grace_part_)
430             continue;
431           
432           SCM  st = cols[i]->get_grob_property ("shortest-starter-duration");
433           Moment this_shortest = *unsmob_moment (st);
434           assert (this_shortest.to_bool());
435           shortest_in_measure = shortest_in_measure <? this_shortest.main_part_;
436         }
437       else if (!shortest_in_measure.infty_b()
438                && Item::breakable_b (cols[i]))
439         {
440           int j = 0;
441           for (; j < durations.size(); j++)
442             {
443               if (durations[j] > shortest_in_measure)
444                 {
445                   counts.insert (1, j);
446                   durations.insert (shortest_in_measure, j);
447                   break;
448                 }
449               else if (durations[j] == shortest_in_measure)
450                 {
451                   counts[j]++;
452                   break;
453                 }
454             }
455
456           if (durations.size() == j)
457             {
458               durations.push (shortest_in_measure);
459               counts.push (1);
460             }
461
462           shortest_in_measure.set_infinite(1); 
463         }
464     }
465
466   int max_idx = -1;
467   int max_count = 0;
468   for (int i =durations.size(); i--;)
469     {
470       if (counts[i] >= max_count)
471         {
472           max_idx = i;
473           max_count = counts[i];
474         }
475
476       //      printf ("duration %d/%d, count %d\n", durations[i].num (), durations[i].den (), counts[i]);
477     }
478
479   SCM  bsd = me->get_grob_property ("base-shortest-duration");
480   Rational d = Rational (1,8);
481   if (Moment *m = unsmob_moment (bsd))
482     d = m->main_part_;
483   
484   if (max_idx >= 0)
485     d = d <? durations[max_idx] ;
486
487   return d;
488 }
489
490 /*
491   Generate spacing for a single measure. We used to have code that did
492   per-measure spacing. Now we have piecewise spacing. We should fix
493   this to support "spacing-regions": some regions have different notes
494   (different time sigs) than others, and should be spaced differently.
495  */
496 void
497 Spacing_spanner::do_measure (Rational shortest, Grob*me, Link_array<Grob> *cols) 
498 {
499
500   Real headwid =       gh_scm2double (me->get_grob_property ("spacing-increment"));
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::musical_b (l))
510         {
511           breakable_column_spacing (me, l, r, shortest);
512
513           /*
514             
515             The case that the right part is broken as well is rather
516             rare, but it is possible, eg. with a single empty measure,
517             or if one staff finishes a tad earlier than the rest.
518             
519            */
520           Item *lb = l->find_prebroken_piece (RIGHT);
521           Item *rb = r->find_prebroken_piece (LEFT);
522           
523           if (lb)
524             breakable_column_spacing (me, lb,r, shortest);
525
526           if (rb)
527             breakable_column_spacing (me, l, rb, shortest);
528           if (lb && rb)
529             breakable_column_spacing (me, lb, rb, shortest);
530           
531           continue ; 
532         }
533
534
535       musical_column_spacing (me, lc, rc, headwid, shortest);
536       if (Item *rb = r->find_prebroken_piece (LEFT))
537         musical_column_spacing (me, lc, rb, headwid, shortest);
538     }    
539 }
540
541
542 /*
543   Generate the space between two musical columns LC and RC, given
544   spacing parameters INCR and SHORTEST.
545  */
546 void
547 Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real increment, Rational shortest)
548 {
549   bool expand_only = false;
550   Real base_note_space = note_spacing (me, lc, rc, shortest, &expand_only);
551
552   Real max_note_space = -infinity_f;
553   Real max_fixed_note_space = -infinity_f;
554
555   SCM seq  = lc->get_grob_property ("right-neighbors");
556
557   /*
558     We adjust the space following a note only if the next note
559     happens after the current note (this is set in the grob
560     property SPACING-SEQUENCE.
561   */
562   for (SCM s = seq; gh_pair_p (s); s = ly_cdr (s))
563     {
564       Grob * wish = unsmob_grob (gh_car (s));
565
566       Item *wish_rcol = Note_spacing::right_column (wish);
567       if (Note_spacing::left_column (wish) != lc
568           || (wish_rcol != rc && wish_rcol != rc->original_l_))
569         continue;
570
571       /*
572         This is probably a waste of time in the case of polyphonic
573         music.  */
574       if (Note_spacing::has_interface (wish))
575         {
576           Real space =0.0;
577           Real fixed =0.0;
578           
579           Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed);
580           max_note_space = max_note_space >? space;
581           max_fixed_note_space = max_fixed_note_space >? fixed;
582         }
583     }
584
585   if (max_note_space < 0)
586     {
587       max_note_space = base_note_space;
588       max_fixed_note_space =  increment;
589     }
590
591   bool ragged = to_boolean (me->paper_l ()->get_scmvar ("raggedright"));
592
593   /*
594     Whatever we do, the fixed space is smaller than the real
595     space.
596
597     TODO: this criterion is discontinuous in the derivative.
598     Maybe it should be continuous?
599   */
600   max_fixed_note_space = max_fixed_note_space <?  max_note_space;
601 #if 0
602   /*
603     This doesn't make sense. For ragged right we want to have the same
604     spacing. Otherwise  the option should be called differently.
605
606     ragged-righted-and-weird-spacing. Whatever.
607
608   */
609   Real strength = (ragged) ? 1.0 : 1 / (max_note_space - max_fixed_note_space);
610   Real distance = (ragged) ? max_fixed_note_space : max_note_space;
611 #else
612
613   /*
614         TODO: make sure that the space doesn't exceed the right margin.
615    */
616   Real strength = 1 / (max_note_space - max_fixed_note_space);
617   Real distance = max_note_space;
618 #endif
619
620   //  Spaceable_grob::add_spring (lc, rc, distance, strength, expand_only);
621
622   Spaceable_grob::add_spring (lc, rc, distance, strength, false);  
623 }
624
625
626 /*
627   The one-size-fits all spacing. It doesn't take into account
628   different spacing wishes from one to the next column.
629  */
630 void
631 Spacing_spanner::standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
632                                    Real * fixed, Real * space,
633                                    Moment shortest)
634 {
635   *fixed = 0.0;
636   Direction d = LEFT;
637   Drul_array<Item*> cols(l,r);
638   
639   do
640     {
641       Interval lext = cols[d]->extent (cols [d], X_AXIS);
642
643       *fixed += -d * lext[-d];
644     }
645   while (flip (&d) != LEFT);
646   
647   if (l->breakable_b (l) && r->breakable_b(r))
648     {
649       Moment *dt = unsmob_moment (l->get_grob_property ("measure-length"));
650       Moment mlen (1);
651       if (dt)
652         mlen = *dt;
653       
654       Real incr = gh_scm2double (me->get_grob_property ("spacing-increment"));
655
656       *space =  *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8;
657     }
658   else
659     {
660       Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
661       bool dummy;
662
663       *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
664     }
665   
666   
667 }
668
669
670 /*
671   Read hints from L and generate springs.
672  */
673 void
674 Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shortest)
675 {
676   Real max_fixed = -infinity_f;
677   Real max_space = -infinity_f;
678
679   standard_breakable_column_spacing (me, l, r, &max_fixed, &max_space ,
680                                      shortest);
681   
682   for (SCM s = l->get_grob_property ("spacing-wishes");
683        gh_pair_p (s); s = gh_cdr (s))
684     {
685       Item * spacing_grob = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
686
687       if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
688         continue;
689
690       Real space;
691       Real fixed_space;
692
693       /*
694         column for the left one settings should be ok due automatic
695         pointer munging.
696
697       */
698       assert (spacing_grob-> column_l () == l);
699
700       Staff_spacing::get_spacing_params (spacing_grob,
701                                          &space, &fixed_space);  
702       if (space > max_space)
703         {
704           max_space = space;
705           max_fixed = fixed_space;
706         }
707     }
708
709   
710   
711     
712   if (isinf (max_space))
713     {
714     /*
715       One situation where this can happen is when there is a column
716       that only serves as a spanning point for a short staff-symbol.
717
718      ===============X===
719
720          |=======Y
721
722
723       (here no StaffSpacing from Y to X is found.)
724     */      
725       programming_error ("No StaffSpacing wishes found");
726       max_space = 2.0;
727       max_fixed = 1.0;
728     }
729
730   
731   if (l->break_status_dir() == RIGHT
732       && Paper_column::when_mom (l) == Paper_column::when_mom (r))
733     {
734       /* Start of line: this space is not stretchable */
735       max_fixed = max_space;
736     }
737
738   /*
739     Hmm.  we do 1/0 in the next thing. Perhaps we should check if this
740     works on all architectures.
741    */
742   
743   bool ragged = to_boolean (me->paper_l ()->get_scmvar ("raggedright"));
744   Real strength = (ragged) ? 1.0 : 1 / (max_space - max_fixed);
745   Real distance = (ragged) ? max_fixed : max_space;
746   Spaceable_grob::add_spring (l, r, distance, strength, false);
747 }
748
749
750 /**
751   Get the measure wide ant for arithmetic spacing.
752   */
753 Real
754 Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool * expand_only) 
755 {
756   Real k = gh_scm2double (me->get_grob_property ("shortest-duration-space"));
757   Real incr = gh_scm2double (me->get_grob_property ("spacing-increment"));
758   
759   if (d < shortest)
760     {
761       /*
762         We don't space really short notes using the log of the
763         duration, since it would disproportionally stretches the long
764         notes in a piece. In stead, we use geometric spacing with constant 0.5
765         (i.e. linear.)
766
767         This should probably be tunable, to use other base numbers.
768
769         In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
770         3.6 mm (total).  head-width = 2.4, so we 1.2mm for 16th, 1.5
771         mm for 8th. (white space), suggesting that we use
772
773         (1.2 / 1.5)^{-log2(duration ratio)}
774         
775
776        */
777       Rational ratio = d.main_part_ / shortest;
778
779 #if 0
780       *expand_only = true;
781 #endif
782       return ((k-1) + double (ratio)) * incr;
783     }
784   else
785     {
786       /*
787           John S. Gourlay. ``Spacing a Line of Music,'' Technical
788           Report OSU-CISRC-10/87-TR35, Department of Computer and
789           Information Science, The Ohio State University, 1987.
790        */
791       Real log =  log_2 (shortest);
792       k -= log;
793       Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
794       *expand_only = false;      
795    
796       return (log_2 (compdur) + k) * incr;
797     }
798 }
799
800 Real
801 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
802                                Moment shortest, bool * expand_only) 
803 {
804   Moment shortest_playing_len = 0;
805   SCM s = lc->get_grob_property ("shortest-playing-duration");
806
807   if (unsmob_moment (s))
808     shortest_playing_len = *unsmob_moment (s);
809   
810   if (! shortest_playing_len.to_bool ())
811     {
812       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
813       shortest_playing_len = 1;
814     }
815
816   Moment lwhen = Paper_column::when_mom (lc);
817   Moment rwhen =  Paper_column::when_mom (rc);
818
819   Moment delta_t = rwhen - lwhen;
820   Real dist = 0.0;
821
822   /*
823     In normal situations, the next column is at most
824     SHORTEST_PLAYING_LEN away. However chord-tremolos do funky faking stuff
825     with durations, invalidating this assumption. Here we kludge
826     around to get chord tremolos to behave properly.
827     
828    */
829   shortest_playing_len = shortest_playing_len >? delta_t;
830   if (delta_t.main_part_ && !lwhen.grace_part_)
831     {
832       dist = get_duration_space (me, shortest_playing_len, shortest.main_part_, expand_only);
833       dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
834     }
835   else if (delta_t.grace_part_)
836     {
837       /*
838         TODO: figure out how to space grace notes.
839       */
840       dist = get_duration_space (me, shortest, shortest.main_part_, expand_only);
841
842       Real grace_fact = 1.0;
843       SCM gf = me->get_grob_property ("grace-space-factor");
844       if (gh_number_p (gf))
845         grace_fact = gh_scm2double (gf);
846
847       dist *= grace_fact;
848     }
849
850   
851   return dist;
852 }
853
854
855
856 ADD_INTERFACE (Spacing_spanner,"spacing-spanner-interface",
857   "
858 The space taken by a note is dependent on its duration. Doubling a
859 duration adds spacing-increment to the space. The most common shortest
860 note gets shortest-duration-space. Notes that are even shorter are
861 spaced proportonial to their duration.
862
863 Typically, the increment is the width of a black note head.  In a
864 piece with lots of 8th notes, and some 16th notes, the eighth note
865 gets 2 note heads width (i.e. the space following a note is 1 note
866 head width) A 16th note is followed by 0.5 note head width. The
867 quarter note is followed by  3 NHW, the half by 4 NHW, etc.
868 ",
869   "grace-space-factor spacing-increment base-shortest-duration shortest-duration-space common-shortest-duration");
870
871
872
873 ADD_INTERFACE (Spacing_interface,"spacing-interface",
874   "Something to do with line breaking and spacing. Kill this one after determining line breaks.",
875   "");
876