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