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