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 we encompass a separation-item that spans multiple staves,
183 we filter out the grobs that don't belong to our staff */
184 if (me->common_refpoint (breakables[j], Y_AXIS) == me->get_parent (Y_AXIS)
185 && breakables[j]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
186 new_encompasses.push_back (breakables[j]);
189 new_encompasses.push_back (g);
192 SCM encompass_scm = me->get_object ("encompass-objects");
193 if (Grob_array::unsmob (encompass_scm))
195 vector<Grob *> &arr =
196 unsmob_grob_array (encompass_scm)->array_reference ();
197 arr = new_encompasses;
202 Slur::get_curve (Grob *me)
206 for (SCM s = me->get_property ("control-points"); scm_is_pair (s);
208 b.control_[i++] = ly_scm2offset (scm_car (s));
214 Slur::add_column (Grob *me, Grob *n)
216 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
217 add_bound_item (dynamic_cast<Spanner *> (me), n);
221 Slur::add_extra_encompass (Grob *me, Grob *n)
223 Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
226 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1, "");
228 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
230 int start = robust_scm2int (start_scm, 0);
231 int end = robust_scm2int (end_scm, 0);
232 Grob *script = unsmob_grob (grob);
233 Grob *slur = unsmob_grob (script->get_object ("slur"));
237 SCM avoid = script->get_property ("avoid-slur");
238 if (avoid != ly_symbol2scm ("outside") && avoid != ly_symbol2scm ("around"))
241 Real offset = robust_scm2double (offset_scm, 0.0);
242 Direction dir = get_grob_direction (script);
243 return scm_from_double (offset + dir * slur->pure_height (slur, start, end).length () / 4);
246 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, outside_slur_callback, 2, 1, "");
248 Slur::outside_slur_callback (SCM grob, SCM offset_scm)
250 Grob *script = unsmob_grob (grob);
251 Grob *slur = unsmob_grob (script->get_object ("slur"));
256 SCM avoid = script->get_property ("avoid-slur");
257 if (avoid != ly_symbol2scm ("outside")
258 && avoid != ly_symbol2scm ("around"))
261 Direction dir = get_grob_direction (script);
265 Grob *cx = script->common_refpoint (slur, X_AXIS);
266 Grob *cy = script->common_refpoint (slur, Y_AXIS);
268 Bezier curve = Slur::get_curve (slur);
270 curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
271 slur->relative_coordinate (cy, Y_AXIS)));
273 Interval yext = robust_relative_extent (script, cy, Y_AXIS);
274 Interval xext = robust_relative_extent (script, cx, X_AXIS);
276 Real offset = robust_scm2double (offset_scm, 0);
277 yext.translate (offset);
279 /* FIXME: slur property, script property? */
280 Real slur_padding = robust_scm2double (script->get_property ("slur-padding"),
282 yext.widen (slur_padding);
285 Interval bezext (curve.control_[0][X_AXIS], curve.control_[3][X_AXIS]);
286 bool consider[] = { false, false, false };
287 Real ys[] = {0, 0, 0};
288 bool do_shift = false;
290 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
292 Real x = xext.linear_combination ((Direction) d);
293 consider[k] = bezext.contains (x);
298 = (fabs (bezext[LEFT] - x) < EPS)
299 ? curve.control_[0][Y_AXIS]
300 : ((fabs (bezext[RIGHT] - x) < EPS)
301 ? curve.control_[3][Y_AXIS]
302 : curve.get_other_coordinate (X_AXIS, x));
304 /* Request shift if slur is contained script's Y, or if
305 script is inside slur and avoid == outside. */
306 if (yext.contains (ys[k])
307 || (dir * ys[k] > dir * yext[-dir] && avoid == ly_symbol2scm ("outside")))
312 Real avoidance_offset = 0.0;
313 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
315 avoidance_offset = dir * (max (dir * avoidance_offset,
316 dir * (ys[k] - yext[-dir] + dir * slur_padding)));
318 return scm_from_double (offset + avoidance_offset);
322 * Used by Slur_engraver:: and Phrasing_slur_engraver::
325 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
326 vector<Grob*> &slurs,
327 vector<Grob*> &end_slurs)
329 if (slurs.empty () && end_slurs.empty ())
332 Grob *e = info.grob ();
333 SCM avoid = e->get_property ("avoid-slur");
334 if (Tie::has_interface (e)
335 || avoid == ly_symbol2scm ("inside"))
337 for (vsize i = slurs.size (); i--;)
338 add_extra_encompass (slurs[i], e);
339 for (vsize i = end_slurs.size (); i--;)
340 add_extra_encompass (end_slurs[i], e);
342 else if (avoid == ly_symbol2scm ("outside")
343 || avoid == ly_symbol2scm ("around"))
346 if (end_slurs.size () && !slurs.size ())
353 chain_offset_callback (e, outside_slur_callback_proc, Y_AXIS);
354 e->set_object ("slur", slur->self_scm ());
358 e->warning (_f ("Ignoring grob for slur: %s. avoid-slur not set?",
359 e->name().c_str ()));
362 MAKE_SCHEME_CALLBACK (Slur, calc_cross_staff, 1)
364 Slur::calc_cross_staff (SCM smob)
366 Grob *me = unsmob_grob (smob);
368 extract_grob_set (me, "note-columns", cols);
369 extract_grob_set (me, "encompass-objects", extras);
371 /* the separation items are dealt with in replace_breakable_encompass_objects
372 so we can ignore them here */
373 vector<Grob*> non_sep_extras;
374 for (vsize i = 0; i < extras.size (); i++)
375 if (!Separation_item::has_interface (extras[i]))
376 non_sep_extras.push_back (extras[i]);
378 Grob *common = common_refpoint_of_array (cols, me, Y_AXIS);
379 common = common_refpoint_of_array (non_sep_extras, common, Y_AXIS);
381 return scm_from_bool (common != me->get_parent (Y_AXIS));
389 "avoid-slur " /* UGH. */