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