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