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"
19 Spacing_spanner::Spacing_spanner (SCM s
)
22 set_extent_callback (0, X_AXIS
);
23 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 (Link_array
<Paper_column
> cols
) const
44 shortest
.set_infinite (1);
47 for (int i
=0 ; i
< cols
.size (); i
++)
49 if (cols
[i
]->musical_b ())
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 ())
57 mean_shortest
+= this_shortest
;
63 Array
<Spring
> meas_springs
;
65 Real non_musical_space_strength
= paper_l ()->get_var ("breakable_column_space_strength");
66 for (int i
= 0; i
< cols
.size () - 1; i
++)
70 Item
* lb
= l
->find_prebroken_piece (RIGHT
);
71 Item
* rb
= r
->find_prebroken_piece (LEFT
);
73 Item
* combinations
[4][2]={{l
,r
}, {lb
,r
}, {l
,rb
},{lb
,rb
}};
75 for (int j
=0; j
< 4; j
++)
77 Paper_column
* lc
= dynamic_cast<Paper_column
*> (combinations
[j
][0]);
78 Paper_column
*rc
= dynamic_cast<Paper_column
*> (combinations
[j
][1]);
83 s
.item_l_drul_
[LEFT
] = lc
;
84 s
.item_l_drul_
[RIGHT
] = rc
;
86 SCM hint
= lc
->get_elt_property ("extra-space");
87 SCM next_hint
= rc
->get_elt_property ("extra-space");
88 SCM stretch_hint
= lc
->get_elt_property ("stretch-distance");
89 SCM next_stretch_hint
= rc
->get_elt_property ("stretch-distance");
94 left_distance
= gh_scm2double (gh_cdr (hint
));
96 // 2nd condition should be (i+1 < col_count()), ie. not the last column in score. FIXME
97 else if (!lc
->musical_b() && i
+1 < cols
.size ())
99 left_distance
= default_bar_spacing (lc
,rc
,shortest
);
101 else if (lc
->musical_b())
103 left_distance
= note_spacing (lc
, rc
, shortest
);
106 s
.distance_f_
= left_distance
;
109 Only do tight spaces *after* barlines (breakable columns),
112 We want the space before barline to be like the note
113 spacing in the measure.
115 if (lc
->breakable_b () || lc
->original_l_
)
116 s
.strength_f_
= non_musical_space_strength
;
117 else if (!lc
->musical_b ())
118 left_distance
*= paper_l ()->get_var ("decrease_nonmus_spacing_factor");
121 Real right_dist
= 0.0;
122 if (gh_pair_p (next_hint
))
124 right_dist
+= - gh_scm2double (gh_car (next_hint
));
128 Interval
ext (rc
->extent (X_AXIS
));
129 right_dist
= ext
.empty_b() ? 0.0 : - ext
[LEFT
];
133 don't want to create too much extra space for accidentals
135 if (lc
->musical_b () && rc
->musical_b ())
137 if (!to_boolean (rc
->get_elt_property ("contains-grace")))
138 right_dist
*= paper_l ()->get_var ("musical_to_musical_left_spacing_factor");
141 if (rc
->musical_b () && to_boolean (rc
->get_elt_property ("contains-grace")))
142 right_dist
*= paper_l ()->get_var ("before_grace_spacing_factor");
144 s
.distance_f_
= left_distance
+ right_dist
;
146 Real stretch_dist
= 0.;
147 if (gh_number_p (stretch_hint
))
148 stretch_dist
+= gh_scm2double (stretch_hint
);
150 stretch_dist
+= left_distance
;
152 if (gh_pair_p (next_stretch_hint
))
153 // see regtest spacing-tight
154 stretch_dist
+= - gh_scm2double (gh_car (next_stretch_hint
));
156 stretch_dist
+= right_dist
;
158 if (s
.distance_f_
<0)
160 programming_error("Negative dist, setting to 1.0 PT");
163 if (stretch_dist
== 0.0)
166 \bar "". We give it 0 space, with high strength.
168 s
.strength_f_
= 20.0;
171 s
.strength_f_
/= stretch_dist
;
173 meas_springs
.push (s
);
181 Do something if breakable column has no spacing hints set.
184 Spacing_spanner::default_bar_spacing (Paper_column
*lc
, Paper_column
*rc
,
185 Moment shortest
) const
187 Real symbol_distance
= lc
->extent (X_AXIS
)[RIGHT
] ;
188 Real durational_distance
= 0;
189 Moment delta_t
= rc
->when_mom () - lc
->when_mom () ;
192 ugh should use shortest_playing distance
196 durational_distance
= get_duration_space (delta_t
, shortest
);
199 return symbol_distance
>? durational_distance
;
204 Get the measure wide constant for arithmetic spacing.
207 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
208 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
209 The Ohio State University, 1987.
213 Spacing_spanner::get_duration_space (Moment d
, Moment shortest
) const
215 Real log
= log_2 (Moment (1,8) <? shortest
);
216 Real k
= paper_l ()->get_var ("arithmetic_basicspace")
219 return (log_2 (d
) + k
) * paper_l ()->get_var ("arithmetic_multiplier");
224 Spacing_spanner::note_spacing (Paper_column
*lc
, Paper_column
*rc
, Moment shortest
) const
226 Moment shortest_playing_len
= 0;
227 SCM s
= lc
->get_elt_property ("shortest-playing-duration");
228 // SCM s = lc->get_elt_property ("mean-playing-duration");
229 if (SMOB_IS_TYPE_B(Moment
, s
))
230 shortest_playing_len
= *SMOB_TO_TYPE (Moment
, s
);
233 if (! shortest_playing_len
)
235 programming_error ("can't find a ruling note at " + lc
->when_mom ().str ());
236 shortest_playing_len
= 1;
241 programming_error ("no minimum in measure at " + lc
->when_mom ().str ());
244 Moment delta_t
= rc
->when_mom () - lc
->when_mom ();
245 Real dist
= get_duration_space (shortest_playing_len
, shortest
);
246 dist
*= (double)(delta_t
/ shortest_playing_len
);
248 dist
+= stem_dir_correction (lc
,rc
);
254 Correct for optical illusions. See [Wanske] p. 138. The combination
255 up-stem + down-stem should get extra space, the combination
256 down-stem + up-stem less.
258 This should be more advanced, since relative heights of the note
259 heads also influence required correction.
261 Also might not work correctly in case of multi voices or staff
264 TODO: lookup correction distances? More advanced correction?
265 Possibly turn this off?
267 This routine reads the DIR-LIST property of both its L and R arguments. */
269 Spacing_spanner::stem_dir_correction (Paper_column
*l
, Paper_column
*r
) const
271 SCM dl
= l
->get_elt_property ("dir-list");
272 SCM dr
= r
->get_elt_property ("dir-list");
273 if (dl
== SCM_UNDEFINED
|| dr
== SCM_UNDEFINED
)
277 if (scm_ilength (dl
) != 1 && scm_ilength (dr
) != 1)
283 assert (gh_number_p (dl
) && gh_number_p(dr
));
284 int d1
= gh_scm2int (dl
);
285 int d2
= gh_scm2int (dr
);
291 Real correction
= 0.0;
292 Real ssc
= paper_l ()->get_var("stemSpacingCorrection");
297 if (d1
== 1 && d2
== -1)
299 else if (d1
== -1 && d2
== 1)
309 programming_error ("Stem directions not set correctly for optical correction");
315 Spacing_spanner::get_springs () const
317 Array
<Spring
> springs
;
319 Link_array
<Paper_column
> all (pscore_l_
->line_l_
->column_l_arr ()) ;
323 for (int i
= 1; i
< all
.size (); i
++)
325 Paper_column
* sc
= dynamic_cast<Paper_column
*> (all
[i
]);
326 if (sc
->breakable_b ())
328 Link_array
<Paper_column
> measure (all
.slice (j
, i
+1));
329 springs
.concat (do_measure (measure
));
335 farewell, cruel world
337 ((Spacing_spanner
*)this)->suicide ();