]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.3.24
[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 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_empty (X_AXIS);
22   set_empty (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  
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             programming_error("negative dist");
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 ico. multi voices or staff 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 */
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   SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
321   Link_array<Paper_column> measure;
322   for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
323     {
324       Score_element * elt = unsmob_element (gh_car (s));
325       Paper_column* sc = dynamic_cast<Paper_column*> (elt);
326       measure.push (sc);
327       if (sc->breakable_b ())
328         {
329           measure.reverse ();
330           springs.concat (do_measure (measure));
331           measure.clear ();
332           measure.push (sc);
333         }
334     }
335   return springs;
336 }
337
338
339
340
341