]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.3.49
[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 ()
20 {
21   set_extent_callback (0, X_AXIS);
22   set_extent_callback (0, Y_AXIS);  
23   set_elt_property ("transparent", SCM_BOOL_T);
24 }
25
26 /*
27   cut 'n paste from spring-spacer.cc
28
29   generate springs between columns.
30
31
32   The algorithm is partly taken from :
33
34   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
35   OSU-CISRC-10/87-TR35, Department of Computer and Information
36   Science, The Ohio State University, 1987.
37
38   TOO HAIRY.
39   
40  */
41 Array<Spring>
42 Spacing_spanner::do_measure (Link_array<Paper_column> cols) const
43 {
44   Moment shortest;
45   Moment mean_shortest;
46   shortest.set_infinite (1);
47
48   int n = 0;
49   for (int i =0 ; i < cols.size (); i++)  
50     {
51       if (cols[i]->musical_b ())
52         {
53           SCM  st = cols[i]->get_elt_property ("shortest-starter-duration");
54           Moment this_shortest = (*SMOB_TO_TYPE(Moment, st));
55           shortest = shortest <? this_shortest;
56           if (!mean_shortest.infty_b ())
57             {
58               n++;
59               mean_shortest += this_shortest;
60             }
61         }
62     }
63   mean_shortest /= n;
64
65   Array<Spring> meas_springs;
66
67   Real non_musical_space_strength = paper_l ()->get_var ("breakable_column_space_strength");
68   for (int i= 0; i < cols.size () - 1; i++)
69     {
70       Item * l = cols[i];
71       Item * r = cols[i+1];
72       Item * lb = l->find_broken_piece (RIGHT);
73       Item * rb = r->find_broken_piece (LEFT);      
74
75       Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
76
77       for (int j=0; j < 4; j++)
78         {
79           Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
80           Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
81           if (!lc || !rc)
82             continue;
83
84           Spring s;
85           s.item_l_drul_[LEFT] = lc;
86           s.item_l_drul_[RIGHT] = rc;
87           
88           SCM hint = lc->get_elt_property ("extra-space");
89           SCM next_hint = rc->get_elt_property ("extra-space");
90           SCM stretch_hint = lc->get_elt_property ("stretch-distance");
91           SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");      
92
93           Real left_distance;
94           if (gh_pair_p (hint))
95             {
96               left_distance = gh_scm2double (gh_cdr (hint)); 
97             }
98            // 2nd condition should be (i+1 < col_count()), ie. not the last column in score.  FIXME
99           else if (!lc->musical_b() && i+1 < cols.size ()) 
100             {
101               left_distance= default_bar_spacing (lc,rc,shortest);
102             }
103           else if (lc->musical_b())
104             {
105               left_distance  = note_spacing (lc, rc, shortest);
106             }
107
108           s.distance_f_ = left_distance;
109
110           /*
111             Only do tight spaces *after* barlines (breakable columns),
112             not before.
113
114             We want the space before barline to be like the note
115             spacing in the measure.
116           */
117           if (lc->breakable_b () || lc->original_l_)
118             s.strength_f_ = non_musical_space_strength;
119           else if (!lc->musical_b ())
120             left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
121
122           
123           Real right_dist = 0.0;
124           if (gh_pair_p (next_hint))
125             {
126               right_dist += - gh_scm2double (gh_car (next_hint));
127             }
128           else
129             {
130               Interval ext (rc->extent (X_AXIS));
131               right_dist =  ext.empty_b() ? 0.0 : - ext [LEFT];
132             }
133
134           /*
135             don't want to create too much extra space for accidentals
136           */
137           if (lc->musical_b () && rc->musical_b ())
138             {
139               if (!to_boolean (rc->get_elt_property ("contains-grace")))
140                 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
141             }
142
143           if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
144             right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
145  
146           s.distance_f_ = left_distance + right_dist;
147             
148           Real stretch_dist = 0.;
149           if (gh_number_p (stretch_hint))
150             stretch_dist += gh_scm2double (stretch_hint);
151           else
152             stretch_dist += left_distance;
153           
154           if (gh_pair_p (next_stretch_hint))
155             // see regtest spacing-tight
156             stretch_dist += - gh_scm2double (gh_car  (next_stretch_hint));
157           else
158             stretch_dist += right_dist;
159
160           if (s.distance_f_ <0)
161             programming_error("negative dist");
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   SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
320   Link_array<Paper_column> measure;
321   for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
322     {
323       Score_element * elt = unsmob_element (gh_car (s));
324       Paper_column* sc = dynamic_cast<Paper_column*> (elt);
325       measure.push (sc);
326       if (sc->breakable_b ())
327         {
328           measure.reverse ();
329           springs.concat (do_measure (measure));
330           measure.clear ();
331           measure.push (sc);
332         }
333     }
334   return springs;
335 }
336
337
338
339
340