]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.5.0
[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> 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 (dynamic_cast<Paper_column*> (cols[i])->musical_b ())
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_mom_)
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   for (int i= 0; i < cols.size () - 1; i++)
77     {
78       Item * l = dynamic_cast<Item*> (cols[i]);
79       Item * r =  dynamic_cast<Item*> (cols[i+1]);
80       Item * lb = dynamic_cast<Item*> (l->find_prebroken_piece (RIGHT));
81       Item * rb = dynamic_cast<Item*> (r->find_prebroken_piece (LEFT));
82
83       Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
84
85
86       /*
87         left refers to the space that is associated with items of the left column, so you have
88
89           LC  <- left_space -><- right_space -> RC
90               <-    total space              ->
91               
92
93         typically, right_space is non-zero when there are
94         accidentals in RC
95           
96        */
97       for (int j=0; j < 4; j++)
98         {
99           Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
100           Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
101           if (!lc || !rc)
102             continue;
103
104           Spring s;
105           s.item_l_drul_[LEFT] = lc;
106           s.item_l_drul_[RIGHT] = rc;
107           
108           SCM hint = lc->get_grob_property ("extra-space");
109           SCM next_hint = rc->get_grob_property ("extra-space");
110           SCM stretch_hint = lc->get_grob_property ("stretch-distance");
111           SCM next_stretch_hint = rc->get_grob_property ("stretch-distance");     
112
113           Real left_distance = 0;
114           if (gh_pair_p (hint))
115             {
116               left_distance = gh_scm2double (gh_cdr (hint)); 
117             }
118            // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score.  FIXME
119           else if (!lc->musical_b () && i+1 < cols.size ()) 
120             {
121               left_distance= default_bar_spacing (me,lc,rc,shortest <? base_shortest_duration);
122             }
123           else if (lc->musical_b ())
124             {
125               left_distance  = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
126             }
127           else
128               programming_error ("uninitialised left_distance");
129           
130           s.distance_f_ = left_distance;
131
132           /*
133             Only do tight spaces *after* barlines (breakable columns),
134             not before.
135
136             We want the space before barline to be like the note
137             spacing in the measure.
138           */
139           SCM sfac =lc->get_grob_property ("space-factor");
140           if (gh_number_p (lc->get_grob_property ("column-space-strength"))
141               && (Item::breakable_b (lc) || lc->original_l_))
142             {
143               s.strength_f_ =
144                 gh_scm2double (lc->get_grob_property ("column-space-strength"));
145             }
146           else if (gh_number_p (sfac))
147             left_distance *= gh_scm2double (sfac);
148
149           
150           Real right_dist = 0.0;
151           if (gh_pair_p (next_hint))
152             {
153               right_dist += - gh_scm2double (gh_car (next_hint));
154             }
155           else
156             {
157               Interval ext (rc->extent (rc, X_AXIS));
158               right_dist =  ext.empty_b () ? 0.0 : - ext [LEFT];
159             }
160
161           /*
162             don't want to create too much extra space for accidentals
163           */
164           if (rc->musical_b ())
165            {
166               if (to_boolean (rc->get_grob_property ("contains-grace")))
167                 right_dist *= gh_scm2double (rc->get_grob_property ("before-grace-spacing-factor")); // fixme.
168               else
169                 right_dist *= gh_scm2double (lc->get_grob_property ("before-musical-spacing-factor"));
170            }
171
172           s.distance_f_ = left_distance + right_dist;
173             
174           Real stretch_dist = 0.;
175           if (gh_number_p (stretch_hint))
176             stretch_dist += gh_scm2double (stretch_hint);
177           else
178             stretch_dist += left_distance;
179           
180           if (gh_pair_p (next_stretch_hint))
181             // see regtest spacing-tight
182             stretch_dist += - gh_scm2double (gh_car (next_stretch_hint));
183           else
184             stretch_dist += right_dist;
185
186           if (s.distance_f_ <0)
187             {
188               programming_error ("Negative dist, setting to 1.0 PT");
189               s.distance_f_ = 1.0;
190             }
191           if (stretch_dist == 0.0)
192             {
193               /*
194                 \bar "".  We give it 0 space, with high strength. 
195                */
196               s.strength_f_ = 20.0; 
197             }
198           else
199             s.strength_f_ /= stretch_dist;
200           
201           s.add_to_cols ();
202         }
203     }
204   
205 }
206
207 /**
208    Do something if breakable column has no spacing hints set.
209  */
210 Real
211 Spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
212                                       Moment shortest) 
213 {
214   Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
215   Real durational_distance = 0;
216   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
217
218   /*
219                 ugh should use shortest_playing distance
220   */
221   if (delta_t)
222     {
223       durational_distance =  get_duration_space (me, delta_t, shortest);
224     }
225
226   return  symbol_distance >? durational_distance;
227 }
228
229
230 /**
231   Get the measure wide ant for arithmetic spacing.
232
233   @see
234   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
235   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
236   The Ohio State University, 1987.
237
238   */
239 Real
240 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest) 
241 {
242   Real log =  log_2 (shortest);
243   Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
244     - log;
245   
246   return (log_2 (d) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
247 }
248
249
250 Real
251 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
252                                Moment shortest) 
253 {
254   Moment shortest_playing_len = 0;
255   SCM s = lc->get_grob_property ("shortest-playing-duration");
256
257   //  SCM s = lc->get_grob_property ("mean-playing-duration");  
258   if (unsmob_moment (s))
259     shortest_playing_len = *unsmob_moment (s);
260   
261   if (! shortest_playing_len)
262     {
263       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
264       shortest_playing_len = 1;
265     }
266   
267   if (! shortest)
268     {
269       programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
270       shortest = 1;
271     }
272   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
273   Real dist = get_duration_space (me, shortest_playing_len, shortest);
274   dist *= (double) (delta_t / shortest_playing_len);
275
276
277
278   /*
279     UGH: KLUDGE!
280   */
281   
282   if (delta_t > Moment (1,32))
283     dist += stem_dir_correction (me, lc,rc);
284
285
286   Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
287   Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
288
289   if (lm && rm)
290     {
291       if (lm->grace_mom_ && rm->grace_mom_)
292         dist *= 0.5;
293       else if (!rm->grace_mom_ && lm->grace_mom_)
294         dist *= 0.7;
295     }
296
297   
298   return dist;
299 }
300
301
302 /**
303    Correct for optical illusions. See [Wanske] p. 138. The combination
304    up-stem + down-stem should get extra space, the combination
305    down-stem + up-stem less.
306
307    This should be more advanced, since relative heights of the note
308    heads also influence required correction.
309
310    Also might not work correctly in case of multi voices or staff
311    changing voices
312
313    TODO: lookup correction distances?  More advanced correction?
314    Possibly turn this off?
315
316    TODO: have to check wether the stems are in the same staff.
317
318    This routine reads the DIR-LIST property of both its L and R arguments.  */
319 Real
320 Spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r) 
321 {
322   SCM dl = l->get_grob_property ("dir-list");
323   SCM dr = r->get_grob_property ("dir-list");
324   
325   if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
326     return 0.;
327
328   dl = gh_car (dl);
329   dr = gh_car (dr);
330
331   assert (gh_number_p (dl) && gh_number_p (dr));
332   int d1 = gh_scm2int (dl);
333   int d2 = gh_scm2int (dr);
334
335   if (d1 == d2)
336     return 0.0;
337
338
339   Real correction = 0.0;
340   Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
341
342   if (d1 && d2 && d1 * d2 == -1)
343     {
344       correction = d1 * ssc;
345     }
346   else
347     programming_error ("Stem directions not set correctly for optical correction");
348   return correction;
349 }
350   
351
352 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
353 SCM
354 Spacing_spanner::set_springs (SCM smob)
355 {
356   Grob *me = unsmob_grob (smob);
357   Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
358
359   int j = 0;
360
361   for (int i = 1; i < all.size (); i++)
362     {
363       Grob *sc = all[i];
364       if (Item::breakable_b (sc))
365         {
366           Link_array<Grob> measure (all.slice (j, i+1));          
367           do_measure (me, measure);
368           j = i;
369         }
370     }
371
372   /*
373     farewell, cruel world
374    */
375   me->suicide ();
376   return SCM_UNSPECIFIED;
377 }
378
379
380
381 /*
382   maximum-duration-for-spacing
383 From: bf250@freenet.carleton.ca (John Sankey)
384 To: gnu-music-discuss@gnu.org
385 Subject: note spacing suggestion
386 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
387
388 Currently, Lily spaces notes by starting with a basic distance,
389 arithmetic_multiplier, which it applies to the minimum duration note
390 of the bar. Then she adds a logarithmic increment, scaled from
391 arithmetic_basicspace, for longer notes. (Then, columns are aligned
392 and justified.) Fundamentally, this matches visual spacing to musical
393 weight and works well.
394
395 A lot of the time in music, I see a section basically in melodic
396 notes that occasionally has a rapid ornamental run (scale). So, there
397 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
398 a return to long notes. Currently, Lily gives the same horizontal
399 space to the 1/32nd notes in their bar (even if set in small size as
400 is commonly done for cadenzii) as she gives to 1/4 notes in bars
401 where 1/4 note is the minimum duration. The resulting visual weight
402 does not match the musical weight over the page.
403
404 Looking at the music I am typesetting, I feel that Lily's spacing
405 could be significantly improved if, with no change in the basic
406 method used, arithmetic_multiplier could be applied referred to the
407 same duration throughout a piece. Of course, the current method
408 should be retained for those who have already set music in it, so I
409 suggest a property called something like arithmetic_base=16 to fix
410 1/16 duration as the reference for arithmetic_multiplier; the default
411 would be a dynamic base is it is now.
412
413 Does anyone else feel that this would be a useful improvement for
414 their music? (Of course, if arithmetic_multiplier became a regular
415 property, this could be used to achieve a similar result by
416 tweaking.)
417   
418  */