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