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