2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2015 Jan Nieuwenhuizen <janneke@gnu.org>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "arpeggio.hh"
23 #include "font-interface.hh"
25 #include "international.hh"
27 #include "output-def.hh"
28 #include "pointer-group-interface.hh"
29 #include "staff-symbol-referencer.hh"
30 #include "staff-symbol.hh"
35 get_squiggle (Grob *me)
37 Font_metric *fm = Font_interface::get_default_font (me);
38 Stencil squiggle = fm->find_by_name ("scripts.arpeggio");
44 Arpeggio::get_common_y (Grob *me)
48 extract_grob_set (me, "stems", stems);
49 for (vsize i = 0; i < stems.size (); i++)
51 Grob *stem = stems[i];
52 common = common->common_refpoint (Staff_symbol_referencer::get_staff_symbol (stem),
59 MAKE_SCHEME_CALLBACK (Arpeggio, calc_cross_staff, 1);
61 Arpeggio::calc_cross_staff (SCM grob)
63 Grob *me = Grob::unsmob (grob);
65 extract_grob_set (me, "stems", stems);
68 for (vsize i = 0; i < stems.size (); i++)
71 vag = Grob::get_vertical_axis_group (stems[i]);
74 if (vag != Grob::get_vertical_axis_group (stems[i]))
82 MAKE_SCHEME_CALLBACK (Arpeggio, calc_positions, 1);
84 Arpeggio::calc_positions (SCM grob)
86 Grob *me = Grob::unsmob (grob);
87 Grob *common = get_common_y (me);
92 Using stems here is not very convenient; should store noteheads
93 instead, and also put them into the support. Now we will mess up
94 in vicinity of a collision.
97 Real my_y = me->relative_coordinate (common, Y_AXIS);
99 extract_grob_set (me, "stems", stems);
100 for (vsize i = 0; i < stems.size (); i++)
102 Grob *stem = stems[i];
103 Grob *ss = Staff_symbol_referencer::get_staff_symbol (stem);
104 Interval iv = Stem::head_positions (stem);
105 iv *= Staff_symbol_referencer::staff_space (me) / 2.0;
106 Real staff_y = ss ? ss->relative_coordinate (common, Y_AXIS) : 0.0;
107 heads.unite (iv + staff_y - my_y);
110 heads *= 1 / Staff_symbol_referencer::staff_space (me);
112 return ly_interval2scm (heads);
115 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
117 Arpeggio::print (SCM smob)
119 Grob *me = Grob::unsmob (smob);
120 Interval heads = robust_scm2interval (me->get_property ("positions"),
122 * Staff_symbol_referencer::staff_space (me);
124 if (heads.is_empty () || heads.length () < 0.5)
126 if (to_boolean (me->get_property ("transparent")))
129 This is part of a cross-staff/-voice span-arpeggio,
130 so we need to ensure `heads' is large enough to encompass
131 a single trill-element since the span-arpeggio depends on
132 its children to prevent collisions.
134 heads.unite (get_squiggle (me).extent (Y_AXIS));
138 me->warning (_ ("no heads for arpeggio found?"));
144 SCM ad = me->get_property ("arpeggio-direction");
145 Direction dir = CENTER;
146 if (is_direction (ad))
150 Stencil squiggle (get_squiggle (me));
153 Compensate for rounding error which may occur when a chord
154 reaches the center line, resulting in an extra squiggle
155 being added to the arpeggio stencil. This value is appreciably
156 larger than the rounding error, which is in the region of 1e-16
157 for a global-staff-size of 20, but small enough that it does not
158 interfere with smaller staff sizes.
160 const Real epsilon = 1e-3;
165 Font_metric *fm = Font_interface::get_default_font (me);
166 arrow = fm->find_by_name ("scripts.arpeggio.arrow." + ::to_string (dir));
167 heads[dir] -= dir * arrow.extent (Y_AXIS).length ();
170 while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
171 mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
173 mol.translate_axis (heads[LEFT], Y_AXIS);
175 mol.add_at_edge (Y_AXIS, dir, arrow, 0);
177 return mol.smobbed_copy ();
180 /* Draws a vertical bracket to the left of a chord
181 Chris Jackson <chris@fluffhouse.org.uk> */
183 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
185 Arpeggio::brew_chord_bracket (SCM smob)
187 Grob *me = Grob::unsmob (smob);
188 Interval heads = robust_scm2interval (me->get_property ("positions"),
190 * Staff_symbol_referencer::staff_space (me);
192 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
193 Real sp = 1.5 * Staff_symbol_referencer::staff_space (me);
194 Real dy = heads.length () + sp;
195 Real x = robust_scm2double (me->get_property ("protrusion"), 0.4);
197 Stencil mol (Lookup::bracket (Y_AXIS, Interval (0, dy), lt, x, lt));
198 mol.translate_axis (heads[LEFT] - sp / 2.0, Y_AXIS);
199 return mol.smobbed_copy ();
202 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
204 Arpeggio::brew_chord_slur (SCM smob)
206 Grob *me = Grob::unsmob (smob);
207 SCM dash_definition = me->get_property ("dash-definition");
208 Interval heads = robust_scm2interval (me->get_property ("positions"),
210 * Staff_symbol_referencer::staff_space (me);
212 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
213 Real dy = heads.length ();
215 Real height_limit = 1.5;
217 Bezier curve = slur_shape (dy, height_limit, ratio);
218 curve.rotate (M_PI / 2);
220 Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
221 mol.translate_axis (heads[LEFT], Y_AXIS);
222 return mol.smobbed_copy ();
226 We have to do a callback, because print () triggers a
227 vertical alignment if it is cross-staff.
229 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
231 Arpeggio::width (SCM smob)
233 Grob *me = Grob::unsmob (smob);
234 return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
237 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
239 Arpeggio::pure_height (SCM smob, SCM, SCM)
241 Grob *me = Grob::unsmob (smob);
242 if (to_boolean (me->get_property ("cross-staff")))
243 return ly_interval2scm (Interval ());
245 return Grob::stencil_height (smob);
248 ADD_INTERFACE (Arpeggio,
249 "Functions and settings for drawing an arpeggio symbol.",
252 "arpeggio-direction "
253 "dash-definition " // TODO: make apply to non-slur arpeggios
256 "script-priority " // TODO: make around-note-interface