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