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