]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.3.55
[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 Spacing_spanner::Spacing_spanner (SCM s)
20   : Spanner (s)
21 {
22   set_extent_callback (0, X_AXIS);
23   set_extent_callback (0, Y_AXIS);  
24   set_elt_property ("transparent", SCM_BOOL_T);
25 }
26
27 /*
28
29   The algorithm is partly taken from :
30
31   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
32   OSU-CISRC-10/87-TR35, Department of Computer and Information
33   Science, The Ohio State University, 1987.
34
35   TOO HAIRY.
36
37   TODO: write comments 
38   
39  */
40 Array<Spring>
41 Spacing_spanner::do_measure (Link_array<Paper_column> cols) const
42 {
43   Moment shortest;
44   Moment mean_shortest;
45   shortest.set_infinite (1);
46
47   int n = 0;
48   for (int i =0 ; i < cols.size (); i++)  
49     {
50       if (cols[i]->musical_b ())
51         {
52           SCM  st = cols[i]->get_elt_property ("shortest-starter-duration");
53           Moment this_shortest = (*SMOB_TO_TYPE(Moment, st));
54           shortest = shortest <? this_shortest;
55           if (!mean_shortest.infty_b ())
56             {
57               n++;
58               mean_shortest += this_shortest;
59             }
60         }
61     }
62   mean_shortest /= n;
63
64   Array<Spring> meas_springs;
65
66   Real non_musical_space_strength = paper_l ()->get_var ("breakable_column_space_strength");
67   for (int i= 0; i < cols.size () - 1; i++)
68     {
69       Item * l = cols[i];
70       Item * r = cols[i+1];
71       Item * lb = l->find_prebroken_piece (RIGHT);
72       Item * rb = r->find_prebroken_piece (LEFT);      
73
74       Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
75
76       for (int j=0; j < 4; j++)
77         {
78           Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
79           Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
80           if (!lc || !rc)
81             continue;
82
83           Spring s;
84           s.item_l_drul_[LEFT] = lc;
85           s.item_l_drul_[RIGHT] = rc;
86           
87           SCM hint = lc->get_elt_property ("extra-space");
88           SCM next_hint = rc->get_elt_property ("extra-space");
89           SCM stretch_hint = lc->get_elt_property ("stretch-distance");
90           SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");      
91
92           Real left_distance;
93           if (gh_pair_p (hint))
94             {
95               left_distance = gh_scm2double (gh_cdr (hint)); 
96             }
97            // 2nd condition should be (i+1 < col_count()), ie. not the last column in score.  FIXME
98           else if (!lc->musical_b() && i+1 < cols.size ()) 
99             {
100               left_distance= default_bar_spacing (lc,rc,shortest);
101             }
102           else if (lc->musical_b())
103             {
104               left_distance  = note_spacing (lc, rc, shortest);
105             }
106
107           s.distance_f_ = left_distance;
108
109           /*
110             Only do tight spaces *after* barlines (breakable columns),
111             not before.
112
113             We want the space before barline to be like the note
114             spacing in the measure.
115           */
116           if (lc->breakable_b () || lc->original_l_)
117             s.strength_f_ = non_musical_space_strength;
118           else if (!lc->musical_b ())
119             left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
120
121           
122           Real right_dist = 0.0;
123           if (gh_pair_p (next_hint))
124             {
125               right_dist += - gh_scm2double (gh_car (next_hint));
126             }
127           else
128             {
129               Interval ext (rc->extent (X_AXIS));
130               right_dist =  ext.empty_b() ? 0.0 : - ext [LEFT];
131             }
132
133           /*
134             don't want to create too much extra space for accidentals
135           */
136           if (lc->musical_b () && rc->musical_b ())
137             {
138               if (!to_boolean (rc->get_elt_property ("contains-grace")))
139                 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
140             }
141
142           if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
143             right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
144  
145           s.distance_f_ = left_distance + right_dist;
146             
147           Real stretch_dist = 0.;
148           if (gh_number_p (stretch_hint))
149             stretch_dist += gh_scm2double (stretch_hint);
150           else
151             stretch_dist += left_distance;
152           
153           if (gh_pair_p (next_stretch_hint))
154             // see regtest spacing-tight
155             stretch_dist += - gh_scm2double (gh_car  (next_stretch_hint));
156           else
157             stretch_dist += right_dist;
158
159           if (s.distance_f_ <0)
160             {
161               programming_error("Negative dist, setting to 1.0 PT");
162               s.distance_f_ = 1.0;
163             }
164           if (stretch_dist == 0.0)
165             {
166               /*
167                 \bar "".  We give it 0 space, with high strength. 
168                */
169               s.strength_f_ = 20.0; 
170             }
171           else
172             s.strength_f_ /= stretch_dist;
173           
174           meas_springs.push (s);        
175         }
176     }
177
178   return meas_springs;
179 }
180
181 /**
182    Do something if breakable column has no spacing hints set.
183  */
184 Real
185 Spacing_spanner::default_bar_spacing (Paper_column *lc, Paper_column *rc,
186                                       Moment shortest) const
187 {
188   Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
189   Real durational_distance = 0;
190   Moment delta_t =  rc->when_mom () - lc->when_mom () ;
191
192   /*
193                 ugh should use shortest_playing distance
194   */
195   if (delta_t)
196     {
197       durational_distance =  get_duration_space (delta_t, shortest);
198     }
199
200   return  symbol_distance >? durational_distance;
201 }
202
203
204 /**
205   Get the measure wide constant for arithmetic spacing.
206
207   @see
208   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
209   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
210   The Ohio State University, 1987.
211
212   */
213 Real
214 Spacing_spanner::get_duration_space (Moment d, Moment shortest) const
215 {
216   Real log = log_2 (Moment (1,8) <? shortest);
217   Real k=   paper_l ()->get_var ("arithmetic_basicspace")
218     - log;
219   
220   return (log_2 (d) + k) * paper_l ()->get_var ("arithmetic_multiplier");
221 }
222
223
224 Real
225 Spacing_spanner::note_spacing (Paper_column *lc, Paper_column *rc, Moment shortest) const
226 {
227   Moment shortest_playing_len = 0;
228   SCM s = lc->get_elt_property ("shortest-playing-duration");
229   //  SCM s = lc->get_elt_property ("mean-playing-duration");  
230   if (SMOB_IS_TYPE_B(Moment, s))
231     shortest_playing_len = *SMOB_TO_TYPE (Moment, s);
232
233   
234   if (! shortest_playing_len)
235     {
236       programming_error ("can't find a ruling note at " + lc->when_mom ().str ());
237       shortest_playing_len = 1;
238     }
239   
240   if (! shortest)
241     {
242       programming_error ("no minimum in measure at " + lc->when_mom ().str ());
243       shortest = 1;
244     }
245   Moment delta_t = rc->when_mom () - lc->when_mom ();
246   Real dist = get_duration_space (shortest_playing_len, shortest);
247   dist *= (double)(delta_t / shortest_playing_len);
248
249   dist += stem_dir_correction (lc,rc);
250   return dist;
251 }
252
253
254 /**
255    Correct for optical illusions. See [Wanske] p. 138. The combination
256    up-stem + down-stem should get extra space, the combination
257    down-stem + up-stem less.
258
259    This should be more advanced, since relative heights of the note
260    heads also influence required correction.
261
262    Also might not work correctly in case of multi voices or staff
263    changing voices
264
265    TODO: lookup correction distances?  More advanced correction?
266    Possibly turn this off?
267
268    This routine reads the DIR-LIST property of both its L and R arguments.  */
269 Real
270 Spacing_spanner::stem_dir_correction (Paper_column*l, Paper_column*r) const
271 {
272   SCM dl = l->get_elt_property ("dir-list");
273   SCM dr = r->get_elt_property ("dir-list");
274   if (dl == SCM_UNDEFINED || dr == SCM_UNDEFINED)
275     return 0.0;
276
277
278   if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
279     return 0.;
280
281   dl = gh_car (dl);
282   dr = gh_car (dr);
283
284   assert (gh_number_p (dl) && gh_number_p(dr));
285   int d1 = gh_scm2int (dl);
286   int d2 = gh_scm2int (dr);
287
288   if (d1 == d2)
289     return 0.0;
290
291   bool err = false;
292   Real correction = 0.0;
293   Real ssc = paper_l ()->get_var("stemSpacingCorrection");
294
295
296   if (d1 && d2)
297     {
298       if (d1 == 1 && d2 == -1)
299         correction = ssc;
300       else if (d1 == -1 && d2 == 1)
301         correction = -ssc;
302       else
303         err = true;
304     }
305   
306   else
307     err = true;
308
309   if (err)
310     programming_error ("Stem directions not set correctly for optical correction");
311   return correction;
312 }
313   
314
315 Array<Spring>
316 Spacing_spanner::get_springs () const
317 {
318   Array<Spring> springs;
319
320   Link_array<Paper_column> all (pscore_l_->line_l_->column_l_arr ()) ;
321
322   int j = 0;
323
324   for (int i = 1; i < all.size (); i++)
325     {
326       Paper_column* sc = dynamic_cast<Paper_column*> (all[i]);
327       if (sc->breakable_b ())
328         {
329           Link_array<Paper_column> measure (all.slice (j, i+1));          
330           springs.concat (do_measure (measure));
331           j = i;
332         }
333     }
334
335   /*
336     farewell, cruel world
337    */
338   ((Spacing_spanner*)this)->suicide ();
339   
340   return springs;
341 }
342
343
344
345
346