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