2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
10 #include "spacing-spanner.hh"
11 #include "paper-column.hh"
12 #include "dimensions.hh"
13 #include "paper-def.hh"
15 #include "paper-score.hh"
16 #include "line-of-score.hh"
20 Spacing_spanner::set_interface (Score_element
*me
)
22 me
->set_extent_callback (0, X_AXIS
);
23 me
->set_extent_callback (0, Y_AXIS
);
28 The algorithm is partly taken from :
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.
40 Spacing_spanner::do_measure (Score_element
*me
, Link_array
<Score_element
> cols
)
46 space as if this duration is present.
48 Moment base_shortest_duration
= *unsmob_moment (me
->get_elt_property ("maximum-duration-for-spacing"));
49 shortest
.set_infinite (1);
52 for (int i
=0 ; i
< cols
.size (); i
++)
54 if (dynamic_cast<Paper_column
*> (cols
[i
])->musical_b ())
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 ())
62 mean_shortest
+= this_shortest
;
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
++)
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
));
76 Item
* combinations
[4][2]={{l
,r
}, {lb
,r
}, {l
,rb
},{lb
,rb
}};
78 for (int j
=0; j
< 4; j
++)
80 Paper_column
* lc
= dynamic_cast<Paper_column
*> (combinations
[j
][0]);
81 Paper_column
*rc
= dynamic_cast<Paper_column
*> (combinations
[j
][1]);
86 s
.item_l_drul_
[LEFT
] = lc
;
87 s
.item_l_drul_
[RIGHT
] = rc
;
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");
97 left_distance
= gh_scm2double (gh_cdr (hint
));
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 ())
102 left_distance
= default_bar_spacing (me
,lc
,rc
,shortest
<? base_shortest_duration
);
104 else if (lc
->musical_b())
106 left_distance
= note_spacing (me
,lc
, rc
, shortest
<? base_shortest_duration
);
109 s
.distance_f_
= left_distance
;
112 Only do tight spaces *after* barlines (breakable columns),
115 We want the space before barline to be like the note
116 spacing in the measure.
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");
124 Real right_dist
= 0.0;
125 if (gh_pair_p (next_hint
))
127 right_dist
+= - gh_scm2double (gh_car (next_hint
));
131 Interval
ext (rc
->extent (X_AXIS
));
132 right_dist
= ext
.empty_b() ? 0.0 : - ext
[LEFT
];
136 don't want to create too much extra space for accidentals
138 if (lc
->musical_b () && rc
->musical_b ())
140 if (!to_boolean (rc
->get_elt_property ("contains-grace")))
141 right_dist
*= me
->paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
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");
147 s
.distance_f_
= left_distance
+ right_dist
;
149 Real stretch_dist
= 0.;
150 if (gh_number_p (stretch_hint
))
151 stretch_dist
+= gh_scm2double (stretch_hint
);
153 stretch_dist
+= left_distance
;
155 if (gh_pair_p (next_stretch_hint
))
156 // see regtest spacing-tight
157 stretch_dist
+= - gh_scm2double (gh_car (next_stretch_hint
));
159 stretch_dist
+= right_dist
;
161 if (s
.distance_f_
<0)
163 programming_error("Negative dist, setting to 1.0 PT");
166 if (stretch_dist
== 0.0)
169 \bar "". We give it 0 space, with high strength.
171 s
.strength_f_
= 20.0;
174 s
.strength_f_
/= stretch_dist
;
183 Do something if breakable column has no spacing hints set.
186 Spacing_spanner::default_bar_spacing (Score_element
*me
, Score_element
*lc
, Score_element
*rc
,
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
);
194 ugh should use shortest_playing distance
198 durational_distance
= get_duration_space (me
, delta_t
, shortest
);
201 return symbol_distance
>? durational_distance
;
206 Get the measure wide ant for arithmetic spacing.
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.
215 Spacing_spanner::get_duration_space (Score_element
*me
, Moment d
, Moment shortest
)
217 Real log
= log_2 (shortest
);
218 Real k
= me
->paper_l ()->get_var ("arithmetic_basicspace")
221 return (log_2 (d
) + k
) * me
->paper_l ()->get_var ("arithmetic_multiplier");
226 Spacing_spanner::note_spacing (Score_element
*me
, Score_element
*lc
, Score_element
*rc
,
229 Moment shortest_playing_len
= 0;
230 SCM s
= lc
->get_elt_property ("shortest-playing-duration");
232 // SCM s = lc->get_elt_property ("mean-playing-duration");
233 if (unsmob_moment (s
))
234 shortest_playing_len
= *unsmob_moment(s
);
236 if (! shortest_playing_len
)
238 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc
).str ());
239 shortest_playing_len
= 1;
244 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc
).str ());
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
);
255 if (delta_t
> Moment (1,32))
256 dist
+= stem_dir_correction (me
, lc
,rc
);
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.
266 This should be more advanced, since relative heights of the note
267 heads also influence required correction.
269 Also might not work correctly in case of multi voices or staff
272 TODO: lookup correction distances? More advanced correction?
273 Possibly turn this off?
275 TODO: have to check wether the stems are in the same staff.
277 This routine reads the DIR-LIST property of both its L and R arguments. */
279 Spacing_spanner::stem_dir_correction (Score_element
*me
, Score_element
*l
, Score_element
*r
)
281 SCM dl
= l
->get_elt_property ("dir-list");
282 SCM dr
= r
->get_elt_property ("dir-list");
284 if (scm_ilength (dl
) != 1 || scm_ilength (dr
) != 1)
290 assert (gh_number_p (dl
) && gh_number_p(dr
));
291 int d1
= gh_scm2int (dl
);
292 int d2
= gh_scm2int (dr
);
298 Real correction
= 0.0;
299 Real ssc
= me
->paper_l ()->get_var("stemSpacingCorrection");
302 if (d1
&& d2
&& d1
* d2
== -1)
304 correction
= d1
* ssc
;
307 programming_error ("Stem directions not set correctly for optical correction");
312 MAKE_SCHEME_CALLBACK(Spacing_spanner
, set_springs
);
314 Spacing_spanner::set_springs (SCM smob
)
316 Score_element
*me
= unsmob_element (smob
);
317 Link_array
<Score_element
> all (me
->pscore_l_
->line_l_
->column_l_arr ()) ;
321 for (int i
= 1; i
< all
.size (); i
++)
323 Score_element
*sc
= all
[i
];
324 if (Item::breakable_b (sc
))
326 Link_array
<Score_element
> measure (all
.slice (j
, i
+1));
327 do_measure (me
, measure
);
333 farewell, cruel world
336 return SCM_UNSPECIFIED
;