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