2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2010 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"
26 #include "output-def.hh"
27 #include "pointer-group-interface.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "staff-symbol.hh"
34 get_squiggle (Grob *me)
36 Font_metric *fm = Font_interface::get_default_font (me);
37 Stencil squiggle = fm->find_by_name ("scripts.arpeggio");
43 Arpeggio::get_common_y (Grob *me)
47 extract_grob_set (me, "stems", stems);
48 for (vsize i = 0; i < stems.size (); i++)
50 Grob *stem = stems[i];
51 common = common->common_refpoint (Staff_symbol_referencer::get_staff_symbol (stem),
58 MAKE_SCHEME_CALLBACK(Arpeggio, calc_positions, 1);
60 Arpeggio::calc_positions (SCM grob)
62 Grob *me = unsmob_grob (grob);
63 Grob *common = get_common_y (me);
68 Using stems here is not very convenient; should store noteheads
69 instead, and also put them into the support. Now we will mess up
70 in vicinity of a collision.
73 Real my_y = me->relative_coordinate (common, Y_AXIS);
75 extract_grob_set (me, "stems", stems);
76 for (vsize i = 0; i < stems.size (); i++)
78 Grob *stem = stems[i];
79 Grob *ss = Staff_symbol_referencer::get_staff_symbol (stem);
80 Interval iv = Stem::head_positions (stem);
81 iv *= Staff_symbol::staff_space (ss) / 2.0;
83 heads.unite (iv + ss->relative_coordinate (common, Y_AXIS)
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 ())
149 mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
152 mol.translate_axis (heads[LEFT], Y_AXIS);
154 mol.add_at_edge (Y_AXIS, dir, arrow, 0);
156 return mol.smobbed_copy ();
159 /* Draws a vertical bracket to the left of a chord
160 Chris Jackson <chris@fluffhouse.org.uk> */
162 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
164 Arpeggio::brew_chord_bracket (SCM smob)
166 Grob *me = unsmob_grob (smob);
167 Interval heads = robust_scm2interval (me->get_property ("positions"),
169 * Staff_symbol_referencer::staff_space (me);
171 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
172 Real sp = 1.5 * Staff_symbol_referencer::staff_space (me);
173 Real dy = heads.length () + sp;
176 Stencil mol (Lookup::bracket (Y_AXIS, Interval (0, dy), lt, x, lt));
177 mol.translate_axis (heads[LEFT] - sp / 2.0, Y_AXIS);
178 return mol.smobbed_copy ();
181 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
183 Arpeggio::brew_chord_slur (SCM smob)
185 Grob *me = unsmob_grob (smob);
186 SCM dash_definition = me->get_property ("dash-definition");
187 Interval heads = robust_scm2interval (me->get_property ("positions"),
189 * Staff_symbol_referencer::staff_space (me);
191 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
192 Real dy = heads.length ();
194 Real height_limit = 1.5;
196 Bezier curve = slur_shape (dy, height_limit, ratio);
197 curve.rotate (M_PI / 2);
199 Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
200 mol.translate_axis (heads[LEFT], Y_AXIS);
201 return mol.smobbed_copy ();
205 We have to do a callback, because print () triggers a
206 vertical alignment if it is cross-staff.
208 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
210 Arpeggio::width (SCM smob)
212 Grob *me = unsmob_grob (smob);
213 return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
216 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
218 Arpeggio::pure_height (SCM smob, SCM, SCM)
220 Grob *me = unsmob_grob (smob);
221 if (to_boolean (me->get_property ("cross-staff")))
222 return ly_interval2scm (Interval ());
224 return Grob::stencil_height (smob);
227 ADD_INTERFACE (Arpeggio,
228 "Functions and settings for drawing an arpeggio symbol.",
231 "arpeggio-direction "
233 "script-priority " // TODO: make around-note-interface
235 "dash-definition " // TODO: make apply to non-slur arpeggios