]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
The grand \paper -> \layout, \bookpaper -> \paper renaming.
[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 (!scm_is_pair (lns) || !scm_is_pair (rns))
100     return false;
101
102   Item * l_neighbor = dynamic_cast<Item*>  (unsmob_grob (scm_car (lns)));
103   Item * r_neighbor = dynamic_cast<Item*>  (unsmob_grob (scm_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"); scm_is_pair (e); e = scm_cdr (e))
137     {
138       Grob * g = unsmob_grob (scm_car (e));
139       if (g && Break_align_interface::has_interface (g))
140         {
141           for (SCM s = g->get_property ("elements"); scm_is_pair (s);
142                s = scm_cdr (s))
143             {
144               Grob *h = unsmob_grob (scm_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 = scm_is_pair (lns) ? scm_car (lns) : SCM_BOOL_F;
181
182           SCM rns = c->get_property ("right-neighbors");
183           rns = scm_is_pair (rns) ? scm_car (rns) : SCM_BOOL_F;
184
185           /*
186             Either object can be non existent, if the score ends
187             prematurely.
188           */
189           rns = scm_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                    scm_is_pair (s); s = scm_cdr (s))
210                 {
211                   Grob *sp = unsmob_grob (scm_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; scm_is_pair (s); s = scm_cdr (s))
277         {
278           Item * wish = dynamic_cast<Item*> (unsmob_grob (scm_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 (scm_is_pair (left_neighs)
309               && unsmob_grob (scm_car (left_neighs)))
310             {
311               Item * it = dynamic_cast<Item*> (unsmob_grob (scm_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 (scm_is_pair (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 (!scm_is_pair (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 (!scm_is_pair (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,
510                              Link_array<Grob> *cols) 
511 {
512
513   Real headwid = robust_scm2double (me->get_property ("spacing-increment"), 1);
514   for (int i= 0; i < cols->size () - 1; i++)
515     {
516       Item * l = dynamic_cast<Item*> (cols->elem (i));
517       Item * r =  dynamic_cast<Item*> (cols->elem (i+1));
518
519       Paper_column * lc = dynamic_cast<Paper_column*> (l);
520       Paper_column * rc = dynamic_cast<Paper_column*> (r);
521
522       if (!Paper_column::is_musical (l))
523         {
524           breakable_column_spacing (me, l, r, global_shortest);
525
526           /*
527             
528             The case that the right part is broken as well is rather
529             rare, but it is possible, eg. with a single empty measure,
530             or if one staff finishes a tad earlier than the rest.
531             
532            */
533           Item *lb = l->find_prebroken_piece (RIGHT);
534           Item *rb = r->find_prebroken_piece (LEFT);
535           
536           if (lb)
537             breakable_column_spacing (me, lb,r, global_shortest);
538
539           if (rb)
540             breakable_column_spacing (me, l, rb, global_shortest);
541           if (lb && rb)
542             breakable_column_spacing (me, lb, rb, global_shortest);
543         }
544       else
545         {
546           musical_column_spacing (me, lc, rc, headwid, global_shortest);
547           if (Item *rb = r->find_prebroken_piece (LEFT))
548             musical_column_spacing (me, lc, rb, headwid, global_shortest);
549         }    
550     }
551 }
552
553 /*
554   Generate the space between two musical columns LC and RC, given
555   spacing parameters INCR and SHORTEST.
556  */
557 void
558 Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real increment, Rational global_shortest)
559 {
560   bool expand_only = false;
561   Real base_note_space = note_spacing (me, lc, rc, global_shortest, &expand_only);
562
563   Real compound_note_space = 0.0;
564   Real compound_fixed_note_space = 0.0;
565   int wish_count = 0;
566   
567   SCM seq  = lc->get_property ("right-neighbors");
568
569   /*
570     We adjust the space following a note only if the next note
571     happens after the current note (this is set in the grob
572     property SPACING-SEQUENCE.
573   */
574   for (SCM s = seq; scm_is_pair (s); s = scm_cdr (s))
575     {
576       Grob * wish = unsmob_grob (scm_car (s));
577
578       Item *wish_rcol = Note_spacing::right_column (wish);
579       if (Note_spacing::left_column (wish) != lc
580           || (wish_rcol != rc && wish_rcol != rc->original_))
581         continue;
582
583       /*
584         This is probably a waste of time in the case of polyphonic
585         music.  */
586       if (Note_spacing::has_interface (wish))
587         {
588           Real space =0.0;
589           Real fixed =0.0;
590           
591           Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed);
592
593           
594           compound_note_space = compound_note_space + space;
595           compound_fixed_note_space = compound_fixed_note_space + fixed;
596           wish_count ++;
597           
598         }
599     }
600
601   if (Paper_column::when_mom (rc).grace_part_ &&
602       !Paper_column::when_mom (lc).grace_part_)
603     {
604       /*
605         Ugh. 0.8 is arbitrary.
606        */
607       compound_note_space *= 0.8; 
608     }
609   
610   if (compound_note_space < 0 || wish_count == 0)
611     {
612       compound_note_space = base_note_space;
613       compound_fixed_note_space =  increment;
614     }
615   else
616     {
617       compound_note_space /= wish_count;
618       compound_fixed_note_space /= wish_count;
619     }
620
621   /*
622     Whatever we do, the fixed space is smaller than the real
623     space.
624
625     TODO: this criterion is discontinuous in the derivative.
626     Maybe it should be continuous?
627   */
628   compound_fixed_note_space = compound_fixed_note_space <? compound_note_space;
629
630   bool packed = to_boolean (me->get_layout ()->c_variable ("packed"));
631   Real strength, distance;
632
633   /*
634     TODO: make sure that the space doesn't exceed the right margin.
635    */
636   if (packed)
637     {
638       /*
639         In packed mode, pack notes as tight as possible.  This makes
640         sense mostly in combination with raggedright mode: the notes
641         are then printed at minimum distance.  This is mostly useful
642         for ancient notation, but may also be useful for some flavours
643         of contemporary music.  If not in raggedright mode, lily will
644         pack as much bars of music as possible into a line, but the
645         line will then be stretched to fill the whole linewidth.
646       */
647       strength = 1.0;
648       distance = compound_fixed_note_space;
649     }
650   else
651     {
652       strength = 1 / (compound_note_space - compound_fixed_note_space);
653       distance = compound_note_space;
654     }
655
656   Spaceable_grob::add_spring (lc, rc, distance, strength);  
657 }
658
659
660 /*
661   The one-size-fits all spacing. It doesn't take into account
662   different spacing wishes from one to the next column.
663  */
664 void
665 Spacing_spanner::standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
666                                    Real * fixed, Real * space,
667                                    Moment shortest)
668 {
669   *fixed = 0.0;
670   Direction d = LEFT;
671   Drul_array<Item*> cols (l,r);
672   
673   do
674     {
675       if (!Paper_column::is_musical (cols[d]))
676         {
677           /*
678             Tied accidentals over barlines cause problems, so lets see
679             what happens if we do this for non musical columns only.
680            */
681           Interval lext = cols[d]->extent (cols [d], X_AXIS);
682           if (!lext.is_empty ())
683             *fixed += -d * lext[-d];
684         }
685     }
686   while (flip (&d) != LEFT);
687   
688
689   if (l->is_breakable (l) && r->is_breakable (r))
690     {
691       Moment *dt = unsmob_moment (l->get_property ("measure-length"));
692       Moment mlen (1);
693       if (dt)
694         mlen = *dt;
695       
696       Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
697
698       *space =  *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8;
699     }
700   else
701     {
702       Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
703
704       if (dt == Moment (0,0))
705         {
706           /*
707             In this case, Staff_spacing should handle the job,
708             using dt when it is 0 is silly.
709            */
710           *space = *fixed + 0.5; 
711         }
712       else
713         {
714           bool dummy;
715           *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
716         }
717     }
718 }
719
720
721 /*
722   Read hints from L and generate springs.
723 */
724 void
725 Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shortest)
726 {
727   Real compound_fixed = 0.0;
728   Real compound_space = 0.0;
729   int wish_count = 0;
730
731   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
732
733   if (dt == Moment (0,0))
734     {
735       for (SCM s = l->get_property ("spacing-wishes");
736            scm_is_pair (s); s = scm_cdr (s))
737         {
738           Item * spacing_grob = dynamic_cast<Item*> (unsmob_grob (scm_car (s)));
739
740           if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
741             continue;
742
743           Real space;
744           Real fixed_space;
745
746           /*
747             column for the left one settings should be ok due automatic
748             pointer munging.
749
750           */
751           assert (spacing_grob-> get_column () == l);
752
753           Staff_spacing::get_spacing_params (spacing_grob,
754                                              &space, &fixed_space);
755
756           if (Paper_column::when_mom (r).grace_part_)
757             {
758               /*
759                 Correct for grace notes.
760
761                 Ugh. The 0.8 is arbitrary.
762               */
763               space *= 0.8;
764             }
765
766
767           compound_space += space;
768           compound_fixed += fixed_space;
769           wish_count ++ ;
770         }
771     }
772
773   if (compound_space <= 0.0 || !wish_count)
774     {
775       standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space ,
776                                          shortest);
777       wish_count = 1;
778     }
779   else
780     {
781       compound_space /= wish_count;
782       compound_fixed /= wish_count;
783     }
784
785   assert (!isinf (compound_space));
786   compound_space = compound_space >? compound_fixed;
787
788   
789   /*
790     Hmm.  we do 1/0 in the next thing. Perhaps we should check if this
791     works on all architectures.
792    */
793
794   /*
795     There used to be code that changed spacing depending on
796     raggedright setting.  Ugh.
797
798     Do it more cleanly, or rename the property. 
799     
800    */
801   Real strength = 1 / (compound_space - compound_fixed);
802   Real distance = compound_space;
803   Spaceable_grob::add_spring (l, r, distance, strength);
804 }
805
806
807 /**
808   Get the measure wide ant for arithmetic spacing.
809   */
810 Real
811 Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool * expand_only) 
812 {
813   Real k = robust_scm2double (me->get_property ("shortest-duration-space"), 1);
814   Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
815   
816   if (d < shortest)
817     {
818       /*
819         We don't space really short notes using the log of the
820         duration, since it would disproportionally stretches the long
821         notes in a piece. In stead, we use geometric spacing with constant 0.5
822         (i.e. linear.)
823
824         This should probably be tunable, to use other base numbers.
825
826         In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
827         3.6 mm (total).  head-width = 2.4, so we 1.2mm for 16th, 1.5
828         mm for 8th. (white space), suggesting that we use
829
830         (1.2 / 1.5)^{-log2(duration ratio)}
831         
832
833        */
834       Rational ratio = d.main_part_ / shortest;
835
836       return ((k-1) + double (ratio)) * incr;
837     }
838   else
839     {
840       /*
841           John S. Gourlay. ``Spacing a Line of Music,'' Technical
842           Report OSU-CISRC-10/87-TR35, Department of Computer and
843           Information Science, The Ohio State University, 1987.
844        */
845       Real log =  log_2 (shortest);
846       k -= log;
847       Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
848       *expand_only = false;      
849    
850       return (log_2 (compdur) + k) * incr;
851     }
852 }
853
854 Real
855 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
856                                Moment shortest, bool * expand_only) 
857 {
858   Moment shortest_playing_len = 0;
859   SCM s = lc->get_property ("shortest-playing-duration");
860
861   if (unsmob_moment (s))
862     shortest_playing_len = *unsmob_moment (s);
863   
864   if (! shortest_playing_len.to_bool ())
865     {
866       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ());
867       shortest_playing_len = 1;
868     }
869
870   Moment lwhen = Paper_column::when_mom (lc);
871   Moment rwhen =  Paper_column::when_mom (rc);
872
873   Moment delta_t = rwhen - lwhen;
874   if (!Paper_column::is_musical (rc))
875     {
876       /*
877         when toying with mmrests, it is possible to have musical
878         column on the left and non-musical on the right, spanning
879         several measures.
880        */
881       
882       Moment *dt = unsmob_moment (rc->get_property ("measure-length"));
883       if (dt)
884         {
885           delta_t = delta_t <? *dt;
886
887           /*
888             The following is an extra safety measure, such that
889             the length of a mmrest event doesn't cause havoc.
890            */
891           shortest_playing_len = shortest_playing_len <? *dt;
892         }
893     }
894   Real dist = 0.0;
895
896   /*
897     In normal situations, the next column is at most
898     SHORTEST_PLAYING_LEN away. However chord-tremolos do funky faking stuff
899     with durations, invalidating this assumption. Here we kludge
900     around to get chord tremolos to behave properly.
901     
902    */
903   shortest_playing_len = shortest_playing_len >? delta_t;
904   if (delta_t.main_part_ && !lwhen.grace_part_)
905     {
906       dist = get_duration_space (me, shortest_playing_len, shortest.main_part_, expand_only);
907       dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
908     }
909   else if (delta_t.grace_part_)
910     {
911       /*
912         TODO: figure out how to space grace notes.
913       */
914       dist = get_duration_space (me, shortest, shortest.main_part_, expand_only);
915
916       Real grace_fact
917         = robust_scm2double (me->get_property ("grace-space-factor"), 1);
918
919       dist *= grace_fact;
920     }
921
922   
923   return dist;
924 }
925
926
927
928 ADD_INTERFACE (Spacing_spanner,"spacing-spanner-interface",
929 "The space taken by a note is dependent on its duration. Doubling a\n"
930 "duration adds spacing-increment to the space. The most common shortest\n"
931 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
932 "spaced proportonial to their duration.\n"
933 "\n"
934 "Typically, the increment is the width of a black note head.  In a\n"
935 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
936 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
937 "head width) A 16th note is followed by 0.5 note head width. The\n"
938 "quarter note is followed by  3 NHW, the half by 4 NHW, etc.\n",
939   "grace-space-factor spacing-increment base-shortest-duration shortest-duration-space common-shortest-duration");
940
941
942
943 ADD_INTERFACE (Spacing_interface, "spacing-interface",
944   "Something to do with line breaking and spacing. Kill this one after determining line breaks.",
945   "");
946