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