]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
967783c294df4cd3fe93a2c71d7ee567a024f63a
[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_sym, 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_scm_sym);
93           SCM next_hint = rc->get_elt_property (extra_space_scm_sym);
94           SCM stretch_hint = lc->get_elt_property (stretch_distance_scm_sym);
95           SCM next_stretch_hint = rc->get_elt_property (stretch_distance_scm_sym);        
96
97           Real left_distance;
98           if (hint != SCM_BOOL_F)
99             {
100               hint = SCM_CDDR (hint);
101               
102               left_distance = gh_scm2double (hint); 
103             }
104           else if (!lc->musical_b() && i+1 < col_count())
105             {
106               left_distance= default_bar_spacing (lc,rc,shortest);
107             }
108           else if (lc->musical_b())
109             {
110               left_distance  = note_spacing (lc, rc, shortest);
111             }
112
113           s.distance_f_ = left_distance;
114
115           /*
116             Only do tight spaces *after* barlines (breakable columns),
117             not before.
118
119             We want the space before barline to be like the note
120             spacing in the measure.
121           */
122           if (lc->breakable_b () || lc->original_l_)
123             s.strength_f_ = non_musical_space_strength;
124           else if (!lc->musical_b ())
125             left_distance *= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
126
127           
128           Real right_dist = 0.0;
129           if (next_hint != SCM_BOOL_F)
130             {
131               next_hint = SCM_CADR(next_hint);
132               right_dist += - gh_scm2double (next_hint);
133             }
134           else
135             {
136               Interval ext (rc->extent (X_AXIS));
137               right_dist =  ext.empty_b() ? 0.0 : - ext [LEFT];
138             }
139
140           /*
141             don't want to create too much extra space for accidentals
142           */
143           if (lc->musical_b () && rc->musical_b ())
144             {
145               if (rc->get_elt_property (contains_grace_scm_sym) == SCM_BOOL_F)
146                 right_dist *= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
147             }
148
149           if (rc->musical_b () && rc->get_elt_property (contains_grace_scm_sym) != SCM_BOOL_F)
150             right_dist *= paper_l ()->get_var ("before_grace_spacing_factor");
151  
152  
153           s.distance_f_ = left_distance + right_dist;
154             
155           Real stretch_dist = 0.;
156           if (stretch_hint != SCM_BOOL_F)
157             stretch_dist += gh_scm2double (SCM_CDDR (stretch_hint));
158           else
159             stretch_dist += left_distance;
160           
161           if (next_stretch_hint != SCM_BOOL_F)
162             // see regtest spacing-tight
163             stretch_dist += - gh_scm2double (SCM_CADR (next_stretch_hint));
164           else
165             stretch_dist += right_dist;
166
167           if (s.distance_f_ <0)
168             programming_error("negative dist");
169           
170           if (stretch_dist == 0.0)
171             {
172               /*
173                 \bar "".  We give it 0 space, with high strength. 
174                */
175               s.strength_f_ = 20.0; 
176             }
177           else
178             s.strength_f_ /= stretch_dist;
179           
180           meas_springs.push (s);        
181         }
182     }
183
184   return meas_springs;
185 }
186
187 /**
188    Do something if breakable column has no spacing hints set.
189  */
190 Real
191 Spacing_spanner::default_bar_spacing (Score_column *lc, Score_column *rc,
192                                       Moment shortest) const
193 {
194   Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
195   Real durational_distance = 0;
196   Moment delta_t =  rc->when_mom () - lc->when_mom () ;
197
198   /*
199                 ugh should use shortest_playing distance
200   */
201   if (delta_t)
202     {
203       Real k=  paper_l()->arithmetic_constant (shortest);
204       durational_distance =  paper_l()->length_mom_to_dist (delta_t,k);
205     }
206
207   return  symbol_distance >? durational_distance;
208 }
209
210
211 Real
212 Spacing_spanner::note_spacing (Score_column *lc, Score_column *rc, Moment shortest) const
213 {
214   Moment shortest_playing_len = lc->shortest_playing_mom_;
215   if (! shortest_playing_len)
216     {
217       programming_error ("Can't find a ruling note at " + lc->when_mom ().str ());
218       shortest_playing_len = 1;
219     }
220   
221   if (! shortest)
222     {
223       programming_error ("no minimum in measure at " + lc->when_mom ().str ());
224       shortest = 1;
225     }
226   Moment delta_t = rc->when_mom () - lc->when_mom ();
227   Real k=  paper_l()->arithmetic_constant(shortest);
228   Real dist = paper_l()->length_mom_to_dist (shortest_playing_len, k);
229   dist *= (double)(delta_t / shortest_playing_len);
230
231   dist += stem_dir_correction (lc,rc);
232   return dist;
233 }
234
235
236 /**
237    Correct for optical illusions. See [Wanske] p. 138. The combination
238    up-stem + down-stem should get extra space, the combination
239    down-stem + up-stem less.
240
241    This should be more advanced, since relative heights of the note
242    heads also influence required correction.
243
244    Also might not work correctly ico. multi voices or staff changing voices
245
246    TODO: lookup correction distances?  More advanced correction?
247    Possibly turn this off?
248
249    This routine reads the DIR_LIST property of both its L and R arguments.
250 */
251 Real
252 Spacing_spanner::stem_dir_correction (Score_column*l, Score_column*r) const
253 {
254   SCM dl = l->get_elt_property (dir_list_scm_sym);
255   SCM dr = r->get_elt_property (dir_list_scm_sym);
256   if (dl == SCM_BOOL_F || dr == SCM_BOOL_F)
257     return 0.0;
258
259   dl = SCM_CDR (dl);
260   dr = SCM_CDR (dr);
261
262   if (scm_ilength (dl) != 1 && scm_ilength (dr) != 1)
263     return 0.;
264
265   dl = SCM_CAR(dl);
266   dr = SCM_CAR(dr);
267
268   assert (gh_number_p (dl) && gh_number_p(dr));
269   int d1 = gh_scm2int (dl);
270   int d2 = gh_scm2int (dr);
271
272   if (d1 == d2)
273     return 0.0;
274
275   bool err = false;
276   Real correction = 0.0;
277   Real ssc = paper_l ()->get_realvar(ly_symbol ("stemSpacingCorrection"));
278
279
280   if (d1 && d2)
281     {
282       if (d1 == 1 && d2 == -1)
283         correction = ssc;
284       else if (d1 == -1 && d2 == 1)
285         correction = -ssc;
286       else
287         err = true;
288     }
289   
290   else
291     err = true;
292
293   if (err)
294     programming_error ("Stem directions not set correctly for optical correction");
295   return correction;
296 }
297   
298
299 Array<Spring>
300 Spacing_spanner::get_springs () const
301 {
302   Array<Spring> springs;
303   int last_break =0;
304   for (int i=1; i < col_count (); i++)
305     {
306       if (scol (i)->breakable_b ())
307         {
308           springs.concat (do_measure (last_break, i));
309           last_break  = i;
310         }
311     }
312   return springs;
313 }
314
315
316
317