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