]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.5.29
[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--2002 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             right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
167
168           s.distance_f_ = left_distance + right_dist;
169             
170           Real stretch_dist = 0.;
171           if (gh_number_p (stretch_hint))
172             stretch_dist += gh_scm2double (stretch_hint);
173           else
174             stretch_dist += left_distance;
175           
176           if (gh_pair_p (next_stretch_hint))
177             // see regtest spacing-tight
178             stretch_dist += - gh_scm2double (ly_car (next_stretch_hint));
179           else
180             stretch_dist += right_dist;
181
182           if (s.distance_f_ <0)
183             {
184               programming_error ("Negative dist, setting to 1.0 PT");
185               s.distance_f_ = 1.0;
186             }
187           if (stretch_dist == 0.0)
188             {
189               /*
190                 \bar "".  We give it 0 space, with high strength. 
191                */
192               s.strength_f_ = 20.0; 
193             }
194           else
195             s.strength_f_ /= stretch_dist;
196           
197           springs.push (s);
198         }
199     }
200
201   Spacing_spanner::stretch_to_regularity (me, &springs, cols);
202   for (int i=springs.size (); i --;)
203     springs[i].add_to_cols ();
204 }
205
206 /*
207   Look at COLS, searching for columns that have 'regular-distance-to
208   set. A sequence of columns that have this property set should have
209   an equal distance (an equispaced run). Extract the projected
210   distance from SPRINGS, and scale SPRINGS for the equispaced run, to the
211   widest space necessary.
212
213
214   TODO:
215   
216   -- inefficient code; maybe it is easier to twiddle with the springs
217   after they've become grob properties (ie. have their
218   minimum-distances set)
219
220   -- does not adjust strength field of the springs very well: result
221   awkward spacing at the start of a line. (?)
222
223   -- will be confused when there are multiple equispaced runs in a measure.
224
225   -- dealing with springs for line breaks is a little tricky; in any
226   case, we will only space per measure.
227
228   -- we scale to actual distances, not to optical effects. Eg. if the
229   equispaced run contains optical corrections, then the scaling will
230   cancel those.
231
232   -- Regular_spacing_engraver doesn't mark the first column of the
233   next bar, making the space before a barline too short, in this case
234
235
236        x<- 16ths--> x(8th)
237        x(8th)       x(8th)      <- equispaced run.      
238   
239 */
240
241 void
242 Spacing_spanner::stretch_to_regularity (Grob *me,
243                                         Array<Spring> * springs,
244                                         Link_array<Grob> const & cols)
245 {
246   /*
247     Find the starting column of the run. REGULAR-DISTANCE-TO points
248     back to a previous column, so we look ahead to find a column
249     pointing back to the first one.
250     
251    */
252   Grob    * first_regular_spaced_col = 0;
253   for (int i = 0 ;  i <  cols.size () && !first_regular_spaced_col; i++)
254     {
255       SCM rdt = cols[i]->get_grob_property ("regular-distance-to");
256       if (cols.find_l (unsmob_grob (rdt)))
257         first_regular_spaced_col = unsmob_grob (rdt);
258     }
259   for (int i = springs->size ();  i-- ;)
260     springs->elem (i).set_to_cols ();
261   
262   int i;
263   for (i = 0; i < springs->size ()
264          && springs->elem (i).item_l_drul_[RIGHT] != first_regular_spaced_col;
265        i++)
266     ;
267
268
269   if (i==springs->size ())
270     return ;
271     
272   Real maxdist = 0.0;
273   Real dist  =0.0;
274   Grob *last_col = first_regular_spaced_col;
275   Grob *last_regular_spaced_col = first_regular_spaced_col;
276   
277
278   /*
279     find the max distance for this run. 
280    */
281   for (int j = i;  j < springs->size (); j++)
282     {
283       Spring *s = &(springs->elem_ref (j));
284       if (s->item_l_drul_[LEFT] != last_col)
285         continue;
286       
287       dist += s->distance_f_;
288
289       last_col = s->item_l_drul_[RIGHT];
290       SCM rdt = last_col->get_grob_property ("regular-distance-to");
291       if (unsmob_grob (rdt) == last_regular_spaced_col)
292         {
293           maxdist = maxdist >? dist;
294           dist = 0.0;
295           last_regular_spaced_col = last_col;
296         }
297
298     }
299
300   /*
301     Scale the springs
302    */
303   dist =0.0;
304   last_col =  first_regular_spaced_col;
305   last_regular_spaced_col = first_regular_spaced_col;
306   for (int j = i;   j < springs->size (); j++)
307     {
308       Spring *s = &springs->elem_ref (j);
309       if (s->item_l_drul_[LEFT] != last_col)
310         continue;
311       dist += s->distance_f_;
312
313       last_col = s->item_l_drul_[RIGHT];
314       SCM rdt = last_col->get_grob_property ("regular-distance-to");
315       if (unsmob_grob (rdt) == last_regular_spaced_col)
316         {
317           do {
318             springs->elem_ref (i).distance_f_ *= maxdist / dist;
319             springs->elem_ref (i).strength_f_ *= dist / maxdist;            
320           } while (i++ < j);
321           last_regular_spaced_col = last_col;
322           dist =0.0;
323         }
324     }
325 }
326
327 /**
328    Do something if breakable column has no spacing hints set.
329  */
330 Real
331 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
332                                       Moment shortest) 
333 {
334   Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
335   Real durational_distance = 0;
336   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
337
338   /*
339                 ugh should use shortest_playing distance
340   */
341   if (delta_t.to_bool ())
342     {
343       durational_distance =  get_duration_space (me, delta_t, shortest);
344     }
345
346   return  symbol_distance >? durational_distance;
347 }
348
349
350 /**
351   Get the measure wide ant for arithmetic spacing.
352
353   @see
354   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
355   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
356   The Ohio State University, 1987.
357
358   */
359 Real
360 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest) 
361 {
362   Real log =  log_2 (shortest.main_part_);
363   Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
364     - log;
365
366   Rational compdur = d.main_part_  + d.grace_part_ / Rational (3);
367   return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
368 }
369
370
371 Real
372 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
373                                Moment shortest) 
374 {
375   Moment shortest_playing_len = 0;
376   SCM s = lc->get_grob_property ("shortest-playing-duration");
377
378   //  SCM s = lc->get_grob_property ("mean-playing-duration");  
379   if (unsmob_moment (s))
380     shortest_playing_len = *unsmob_moment (s);
381   
382   if (! shortest_playing_len.to_bool ())
383     {
384       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
385       shortest_playing_len = 1;
386     }
387   
388   if (! shortest.to_bool ())
389     {
390       programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
391       shortest = 1;
392     }
393   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
394   Real dist = get_duration_space (me, shortest_playing_len, shortest);
395
396
397   /*
398     ugh: 0.1 is an arbitrary distance.
399    */
400   dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
401     + 0.1 * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
402
403
404
405   Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
406   Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
407
408   if (lm && rm)
409     {
410       if (lm->grace_part_ && rm->grace_part_)
411         dist *= 0.5;
412       else if (!rm->grace_part_ && lm->grace_part_)
413         dist *= 0.7;
414     }
415
416   
417   return dist;
418 }
419
420 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
421 SCM
422 Spacing_spanner::set_springs (SCM smob)
423 {
424   Grob *me = unsmob_grob (smob);
425   Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
426
427   int j = 0;
428
429   for (int i = 1; i < all.size (); i++)
430     {
431       Grob *sc = all[i];
432       if (Item::breakable_b (sc))
433         {
434           Link_array<Grob> measure (all.slice (j, i+1));          
435           do_measure (me, measure);
436           j = i;
437         }
438     }
439
440   /*
441     farewell, cruel world
442    */
443   me->suicide ();
444   return SCM_UNSPECIFIED;
445 }
446
447
448
449 /*
450   maximum-duration-for-spacing
451 From: bf250@freenet.carleton.ca (John Sankey)
452 To: gnu-music-discuss@gnu.org
453 Subject: note spacing suggestion
454 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
455
456 Currently, Lily spaces notes by starting with a basic distance,
457 arithmetic_multiplier, which it applies to the minimum duration note
458 of the bar. Then she adds a logarithmic increment, scaled from
459 arithmetic_basicspace, for longer notes. (Then, columns are aligned
460 and justified.) Fundamentally, this matches visual spacing to musical
461 weight and works well.
462
463 A lot of the time in music, I see a section basically in melodic
464 notes that occasionally has a rapid ornamental run (scale). So, there
465 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
466 a return to long notes. Currently, Lily gives the same horizontal
467 space to the 1/32nd notes in their bar (even if set in small size as
468 is commonly done for cadenzii) as she gives to 1/4 notes in bars
469 where 1/4 note is the minimum duration. The resulting visual weight
470 does not match the musical weight over the page.
471
472 Looking at the music I am typesetting, I feel that Lily's spacing
473 could be significantly improved if, with no change in the basic
474 method used, arithmetic_multiplier could be applied referred to the
475 same duration throughout a piece. Of course, the current method
476 should be retained for those who have already set music in it, so I
477 suggest a property called something like arithmetic_base=16 to fix
478 1/16 duration as the reference for arithmetic_multiplier; the default
479 would be a dynamic base is it is now.
480
481 Does anyone else feel that this would be a useful improvement for
482 their music? (Of course, if arithmetic_multiplier became a regular
483 property, this could be used to achieve a similar result by
484 tweaking.)
485   
486  */