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/>.
22 #include "grob-info.hh"
23 #include "grob-array.hh"
26 #include "directional-element-interface.hh"
27 #include "font-interface.hh"
29 #include "pointer-group-interface.hh"
31 #include "main.hh" // DEBUG_SLUR_SCORING
32 #include "note-column.hh"
33 #include "output-def.hh"
35 #include "staff-symbol-referencer.hh"
37 #include "text-interface.hh"
40 #include "slur-scoring.hh"
41 #include "separation-item.hh"
42 #include "international.hh"
46 MAKE_SCHEME_CALLBACK (Slur
, calc_direction
, 1)
48 Slur::calc_direction (SCM smob
)
50 Grob
*me
= unsmob_grob (smob
);
51 extract_grob_set (me
, "note-columns", encompasses
);
53 if (encompasses
.empty ())
60 for (vsize i
= 0; i
< encompasses
.size (); i
++)
62 if (Note_column::dir (encompasses
[i
]) < 0)
68 return scm_from_int (d
);
71 MAKE_SCHEME_CALLBACK (Slur
, pure_height
, 3);
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
);
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
);
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
);
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 ())
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
);
133 SCM dash_definition
= me
->get_property ("dash-definition");
134 a
= Lookup::slur (one
,
135 get_grob_direction (me
) * base_thick
,
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
))
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
),
157 Stencil tm
= *unsmob_stencil (Text_interface::interpret_markup
158 (me
->layout ()->self_scm (), properties
,
160 a
.add_at_edge (Y_AXIS
, get_grob_direction (me
), tm
, 1.0);
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
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
]);
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
;
207 Slur::get_curve (Grob
*me
)
211 for (SCM s
= me
->get_property ("control-points"); scm_is_pair (s
);
213 b
.control_
[i
++] = ly_scm2offset (scm_car (s
));
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
);
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"));
242 SCM avoid
= script
->get_property ("avoid-slur");
243 if (avoid
!= ly_symbol2scm ("outside") && avoid
!= ly_symbol2scm ("around"))
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"));
261 SCM avoid
= script
->get_property ("avoid-slur");
262 if (avoid
!= ly_symbol2scm ("outside")
263 && avoid
!= ly_symbol2scm ("around"))
266 Direction dir
= get_grob_direction (script
);
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"),
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
);
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")))
317 Real avoidance_offset
= 0.0;
320 for (int d
= LEFT
, k
= 0; d
<= RIGHT
; d
++, 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::
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 ())
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"))
353 if (end_slurs
.size () && !slurs
.size ())
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
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
)
383 Grob
*me
= unsmob_grob (smob
);
384 Grob
*slur
= unsmob_grob (me
->get_object ("slur"));
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")))
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
));
425 "avoid-slur " /* UGH. */