]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.3.101
[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"));
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   if (d1 && d2 && d1 * d2 == -1)
317     {
318       correction = d1 * ssc;
319     }
320   else
321     programming_error ("Stem directions not set correctly for optical correction");
322   return correction;
323 }
324   
325
326 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs,1);
327 SCM
328 Spacing_spanner::set_springs (SCM smob)
329 {
330   Score_element *me = unsmob_element (smob);
331   Link_array<Score_element> all (me->pscore_l_->line_l_->column_l_arr ()) ;
332
333   int j = 0;
334
335   for (int i = 1; i < all.size (); i++)
336     {
337       Score_element *sc = all[i];
338       if (Item::breakable_b (sc))
339         {
340           Link_array<Score_element> measure (all.slice (j, i+1));         
341           do_measure (me, measure);
342           j = i;
343         }
344     }
345
346   /*
347     farewell, cruel world
348    */
349   me->suicide ();
350   return SCM_UNSPECIFIED;
351 }
352
353
354
355 /*
356   maximum-duration-for-spacing
357 From: bf250@freenet.carleton.ca (John Sankey)
358 To: gnu-music-discuss@gnu.org
359 Subject: note spacing suggestion
360 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
361
362 Currently, Lily spaces notes by starting with a basic distance,
363 arithmetic_multiplier, which it applies to the minimum duration note
364 of the bar. Then she adds a logarithmic increment, scaled from
365 arithmetic_basicspace, for longer notes. (Then, columns are aligned
366 and justified.) Fundamentally, this matches visual spacing to musical
367 weight and works well.
368
369 A lot of the time in music, I see a section basically in melodic
370 notes that occasionally has a rapid ornamental run (scale). So, there
371 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
372 a return to long notes. Currently, Lily gives the same horizontal
373 space to the 1/32nd notes in their bar (even if set in small size as
374 is commonly done for cadenzii) as she gives to 1/4 notes in bars
375 where 1/4 note is the minimum duration. The resulting visual weight
376 does not match the musical weight over the page.
377
378 Looking at the music I am typesetting, I feel that Lily's spacing
379 could be significantly improved if, with no change in the basic
380 method used, arithmetic_multiplier could be applied referred to the
381 same duration throughout a piece. Of course, the current method
382 should be retained for those who have already set music in it, so I
383 suggest a property called something like arithmetic_base=16 to fix
384 1/16 duration as the reference for arithmetic_multiplier; the default
385 would be a dynamic base is it is now.
386
387 Does anyone else feel that this would be a useful improvement for
388 their music? (Of course, if arithmetic_multiplier became a regular
389 property, this could be used to achieve a similar result by
390 tweaking.)
391   
392  */