Add display method for \bendAfter.
[lilypond/mpolesky.git] / lily / slur.cc
blob61aaba38e3756aadcbcfcf1c0cc599b0959c64d9
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
21 #include "slur.hh"
22 #include "grob-info.hh"
23 #include "grob-array.hh"
24 #include "beam.hh"
25 #include "bezier.hh"
26 #include "directional-element-interface.hh"
27 #include "font-interface.hh"
28 #include "item.hh"
29 #include "pointer-group-interface.hh"
30 #include "lookup.hh"
31 #include "main.hh" // DEBUG_SLUR_SCORING
32 #include "note-column.hh"
33 #include "output-def.hh"
34 #include "spanner.hh"
35 #include "staff-symbol-referencer.hh"
36 #include "stem.hh"
37 #include "text-interface.hh"
38 #include "tie.hh"
39 #include "warn.hh"
40 #include "slur-scoring.hh"
41 #include "separation-item.hh"
42 #include "international.hh"
46 MAKE_SCHEME_CALLBACK (Slur, calc_direction, 1)
47 SCM
48 Slur::calc_direction (SCM smob)
50 Grob *me = unsmob_grob (smob);
51 extract_grob_set (me, "note-columns", encompasses);
53 if (encompasses.empty ())
55 me->suicide ();
56 return SCM_BOOL_F;
59 Direction d = DOWN;
60 for (vsize i = 0; i < encompasses.size (); i++)
62 if (Note_column::dir (encompasses[i]) < 0)
64 d = UP;
65 break;
68 return scm_from_int (d);
71 MAKE_SCHEME_CALLBACK (Slur, pure_height, 3);
72 SCM
73 Slur::pure_height (SCM smob, SCM start_scm, SCM end_scm)
75 Grob *me = unsmob_grob (smob);
76 int start = scm_to_int (start_scm);
77 int end = scm_to_int (end_scm);
78 Real height = robust_scm2double (me->get_property ("height-limit"), 2.0);
80 extract_grob_set (me, "note-columns", encompasses);
81 Interval ret;
83 Grob *parent = me->get_parent (Y_AXIS);
84 if (common_refpoint_of_array (encompasses, me, Y_AXIS) != parent)
85 /* this could happen if, for example, we are a cross-staff slur.
86 in this case, we want to be ignored */
87 return ly_interval2scm (Interval ());
89 for (vsize i = 0; i < encompasses.size (); i++)
91 Interval d = encompasses[i]->pure_height (parent, start, end);
92 if (!d.is_empty ())
93 ret.unite (d);
96 ret.widen (height * 0.5);
97 return ly_interval2scm (ret);
100 MAKE_SCHEME_CALLBACK (Slur, height, 1);
102 Slur::height (SCM smob)
104 Grob *me = unsmob_grob (smob);
106 // FIXME uncached
107 Stencil *m = me->get_stencil ();
108 return m ? ly_interval2scm (m->extent (Y_AXIS))
109 : ly_interval2scm (Interval ());
112 MAKE_SCHEME_CALLBACK (Slur, print, 1);
114 Slur::print (SCM smob)
116 Grob *me = unsmob_grob (smob);
117 extract_grob_set (me, "note-columns", encompasses);
118 if (encompasses.empty ())
120 me->suicide ();
121 return SCM_EOL;
124 Real staff_thick = Staff_symbol_referencer::line_thickness (me);
125 Real base_thick = staff_thick
126 * robust_scm2double (me->get_property ("thickness"), 1);
127 Real line_thick = staff_thick
128 * robust_scm2double (me->get_property ("line-thickness"), 1);
130 Bezier one = get_curve (me);
131 Stencil a;
133 SCM dash_definition = me->get_property ("dash-definition");
134 a = Lookup::slur (one,
135 get_grob_direction (me) * base_thick,
136 line_thick,
137 dash_definition);
139 #if DEBUG_SLUR_SCORING
140 SCM annotation = me->get_property ("annotation");
141 if (!scm_is_string (annotation))
143 SCM debug = me->layout ()->lookup_variable (ly_symbol2scm ("debug-slur-scoring"));
144 if (to_boolean (debug))
145 annotation = me->get_property ("quant-score");
148 if (scm_is_string (annotation))
150 string str;
151 SCM properties = Font_interface::text_font_alist_chain (me);
153 if (!scm_is_number (me->get_property ("font-size")))
154 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-6), SCM_EOL),
155 properties);
157 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
158 (me->layout ()->self_scm (), properties,
159 annotation));
160 a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0);
162 #endif
164 return a.smobbed_copy ();
169 it would be better to do this at engraver level, but that is
170 fragile, as the breakable items are generated on staff level, at
171 which point slur starts and ends have to be tracked
173 void
174 Slur::replace_breakable_encompass_objects (Grob *me)
176 extract_grob_set (me, "encompass-objects", extra_objects);
177 vector<Grob *> new_encompasses;
179 for (vsize i = 0; i < extra_objects.size (); i++)
181 Grob *g = extra_objects[i];
183 if (Separation_item::has_interface (g))
185 extract_grob_set (g, "elements", breakables);
186 for (vsize j = 0; j < breakables.size (); j++)
187 /* if we encompass a separation-item that spans multiple staves,
188 we filter out the grobs that don't belong to our staff */
189 if (me->common_refpoint (breakables[j], Y_AXIS) == me->get_parent (Y_AXIS)
190 && breakables[j]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
191 new_encompasses.push_back (breakables[j]);
193 else
194 new_encompasses.push_back (g);
197 SCM encompass_scm = me->get_object ("encompass-objects");
198 if (Grob_array::unsmob (encompass_scm))
200 vector<Grob *> &arr =
201 unsmob_grob_array (encompass_scm)->array_reference ();
202 arr = new_encompasses;
206 Bezier
207 Slur::get_curve (Grob *me)
209 Bezier b;
210 int i = 0;
211 for (SCM s = me->get_property ("control-points"); scm_is_pair (s);
212 s = scm_cdr (s))
213 b.control_[i++] = ly_scm2offset (scm_car (s));
215 return b;
218 void
219 Slur::add_column (Grob *me, Grob *n)
221 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
222 add_bound_item (dynamic_cast<Spanner *> (me), n);
225 void
226 Slur::add_extra_encompass (Grob *me, Grob *n)
228 Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
231 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1, "");
233 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
235 int start = robust_scm2int (start_scm, 0);
236 int end = robust_scm2int (end_scm, 0);
237 Grob *script = unsmob_grob (grob);
238 Grob *slur = unsmob_grob (script->get_object ("slur"));
239 if (!slur)
240 return offset_scm;
242 SCM avoid = script->get_property ("avoid-slur");
243 if (avoid != ly_symbol2scm ("outside") && avoid != ly_symbol2scm ("around"))
244 return offset_scm;
246 Real offset = robust_scm2double (offset_scm, 0.0);
247 Direction dir = get_grob_direction (script);
248 return scm_from_double (offset + dir * slur->pure_height (slur, start, end).length () / 4);
251 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, outside_slur_callback, 2, 1, "");
253 Slur::outside_slur_callback (SCM grob, SCM offset_scm)
255 Grob *script = unsmob_grob (grob);
256 Grob *slur = unsmob_grob (script->get_object ("slur"));
258 if (!slur)
259 return offset_scm;
261 SCM avoid = script->get_property ("avoid-slur");
262 if (avoid != ly_symbol2scm ("outside")
263 && avoid != ly_symbol2scm ("around"))
264 return offset_scm;
266 Direction dir = get_grob_direction (script);
267 if (dir == CENTER)
268 return offset_scm;
270 Grob *cx = script->common_refpoint (slur, X_AXIS);
271 Grob *cy = script->common_refpoint (slur, Y_AXIS);
273 Bezier curve = Slur::get_curve (slur);
275 curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
276 slur->relative_coordinate (cy, Y_AXIS)));
278 Interval yext = robust_relative_extent (script, cy, Y_AXIS);
279 Interval xext = robust_relative_extent (script, cx, X_AXIS);
281 Real offset = robust_scm2double (offset_scm, 0);
282 yext.translate (offset);
284 /* FIXME: slur property, script property? */
285 Real slur_padding = robust_scm2double (script->get_property ("slur-padding"),
286 0.0);
287 yext.widen (slur_padding);
289 const Real EPS = 1e-3;
290 Interval bezext (curve.control_[0][X_AXIS], curve.control_[3][X_AXIS]);
291 bool consider[] = {false, false, false};
292 Real ys[] = {0, 0, 0};
293 bool do_shift = false;
295 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
297 Real x = xext.linear_combination ((Direction) d);
298 consider[k] = bezext.contains (x);
300 if (consider[k])
302 ys[k]
303 = (fabs (bezext[LEFT] - x) < EPS)
304 ? curve.control_[0][Y_AXIS]
305 : ((fabs (bezext[RIGHT] - x) < EPS)
306 ? curve.control_[3][Y_AXIS]
307 : curve.get_other_coordinate (X_AXIS, x));
309 /* Request shift if slur is contained script's Y, or if
310 script is inside slur and avoid == outside. */
311 if (yext.contains (ys[k])
312 || (dir * ys[k] > dir * yext[-dir] && avoid == ly_symbol2scm ("outside")))
313 do_shift = true;
317 Real avoidance_offset = 0.0;
318 if (do_shift)
320 for (int d = LEFT, k = 0; d <= RIGHT; d++, k++)
321 if (consider[k])
322 avoidance_offset = dir * (max (dir * avoidance_offset,
323 dir * (ys[k] - yext[-dir] + dir * slur_padding)));
325 return scm_from_double (offset + avoidance_offset);
329 * Used by Slur_engraver:: and Phrasing_slur_engraver::
331 void
332 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
333 vector<Grob*> &slurs,
334 vector<Grob*> &end_slurs)
336 if (slurs.empty () && end_slurs.empty ())
337 return;
339 Grob *e = info.grob ();
340 SCM avoid = e->get_property ("avoid-slur");
341 if (Tie::has_interface (e)
342 || avoid == ly_symbol2scm ("inside"))
344 for (vsize i = slurs.size (); i--;)
345 add_extra_encompass (slurs[i], e);
346 for (vsize i = end_slurs.size (); i--;)
347 add_extra_encompass (end_slurs[i], e);
349 else if (avoid == ly_symbol2scm ("outside")
350 || avoid == ly_symbol2scm ("around"))
352 Grob *slur;
353 if (end_slurs.size () && !slurs.size ())
354 slur = end_slurs[0];
355 else
356 slur = slurs[0];
358 if (slur)
360 chain_offset_callback (e, outside_slur_callback_proc, Y_AXIS);
361 chain_callback (e, outside_slur_cross_staff_proc, ly_symbol2scm("cross-staff"));
362 e->set_object ("slur", slur->self_scm ());
365 else if (avoid != ly_symbol2scm ("ignore"))
366 e->warning (_f ("Ignoring grob for slur: %s. avoid-slur not set?",
367 e->name().c_str ()));
371 A callback that will be chained together with the original cross-staff
372 value of a grob that is placed 'outside or 'around a slur. This just says
373 that any grob becomes cross-staff if it is placed 'outside or 'around a
374 cross-staff slur.
376 MAKE_SCHEME_CALLBACK (Slur, outside_slur_cross_staff, 2)
378 Slur::outside_slur_cross_staff (SCM smob, SCM previous)
380 if (previous == SCM_BOOL_T)
381 return previous;
383 Grob *me = unsmob_grob (smob);
384 Grob *slur = unsmob_grob (me->get_object ("slur"));
386 if (!slur)
387 return SCM_BOOL_F;
388 return slur->get_property ("cross-staff");
391 MAKE_SCHEME_CALLBACK (Slur, calc_cross_staff, 1)
393 Slur::calc_cross_staff (SCM smob)
395 Grob *me = unsmob_grob (smob);
397 extract_grob_set (me, "note-columns", cols);
398 extract_grob_set (me, "encompass-objects", extras);
400 for (vsize i = 0; i < cols.size (); i++)
402 if (Grob *s = Note_column::get_stem (cols[i]))
403 if (to_boolean (s->get_property ("cross-staff")))
404 return SCM_BOOL_T;
407 /* the separation items are dealt with in replace_breakable_encompass_objects
408 so we can ignore them here */
409 vector<Grob*> non_sep_extras;
410 for (vsize i = 0; i < extras.size (); i++)
411 if (!Separation_item::has_interface (extras[i]))
412 non_sep_extras.push_back (extras[i]);
414 Grob *common = common_refpoint_of_array (cols, me, Y_AXIS);
415 common = common_refpoint_of_array (non_sep_extras, common, Y_AXIS);
417 return scm_from_bool (common != me->get_parent (Y_AXIS));
420 ADD_INTERFACE (Slur,
421 "A slur.",
423 /* properties */
424 "annotation "
425 "avoid-slur " /* UGH. */
426 "control-points "
427 "dash-definition "
428 "details "
429 "direction "
430 "eccentricity "
431 "encompass-objects "
432 "height-limit "
433 "inspect-quants "
434 "inspect-index "
435 "line-thickness "
436 "note-columns "
437 "positions "
438 "quant-score "
439 "ratio "
440 "thickness "