]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.3.50
[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
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_broken_piece (RIGHT);
71       Item * rb = r->find_broken_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             programming_error("negative dist");
160           
161           if (stretch_dist == 0.0)
162             {
163               /*
164                 \bar "".  We give it 0 space, with high strength. 
165                */
166               s.strength_f_ = 20.0; 
167             }
168           else
169             s.strength_f_ /= stretch_dist;
170           
171           meas_springs.push (s);        
172         }
173     }
174
175   return meas_springs;
176 }
177
178 /**
179    Do something if breakable column has no spacing hints set.
180  */
181 Real
182 Spacing_spanner::default_bar_spacing (Paper_column *lc, Paper_column *rc,
183                                       Moment shortest) const
184 {
185   Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
186   Real durational_distance = 0;
187   Moment delta_t =  rc->when_mom () - lc->when_mom () ;
188
189   /*
190                 ugh should use shortest_playing distance
191   */
192   if (delta_t)
193     {
194       durational_distance =  get_duration_space (delta_t, shortest);
195     }
196
197   return  symbol_distance >? durational_distance;
198 }
199
200
201 /**
202   Get the measure wide constant for arithmetic spacing.
203
204   @see
205   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
206   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
207   The Ohio State University, 1987.
208
209   */
210 Real
211 Spacing_spanner::get_duration_space (Moment d, Moment shortest) const
212 {
213   Real log = log_2 (Moment (1,8) <? shortest);
214   Real k=   paper_l ()->get_var ("arithmetic_basicspace")
215     - log;
216   
217   return (log_2 (d) + k) * paper_l ()->get_var ("arithmetic_multiplier");
218 }
219
220
221 Real
222 Spacing_spanner::note_spacing (Paper_column *lc, Paper_column *rc, Moment shortest) const
223 {
224   Moment shortest_playing_len = 0;
225   SCM s = lc->get_elt_property ("shortest-playing-duration");
226   //  SCM s = lc->get_elt_property ("mean-playing-duration");  
227   if (SMOB_IS_TYPE_B(Moment, s))
228     shortest_playing_len = *SMOB_TO_TYPE (Moment, s);
229
230   
231   if (! shortest_playing_len)
232     {
233       programming_error ("can't find a ruling note at " + lc->when_mom ().str ());
234       shortest_playing_len = 1;
235     }
236   
237   if (! shortest)
238     {
239       programming_error ("no minimum in measure at " + lc->when_mom ().str ());
240       shortest = 1;
241     }
242   Moment delta_t = rc->when_mom () - lc->when_mom ();
243   Real dist = get_duration_space (shortest_playing_len, shortest);
244   dist *= (double)(delta_t / shortest_playing_len);
245
246   dist += stem_dir_correction (lc,rc);
247   return dist;
248 }
249
250
251 /**
252    Correct for optical illusions. See [Wanske] p. 138. The combination
253    up-stem + down-stem should get extra space, the combination
254    down-stem + up-stem less.
255
256    This should be more advanced, since relative heights of the note
257    heads also influence required correction.
258
259    Also might not work correctly in case of multi voices or staff
260    changing voices
261
262    TODO: lookup correction distances?  More advanced correction?
263    Possibly turn this off?
264
265    This routine reads the DIR-LIST property of both its L and R arguments.  */
266 Real
267 Spacing_spanner::stem_dir_correction (Paper_column*l, Paper_column*r) const
268 {
269   SCM dl = l->get_elt_property ("dir-list");
270   SCM dr = r->get_elt_property ("dir-list");
271   if (dl == SCM_UNDEFINED || dr == SCM_UNDEFINED)
272     return 0.0;
273
274
275   if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
276     return 0.;
277
278   dl = gh_car (dl);
279   dr = gh_car (dr);
280
281   assert (gh_number_p (dl) && gh_number_p(dr));
282   int d1 = gh_scm2int (dl);
283   int d2 = gh_scm2int (dr);
284
285   if (d1 == d2)
286     return 0.0;
287
288   bool err = false;
289   Real correction = 0.0;
290   Real ssc = paper_l ()->get_var("stemSpacingCorrection");
291
292
293   if (d1 && d2)
294     {
295       if (d1 == 1 && d2 == -1)
296         correction = ssc;
297       else if (d1 == -1 && d2 == 1)
298         correction = -ssc;
299       else
300         err = true;
301     }
302   
303   else
304     err = true;
305
306   if (err)
307     programming_error ("Stem directions not set correctly for optical correction");
308   return correction;
309 }
310   
311
312 Array<Spring>
313 Spacing_spanner::get_springs () const
314 {
315   Array<Spring> springs;
316   
317   SCM last_col = pscore_l_->line_l_->get_elt_property ("columns");
318   Link_array<Paper_column> measure;
319   for (SCM s = last_col; gh_pair_p (s); s = gh_cdr (s))
320     {
321       Score_element * elt = unsmob_element (gh_car (s));
322       Paper_column* sc = dynamic_cast<Paper_column*> (elt);
323       measure.push (sc);
324       if (sc->breakable_b ())
325         {
326           measure.reverse ();
327           springs.concat (do_measure (measure));
328           measure.clear ();
329           measure.push (sc);
330         }
331     }
332   return springs;
333 }
334
335
336
337
338