]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.1.67
[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 (stretch_dist == 0.0)
169             {
170               /*
171                 \bar "".  We give it 0 space, with high strength. 
172                */
173               s.strength_f_ = 20.0; 
174             }
175           else
176             s.strength_f_ /= stretch_dist;
177           
178           meas_springs.push (s);        
179         }
180     }
181
182   return meas_springs;
183 }
184
185 /**
186    Do something if breakable column has no spacing hints set.
187  */
188 Real
189 Spacing_spanner::default_bar_spacing (Score_column *lc, Score_column *rc,
190                                       Moment shortest) const
191 {
192   Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
193   Real durational_distance = 0;
194   Moment delta_t =  rc->when_mom () - lc->when_mom () ;
195
196   /*
197                 ugh should use shortest_playing distance
198   */
199   if (delta_t)
200     {
201       Real k=  paper_l()->arithmetic_constant (shortest);
202       durational_distance =  paper_l()->length_mom_to_dist (delta_t,k);
203     }
204
205   return  symbol_distance >? durational_distance;
206 }
207
208
209 Real
210 Spacing_spanner::note_spacing (Score_column *lc, Score_column *rc, Moment shortest) const
211 {
212   Moment shortest_playing_len = lc->shortest_playing_mom_;
213   if (! shortest_playing_len)
214     {
215       warning (_f ("can't find a ruling note at %s", 
216                    lc->when_mom ().str ()));
217       shortest_playing_len = 1;
218     }
219   if (! shortest)
220     {
221       warning (_f ("no minimum in measure at %s", 
222                    lc->when_mom ().str ()));
223       shortest = 1;
224     }
225   Moment delta_t = rc->when_mom () - lc->when_mom ();
226   Real k=  paper_l()->arithmetic_constant(shortest);
227   Real dist = paper_l()->length_mom_to_dist (shortest_playing_len, k);
228   dist *= (double)(delta_t / shortest_playing_len);
229
230   dist += stem_dir_correction (lc,rc);
231   return dist;
232 }
233
234
235 /**
236    Correct for optical illusions. See [Wanske] p. 138. The combination
237    up-stem + down-stem should get extra space, the combination
238    down-stem + up-stem less.
239
240    This should be more advanced, since relative heights of the note
241    heads also influence required correction.
242
243    Also might not work correctly ico. multi voices or staff changing voices
244
245    TODO: lookup correction distances?  More advanced correction?
246    Possibly turn this off?
247
248    This routine reads the DIR_LIST property of both its L and R arguments.
249 */
250 Real
251 Spacing_spanner::stem_dir_correction (Score_column*l, Score_column*r) const
252 {
253   SCM dl = l->get_elt_property (dir_list_scm_sym);
254   SCM dr = r->get_elt_property (dir_list_scm_sym);
255   if (dl == SCM_BOOL_F || dr == SCM_BOOL_F)
256     return 0.0;
257
258   dl = SCM_CDR (dl);
259   dr = SCM_CDR (dr);
260
261   if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
262     return 0.;
263
264   dl = SCM_CAR(dl);
265   dr = SCM_CAR(dr);
266
267   assert (gh_number_p (dl) && gh_number_p(dr));
268   int d1 = gh_scm2int (dl);
269   int d2 = gh_scm2int (dr);
270
271   if (d1 == d2)
272     return 0.0;
273
274   bool err = false;
275   Real correction = 0.0;
276   Real ssc = paper_l ()->get_realvar(ly_symbol ("stemSpacingCorrection"));
277
278
279   if (d1 && d2)
280     {
281       if (d1 == 1 && d2 == -1)
282         correction = ssc;
283       else if (d1 == -1 && d2 == 1)
284         correction = -ssc;
285       else
286         err = true;
287     }
288   
289   else
290     err = true;
291
292   if (err)
293     programming_error ("Stem directions not set correctly for optical correction");
294   return correction;
295 }
296   
297
298 Array<Spring>
299 Spacing_spanner::get_springs () const
300 {
301   Array<Spring> springs;
302   int last_break =0;
303   for (int i=1; i < col_count (); i++)
304     {
305       if (scol (i)->breakable_b ())
306         {
307           springs.concat (do_measure (last_break, i));
308           last_break  = i;
309         }
310     }
311   return springs;
312 }
313
314
315
316