]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
* lily/spacing-spanner.cc, ly/paper-init.ly: introduced paper
[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--2003 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_columns (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_columns (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.to_string ())); 
380         }
381     }
382   prune_loose_columns (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 (Paper_column::when_mom (rc).grace_part_ &&
590       !Paper_column::when_mom (lc).grace_part_)
591     {
592       /*
593         Ugh. 0.8 is arbitrary.
594        */
595       max_note_space *= 0.8; 
596     }
597   
598   if (max_note_space < 0)
599     {
600       max_note_space = base_note_space;
601       max_fixed_note_space =  increment;
602     }
603
604   /*
605     Whatever we do, the fixed space is smaller than the real
606     space.
607
608     TODO: this criterion is discontinuous in the derivative.
609     Maybe it should be continuous?
610   */
611   max_fixed_note_space = max_fixed_note_space <? max_note_space;
612
613   bool packed = to_boolean (me->get_paper ()->get_scmvar ("packed"));
614   Real strength, distance;
615
616   /*
617     TODO: make sure that the space doesn't exceed the right margin.
618    */
619   if (packed)
620     {
621       /*
622         In packed mode, pack notes as tight as possible.  This makes
623         sense mostly in combination with raggedright mode: the notes
624         are then printed at minimum distance.  This is mostly useful
625         for ancient notation, but may also be useful for some flavours
626         of contemporary music.  If not in raggedright mode, lily will
627         pack as much bars of music as possible into a line, but the
628         line will then be stretched to fill the whole linewidth.
629       */
630       strength = 1.0;
631       distance = max_fixed_note_space;
632     }
633   else
634     {
635       strength = 1 / (max_note_space - max_fixed_note_space);
636       distance = max_note_space;
637     }
638
639   //  Spaceable_grob::add_spring (lc, rc, distance, strength, expand_only);
640
641   Spaceable_grob::add_spring (lc, rc, distance, strength, false);  
642 }
643
644
645 /*
646   The one-size-fits all spacing. It doesn't take into account
647   different spacing wishes from one to the next column.
648  */
649 void
650 Spacing_spanner::standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
651                                    Real * fixed, Real * space,
652                                    Moment shortest)
653 {
654  
655   *fixed = 0.0;
656   Direction d = LEFT;
657   Drul_array<Item*> cols(l,r);
658   
659   do
660     {
661       if (!Paper_column::musical_b (cols[d]))
662         {
663           /*
664             Tied accidentals over barlines cause problems, so lets see
665             what happens if we do this for non musical columns only.
666            */
667           Interval lext = cols[d]->extent (cols [d], X_AXIS);
668           *fixed += -d * lext[-d];
669         }
670     }
671   while (flip (&d) != LEFT);
672
673   if (l->breakable_b (l) && r->breakable_b(r))
674     {
675       Moment *dt = unsmob_moment (l->get_grob_property ("measure-length"));
676       Moment mlen (1);
677       if (dt)
678         mlen = *dt;
679       
680       Real incr = gh_scm2double (me->get_grob_property ("spacing-increment"));
681
682       *space =  *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8;
683     }
684   else
685     {
686       Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
687       bool dummy;
688
689       *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
690     }
691 }
692
693
694 /*
695   Read hints from L and generate springs.
696  */
697 void
698 Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shortest)
699 {
700   Real max_fixed = -infinity_f;
701   Real max_space = -infinity_f;
702
703   standard_breakable_column_spacing (me, l, r, &max_fixed, &max_space ,
704                                      shortest);
705   
706   for (SCM s = l->get_grob_property ("spacing-wishes");
707        gh_pair_p (s); s = gh_cdr (s))
708     {
709       Item * spacing_grob = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
710
711       if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
712         continue;
713
714       Real space;
715       Real fixed_space;
716
717       /*
718         column for the left one settings should be ok due automatic
719         pointer munging.
720
721       */
722       assert (spacing_grob-> get_column () == l);
723
724       Staff_spacing::get_spacing_params (spacing_grob,
725                                          &space, &fixed_space);
726
727       if (Paper_column::when_mom (r).grace_part_)
728         {
729           /*
730             Correct for grace notes.
731
732             Ugh. The 0.8 is arbitrary.
733            */
734           space *= 0.8;
735         }
736       if (space > max_space)
737         {
738           max_space = space;
739           max_fixed = fixed_space;
740         }
741     }
742
743   
744   
745     
746   if (isinf (max_space))
747     {
748     /*
749       One situation where this can happen is when there is a column
750       that only serves as a spanning point for a short staff-symbol.
751
752      ===============X===
753
754          |=======Y
755
756
757       (here no StaffSpacing from Y to X is found.)
758     */      
759       warning ("No spacing wishes found. Does your score have a staff?");
760       max_space = 2.0;
761       max_fixed = 1.0;
762     }
763
764   
765   if (l->break_status_dir() == RIGHT
766       && Paper_column::when_mom (l) == Paper_column::when_mom (r))
767     {
768       /* Start of line: this space is not stretchable */
769       max_fixed = max_space;
770     }
771
772   /*
773     Hmm.  we do 1/0 in the next thing. Perhaps we should check if this
774     works on all architectures.
775    */
776
777   /*
778     There used to be code that changed spacing depending on
779     raggedright setting.  Ugh.
780
781     Do it more cleanly, or rename the property. 
782     
783    */
784   Real strength = 1 / (max_space - max_fixed);
785   Real distance =  max_space;
786   Spaceable_grob::add_spring (l, r, distance, strength, false);
787 }
788
789
790 /**
791   Get the measure wide ant for arithmetic spacing.
792   */
793 Real
794 Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool * expand_only) 
795 {
796   Real k = gh_scm2double (me->get_grob_property ("shortest-duration-space"));
797   Real incr = gh_scm2double (me->get_grob_property ("spacing-increment"));
798   
799   if (d < shortest)
800     {
801       /*
802         We don't space really short notes using the log of the
803         duration, since it would disproportionally stretches the long
804         notes in a piece. In stead, we use geometric spacing with constant 0.5
805         (i.e. linear.)
806
807         This should probably be tunable, to use other base numbers.
808
809         In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
810         3.6 mm (total).  head-width = 2.4, so we 1.2mm for 16th, 1.5
811         mm for 8th. (white space), suggesting that we use
812
813         (1.2 / 1.5)^{-log2(duration ratio)}
814         
815
816        */
817       Rational ratio = d.main_part_ / shortest;
818
819 #if 0
820       *expand_only = true;
821 #endif
822       return ((k-1) + double (ratio)) * incr;
823     }
824   else
825     {
826       /*
827           John S. Gourlay. ``Spacing a Line of Music,'' Technical
828           Report OSU-CISRC-10/87-TR35, Department of Computer and
829           Information Science, The Ohio State University, 1987.
830        */
831       Real log =  log_2 (shortest);
832       k -= log;
833       Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
834       *expand_only = false;      
835    
836       return (log_2 (compdur) + k) * incr;
837     }
838 }
839
840 Real
841 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
842                                Moment shortest, bool * expand_only) 
843 {
844   Moment shortest_playing_len = 0;
845   SCM s = lc->get_grob_property ("shortest-playing-duration");
846
847   if (unsmob_moment (s))
848     shortest_playing_len = *unsmob_moment (s);
849   
850   if (! shortest_playing_len.to_bool ())
851     {
852       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ());
853       shortest_playing_len = 1;
854     }
855
856   Moment lwhen = Paper_column::when_mom (lc);
857   Moment rwhen =  Paper_column::when_mom (rc);
858
859   Moment delta_t = rwhen - lwhen;
860   Real dist = 0.0;
861
862   /*
863     In normal situations, the next column is at most
864     SHORTEST_PLAYING_LEN away. However chord-tremolos do funky faking stuff
865     with durations, invalidating this assumption. Here we kludge
866     around to get chord tremolos to behave properly.
867     
868    */
869   shortest_playing_len = shortest_playing_len >? delta_t;
870   if (delta_t.main_part_ && !lwhen.grace_part_)
871     {
872       dist = get_duration_space (me, shortest_playing_len, shortest.main_part_, expand_only);
873       dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
874     }
875   else if (delta_t.grace_part_)
876     {
877       /*
878         TODO: figure out how to space grace notes.
879       */
880       dist = get_duration_space (me, shortest, shortest.main_part_, expand_only);
881
882       Real grace_fact = 1.0;
883       SCM gf = me->get_grob_property ("grace-space-factor");
884       if (gh_number_p (gf))
885         grace_fact = gh_scm2double (gf);
886
887       dist *= grace_fact;
888     }
889
890   
891   return dist;
892 }
893
894
895
896 ADD_INTERFACE (Spacing_spanner,"spacing-spanner-interface",
897 "The space taken by a note is dependent on its duration. Doubling a\n"
898 "duration adds spacing-increment to the space. The most common shortest\n"
899 "note gets shortest-duration-space. Notes that are even shorter are\n"
900 "spaced proportonial to their duration.\n"
901 "\n"
902 "Typically, the increment is the width of a black note head.  In a\n"
903 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
904 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
905 "head width) A 16th note is followed by 0.5 note head width. The\n"
906 "quarter note is followed by  3 NHW, the half by 4 NHW, etc.\n",
907   "grace-space-factor spacing-increment base-shortest-duration shortest-duration-space common-shortest-duration");
908
909
910
911 ADD_INTERFACE (Spacing_interface,"spacing-interface",
912   "Something to do with line breaking and spacing. Kill this one after determining line breaks.",
913   "");
914