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