]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
64ebe46a6b9b52b76cedb9eb4f8ed46c3f32c473
[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       warning (_f ("can't find a ruling note at %s", 
219                    lc->when_mom ().str ()));
220       shortest_playing_len = 1;
221     }
222   if (! shortest)
223     {
224       warning (_f ("no minimum in measure at %s", 
225                    lc->when_mom ().str ()));
226       shortest = 1;
227     }
228   Moment delta_t = rc->when_mom () - lc->when_mom ();
229   Real k=  paper_l()->arithmetic_constant(shortest);
230   Real dist = paper_l()->length_mom_to_dist (shortest_playing_len, k);
231   dist *= (double)(delta_t / shortest_playing_len);
232
233   dist += stem_dir_correction (lc,rc);
234   return dist;
235 }
236
237
238 /**
239    Correct for optical illusions. See [Wanske] p. 138. The combination
240    up-stem + down-stem should get extra space, the combination
241    down-stem + up-stem less.
242
243    This should be more advanced, since relative heights of the note
244    heads also influence required correction.
245
246    Also might not work correctly ico. multi voices or staff changing voices
247
248    TODO: lookup correction distances?  More advanced correction?
249    Possibly turn this off?
250
251    This routine reads the DIR_LIST property of both its L and R arguments.
252 */
253 Real
254 Spacing_spanner::stem_dir_correction (Score_column*l, Score_column*r) const
255 {
256   SCM dl = l->get_elt_property (dir_list_scm_sym);
257   SCM dr = r->get_elt_property (dir_list_scm_sym);
258   if (dl == SCM_BOOL_F || dr == SCM_BOOL_F)
259     return 0.0;
260
261   dl = SCM_CDR (dl);
262   dr = SCM_CDR (dr);
263
264   if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
265     return 0.;
266
267   dl = SCM_CAR(dl);
268   dr = SCM_CAR(dr);
269
270   assert (gh_number_p (dl) && gh_number_p(dr));
271   int d1 = gh_scm2int (dl);
272   int d2 = gh_scm2int (dr);
273
274   if (d1 == d2)
275     return 0.0;
276
277   bool err = false;
278   Real correction = 0.0;
279   Real ssc = paper_l ()->get_realvar(ly_symbol ("stemSpacingCorrection"));
280
281
282   if (d1 && d2)
283     {
284       if (d1 == 1 && d2 == -1)
285         correction = ssc;
286       else if (d1 == -1 && d2 == 1)
287         correction = -ssc;
288       else
289         err = true;
290     }
291   
292   else
293     err = true;
294
295   if (err)
296     programming_error ("Stem directions not set correctly for optical correction");
297   return correction;
298 }
299   
300
301 Array<Spring>
302 Spacing_spanner::get_springs () const
303 {
304   Array<Spring> springs;
305   int last_break =0;
306   for (int i=1; i < col_count (); i++)
307     {
308       if (scol (i)->breakable_b ())
309         {
310           springs.concat (do_measure (last_break, i));
311           last_break  = i;
312         }
313     }
314   return springs;
315 }
316
317
318
319