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