]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
* lily/parser.yy (Music_list): add error-found to music with errors.
[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, 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; scm_is_pair (s); s = scm_cdr (s))
576     {
577       Grob * wish = unsmob_grob (scm_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);  
658 }
659
660
661 /*
662   The one-size-fits all spacing. It doesn't take into account
663   different spacing wishes from one to the next column.
664  */
665 void
666 Spacing_spanner::standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
667                                    Real * fixed, Real * space,
668                                    Moment shortest)
669 {
670   *fixed = 0.0;
671   Direction d = LEFT;
672   Drul_array<Item*> cols (l,r);
673   
674   do
675     {
676       if (!Paper_column::is_musical (cols[d]))
677         {
678           /*
679             Tied accidentals over barlines cause problems, so lets see
680             what happens if we do this for non musical columns only.
681            */
682           Interval lext = cols[d]->extent (cols [d], X_AXIS);
683           if (!lext.is_empty ())
684             *fixed += -d * lext[-d];
685         }
686     }
687   while (flip (&d) != LEFT);
688   
689
690   if (l->is_breakable (l) && r->is_breakable (r))
691     {
692       Moment *dt = unsmob_moment (l->get_property ("measure-length"));
693       Moment mlen (1);
694       if (dt)
695         mlen = *dt;
696       
697       Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
698
699       *space =  *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8;
700     }
701   else
702     {
703       Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
704
705       if (dt == Moment (0,0))
706         {
707           /*
708             In this case, Staff_spacing should handle the job,
709             using dt when it is 0 is silly.
710            */
711           *space = *fixed + 0.5; 
712         }
713       else
714         {
715           bool dummy;
716           *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
717         }
718     }
719 }
720
721
722 /*
723   Read hints from L and generate springs.
724 */
725 void
726 Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shortest)
727 {
728   Real compound_fixed = 0.0;
729   Real compound_space = 0.0;
730   int wish_count = 0;
731
732   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
733
734   if (dt == Moment (0,0))
735     {
736       for (SCM s = l->get_property ("spacing-wishes");
737            scm_is_pair (s); s = scm_cdr (s))
738         {
739           Item * spacing_grob = dynamic_cast<Item*> (unsmob_grob (scm_car (s)));
740
741           if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
742             continue;
743
744           Real space;
745           Real fixed_space;
746
747           /*
748             column for the left one settings should be ok due automatic
749             pointer munging.
750
751           */
752           assert (spacing_grob-> get_column () == l);
753
754           Staff_spacing::get_spacing_params (spacing_grob,
755                                              &space, &fixed_space);
756
757           if (Paper_column::when_mom (r).grace_part_)
758             {
759               /*
760                 Correct for grace notes.
761
762                 Ugh. The 0.8 is arbitrary.
763               */
764               space *= 0.8;
765             }
766
767
768           compound_space += space;
769           compound_fixed += fixed_space;
770           wish_count ++ ;
771         }
772     }
773
774   if (compound_space <= 0.0 || !wish_count)
775     {
776       standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space ,
777                                          shortest);
778       wish_count = 1;
779     }
780   else
781     {
782       compound_space /= wish_count;
783       compound_fixed /= wish_count;
784     }
785
786   assert (!isinf (compound_space));
787   compound_space = compound_space >? compound_fixed;
788
789   
790   /*
791     Hmm.  we do 1/0 in the next thing. Perhaps we should check if this
792     works on all architectures.
793    */
794
795   /*
796     There used to be code that changed spacing depending on
797     raggedright setting.  Ugh.
798
799     Do it more cleanly, or rename the property. 
800     
801    */
802   Real strength = 1 / (compound_space - compound_fixed);
803   Real distance = compound_space;
804   Spaceable_grob::add_spring (l, r, distance, strength);
805 }
806
807
808 /**
809   Get the measure wide ant for arithmetic spacing.
810   */
811 Real
812 Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool * expand_only) 
813 {
814   Real k = robust_scm2double (me->get_property ("shortest-duration-space"), 1);
815   Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
816   
817   if (d < shortest)
818     {
819       /*
820         We don't space really short notes using the log of the
821         duration, since it would disproportionally stretches the long
822         notes in a piece. In stead, we use geometric spacing with constant 0.5
823         (i.e. linear.)
824
825         This should probably be tunable, to use other base numbers.
826
827         In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
828         3.6 mm (total).  head-width = 2.4, so we 1.2mm for 16th, 1.5
829         mm for 8th. (white space), suggesting that we use
830
831         (1.2 / 1.5)^{-log2(duration ratio)}
832         
833
834        */
835       Rational ratio = d.main_part_ / shortest;
836
837       return ((k-1) + double (ratio)) * incr;
838     }
839   else
840     {
841       /*
842           John S. Gourlay. ``Spacing a Line of Music,'' Technical
843           Report OSU-CISRC-10/87-TR35, Department of Computer and
844           Information Science, The Ohio State University, 1987.
845        */
846       Real log =  log_2 (shortest);
847       k -= log;
848       Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
849       *expand_only = false;      
850    
851       return (log_2 (compdur) + k) * incr;
852     }
853 }
854
855 Real
856 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
857                                Moment shortest, bool * expand_only) 
858 {
859   Moment shortest_playing_len = 0;
860   SCM s = lc->get_property ("shortest-playing-duration");
861
862   if (unsmob_moment (s))
863     shortest_playing_len = *unsmob_moment (s);
864   
865   if (! shortest_playing_len.to_bool ())
866     {
867       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ());
868       shortest_playing_len = 1;
869     }
870
871   Moment lwhen = Paper_column::when_mom (lc);
872   Moment rwhen =  Paper_column::when_mom (rc);
873
874   Moment delta_t = rwhen - lwhen;
875   if (!Paper_column::is_musical (rc))
876     {
877       /*
878         when toying with mmrests, it is possible to have musical
879         column on the left and non-musical on the right, spanning
880         several measures.
881        */
882       
883       Moment *dt = unsmob_moment (rc->get_property ("measure-length"));
884       if (dt)
885         {
886           delta_t = delta_t <? *dt;
887
888           /*
889             The following is an extra safety measure, such that
890             the length of a mmrest event doesn't cause havoc.
891            */
892           shortest_playing_len = shortest_playing_len <? *dt;
893         }
894     }
895   Real dist = 0.0;
896
897   /*
898     In normal situations, the next column is at most
899     SHORTEST_PLAYING_LEN away. However chord-tremolos do funky faking stuff
900     with durations, invalidating this assumption. Here we kludge
901     around to get chord tremolos to behave properly.
902     
903    */
904   shortest_playing_len = shortest_playing_len >? delta_t;
905   if (delta_t.main_part_ && !lwhen.grace_part_)
906     {
907       dist = get_duration_space (me, shortest_playing_len, shortest.main_part_, expand_only);
908       dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
909     }
910   else if (delta_t.grace_part_)
911     {
912       /*
913         TODO: figure out how to space grace notes.
914       */
915       dist = get_duration_space (me, shortest, shortest.main_part_, expand_only);
916
917       Real grace_fact
918         = robust_scm2double (me->get_property ("grace-space-factor"), 1);
919
920       dist *= grace_fact;
921     }
922
923   
924   return dist;
925 }
926
927
928
929 ADD_INTERFACE (Spacing_spanner,"spacing-spanner-interface",
930 "The space taken by a note is dependent on its duration. Doubling a\n"
931 "duration adds spacing-increment to the space. The most common shortest\n"
932 "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
933 "spaced proportonial to their duration.\n"
934 "\n"
935 "Typically, the increment is the width of a black note head.  In a\n"
936 "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
937 "gets 2 note heads width (i.e. the space following a note is 1 note\n"
938 "head width) A 16th note is followed by 0.5 note head width. The\n"
939 "quarter note is followed by  3 NHW, the half by 4 NHW, etc.\n",
940   "grace-space-factor spacing-increment base-shortest-duration shortest-duration-space common-shortest-duration");
941
942
943
944 ADD_INTERFACE (Spacing_interface, "spacing-interface",
945   "Something to do with line breaking and spacing. Kill this one after determining line breaks.",
946   "");
947