2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2012 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_positions, 1);
61 Arpeggio::calc_positions (SCM grob)
63 Grob *me = unsmob_grob (grob);
64 Grob *common = get_common_y (me);
69 Using stems here is not very convenient; should store noteheads
70 instead, and also put them into the support. Now we will mess up
71 in vicinity of a collision.
74 Real my_y = me->relative_coordinate (common, Y_AXIS);
76 extract_grob_set (me, "stems", stems);
77 for (vsize i = 0; i < stems.size (); i++)
79 Grob *stem = stems[i];
80 Grob *ss = Staff_symbol_referencer::get_staff_symbol (stem);
81 Interval iv = Stem::head_positions (stem);
82 iv *= Staff_symbol_referencer::staff_space (me) / 2.0;
83 Real staff_y = ss ? ss->relative_coordinate (common, Y_AXIS) : 0.0;
84 heads.unite (iv + staff_y - my_y);
87 heads *= 1 / Staff_symbol_referencer::staff_space (me);
89 return ly_interval2scm (heads);
92 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
94 Arpeggio::print (SCM smob)
96 Grob *me = unsmob_grob (smob);
97 Interval heads = robust_scm2interval (me->get_property ("positions"),
99 * Staff_symbol_referencer::staff_space (me);
101 if (heads.is_empty () || heads.length () < 0.5)
103 if (to_boolean (me->get_property ("transparent")))
106 This is part of a cross-staff/-voice span-arpeggio,
107 so we need to ensure `heads' is large enough to encompass
108 a single trill-element since the span-arpeggio depends on
109 its children to prevent collisions.
111 heads.unite (get_squiggle (me).extent (Y_AXIS));
115 me->warning (_ ("no heads for arpeggio found?"));
121 SCM ad = me->get_property ("arpeggio-direction");
122 Direction dir = CENTER;
123 if (is_direction (ad))
127 Stencil squiggle (get_squiggle (me));
130 Compensate for rounding error which may occur when a chord
131 reaches the center line, resulting in an extra squiggle
132 being added to the arpeggio stencil. This value is appreciably
133 larger than the rounding error, which is in the region of 1e-16
134 for a global-staff-size of 20, but small enough that it does not
135 interfere with smaller staff sizes.
137 const Real epsilon = 1e-3;
142 Font_metric *fm = Font_interface::get_default_font (me);
143 arrow = fm->find_by_name ("scripts.arpeggio.arrow." + to_string (dir));
144 heads[dir] -= dir * arrow.extent (Y_AXIS).length ();
147 while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
148 mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
150 mol.translate_axis (heads[LEFT], Y_AXIS);
152 mol.add_at_edge (Y_AXIS, dir, arrow, 0);
154 return mol.smobbed_copy ();
157 /* Draws a vertical bracket to the left of a chord
158 Chris Jackson <chris@fluffhouse.org.uk> */
160 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
162 Arpeggio::brew_chord_bracket (SCM smob)
164 Grob *me = unsmob_grob (smob);
165 Interval heads = robust_scm2interval (me->get_property ("positions"),
167 * Staff_symbol_referencer::staff_space (me);
169 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
170 Real sp = 1.5 * Staff_symbol_referencer::staff_space (me);
171 Real dy = heads.length () + sp;
172 Real x = robust_scm2double (me->get_property ("protrusion"), 0.4);
174 Stencil mol (Lookup::bracket (Y_AXIS, Interval (0, dy), lt, x, lt));
175 mol.translate_axis (heads[LEFT] - sp / 2.0, Y_AXIS);
176 return mol.smobbed_copy ();
179 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
181 Arpeggio::brew_chord_slur (SCM smob)
183 Grob *me = unsmob_grob (smob);
184 SCM dash_definition = me->get_property ("dash-definition");
185 Interval heads = robust_scm2interval (me->get_property ("positions"),
187 * Staff_symbol_referencer::staff_space (me);
189 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
190 Real dy = heads.length ();
192 Real height_limit = 1.5;
194 Bezier curve = slur_shape (dy, height_limit, ratio);
195 curve.rotate (M_PI / 2);
197 Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
198 mol.translate_axis (heads[LEFT], Y_AXIS);
199 return mol.smobbed_copy ();
203 We have to do a callback, because print () triggers a
204 vertical alignment if it is cross-staff.
206 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
208 Arpeggio::width (SCM smob)
210 Grob *me = unsmob_grob (smob);
211 return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
214 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
216 Arpeggio::pure_height (SCM smob, SCM, SCM)
218 Grob *me = unsmob_grob (smob);
219 if (to_boolean (me->get_property ("cross-staff")))
220 return ly_interval2scm (Interval ());
222 return Grob::stencil_height (smob);
225 ADD_INTERFACE (Arpeggio,
226 "Functions and settings for drawing an arpeggio symbol.",
229 "arpeggio-direction "
230 "dash-definition " // TODO: make apply to non-slur arpeggios
233 "script-priority " // TODO: make around-note-interface