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 ());
101 MAKE_SCHEME_CALLBACK (Slur, print, 1);
103 Slur::print (SCM smob)
105 Grob *me = unsmob_grob (smob);
106 extract_grob_set (me, "note-columns", encompasses);
107 if (encompasses.empty ())
113 Real staff_thick = Staff_symbol_referencer::line_thickness (me);
114 Real base_thick = staff_thick
115 * robust_scm2double (me->get_property ("thickness"), 1);
116 Real line_thick = staff_thick
117 * robust_scm2double (me->get_property ("line-thickness"), 1);
119 Bezier one = get_curve (me);
122 SCM p = me->get_property ("dash-period");
123 SCM f = me->get_property ("dash-fraction");
124 if (scm_is_number (p) && scm_is_number (f))
125 a = Lookup::dashed_slur (one, line_thick, robust_scm2double (p, 1.0),
126 robust_scm2double (f, 0));
128 a = Lookup::slur (one,
129 get_grob_direction (me) * base_thick,
132 #if DEBUG_SLUR_SCORING
133 SCM quant_score = me->get_property ("quant-score");
135 if (to_boolean (me->layout ()
136 ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")))
137 && scm_is_string (quant_score))
140 SCM properties = Font_interface::text_font_alist_chain (me);
143 if (!scm_is_number (me->get_property ("font-size")))
144 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-6), SCM_EOL),
147 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
148 (me->layout ()->self_scm (), properties,
150 a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0);
154 return a.smobbed_copy ();
159 it would be better to do this at engraver level, but that is
160 fragile, as the breakabl items are generated on staff level, at
161 which point slur starts and ends have to be tracked
164 Slur::replace_breakable_encompass_objects (Grob *me)
166 extract_grob_set (me, "encompass-objects", extra_objects);
167 vector<Grob *> new_encompasses;
169 for (vsize i = 0; i < extra_objects.size (); i++)
171 Grob *g = extra_objects[i];
173 if (Separation_item::has_interface (g))
175 extract_grob_set (g, "elements", breakables);
176 for (vsize j = 0; j < breakables.size (); j++)
177 /* if we encompass a separation-item that spans multiple staves,
178 we filter out the grobs that don't belong to our staff */
179 if (me->common_refpoint (breakables[j], Y_AXIS) == me->get_parent (Y_AXIS)
180 && breakables[j]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
181 new_encompasses.push_back (breakables[j]);
184 new_encompasses.push_back (g);
187 SCM encompass_scm = me->get_object ("encompass-objects");
188 if (Grob_array::unsmob (encompass_scm))
190 vector<Grob *> &arr =
191 unsmob_grob_array (encompass_scm)->array_reference ();
192 arr = new_encompasses;
197 Slur::get_curve (Grob *me)
201 for (SCM s = me->get_property ("control-points"); scm_is_pair (s);
203 b.control_[i++] = ly_scm2offset (scm_car (s));
209 Slur::add_column (Grob *me, Grob *n)
211 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
212 add_bound_item (dynamic_cast<Spanner *> (me), n);
216 Slur::add_extra_encompass (Grob *me, Grob *n)
218 Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
221 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1, "");
223 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
225 int start = robust_scm2int (start_scm, 0);
226 int end = robust_scm2int (end_scm, 0);
227 Grob *script = unsmob_grob (grob);
228 Grob *slur = unsmob_grob (script->get_object ("slur"));
232 SCM avoid = script->get_property ("avoid-slur");
233 if (avoid != ly_symbol2scm ("outside") && avoid != ly_symbol2scm ("around"))
236 Real offset = robust_scm2double (offset_scm, 0.0);
237 Direction dir = get_grob_direction (script);
238 return scm_from_double (offset + dir * slur->pure_height (slur, start, end).length () / 4);
241 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, outside_slur_callback, 2, 1, "");
243 Slur::outside_slur_callback (SCM grob, SCM offset_scm)
245 Grob *script = unsmob_grob (grob);
246 Grob *slur = unsmob_grob (script->get_object ("slur"));
251 SCM avoid = script->get_property ("avoid-slur");
252 if (avoid != ly_symbol2scm ("outside")
253 && avoid != ly_symbol2scm ("around"))
256 Direction dir = get_grob_direction (script);
260 Grob *cx = script->common_refpoint (slur, X_AXIS);
261 Grob *cy = script->common_refpoint (slur, Y_AXIS);
263 Bezier curve = Slur::get_curve (slur);
265 curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
266 slur->relative_coordinate (cy, Y_AXIS)));
268 Interval yext = robust_relative_extent (script, cy, Y_AXIS);
269 Interval xext = robust_relative_extent (script, cx, X_AXIS);
271 Real offset = robust_scm2double (offset_scm, 0);
272 yext.translate (offset);
274 /* FIXME: slur property, script property? */
275 Real slur_padding = robust_scm2double (script->get_property ("slur-padding"),
277 yext.widen (slur_padding);
280 Interval bezext (curve.control_[0][X_AXIS], curve.control_[3][X_AXIS]);
281 bool consider[] = { false, false, false };
282 Real ys[] = {0, 0, 0};
283 bool do_shift = false;
285 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
287 Real x = xext.linear_combination ((Direction) d);
288 consider[k] = bezext.contains (x);
293 = (fabs (bezext[LEFT] - x) < EPS)
294 ? curve.control_[0][Y_AXIS]
295 : ((fabs (bezext[RIGHT] - x) < EPS)
296 ? curve.control_[3][Y_AXIS]
297 : curve.get_other_coordinate (X_AXIS, x));
299 /* Request shift if slur is contained script's Y, or if
300 script is inside slur and avoid == outside. */
301 if (yext.contains (ys[k])
302 || (dir * ys[k] > dir * yext[-dir] && avoid == ly_symbol2scm ("outside")))
307 Real avoidance_offset = 0.0;
308 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
310 avoidance_offset = dir * (max (dir * avoidance_offset,
311 dir * (ys[k] - yext[-dir] + dir * slur_padding)));
313 return scm_from_double (offset + avoidance_offset);
317 * Used by Slur_engraver:: and Phrasing_slur_engraver::
320 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
321 vector<Grob*> &slurs,
322 vector<Grob*> &end_slurs)
324 if (slurs.empty () && end_slurs.empty ())
327 Grob *e = info.grob ();
328 SCM avoid = e->get_property ("avoid-slur");
329 if (Tie::has_interface (e)
330 || avoid == ly_symbol2scm ("inside"))
332 for (vsize i = slurs.size (); i--;)
333 add_extra_encompass (slurs[i], e);
334 for (vsize i = end_slurs.size (); i--;)
335 add_extra_encompass (end_slurs[i], e);
337 else if (avoid == ly_symbol2scm ("outside")
338 || avoid == ly_symbol2scm ("around"))
341 if (end_slurs.size () && !slurs.size ())
348 chain_offset_callback (e, outside_slur_callback_proc, Y_AXIS);
349 e->set_object ("slur", slur->self_scm ());
353 e->warning (_f ("Ignoring grob for slur: %s. avoid-slur not set?",
354 e->name().c_str ()));
357 MAKE_SCHEME_CALLBACK (Slur, calc_cross_staff, 1)
359 Slur::calc_cross_staff (SCM smob)
361 Grob *me = unsmob_grob (smob);
363 extract_grob_set (me, "note-columns", cols);
364 extract_grob_set (me, "encompass-objects", extras);
366 for (vsize i = 0; i < cols.size (); i++)
368 if (Grob *s = Note_column::get_stem (cols[i]))
369 if (to_boolean (s->get_property ("cross-staff")))
373 /* the separation items are dealt with in replace_breakable_encompass_objects
374 so we can ignore them here */
375 vector<Grob*> non_sep_extras;
376 for (vsize i = 0; i < extras.size (); i++)
377 if (!Separation_item::has_interface (extras[i]))
378 non_sep_extras.push_back (extras[i]);
380 Grob *common = common_refpoint_of_array (cols, me, Y_AXIS);
381 common = common_refpoint_of_array (non_sep_extras, common, Y_AXIS);
383 return scm_from_bool (common != me->get_parent (Y_AXIS));
391 "avoid-slur " /* UGH. */