2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2011 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"
22 #include "all-font-metrics.hh"
24 #include "font-interface.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;
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_brace, 1);
181 Arpeggio::brew_chord_brace (SCM smob)
183 Grob *me = unsmob_grob (smob);
184 Interval heads = robust_scm2interval (me->get_property ("positions"),
186 * Staff_symbol_referencer::staff_space (me);
187 int minimum_brace_height = robust_scm2int (
188 me->get_property ("minimum-brace-height"), 1);
189 Font_metric *fm = Font_interface::get_default_font (me);
193 int hi = max ((int) fm->count () - 1, 2);
195 /* do a binary search for each Y, not very efficient, but passable? */
199 int cmp = (lo + hi) / 2;
200 b = fm->get_indexed_char_dimensions (cmp);
201 if (b[Y_AXIS].is_empty () || b[Y_AXIS].length () > heads.length ()+1)
208 if (lo < minimum_brace_height)
209 lo = minimum_brace_height;
211 Stencil mol (unsmob_metrics (me->get_property ("font"))
212 ->find_by_name ("brace" + to_string (lo)));
213 mol.translate_axis ((heads[RIGHT] + heads[LEFT]) / 2, Y_AXIS);
215 return mol.smobbed_copy ();
218 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
220 Arpeggio::brew_chord_slur (SCM smob)
222 Grob *me = unsmob_grob (smob);
223 SCM dash_definition = me->get_property ("dash-definition");
224 Interval heads = robust_scm2interval (me->get_property ("positions"),
226 * Staff_symbol_referencer::staff_space (me);
228 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
229 Real dy = heads.length ();
231 Real height_limit = 1.5;
233 Bezier curve = slur_shape (dy, height_limit, ratio);
234 curve.rotate (M_PI / 2);
236 Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
237 mol.translate_axis (heads[LEFT], Y_AXIS);
238 return mol.smobbed_copy ();
242 We have to do a callback, because print () triggers a
243 vertical alignment if it is cross-staff.
245 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
247 Arpeggio::width (SCM smob)
249 Grob *me = unsmob_grob (smob);
250 return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
253 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
255 Arpeggio::pure_height (SCM smob, SCM, SCM)
257 Grob *me = unsmob_grob (smob);
258 if (to_boolean (me->get_property ("cross-staff")))
259 return ly_interval2scm (Interval ());
261 return Grob::stencil_height (smob);
264 ADD_INTERFACE (Arpeggio,
265 "Functions and settings for drawing an arpeggio symbol.",
268 "arpeggio-direction "
269 "minimum-brace-height "
271 "script-priority " // TODO: make around-note-interface
273 "dash-definition " // TODO: make apply to non-slur arpeggios