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"
17 #include "pointer-group-interface.hh"
19 #include "main.hh" // DEBUG_SLUR_SCORING
20 #include "note-column.hh"
21 #include "output-def.hh"
23 #include "staff-symbol-referencer.hh"
24 #include "staff-symbol.hh"
26 #include "text-interface.hh"
29 #include "slur-scoring.hh"
30 #include "separation-item.hh"
31 #include "script-interface.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, 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 (breakables[j]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
184 new_encompasses.push_back (breakables[j]);
187 new_encompasses.push_back (g);
190 SCM encompass_scm = me->get_object ("encompass-objects");
191 if (Grob_array::unsmob (encompass_scm))
193 vector<Grob *> &arr =
194 unsmob_grob_array (encompass_scm)->array_reference ();
195 arr = new_encompasses;
200 Slur::get_curve (Grob *me)
204 for (SCM s = me->get_property ("control-points"); scm_is_pair (s);
206 b.control_[i++] = ly_scm2offset (scm_car (s));
212 Slur::add_column (Grob *me, Grob *n)
214 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
215 add_bound_item (dynamic_cast<Spanner *> (me), n);
219 Slur::add_extra_encompass (Grob *me, Grob *n)
221 Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
224 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1);
226 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
228 int start = robust_scm2int (start_scm, 0);
229 int end = robust_scm2int (end_scm, 0);
230 Grob *script = unsmob_grob (grob);
231 Grob *slur = unsmob_grob (script->get_object ("slur"));
235 SCM avoid = script->get_property ("avoid-slur");
236 if (avoid != ly_symbol2scm ("outside") && avoid != ly_symbol2scm ("around"))
239 Real offset = robust_scm2double (offset_scm, 0.0);
240 Direction dir = get_grob_direction (script);
241 return scm_from_double (offset + dir * slur->pure_height (slur, start, end).length () / 4);
244 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, outside_slur_callback, 2, 1);
246 Slur::outside_slur_callback (SCM grob, SCM offset_scm)
248 Grob *script = unsmob_grob (grob);
249 Grob *slur = unsmob_grob (script->get_object ("slur"));
254 SCM avoid = script->get_property ("avoid-slur");
255 if (avoid != ly_symbol2scm ("outside")
256 && avoid != ly_symbol2scm ("around"))
259 Direction dir = get_grob_direction (script);
263 Grob *cx = script->common_refpoint (slur, X_AXIS);
264 Grob *cy = script->common_refpoint (slur, Y_AXIS);
266 Bezier curve = Slur::get_curve (slur);
268 curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
269 slur->relative_coordinate (cy, Y_AXIS)));
271 Interval yext = robust_relative_extent (script, cy, Y_AXIS);
272 Interval xext = robust_relative_extent (script, cx, X_AXIS);
274 Real offset = robust_scm2double (offset_scm, 0);
275 yext.translate (offset);
277 /* FIXME: slur property, script property? */
278 Real slur_padding = robust_scm2double (script->get_property ("slur-padding"),
280 yext.widen (slur_padding);
283 Interval bezext (curve.control_[0][X_AXIS], curve.control_[3][X_AXIS]);
284 bool consider[] = { false, false, false };
285 Real ys[] = {0, 0, 0};
286 bool do_shift = false;
288 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
290 Real x = xext.linear_combination ((Direction) d);
291 consider[k] = bezext.contains (x);
296 = (fabs (bezext[LEFT] - x) < EPS)
297 ? curve.control_[0][Y_AXIS]
298 : ((fabs (bezext[RIGHT] - x) < EPS)
299 ? curve.control_[3][Y_AXIS]
300 : curve.get_other_coordinate (X_AXIS, x));
302 /* Request shift if slur is contained script's Y, or if
303 script is inside slur and avoid == outside. */
304 if (yext.contains (ys[k])
305 || (dir * ys[k] > dir * yext[-dir] && avoid == ly_symbol2scm ("outside")))
310 Real avoidance_offset = 0.0;
311 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
313 avoidance_offset = dir * (max (dir * avoidance_offset,
314 dir * (ys[k] - yext[-dir] + dir * slur_padding)));
316 return scm_from_double (offset + avoidance_offset);
320 * Used by Slur_engraver:: and Phrasing_slur_engraver::
323 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
324 vector<Grob*> &slurs,
325 vector<Grob*> &end_slurs)
327 if (slurs.empty () && end_slurs.empty ())
330 Grob *e = info.grob ();
331 SCM avoid = e->get_property ("avoid-slur");
332 if (Tie::has_interface (e)
333 || avoid == ly_symbol2scm ("inside"))
335 for (vsize i = slurs.size (); i--;)
336 add_extra_encompass (slurs[i], e);
337 for (vsize i = end_slurs.size (); i--;)
338 add_extra_encompass (end_slurs[i], e);
340 else if (avoid == ly_symbol2scm ("outside")
341 || avoid == ly_symbol2scm ("around"))
344 if (end_slurs.size () && !slurs.size ())
351 chain_offset_callback (e, outside_slur_callback_proc, Y_AXIS);
352 e->set_object ("slur", slur->self_scm ());
356 e->warning ("Ignoring grob for slur. avoid-slur not set?");
365 "avoid-slur " /* UGH. */