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 a
= Lookup::slur (one
,
124 get_grob_direction (me
) * base_thick
,
128 #if DEBUG_SLUR_SCORING
129 SCM annotation
= me
->get_property ("annotation");
130 if (!scm_is_string (annotation
))
132 SCM debug
= me
->layout ()->lookup_variable (ly_symbol2scm ("debug-slur-scoring"));
133 if (to_boolean (debug
))
134 annotation
= me
->get_property ("quant-score");
137 if (scm_is_string (annotation
))
140 SCM properties
= Font_interface::text_font_alist_chain (me
);
142 if (!scm_is_number (me
->get_property ("font-size")))
143 properties
= scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-6), SCM_EOL
),
146 Stencil tm
= *unsmob_stencil (Text_interface::interpret_markup
147 (me
->layout ()->self_scm (), properties
,
149 a
.add_at_edge (Y_AXIS
, get_grob_direction (me
), tm
, 1.0);
153 return a
.smobbed_copy ();
158 it would be better to do this at engraver level, but that is
159 fragile, as the breakable items are generated on staff level, at
160 which point slur starts and ends have to be tracked
163 Slur::replace_breakable_encompass_objects (Grob
*me
)
165 extract_grob_set (me
, "encompass-objects", extra_objects
);
166 vector
<Grob
*> new_encompasses
;
168 for (vsize i
= 0; i
< extra_objects
.size (); i
++)
170 Grob
*g
= extra_objects
[i
];
172 if (Separation_item::has_interface (g
))
174 extract_grob_set (g
, "elements", breakables
);
175 for (vsize j
= 0; j
< breakables
.size (); j
++)
176 /* if we encompass a separation-item that spans multiple staves,
177 we filter out the grobs that don't belong to our staff */
178 if (me
->common_refpoint (breakables
[j
], Y_AXIS
) == me
->get_parent (Y_AXIS
)
179 && breakables
[j
]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
180 new_encompasses
.push_back (breakables
[j
]);
183 new_encompasses
.push_back (g
);
186 SCM encompass_scm
= me
->get_object ("encompass-objects");
187 if (Grob_array::unsmob (encompass_scm
))
189 vector
<Grob
*> &arr
=
190 unsmob_grob_array (encompass_scm
)->array_reference ();
191 arr
= new_encompasses
;
196 Slur::get_curve (Grob
*me
)
200 for (SCM s
= me
->get_property ("control-points"); scm_is_pair (s
);
202 b
.control_
[i
++] = ly_scm2offset (scm_car (s
));
208 Slur::add_column (Grob
*me
, Grob
*n
)
210 Pointer_group_interface::add_grob (me
, ly_symbol2scm ("note-columns"), n
);
211 add_bound_item (dynamic_cast<Spanner
*> (me
), n
);
215 Slur::add_extra_encompass (Grob
*me
, Grob
*n
)
217 Pointer_group_interface::add_grob (me
, ly_symbol2scm ("encompass-objects"), n
);
220 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur
, pure_outside_slur_callback
, 4, 1, "");
222 Slur::pure_outside_slur_callback (SCM grob
, SCM start_scm
, SCM end_scm
, SCM offset_scm
)
224 int start
= robust_scm2int (start_scm
, 0);
225 int end
= robust_scm2int (end_scm
, 0);
226 Grob
*script
= unsmob_grob (grob
);
227 Grob
*slur
= unsmob_grob (script
->get_object ("slur"));
231 SCM avoid
= script
->get_property ("avoid-slur");
232 if (avoid
!= ly_symbol2scm ("outside") && avoid
!= ly_symbol2scm ("around"))
235 Real offset
= robust_scm2double (offset_scm
, 0.0);
236 Direction dir
= get_grob_direction (script
);
237 return scm_from_double (offset
+ dir
* slur
->pure_height (slur
, start
, end
).length () / 4);
240 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur
, outside_slur_callback
, 2, 1, "");
242 Slur::outside_slur_callback (SCM grob
, SCM offset_scm
)
244 Grob
*script
= unsmob_grob (grob
);
245 Grob
*slur
= unsmob_grob (script
->get_object ("slur"));
250 SCM avoid
= script
->get_property ("avoid-slur");
251 if (avoid
!= ly_symbol2scm ("outside")
252 && avoid
!= ly_symbol2scm ("around"))
255 Direction dir
= get_grob_direction (script
);
259 Grob
*cx
= script
->common_refpoint (slur
, X_AXIS
);
260 Grob
*cy
= script
->common_refpoint (slur
, Y_AXIS
);
262 Bezier curve
= Slur::get_curve (slur
);
264 curve
.translate (Offset (slur
->relative_coordinate (cx
, X_AXIS
),
265 slur
->relative_coordinate (cy
, Y_AXIS
)));
267 Interval yext
= robust_relative_extent (script
, cy
, Y_AXIS
);
268 Interval xext
= robust_relative_extent (script
, cx
, X_AXIS
);
270 Real offset
= robust_scm2double (offset_scm
, 0);
271 yext
.translate (offset
);
273 /* FIXME: slur property, script property? */
274 Real slur_padding
= robust_scm2double (script
->get_property ("slur-padding"),
276 yext
.widen (slur_padding
);
278 const Real EPS
= 1e-3;
279 Interval
bezext (curve
.control_
[0][X_AXIS
], curve
.control_
[3][X_AXIS
]);
280 bool consider
[] = {false, false, false};
281 Real ys
[] = {0, 0, 0};
282 bool do_shift
= false;
284 for (int d
= LEFT
, k
= 0; d
<= RIGHT
; d
++, k
++)
286 Real x
= xext
.linear_combination ((Direction
) d
);
287 consider
[k
] = bezext
.contains (x
);
292 = (fabs (bezext
[LEFT
] - x
) < EPS
)
293 ? curve
.control_
[0][Y_AXIS
]
294 : ((fabs (bezext
[RIGHT
] - x
) < EPS
)
295 ? curve
.control_
[3][Y_AXIS
]
296 : curve
.get_other_coordinate (X_AXIS
, x
));
298 /* Request shift if slur is contained script's Y, or if
299 script is inside slur and avoid == outside. */
300 if (yext
.contains (ys
[k
])
301 || (dir
* ys
[k
] > dir
* yext
[-dir
] && avoid
== ly_symbol2scm ("outside")))
306 Real avoidance_offset
= 0.0;
309 for (int d
= LEFT
, k
= 0; d
<= RIGHT
; d
++, k
++)
311 avoidance_offset
= dir
* (max (dir
* avoidance_offset
,
312 dir
* (ys
[k
] - yext
[-dir
] + dir
* slur_padding
)));
314 return scm_from_double (offset
+ avoidance_offset
);
318 * Used by Slur_engraver:: and Phrasing_slur_engraver::
321 Slur::auxiliary_acknowledge_extra_object (Grob_info
const &info
,
322 vector
<Grob
*> &slurs
,
323 vector
<Grob
*> &end_slurs
)
325 if (slurs
.empty () && end_slurs
.empty ())
328 Grob
*e
= info
.grob ();
329 SCM avoid
= e
->get_property ("avoid-slur");
330 if (Tie::has_interface (e
)
331 || avoid
== ly_symbol2scm ("inside"))
333 for (vsize i
= slurs
.size (); i
--;)
334 add_extra_encompass (slurs
[i
], e
);
335 for (vsize i
= end_slurs
.size (); i
--;)
336 add_extra_encompass (end_slurs
[i
], e
);
338 else if (avoid
== ly_symbol2scm ("outside")
339 || avoid
== ly_symbol2scm ("around"))
342 if (end_slurs
.size () && !slurs
.size ())
349 chain_offset_callback (e
, outside_slur_callback_proc
, Y_AXIS
);
350 chain_callback (e
, outside_slur_cross_staff_proc
, ly_symbol2scm("cross-staff"));
351 e
->set_object ("slur", slur
->self_scm ());
354 else if (avoid
!= ly_symbol2scm ("ignore"))
355 e
->warning (_f ("Ignoring grob for slur: %s. avoid-slur not set?",
356 e
->name().c_str ()));
360 A callback that will be chained together with the original cross-staff
361 value of a grob that is placed 'outside or 'around a slur. This just says
362 that any grob becomes cross-staff if it is placed 'outside or 'around a
365 MAKE_SCHEME_CALLBACK (Slur
, outside_slur_cross_staff
, 2)
367 Slur::outside_slur_cross_staff (SCM smob
, SCM previous
)
369 if (previous
== SCM_BOOL_T
)
372 Grob
*me
= unsmob_grob (smob
);
373 Grob
*slur
= unsmob_grob (me
->get_object ("slur"));
377 return slur
->get_property ("cross-staff");
380 MAKE_SCHEME_CALLBACK (Slur
, calc_cross_staff
, 1)
382 Slur::calc_cross_staff (SCM smob
)
384 Grob
*me
= unsmob_grob (smob
);
386 extract_grob_set (me
, "note-columns", cols
);
387 extract_grob_set (me
, "encompass-objects", extras
);
389 for (vsize i
= 0; i
< cols
.size (); i
++)
391 if (Grob
*s
= Note_column::get_stem (cols
[i
]))
392 if (to_boolean (s
->get_property ("cross-staff")))
396 /* the separation items are dealt with in replace_breakable_encompass_objects
397 so we can ignore them here */
398 vector
<Grob
*> non_sep_extras
;
399 for (vsize i
= 0; i
< extras
.size (); i
++)
400 if (!Separation_item::has_interface (extras
[i
]))
401 non_sep_extras
.push_back (extras
[i
]);
403 Grob
*common
= common_refpoint_of_array (cols
, me
, Y_AXIS
);
404 common
= common_refpoint_of_array (non_sep_extras
, common
, Y_AXIS
);
406 return scm_from_bool (common
!= me
->get_parent (Y_AXIS
));
414 "avoid-slur " /* UGH. */