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