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"
25 #include "text-interface.hh"
28 #include "slur-scoring.hh"
29 #include "separation-item.hh"
30 #include "international.hh"
34 MAKE_SCHEME_CALLBACK (Slur, calc_direction, 1)
36 Slur::calc_direction (SCM smob)
38 Grob *me = unsmob_grob (smob);
39 extract_grob_set (me, "note-columns", encompasses);
41 if (encompasses.empty ())
48 for (vsize i = 0; i < encompasses.size (); i++)
50 if (Note_column::dir (encompasses[i]) < 0)
56 return scm_from_int (d);
59 MAKE_SCHEME_CALLBACK (Slur, pure_height, 3);
61 Slur::pure_height (SCM smob, SCM start_scm, SCM end_scm)
63 Grob *me = unsmob_grob (smob);
64 int start = scm_to_int (start_scm);
65 int end = scm_to_int (end_scm);
66 Real height = robust_scm2double (me->get_property ("height-limit"), 2.0);
68 extract_grob_set (me, "note-columns", encompasses);
71 Grob *parent = me->get_parent (Y_AXIS);
72 if (common_refpoint_of_array (encompasses, me, Y_AXIS) != parent)
73 /* this could happen if, for example, we are a cross-staff slur.
74 in this case, we want to be ignored */
75 return ly_interval2scm (Interval ());
77 for (vsize i = 0; i < encompasses.size (); i++)
79 Interval d = encompasses[i]->pure_height (parent, start, end);
84 ret.widen (height * 0.5);
85 return ly_interval2scm (ret);
88 MAKE_SCHEME_CALLBACK (Slur, height, 1);
90 Slur::height (SCM smob)
92 Grob *me = unsmob_grob (smob);
95 Stencil *m = me->get_stencil ();
96 return m ? ly_interval2scm (m->extent (Y_AXIS))
97 : ly_interval2scm (Interval ());
101 Ugh should have dash-length + dash-period
103 MAKE_SCHEME_CALLBACK (Slur, print, 1);
105 Slur::print (SCM smob)
107 Grob *me = unsmob_grob (smob);
108 extract_grob_set (me, "note-columns", encompasses);
109 if (encompasses.empty ())
115 Real staff_thick = Staff_symbol_referencer::line_thickness (me);
116 Real base_thick = staff_thick
117 * robust_scm2double (me->get_property ("thickness"), 1);
118 Real line_thick = staff_thick
119 * robust_scm2double (me->get_property ("line-thickness"), 1);
121 Bezier one = get_curve (me);
125 TODO: replace dashed with generic property.
127 SCM p = me->get_property ("dash-period");
128 SCM f = me->get_property ("dash-fraction");
129 if (scm_is_number (p) && scm_is_number (f))
130 a = Lookup::dashed_slur (one, line_thick, robust_scm2double (p, 1.0),
131 robust_scm2double (f, 0));
133 a = Lookup::slur (one,
134 get_grob_direction (me) * base_thick,
137 #if DEBUG_SLUR_SCORING
138 SCM quant_score = me->get_property ("quant-score");
140 if (to_boolean (me->layout ()
141 ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")))
142 && scm_is_string (quant_score))
145 SCM properties = Font_interface::text_font_alist_chain (me);
148 if (!scm_is_number (me->get_property ("font-size")))
149 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-6), SCM_EOL),
152 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
153 (me->layout ()->self_scm (), properties,
155 a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0);
159 return a.smobbed_copy ();
164 it would be better to do this at engraver level, but that is
165 fragile, as the breakabl items are generated on staff level, at
166 which point slur starts and ends have to be tracked
169 Slur::replace_breakable_encompass_objects (Grob *me)
171 extract_grob_set (me, "encompass-objects", extra_objects);
172 vector<Grob *> new_encompasses;
174 for (vsize i = 0; i < extra_objects.size (); i++)
176 Grob *g = extra_objects[i];
178 if (Separation_item::has_interface (g))
180 extract_grob_set (g, "elements", breakables);
181 for (vsize j = 0; j < breakables.size (); j++)
182 if (breakables[j]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
183 new_encompasses.push_back (breakables[j]);
186 new_encompasses.push_back (g);
189 SCM encompass_scm = me->get_object ("encompass-objects");
190 if (Grob_array::unsmob (encompass_scm))
192 vector<Grob *> &arr =
193 unsmob_grob_array (encompass_scm)->array_reference ();
194 arr = new_encompasses;
199 Slur::get_curve (Grob *me)
203 for (SCM s = me->get_property ("control-points"); scm_is_pair (s);
205 b.control_[i++] = ly_scm2offset (scm_car (s));
211 Slur::add_column (Grob *me, Grob *n)
213 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
214 add_bound_item (dynamic_cast<Spanner *> (me), n);
218 Slur::add_extra_encompass (Grob *me, Grob *n)
220 Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
223 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1, "");
225 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
227 int start = robust_scm2int (start_scm, 0);
228 int end = robust_scm2int (end_scm, 0);
229 Grob *script = unsmob_grob (grob);
230 Grob *slur = unsmob_grob (script->get_object ("slur"));
234 SCM avoid = script->get_property ("avoid-slur");
235 if (avoid != ly_symbol2scm ("outside") && avoid != ly_symbol2scm ("around"))
238 Real offset = robust_scm2double (offset_scm, 0.0);
239 Direction dir = get_grob_direction (script);
240 return scm_from_double (offset + dir * slur->pure_height (slur, start, end).length () / 4);
243 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, outside_slur_callback, 2, 1, "");
245 Slur::outside_slur_callback (SCM grob, SCM offset_scm)
247 Grob *script = unsmob_grob (grob);
248 Grob *slur = unsmob_grob (script->get_object ("slur"));
253 SCM avoid = script->get_property ("avoid-slur");
254 if (avoid != ly_symbol2scm ("outside")
255 && avoid != ly_symbol2scm ("around"))
258 Direction dir = get_grob_direction (script);
262 Grob *cx = script->common_refpoint (slur, X_AXIS);
263 Grob *cy = script->common_refpoint (slur, Y_AXIS);
265 Bezier curve = Slur::get_curve (slur);
267 curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
268 slur->relative_coordinate (cy, Y_AXIS)));
270 Interval yext = robust_relative_extent (script, cy, Y_AXIS);
271 Interval xext = robust_relative_extent (script, cx, X_AXIS);
273 Real offset = robust_scm2double (offset_scm, 0);
274 yext.translate (offset);
276 /* FIXME: slur property, script property? */
277 Real slur_padding = robust_scm2double (script->get_property ("slur-padding"),
279 yext.widen (slur_padding);
282 Interval bezext (curve.control_[0][X_AXIS], curve.control_[3][X_AXIS]);
283 bool consider[] = { false, false, false };
284 Real ys[] = {0, 0, 0};
285 bool do_shift = false;
287 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
289 Real x = xext.linear_combination ((Direction) d);
290 consider[k] = bezext.contains (x);
295 = (fabs (bezext[LEFT] - x) < EPS)
296 ? curve.control_[0][Y_AXIS]
297 : ((fabs (bezext[RIGHT] - x) < EPS)
298 ? curve.control_[3][Y_AXIS]
299 : curve.get_other_coordinate (X_AXIS, x));
301 /* Request shift if slur is contained script's Y, or if
302 script is inside slur and avoid == outside. */
303 if (yext.contains (ys[k])
304 || (dir * ys[k] > dir * yext[-dir] && avoid == ly_symbol2scm ("outside")))
309 Real avoidance_offset = 0.0;
310 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
312 avoidance_offset = dir * (max (dir * avoidance_offset,
313 dir * (ys[k] - yext[-dir] + dir * slur_padding)));
315 return scm_from_double (offset + avoidance_offset);
319 * Used by Slur_engraver:: and Phrasing_slur_engraver::
322 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
323 vector<Grob*> &slurs,
324 vector<Grob*> &end_slurs)
326 if (slurs.empty () && end_slurs.empty ())
329 Grob *e = info.grob ();
330 SCM avoid = e->get_property ("avoid-slur");
331 if (Tie::has_interface (e)
332 || avoid == ly_symbol2scm ("inside"))
334 for (vsize i = slurs.size (); i--;)
335 add_extra_encompass (slurs[i], e);
336 for (vsize i = end_slurs.size (); i--;)
337 add_extra_encompass (end_slurs[i], e);
339 else if (avoid == ly_symbol2scm ("outside")
340 || avoid == ly_symbol2scm ("around"))
343 if (end_slurs.size () && !slurs.size ())
350 chain_offset_callback (e, outside_slur_callback_proc, Y_AXIS);
351 e->set_object ("slur", slur->self_scm ());
355 e->warning (_f ("Ignoring grob for slur: %s. avoid-slur not set?",
356 e->name().c_str ()));
359 MAKE_SCHEME_CALLBACK (Slur, calc_cross_staff, 1)
361 Slur::calc_cross_staff (SCM smob)
363 Grob *me = unsmob_grob (smob);
364 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
365 assert (staff); // delete me
366 extract_grob_set (me, "note-columns", cols);
368 for (vsize i = 0; i < cols.size (); i++)
369 if (Staff_symbol_referencer::get_staff_symbol (cols[i]) != staff)
379 "avoid-slur " /* UGH. */