]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.5.17
[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--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9
10 #include "spacing-spanner.hh"
11 #include "paper-column.hh"
12 #include "dimensions.hh"
13 #include "paper-def.hh"
14 #include "warn.hh"
15 #include "paper-score.hh"
16 #include "line-of-score.hh"
17 #include "misc.hh"
18
19 void
20 Spacing_spanner::set_interface (Grob*me)
21 {
22   me->set_extent_callback (SCM_EOL, X_AXIS);
23   me->set_extent_callback (SCM_EOL, Y_AXIS) ; 
24 }
25
26 /*
27
28   The algorithm is partly taken from :
29
30   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
31   OSU-CISRC-10/87-TR35, Department of Computer and Information
32   Science, The Ohio State University, 1987.
33
34   TOO HAIRY.
35
36   TODO: write comments 
37   
38  */
39 void
40 Spacing_spanner::do_measure (Grob*me, Link_array<Grob> const & cols) 
41 {
42   Moment shortest;
43   Moment mean_shortest;
44
45   /*
46     space as if this duration  is present. 
47    */
48   Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
49   shortest.set_infinite (1);
50
51   int n = 0;
52   for (int i =0 ; i < cols.size (); i++)  
53     {
54       if (Paper_column::musical_b (cols[i]))
55         {
56           Moment *when = unsmob_moment (cols[i]->get_grob_property  ("when"));
57
58           /*
59             ignore grace notes for shortest notes.
60            */
61           if (when && when->grace_part_)
62             continue;
63           
64           SCM  st = cols[i]->get_grob_property ("shortest-starter-duration");
65           Moment this_shortest = *unsmob_moment (st);
66           shortest = shortest <? this_shortest;
67           if (!mean_shortest.main_part_.infty_b ())
68             {
69               n++;
70               mean_shortest += this_shortest;
71             }
72         }
73     }
74   mean_shortest /= n;
75
76   Array<Spring> springs;
77   for (int i= 0; i < cols.size () - 1; i++)
78     {
79       Item * l = dynamic_cast<Item*> (cols[i]);
80       Item * r =  dynamic_cast<Item*> (cols[i+1]);
81       Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
82       Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
83
84       Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
85
86
87       /*
88         left refers to the space that is associated with items of the left column, so you have
89
90           LC  <- left_space -><- right_space -> RC
91               <-    total space              ->
92               
93
94         typically, right_space is non-zero when there are
95         accidentals in RC
96           
97        */
98       for (int j=0; j < 4; j++)
99         {
100           Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
101           Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
102           if (!lc || !rc)
103             continue;
104
105           Spring s;
106           s.item_l_drul_[LEFT] = lc;
107           s.item_l_drul_[RIGHT] = rc;
108           
109           SCM hint = lc->get_grob_property ("extra-space");
110           SCM next_hint = rc->get_grob_property ("extra-space");
111           SCM stretch_hint = lc->get_grob_property ("stretch-distance");
112           SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");     
113
114           Real left_distance = 0;
115           if (gh_pair_p (hint))
116             {
117               left_distance = gh_scm2double (ly_cdr (hint)); 
118             }
119            // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score.  FIXME
120           else if (!Paper_column::musical_b (lc) && i+1 < cols.size ()) 
121             {
122               left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
123             }
124           else if (Paper_column::musical_b ( lc))
125             {
126               left_distance  = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
127             }
128           else
129               programming_error ("uninitialised left_distance");
130           
131           s.distance_f_ = left_distance;
132
133           /*
134             Only do tight spaces *after* barlines (breakable columns),
135             not before.
136
137             We want the space before barline to be like the note
138             spacing in the measure.
139           */
140           SCM sfac =lc->get_grob_property ("space-factor");
141           if (gh_number_p (lc->get_grob_property ("column-space-strength"))
142               && (Item::breakable_b (lc) || lc->original_l_))
143             {
144               s.strength_f_ =
145                 gh_scm2double (lc->get_grob_property ("column-space-strength"));
146             }
147           else if (gh_number_p (sfac))
148             left_distance *= gh_scm2double (sfac);
149
150           
151           Real right_dist = 0.0;
152           if (gh_pair_p (next_hint))
153             {
154               right_dist += - gh_scm2double (ly_car (next_hint));
155             }
156           else
157             {
158               Interval ext (rc->extent (rc, X_AXIS));
159               right_dist =  ext.empty_b () ? 0.0 : - ext [LEFT];
160             }
161
162           /*
163             don't want to create too much extra space for accidentals
164           */
165           if (Paper_column::musical_b (rc))
166            {
167               if (to_boolean (rc->get_grob_property ("contains-grace")))
168                 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
169               else
170                 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
171            }
172
173           s.distance_f_ = left_distance + right_dist;
174             
175           Real stretch_dist = 0.;
176           if (gh_number_p (stretch_hint))
177             stretch_dist += gh_scm2double (stretch_hint);
178           else
179             stretch_dist += left_distance;
180           
181           if (gh_pair_p (next_stretch_hint))
182             // see regtest spacing-tight
183             stretch_dist += - gh_scm2double (ly_car (next_stretch_hint));
184           else
185             stretch_dist += right_dist;
186
187           if (s.distance_f_ <0)
188             {
189               programming_error ("Negative dist, setting to 1.0 PT");
190               s.distance_f_ = 1.0;
191             }
192           if (stretch_dist == 0.0)
193             {
194               /*
195                 \bar "".  We give it 0 space, with high strength. 
196                */
197               s.strength_f_ = 20.0; 
198             }
199           else
200             s.strength_f_ /= stretch_dist;
201           
202           springs.push (s);
203         }
204     }
205
206   Spacing_spanner::stretch_to_regularity (me, &springs, cols);
207   for (int i=springs.size (); i --;)
208     springs[i].add_to_cols ();
209 }
210
211 /*
212   Look at COLS, searching for columns that have 'regular-distance-to
213   set. A sequence of columns that have this property set should have
214   an equal distance (an equispaced run). Extract the projected
215   distance from SPRINGS, and scale SPRINGS for the equispaced run, to the
216   widest space necessary.
217
218
219   TODO:
220   
221   -- inefficient code; maybe it is easier to twiddle with the springs
222   after they've become grob properties (ie. have their
223   minimum-distances set)
224
225   -- does not adjust strength field of the springs very well: result
226   awkward spacing at the start of a line. (?)
227
228   -- will be confused when there are multiple equispaced runs in a measure.
229
230   -- dealing with springs for line breaks is a little tricky; in any
231   case, we will only space per measure.
232
233   -- we scale to actual distances, not to optical effects. Eg. if the
234   equispaced run contains optical corrections, then the scaling will
235   cancel those.
236
237   -- Regular_spacing_engraver doesn't mark the first column of the
238   next bar, making the space before a barline too short, in this case
239
240
241        x<- 16ths--> x(8th)
242        x(8th)       x(8th)      <- equispaced run.      
243   
244 */
245
246 void
247 Spacing_spanner::stretch_to_regularity (Grob *me,
248                                         Array<Spring> * springs,
249                                         Link_array<Grob> const & cols)
250 {
251   /*
252     Find the starting column of the run. REGULAR-DISTANCE-TO points
253     back to a previous column, so we look ahead to find a column
254     pointing back to the first one.
255     
256    */
257   Grob    * first_regular_spaced_col = 0;
258   for (int i = 0 ;  i <  cols.size () && !first_regular_spaced_col; i++)
259     {
260       SCM rdt = cols[i]->get_grob_property ("regular-distance-to");
261       if (cols.find_l (unsmob_grob (rdt)))
262         first_regular_spaced_col = unsmob_grob (rdt);
263     }
264   for (int i = springs->size ();  i-- ;)
265     springs->elem (i).set_to_cols ();
266   
267   int i;
268   for (i = 0; i < springs->size ()
269          && springs->elem (i).item_l_drul_[RIGHT] != first_regular_spaced_col;
270        i++)
271     ;
272
273
274   if (i==springs->size ())
275     return ;
276     
277   Real maxdist = 0.0;
278   Real dist  =0.0;
279   Grob *last_col = first_regular_spaced_col;
280   Grob *last_regular_spaced_col = first_regular_spaced_col;
281   
282
283   /*
284     find the max distance for this run. 
285    */
286   for (int j = i;  j < springs->size (); j++)
287     {
288       Spring *s = &(springs->elem_ref (j));
289       if (s->item_l_drul_[LEFT] != last_col)
290         continue;
291       
292       dist += s->distance_f_;
293
294       last_col = s->item_l_drul_[RIGHT];
295       SCM rdt = last_col->get_grob_property ("regular-distance-to");
296       if (unsmob_grob (rdt) == last_regular_spaced_col)
297         {
298           maxdist = maxdist >? dist;
299           dist = 0.0;
300           last_regular_spaced_col = last_col;
301         }
302
303     }
304
305   /*
306     Scale the springs
307    */
308   dist =0.0;
309   last_col =  first_regular_spaced_col;
310   last_regular_spaced_col = first_regular_spaced_col;
311   for (int j = i;   j < springs->size (); j++)
312     {
313       Spring *s = &springs->elem_ref (j);
314       if (s->item_l_drul_[LEFT] != last_col)
315         continue;
316       dist += s->distance_f_;
317
318       last_col = s->item_l_drul_[RIGHT];
319       SCM rdt = last_col->get_grob_property ("regular-distance-to");
320       if (unsmob_grob (rdt) == last_regular_spaced_col)
321         {
322           do {
323             springs->elem_ref (i).distance_f_ *= maxdist / dist;
324             springs->elem_ref (i).strength_f_ *= dist / maxdist;            
325           } while (i++ < j);
326           last_regular_spaced_col = last_col;
327           dist =0.0;
328         }
329     }
330 }
331
332 /**
333    Do something if breakable column has no spacing hints set.
334  */
335 Real
336 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
337                                       Moment shortest) 
338 {
339   Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
340   Real durational_distance = 0;
341   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
342
343   /*
344                 ugh should use shortest_playing distance
345   */
346   if (delta_t.to_bool ())
347     {
348       durational_distance =  get_duration_space (me, delta_t, shortest);
349     }
350
351   return  symbol_distance >? durational_distance;
352 }
353
354
355 /**
356   Get the measure wide ant for arithmetic spacing.
357
358   @see
359   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
360   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
361   The Ohio State University, 1987.
362
363   */
364 Real
365 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest) 
366 {
367   Real log =  log_2 (shortest.main_part_);
368   Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
369     - log;
370
371   Rational compdur = d.main_part_  + d.grace_part_ / Rational (3);
372   return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
373 }
374
375
376 Real
377 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
378                                Moment shortest) 
379 {
380   Moment shortest_playing_len = 0;
381   SCM s = lc->get_grob_property ("shortest-playing-duration");
382
383   //  SCM s = lc->get_grob_property ("mean-playing-duration");  
384   if (unsmob_moment (s))
385     shortest_playing_len = *unsmob_moment (s);
386   
387   if (! shortest_playing_len.to_bool ())
388     {
389       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
390       shortest_playing_len = 1;
391     }
392   
393   if (! shortest.to_bool ())
394     {
395       programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
396       shortest = 1;
397     }
398   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
399   Real dist = get_duration_space (me, shortest_playing_len, shortest);
400
401
402   /*
403     ugh: 0.1 is an arbitrary distance.
404    */
405   dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
406     + 0.1 * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
407
408
409
410   /*
411     UGH: KLUDGE!
412   */
413   
414   if (delta_t > Moment (Rational (1,32)))
415     dist += stem_dir_correction (me, lc,rc);
416
417
418   Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
419   Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
420
421   if (lm && rm)
422     {
423       if (lm->grace_part_ && rm->grace_part_)
424         dist *= 0.5;
425       else if (!rm->grace_part_ && lm->grace_part_)
426         dist *= 0.7;
427     }
428
429   
430   return dist;
431 }
432
433
434 /**
435    Correct for optical illusions. See [Wanske] p. 138. The combination
436    up-stem + down-stem should get extra space, the combination
437    down-stem + up-stem less.
438
439    This should be more advanced, since relative heights of the note
440    heads also influence required correction.
441
442    Also might not work correctly in case of multi voices or staff
443    changing voices
444
445    TODO: lookup correction distances?  More advanced correction?
446    Possibly turn this off?
447
448    TODO: have to check wether the stems are in the same staff.
449
450    This routine reads the DIR-LIST property of both its L and R arguments.  */
451 Real
452 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r) 
453 {
454   SCM dl = l->get_grob_property ("dir-list");
455   SCM dr = r->get_grob_property ("dir-list");
456   
457   if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
458     return 0.;
459
460   dl = ly_car (dl);
461   dr = ly_car (dr);
462
463   assert (gh_number_p (dl) && gh_number_p (dr));
464   int d1 = gh_scm2int (dl);
465   int d2 = gh_scm2int (dr);
466
467   if (d1 == d2)
468     return 0.0;
469
470
471   Real correction = 0.0;
472   Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
473
474   if (d1 && d2 && d1 * d2 == -1)
475     {
476       correction = d1 * ssc;
477     }
478   else
479     programming_error ("Stem directions not set correctly for optical correction");
480   return correction;
481 }
482   
483
484 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
485 SCM
486 Spacing_spanner::set_springs (SCM smob)
487 {
488   Grob *me = unsmob_grob (smob);
489   Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
490
491   int j = 0;
492
493   for (int i = 1; i < all.size (); i++)
494     {
495       Grob *sc = all[i];
496       if (Item::breakable_b (sc))
497         {
498           Link_array<Grob> measure (all.slice (j, i+1));          
499           do_measure (me, measure);
500           j = i;
501         }
502     }
503
504   /*
505     farewell, cruel world
506    */
507   me->suicide ();
508   return SCM_UNSPECIFIED;
509 }
510
511
512
513 /*
514   maximum-duration-for-spacing
515 From: bf250@freenet.carleton.ca (John Sankey)
516 To: gnu-music-discuss@gnu.org
517 Subject: note spacing suggestion
518 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
519
520 Currently, Lily spaces notes by starting with a basic distance,
521 arithmetic_multiplier, which it applies to the minimum duration note
522 of the bar. Then she adds a logarithmic increment, scaled from
523 arithmetic_basicspace, for longer notes. (Then, columns are aligned
524 and justified.) Fundamentally, this matches visual spacing to musical
525 weight and works well.
526
527 A lot of the time in music, I see a section basically in melodic
528 notes that occasionally has a rapid ornamental run (scale). So, there
529 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
530 a return to long notes. Currently, Lily gives the same horizontal
531 space to the 1/32nd notes in their bar (even if set in small size as
532 is commonly done for cadenzii) as she gives to 1/4 notes in bars
533 where 1/4 note is the minimum duration. The resulting visual weight
534 does not match the musical weight over the page.
535
536 Looking at the music I am typesetting, I feel that Lily's spacing
537 could be significantly improved if, with no change in the basic
538 method used, arithmetic_multiplier could be applied referred to the
539 same duration throughout a piece. Of course, the current method
540 should be retained for those who have already set music in it, so I
541 suggest a property called something like arithmetic_base=16 to fix
542 1/16 duration as the reference for arithmetic_multiplier; the default
543 would be a dynamic base is it is now.
544
545 Does anyone else feel that this would be a useful improvement for
546 their music? (Of course, if arithmetic_multiplier became a regular
547 property, this could be used to achieve a similar result by
548 tweaking.)
549   
550  */