]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.3.99
[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 (Score_element*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 (Score_element*me, Link_array<Score_element> 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_elt_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_elt_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_elt_property ("extra-space");
102           SCM next_hint = rc->get_elt_property ("extra-space");
103           SCM stretch_hint = lc->get_elt_property ("stretch-distance");
104           SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");      
105
106           Real left_distance;
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
121           s.distance_f_ = left_distance;
122
123           /*
124             Only do tight spaces *after* barlines (breakable columns),
125             not before.
126
127             We want the space before barline to be like the note
128             spacing in the measure.
129           */
130           SCM sfac =lc->get_elt_property ("space-factor");
131           if (Item::breakable_b (lc) || lc->original_l_)
132             {
133               s.strength_f_ =
134                 gh_scm2double (lc->get_elt_property ("column-space-strength"));
135             }
136           else if (gh_number_p (sfac))
137             left_distance *= gh_scm2double (sfac);
138
139           
140           Real right_dist = 0.0;
141           if (gh_pair_p (next_hint))
142             {
143               right_dist += - gh_scm2double (gh_car (next_hint));
144             }
145           else
146             {
147               Interval ext (rc->extent (rc, X_AXIS));
148               right_dist =  ext.empty_b() ? 0.0 : - ext [LEFT];
149             }
150
151           /*
152             don't want to create too much extra space for accidentals
153           */
154           if (rc->musical_b ())
155            {
156               if (to_boolean (rc->get_elt_property ("contains-grace")))
157                 right_dist *= gh_scm2double (rc->get_elt_property ("before-grace-spacing-factor")); // fixme.
158               else
159                 right_dist *= gh_scm2double (lc->get_elt_property ("before-musical-spacing-factor"));
160            }
161
162           s.distance_f_ = left_distance + right_dist;
163             
164           Real stretch_dist = 0.;
165           if (gh_number_p (stretch_hint))
166             stretch_dist += gh_scm2double (stretch_hint);
167           else
168             stretch_dist += left_distance;
169           
170           if (gh_pair_p (next_stretch_hint))
171             // see regtest spacing-tight
172             stretch_dist += - gh_scm2double (gh_car  (next_stretch_hint));
173           else
174             stretch_dist += right_dist;
175
176           if (s.distance_f_ <0)
177             {
178               programming_error("Negative dist, setting to 1.0 PT");
179               s.distance_f_ = 1.0;
180             }
181           if (stretch_dist == 0.0)
182             {
183               /*
184                 \bar "".  We give it 0 space, with high strength. 
185                */
186               s.strength_f_ = 20.0; 
187             }
188           else
189             s.strength_f_ /= stretch_dist;
190           
191           s.add_to_cols ();
192         }
193     }
194   
195 }
196
197 /**
198    Do something if breakable column has no spacing hints set.
199  */
200 Real
201 Spacing_spanner::default_bar_spacing (Score_element*me, Score_element *lc, Score_element *rc,
202                                       Moment shortest) 
203 {
204   Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
205   Real durational_distance = 0;
206   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
207
208   /*
209                 ugh should use shortest_playing distance
210   */
211   if (delta_t)
212     {
213       durational_distance =  get_duration_space (me, delta_t, shortest);
214     }
215
216   return  symbol_distance >? durational_distance;
217 }
218
219
220 /**
221   Get the measure wide ant for arithmetic spacing.
222
223   @see
224   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
225   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
226   The Ohio State University, 1987.
227
228   */
229 Real
230 Spacing_spanner::get_duration_space (Score_element*me, Moment d, Moment shortest) 
231 {
232   Real log =  log_2 (shortest);
233   Real k = gh_scm2double (me->get_elt_property  ("arithmetic-basicspace"))
234     - log;
235   
236   return (log_2 (d) + k) * gh_scm2double (me->get_elt_property ("arithmetic-multiplier")) * me->paper_l ()->get_var ("staffspace");
237 }
238
239
240 Real
241 Spacing_spanner::note_spacing (Score_element*me, Score_element *lc, Score_element *rc,
242                                Moment shortest) 
243 {
244   Moment shortest_playing_len = 0;
245   SCM s = lc->get_elt_property ("shortest-playing-duration");
246
247   //  SCM s = lc->get_elt_property ("mean-playing-duration");  
248   if (unsmob_moment (s))
249     shortest_playing_len = *unsmob_moment(s);
250   
251   if (! shortest_playing_len)
252     {
253       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
254       shortest_playing_len = 1;
255     }
256   
257   if (! shortest)
258     {
259       programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
260       shortest = 1;
261     }
262   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
263   Real dist = get_duration_space (me, shortest_playing_len, shortest);
264   dist *= (double)(delta_t / shortest_playing_len);
265
266   /*
267     UGH: KLUDGE!
268   */
269   
270   if (delta_t > Moment (1,32))
271     dist += stem_dir_correction (me, lc,rc);
272   return dist;
273 }
274
275
276 /**
277    Correct for optical illusions. See [Wanske] p. 138. The combination
278    up-stem + down-stem should get extra space, the combination
279    down-stem + up-stem less.
280
281    This should be more advanced, since relative heights of the note
282    heads also influence required correction.
283
284    Also might not work correctly in case of multi voices or staff
285    changing voices
286
287    TODO: lookup correction distances?  More advanced correction?
288    Possibly turn this off?
289
290    TODO: have to check wether the stems are in the same staff.
291
292    This routine reads the DIR-LIST property of both its L and R arguments.  */
293 Real
294 Spacing_spanner::stem_dir_correction (Score_element*me, Score_element*l, Score_element*r) 
295 {
296   SCM dl = l->get_elt_property ("dir-list");
297   SCM dr = r->get_elt_property ("dir-list");
298   
299   if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
300     return 0.;
301
302   dl = gh_car (dl);
303   dr = gh_car (dr);
304
305   assert (gh_number_p (dl) && gh_number_p(dr));
306   int d1 = gh_scm2int (dl);
307   int d2 = gh_scm2int (dr);
308
309   if (d1 == d2)
310     return 0.0;
311
312
313   Real correction = 0.0;
314   Real ssc = gh_scm2double (me->get_elt_property("stem-spacing-correction"));
315
316   ssc *= me->paper_l ()->get_var ("staffspace");
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   Score_element *me = unsmob_element (smob);
333   Link_array<Score_element> 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       Score_element *sc = all[i];
340       if (Item::breakable_b (sc))
341         {
342           Link_array<Score_element> 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  */