2 slur.cc -- implement external interface for Slur
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
11 #include "grob-info.hh"
12 #include "grob-array.hh"
15 #include "directional-element-interface.hh"
16 #include "font-interface.hh"
18 #include "pointer-group-interface.hh"
20 #include "main.hh" // DEBUG_SLUR_SCORING
21 #include "note-column.hh"
22 #include "output-def.hh"
24 #include "staff-symbol-referencer.hh"
26 #include "text-interface.hh"
29 #include "slur-scoring.hh"
30 #include "separation-item.hh"
31 #include "international.hh"
35 MAKE_SCHEME_CALLBACK (Slur, calc_direction, 1)
37 Slur::calc_direction (SCM smob)
39 Grob *me = unsmob_grob (smob);
40 extract_grob_set (me, "note-columns", encompasses);
42 if (encompasses.empty ())
49 for (vsize i = 0; i < encompasses.size (); i++)
51 if (Note_column::dir (encompasses[i]) < 0)
57 return scm_from_int (d);
60 MAKE_SCHEME_CALLBACK (Slur, pure_height, 3);
62 Slur::pure_height (SCM smob, SCM start_scm, SCM end_scm)
64 Grob *me = unsmob_grob (smob);
65 int start = scm_to_int (start_scm);
66 int end = scm_to_int (end_scm);
67 Real height = robust_scm2double (me->get_property ("height-limit"), 2.0);
69 extract_grob_set (me, "note-columns", encompasses);
72 Grob *parent = me->get_parent (Y_AXIS);
73 if (common_refpoint_of_array (encompasses, me, Y_AXIS) != parent)
74 /* this could happen if, for example, we are a cross-staff slur.
75 in this case, we want to be ignored */
76 return ly_interval2scm (Interval ());
78 for (vsize i = 0; i < encompasses.size (); i++)
80 Interval d = encompasses[i]->pure_height (parent, start, end);
85 ret.widen (height * 0.5);
86 return ly_interval2scm (ret);
89 MAKE_SCHEME_CALLBACK (Slur, height, 1);
91 Slur::height (SCM smob)
93 Grob *me = unsmob_grob (smob);
96 Stencil *m = me->get_stencil ();
97 return m ? ly_interval2scm (m->extent (Y_AXIS))
98 : ly_interval2scm (Interval ());
102 Ugh should have dash-length + dash-period
104 MAKE_SCHEME_CALLBACK (Slur, print, 1);
106 Slur::print (SCM smob)
108 Grob *me = unsmob_grob (smob);
109 extract_grob_set (me, "note-columns", encompasses);
110 if (encompasses.empty ())
116 Real staff_thick = Staff_symbol_referencer::line_thickness (me);
117 Real base_thick = staff_thick
118 * robust_scm2double (me->get_property ("thickness"), 1);
119 Real line_thick = staff_thick
120 * robust_scm2double (me->get_property ("line-thickness"), 1);
122 Bezier one = get_curve (me);
126 TODO: replace dashed with generic property.
128 SCM p = me->get_property ("dash-period");
129 SCM f = me->get_property ("dash-fraction");
130 if (scm_is_number (p) && scm_is_number (f))
131 a = Lookup::dashed_slur (one, line_thick, robust_scm2double (p, 1.0),
132 robust_scm2double (f, 0));
134 a = Lookup::slur (one,
135 get_grob_direction (me) * base_thick,
138 #if DEBUG_SLUR_SCORING
139 SCM quant_score = me->get_property ("quant-score");
141 if (to_boolean (me->layout ()
142 ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")))
143 && scm_is_string (quant_score))
146 SCM properties = Font_interface::text_font_alist_chain (me);
149 if (!scm_is_number (me->get_property ("font-size")))
150 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-6), SCM_EOL),
153 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
154 (me->layout ()->self_scm (), properties,
156 a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0);
160 return a.smobbed_copy ();
165 it would be better to do this at engraver level, but that is
166 fragile, as the breakabl items are generated on staff level, at
167 which point slur starts and ends have to be tracked
170 Slur::replace_breakable_encompass_objects (Grob *me)
172 extract_grob_set (me, "encompass-objects", extra_objects);
173 vector<Grob *> new_encompasses;
175 for (vsize i = 0; i < extra_objects.size (); i++)
177 Grob *g = extra_objects[i];
179 if (Separation_item::has_interface (g))
181 extract_grob_set (g, "elements", breakables);
182 for (vsize j = 0; j < breakables.size (); j++)
183 /* if we encompass a separation-item that spans multiple staves,
184 we filter out the grobs that don't belong to our staff */
185 if (me->common_refpoint (breakables[j], Y_AXIS) == me->get_parent (Y_AXIS)
186 && breakables[j]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
187 new_encompasses.push_back (breakables[j]);
190 new_encompasses.push_back (g);
193 SCM encompass_scm = me->get_object ("encompass-objects");
194 if (Grob_array::unsmob (encompass_scm))
196 vector<Grob *> &arr =
197 unsmob_grob_array (encompass_scm)->array_reference ();
198 arr = new_encompasses;
203 Slur::get_curve (Grob *me)
207 for (SCM s = me->get_property ("control-points"); scm_is_pair (s);
209 b.control_[i++] = ly_scm2offset (scm_car (s));
215 Slur::add_column (Grob *me, Grob *n)
217 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
218 add_bound_item (dynamic_cast<Spanner *> (me), n);
222 Slur::add_extra_encompass (Grob *me, Grob *n)
224 Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
227 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1, "");
229 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
231 int start = robust_scm2int (start_scm, 0);
232 int end = robust_scm2int (end_scm, 0);
233 Grob *script = unsmob_grob (grob);
234 Grob *slur = unsmob_grob (script->get_object ("slur"));
238 SCM avoid = script->get_property ("avoid-slur");
239 if (avoid != ly_symbol2scm ("outside") && avoid != ly_symbol2scm ("around"))
242 Real offset = robust_scm2double (offset_scm, 0.0);
243 Direction dir = get_grob_direction (script);
244 return scm_from_double (offset + dir * slur->pure_height (slur, start, end).length () / 4);
247 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, outside_slur_callback, 2, 1, "");
249 Slur::outside_slur_callback (SCM grob, SCM offset_scm)
251 Grob *script = unsmob_grob (grob);
252 Grob *slur = unsmob_grob (script->get_object ("slur"));
257 SCM avoid = script->get_property ("avoid-slur");
258 if (avoid != ly_symbol2scm ("outside")
259 && avoid != ly_symbol2scm ("around"))
262 Direction dir = get_grob_direction (script);
266 Grob *cx = script->common_refpoint (slur, X_AXIS);
267 Grob *cy = script->common_refpoint (slur, Y_AXIS);
269 Bezier curve = Slur::get_curve (slur);
271 curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
272 slur->relative_coordinate (cy, Y_AXIS)));
274 Interval yext = robust_relative_extent (script, cy, Y_AXIS);
275 Interval xext = robust_relative_extent (script, cx, X_AXIS);
277 Real offset = robust_scm2double (offset_scm, 0);
278 yext.translate (offset);
280 /* FIXME: slur property, script property? */
281 Real slur_padding = robust_scm2double (script->get_property ("slur-padding"),
283 yext.widen (slur_padding);
286 Interval bezext (curve.control_[0][X_AXIS], curve.control_[3][X_AXIS]);
287 bool consider[] = { false, false, false };
288 Real ys[] = {0, 0, 0};
289 bool do_shift = false;
291 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
293 Real x = xext.linear_combination ((Direction) d);
294 consider[k] = bezext.contains (x);
299 = (fabs (bezext[LEFT] - x) < EPS)
300 ? curve.control_[0][Y_AXIS]
301 : ((fabs (bezext[RIGHT] - x) < EPS)
302 ? curve.control_[3][Y_AXIS]
303 : curve.get_other_coordinate (X_AXIS, x));
305 /* Request shift if slur is contained script's Y, or if
306 script is inside slur and avoid == outside. */
307 if (yext.contains (ys[k])
308 || (dir * ys[k] > dir * yext[-dir] && avoid == ly_symbol2scm ("outside")))
313 Real avoidance_offset = 0.0;
314 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
316 avoidance_offset = dir * (max (dir * avoidance_offset,
317 dir * (ys[k] - yext[-dir] + dir * slur_padding)));
319 return scm_from_double (offset + avoidance_offset);
323 * Used by Slur_engraver:: and Phrasing_slur_engraver::
326 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
327 vector<Grob*> &slurs,
328 vector<Grob*> &end_slurs)
330 if (slurs.empty () && end_slurs.empty ())
333 Grob *e = info.grob ();
334 SCM avoid = e->get_property ("avoid-slur");
335 if (Tie::has_interface (e)
336 || avoid == ly_symbol2scm ("inside"))
338 for (vsize i = slurs.size (); i--;)
339 add_extra_encompass (slurs[i], e);
340 for (vsize i = end_slurs.size (); i--;)
341 add_extra_encompass (end_slurs[i], e);
343 else if (avoid == ly_symbol2scm ("outside")
344 || avoid == ly_symbol2scm ("around"))
347 if (end_slurs.size () && !slurs.size ())
354 chain_offset_callback (e, outside_slur_callback_proc, Y_AXIS);
355 e->set_object ("slur", slur->self_scm ());
359 e->warning (_f ("Ignoring grob for slur: %s. avoid-slur not set?",
360 e->name().c_str ()));
363 MAKE_SCHEME_CALLBACK (Slur, calc_cross_staff, 1)
365 Slur::calc_cross_staff (SCM smob)
367 Grob *me = unsmob_grob (smob);
369 extract_grob_set (me, "note-columns", cols);
370 extract_grob_set (me, "encompass-objects", extras);
372 for (vsize i = 0; i < cols.size (); i++)
374 if (Grob *s = Note_column::get_stem (cols[i]))
375 if (to_boolean (s->get_property ("cross-staff")))
379 /* the separation items are dealt with in replace_breakable_encompass_objects
380 so we can ignore them here */
381 vector<Grob*> non_sep_extras;
382 for (vsize i = 0; i < extras.size (); i++)
383 if (!Separation_item::has_interface (extras[i]))
384 non_sep_extras.push_back (extras[i]);
386 Grob *common = common_refpoint_of_array (cols, me, Y_AXIS);
387 common = common_refpoint_of_array (non_sep_extras, common, Y_AXIS);
389 return scm_from_bool (common != me->get_parent (Y_AXIS));
397 "avoid-slur " /* UGH. */