]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.3.71
[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--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9
10 #include "spacing-spanner.hh"
11 #include "paper-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 #include "misc.hh"
18
19 void
20 Spacing_spanner::set_interface (Score_element*me)
21 {
22   me->set_extent_callback (0, X_AXIS);
23   me->set_extent_callback (0, Y_AXIS);  
24 }
25
26 /*
27
28   The algorithm is partly taken from :
29
30   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
31   OSU-CISRC-10/87-TR35, Department of Computer and Information
32   Science, The Ohio State University, 1987.
33
34   TOO HAIRY.
35
36   TODO: write comments 
37   
38  */
39 void
40 Spacing_spanner::do_measure (Score_element*me, Link_array<Score_element> cols) 
41 {
42   Moment shortest;
43   Moment mean_shortest;
44   shortest.set_infinite (1);
45
46   int n = 0;
47   for (int i =0 ; i < cols.size (); i++)  
48     {
49       if (dynamic_cast<Paper_column*> (cols[i])->musical_b ())
50         {
51           SCM  st = cols[i]->get_elt_property ("shortest-starter-duration");
52           Moment this_shortest = (*SMOB_TO_TYPE(Moment, st));
53           shortest = shortest <? this_shortest;
54           if (!mean_shortest.infty_b ())
55             {
56               n++;
57               mean_shortest += this_shortest;
58             }
59         }
60     }
61   mean_shortest /= n;
62
63   Real non_musical_space_strength = me->paper_l ()->get_var ("breakable_column_space_strength");
64   for (int i= 0; i < cols.size () - 1; i++)
65     {
66       Item * l = dynamic_cast<Item*> (cols[i]);
67       Item * r =  dynamic_cast<Item*> (cols[i+1]);
68       Item * lb = dynamic_cast<Item*> ( l->find_prebroken_piece (RIGHT));
69       Item * rb = dynamic_cast<Item*> ( r->find_prebroken_piece (LEFT));
70
71       Item* combinations[4][2]={{l,r}, {lb,r}, {l,rb},{lb,rb}};
72
73       for (int j=0; j < 4; j++)
74         {
75           Paper_column * lc = dynamic_cast<Paper_column*> (combinations[j][0]);
76           Paper_column *rc = dynamic_cast<Paper_column*> (combinations[j][1]);
77           if (!lc || !rc)
78             continue;
79
80           Spring s;
81           s.item_l_drul_[LEFT] = lc;
82           s.item_l_drul_[RIGHT] = rc;
83           
84           SCM hint = lc->get_elt_property ("extra-space");
85           SCM next_hint = rc->get_elt_property ("extra-space");
86           SCM stretch_hint = lc->get_elt_property ("stretch-distance");
87           SCM next_stretch_hint = rc->get_elt_property ("stretch-distance");      
88
89           Real left_distance;
90           if (gh_pair_p (hint))
91             {
92               left_distance = gh_scm2double (gh_cdr (hint)); 
93             }
94            // 2nd condition should be (i+1 < col_count()), ie. not the last column in score.  FIXME
95           else if (!lc->musical_b() && i+1 < cols.size ()) 
96             {
97               left_distance= default_bar_spacing (me,lc,rc,shortest);
98             }
99           else if (lc->musical_b())
100             {
101               left_distance  = note_spacing (me,lc, rc, shortest);
102             }
103
104           s.distance_f_ = left_distance;
105
106           /*
107             Only do tight spaces *after* barlines (breakable columns),
108             not before.
109
110             We want the space before barline to be like the note
111             spacing in the measure.
112           */
113           if (Item::breakable_b (lc) || lc->original_l_)
114             s.strength_f_ = non_musical_space_strength;
115           else if (!lc->musical_b ())
116             left_distance *= me->paper_l ()->get_var ("decrease_nonmus_spacing_factor");
117
118           
119           Real right_dist = 0.0;
120           if (gh_pair_p (next_hint))
121             {
122               right_dist += - gh_scm2double (gh_car (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             {
135               if (!to_boolean (rc->get_elt_property ("contains-grace")))
136                 right_dist *= me->paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
137             }
138
139           if (rc->musical_b () && to_boolean (rc->get_elt_property ("contains-grace")))
140             right_dist *= me->paper_l ()->get_var ("before_grace_spacing_factor");
141  
142           s.distance_f_ = left_distance + right_dist;
143             
144           Real stretch_dist = 0.;
145           if (gh_number_p (stretch_hint))
146             stretch_dist += gh_scm2double (stretch_hint);
147           else
148             stretch_dist += left_distance;
149           
150           if (gh_pair_p (next_stretch_hint))
151             // see regtest spacing-tight
152             stretch_dist += - gh_scm2double (gh_car  (next_stretch_hint));
153           else
154             stretch_dist += right_dist;
155
156           if (s.distance_f_ <0)
157             {
158               programming_error("Negative dist, setting to 1.0 PT");
159               s.distance_f_ = 1.0;
160             }
161           if (stretch_dist == 0.0)
162             {
163               /*
164                 \bar "".  We give it 0 space, with high strength. 
165                */
166               s.strength_f_ = 20.0; 
167             }
168           else
169             s.strength_f_ /= stretch_dist;
170           
171           s.add_to_cols ();
172         }
173     }
174   
175 }
176
177 /**
178    Do something if breakable column has no spacing hints set.
179  */
180 Real
181 Spacing_spanner::default_bar_spacing (Score_element*me, Score_element *lc, Score_element *rc,
182                                       Moment shortest) 
183 {
184   Real symbol_distance = lc->extent (X_AXIS)[RIGHT] ;
185   Real durational_distance = 0;
186   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
187
188   /*
189                 ugh should use shortest_playing distance
190   */
191   if (delta_t)
192     {
193       durational_distance =  get_duration_space (me, delta_t, shortest);
194     }
195
196   return  symbol_distance >? durational_distance;
197 }
198
199
200 /**
201   Get the measure wide ant for arithmetic spacing.
202
203   @see
204   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
205   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
206   The Ohio State University, 1987.
207
208   */
209 Real
210 Spacing_spanner::get_duration_space (Score_element*me, Moment d, Moment shortest) 
211 {
212   Real log = log_2 (Moment (1,8) <? shortest);
213   Real k=   me->paper_l ()->get_var ("arithmetic_basicspace")
214     - log;
215   
216   return (log_2 (d) + k) * me->paper_l ()->get_var ("arithmetic_multiplier");
217 }
218
219
220 Real
221 Spacing_spanner::note_spacing (Score_element*me, Score_element *lc, Score_element *rc, Moment shortest) 
222 {
223   Moment shortest_playing_len = 0;
224   SCM s = lc->get_elt_property ("shortest-playing-duration");
225
226   //  SCM s = lc->get_elt_property ("mean-playing-duration");  
227   if (unsmob_moment (s))
228     shortest_playing_len = *unsmob_moment(s);
229   
230   if (! shortest_playing_len)
231     {
232       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
233       shortest_playing_len = 1;
234     }
235   
236   if (! shortest)
237     {
238       programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
239       shortest = 1;
240     }
241   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
242   Real dist = get_duration_space (me, shortest_playing_len, shortest);
243   dist *= (double)(delta_t / shortest_playing_len);
244
245   /*
246     UGH: KLUDGE!
247   */
248   
249   if (delta_t > Moment (1,32))
250     dist += stem_dir_correction (me, lc,rc);
251   return dist;
252 }
253
254
255 /**
256    Correct for optical illusions. See [Wanske] p. 138. The combination
257    up-stem + down-stem should get extra space, the combination
258    down-stem + up-stem less.
259
260    This should be more advanced, since relative heights of the note
261    heads also influence required correction.
262
263    Also might not work correctly in case of multi voices or staff
264    changing voices
265
266    TODO: lookup correction distances?  More advanced correction?
267    Possibly turn this off?
268
269    TODO: have to check wether the stems are in the same staff.
270
271    This routine reads the DIR-LIST property of both its L and R arguments.  */
272 Real
273 Spacing_spanner::stem_dir_correction (Score_element*me, Score_element*l, Score_element*r) 
274 {
275   SCM dl = l->get_elt_property ("dir-list");
276   SCM dr = r->get_elt_property ("dir-list");
277   
278   if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
279     return 0.;
280
281   dl = gh_car (dl);
282   dr = gh_car (dr);
283
284   assert (gh_number_p (dl) && gh_number_p(dr));
285   int d1 = gh_scm2int (dl);
286   int d2 = gh_scm2int (dr);
287
288   if (d1 == d2)
289     return 0.0;
290
291
292   Real correction = 0.0;
293   Real ssc = me->paper_l ()->get_var("stemSpacingCorrection");
294
295
296   if (d1 && d2 && d1 * d2 == -1)
297     {
298       correction = d1 * ssc;
299     }
300   else
301     programming_error ("Stem directions not set correctly for optical correction");
302   return correction;
303 }
304   
305
306 MAKE_SCHEME_CALLBACK(Spacing_spanner, set_springs);
307 SCM
308 Spacing_spanner::set_springs (SCM smob)
309 {
310   Score_element *me = unsmob_element (smob);
311   Link_array<Score_element> all (me->pscore_l_->line_l_->column_l_arr ()) ;
312
313   int j = 0;
314
315   for (int i = 1; i < all.size (); i++)
316     {
317       Score_element *sc = all[i];
318       if (Item::breakable_b (sc))
319         {
320           Link_array<Score_element> measure (all.slice (j, i+1));         
321           do_measure (me, measure);
322           j = i;
323         }
324     }
325
326   /*
327     farewell, cruel world
328    */
329   me->suicide ();
330   return SCM_UNDEFINED;
331 }
332
333
334
335
336