2 slur.cc -- implement external interface for Slur
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2009 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 dash_definition = me->get_property ("dash-definition");
123 // SCM p = me->get_property ("dash-period");
124 // SCM f = me->get_property ("dash-fraction");
125 // SCM interval = me->get_property ("dash-interval");
126 // if (scm_is_number (p) && scm_is_number (f))
127 // a = Lookup::dashed_slur (one, line_thick, robust_scm2double (p, 1.0),
128 // robust_scm2double (f, 0));
130 a = Lookup::slur (one,
131 get_grob_direction (me) * base_thick,
134 // robust_scm2double (p, 1.0),
135 // robust_scm2double (f, 0),
136 // robust_scm2double (interval, 1));
138 #if DEBUG_SLUR_SCORING
139 SCM annotation = me->get_property ("annotation");
140 if (!scm_is_string (annotation))
142 SCM debug = me->layout ()->lookup_variable (ly_symbol2scm ("debug-slur-scoring"));
143 if (to_boolean (debug))
144 annotation = me->get_property ("quant-score");
147 if (scm_is_string (annotation))
150 SCM properties = Font_interface::text_font_alist_chain (me);
152 if (!scm_is_number (me->get_property ("font-size")))
153 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-6), SCM_EOL),
156 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
157 (me->layout ()->self_scm (), properties,
159 a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0);
163 return a.smobbed_copy ();
168 it would be better to do this at engraver level, but that is
169 fragile, as the breakabl items are generated on staff level, at
170 which point slur starts and ends have to be tracked
173 Slur::replace_breakable_encompass_objects (Grob *me)
175 extract_grob_set (me, "encompass-objects", extra_objects);
176 vector<Grob *> new_encompasses;
178 for (vsize i = 0; i < extra_objects.size (); i++)
180 Grob *g = extra_objects[i];
182 if (Separation_item::has_interface (g))
184 extract_grob_set (g, "elements", breakables);
185 for (vsize j = 0; j < breakables.size (); j++)
186 /* if we encompass a separation-item that spans multiple staves,
187 we filter out the grobs that don't belong to our staff */
188 if (me->common_refpoint (breakables[j], Y_AXIS) == me->get_parent (Y_AXIS)
189 && breakables[j]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
190 new_encompasses.push_back (breakables[j]);
193 new_encompasses.push_back (g);
196 SCM encompass_scm = me->get_object ("encompass-objects");
197 if (Grob_array::unsmob (encompass_scm))
199 vector<Grob *> &arr =
200 unsmob_grob_array (encompass_scm)->array_reference ();
201 arr = new_encompasses;
206 Slur::get_curve (Grob *me)
210 for (SCM s = me->get_property ("control-points"); scm_is_pair (s);
212 b.control_[i++] = ly_scm2offset (scm_car (s));
218 Slur::add_column (Grob *me, Grob *n)
220 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
221 add_bound_item (dynamic_cast<Spanner *> (me), n);
225 Slur::add_extra_encompass (Grob *me, Grob *n)
227 Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
230 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1, "");
232 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
234 int start = robust_scm2int (start_scm, 0);
235 int end = robust_scm2int (end_scm, 0);
236 Grob *script = unsmob_grob (grob);
237 Grob *slur = unsmob_grob (script->get_object ("slur"));
241 SCM avoid = script->get_property ("avoid-slur");
242 if (avoid != ly_symbol2scm ("outside") && avoid != ly_symbol2scm ("around"))
245 Real offset = robust_scm2double (offset_scm, 0.0);
246 Direction dir = get_grob_direction (script);
247 return scm_from_double (offset + dir * slur->pure_height (slur, start, end).length () / 4);
250 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, outside_slur_callback, 2, 1, "");
252 Slur::outside_slur_callback (SCM grob, SCM offset_scm)
254 Grob *script = unsmob_grob (grob);
255 Grob *slur = unsmob_grob (script->get_object ("slur"));
260 SCM avoid = script->get_property ("avoid-slur");
261 if (avoid != ly_symbol2scm ("outside")
262 && avoid != ly_symbol2scm ("around"))
265 Direction dir = get_grob_direction (script);
269 Grob *cx = script->common_refpoint (slur, X_AXIS);
270 Grob *cy = script->common_refpoint (slur, Y_AXIS);
272 Bezier curve = Slur::get_curve (slur);
274 curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
275 slur->relative_coordinate (cy, Y_AXIS)));
277 Interval yext = robust_relative_extent (script, cy, Y_AXIS);
278 Interval xext = robust_relative_extent (script, cx, X_AXIS);
280 Real offset = robust_scm2double (offset_scm, 0);
281 yext.translate (offset);
283 /* FIXME: slur property, script property? */
284 Real slur_padding = robust_scm2double (script->get_property ("slur-padding"),
286 yext.widen (slur_padding);
289 Interval bezext (curve.control_[0][X_AXIS], curve.control_[3][X_AXIS]);
290 bool consider[] = { false, false, false };
291 Real ys[] = {0, 0, 0};
292 bool do_shift = false;
294 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
296 Real x = xext.linear_combination ((Direction) d);
297 consider[k] = bezext.contains (x);
302 = (fabs (bezext[LEFT] - x) < EPS)
303 ? curve.control_[0][Y_AXIS]
304 : ((fabs (bezext[RIGHT] - x) < EPS)
305 ? curve.control_[3][Y_AXIS]
306 : curve.get_other_coordinate (X_AXIS, x));
308 /* Request shift if slur is contained script's Y, or if
309 script is inside slur and avoid == outside. */
310 if (yext.contains (ys[k])
311 || (dir * ys[k] > dir * yext[-dir] && avoid == ly_symbol2scm ("outside")))
316 Real avoidance_offset = 0.0;
319 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
321 avoidance_offset = dir * (max (dir * avoidance_offset,
322 dir * (ys[k] - yext[-dir] + dir * slur_padding)));
324 return scm_from_double (offset + avoidance_offset);
328 * Used by Slur_engraver:: and Phrasing_slur_engraver::
331 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
332 vector<Grob*> &slurs,
333 vector<Grob*> &end_slurs)
335 if (slurs.empty () && end_slurs.empty ())
338 Grob *e = info.grob ();
339 SCM avoid = e->get_property ("avoid-slur");
340 if (Tie::has_interface (e)
341 || avoid == ly_symbol2scm ("inside"))
343 for (vsize i = slurs.size (); i--;)
344 add_extra_encompass (slurs[i], e);
345 for (vsize i = end_slurs.size (); i--;)
346 add_extra_encompass (end_slurs[i], e);
348 else if (avoid == ly_symbol2scm ("outside")
349 || avoid == ly_symbol2scm ("around"))
352 if (end_slurs.size () && !slurs.size ())
359 chain_offset_callback (e, outside_slur_callback_proc, Y_AXIS);
360 chain_callback (e, outside_slur_cross_staff_proc, ly_symbol2scm("cross-staff"));
361 e->set_object ("slur", slur->self_scm ());
365 e->warning (_f ("Ignoring grob for slur: %s. avoid-slur not set?",
366 e->name().c_str ()));
370 A callback that will be chained together with the original cross-staff
371 value of a grob that is placed 'outside or 'around a slur. This just says
372 that any grob becomes cross-staff if it is placed 'outside or 'around a
375 MAKE_SCHEME_CALLBACK (Slur, outside_slur_cross_staff, 2)
377 Slur::outside_slur_cross_staff (SCM smob, SCM previous)
379 if (previous == SCM_BOOL_T)
382 Grob *me = unsmob_grob (smob);
383 Grob *slur = unsmob_grob (me->get_object ("slur"));
387 return slur->get_property ("cross-staff");
390 MAKE_SCHEME_CALLBACK (Slur, calc_cross_staff, 1)
392 Slur::calc_cross_staff (SCM smob)
394 Grob *me = unsmob_grob (smob);
396 extract_grob_set (me, "note-columns", cols);
397 extract_grob_set (me, "encompass-objects", extras);
399 for (vsize i = 0; i < cols.size (); i++)
401 if (Grob *s = Note_column::get_stem (cols[i]))
402 if (to_boolean (s->get_property ("cross-staff")))
406 /* the separation items are dealt with in replace_breakable_encompass_objects
407 so we can ignore them here */
408 vector<Grob*> non_sep_extras;
409 for (vsize i = 0; i < extras.size (); i++)
410 if (!Separation_item::has_interface (extras[i]))
411 non_sep_extras.push_back (extras[i]);
413 Grob *common = common_refpoint_of_array (cols, me, Y_AXIS);
414 common = common_refpoint_of_array (non_sep_extras, common, Y_AXIS);
416 return scm_from_bool (common != me->get_parent (Y_AXIS));
424 "avoid-slur " /* UGH. */