Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / arpeggio.cc
blobb609f846d7a350fd6a541dec4bb2540285d4bcea
1 /*
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 "bezier.hh"
23 #include "font-interface.hh"
24 #include "grob.hh"
25 #include "lookup.hh"
26 #include "output-def.hh"
27 #include "pointer-group-interface.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "staff-symbol.hh"
30 #include "stem.hh"
31 #include "warn.hh"
33 static Stencil
34 get_squiggle (Grob *me)
36 Font_metric *fm = Font_interface::get_default_font (me);
37 Stencil squiggle = fm->find_by_name ("scripts.arpeggio");
39 return squiggle;
42 Grob *
43 Arpeggio::get_common_y (Grob *me)
45 Grob *common = 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),
52 Y_AXIS);
55 return common;
58 MAKE_SCHEME_CALLBACK (Arpeggio, calc_positions, 1);
59 SCM
60 Arpeggio::calc_positions (SCM grob)
62 Grob *me = unsmob_grob (grob);
63 Grob *common = get_common_y (me);
66 TODO:
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.
72 Interval heads;
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_referencer::staff_space (me) / 2.0;
82 Real staff_y = ss ? ss->relative_coordinate (common, Y_AXIS) : 0.0;
83 heads.unite (iv + staff_y - my_y);
86 heads *= 1 / Staff_symbol_referencer::staff_space (me);
88 return ly_interval2scm (heads);
91 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
92 SCM
93 Arpeggio::print (SCM smob)
95 Grob *me = unsmob_grob (smob);
96 Interval heads = robust_scm2interval (me->get_property ("positions"),
97 Interval ())
98 * Staff_symbol_referencer::staff_space (me);
100 if (heads.is_empty () || heads.length () < 0.5)
102 if (to_boolean (me->get_property ("transparent")))
105 This is part of a cross-staff/-voice span-arpeggio,
106 so we need to ensure `heads' is large enough to encompass
107 a single trill-element since the span-arpeggio depends on
108 its children to prevent collisions.
110 heads.unite (get_squiggle (me).extent (Y_AXIS));
112 else
114 me->warning ("no heads for arpeggio found?");
115 me->suicide ();
116 return SCM_EOL;
120 SCM ad = me->get_property ("arpeggio-direction");
121 Direction dir = CENTER;
122 if (is_direction (ad))
123 dir = to_dir (ad);
125 Stencil mol;
126 Stencil squiggle (get_squiggle (me));
129 Compensate for rounding error which may occur when a chord
130 reaches the center line, resulting in an extra squiggle
131 being added to the arpeggio stencil. This value is appreciably
132 larger than the rounding error, which is in the region of 1e-16
133 for a global-staff-size of 20, but small enough that it does not
134 interfere with smaller staff sizes.
136 const Real epsilon = 1e-3;
138 Stencil arrow;
139 if (dir)
141 Font_metric *fm = Font_interface::get_default_font (me);
142 arrow = fm->find_by_name ("scripts.arpeggio.arrow." + to_string (dir));
143 heads[dir] -= dir * arrow.extent (Y_AXIS).length ();
146 while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
147 mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
149 mol.translate_axis (heads[LEFT], Y_AXIS);
150 if (dir)
151 mol.add_at_edge (Y_AXIS, dir, arrow, 0);
153 return mol.smobbed_copy ();
156 /* Draws a vertical bracket to the left of a chord
157 Chris Jackson <chris@fluffhouse.org.uk> */
159 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
161 Arpeggio::brew_chord_bracket (SCM smob)
163 Grob *me = unsmob_grob (smob);
164 Interval heads = robust_scm2interval (me->get_property ("positions"),
165 Interval ())
166 * Staff_symbol_referencer::staff_space (me);
168 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
169 Real sp = 1.5 * Staff_symbol_referencer::staff_space (me);
170 Real dy = heads.length () + sp;
171 Real x = 0.7;
173 Stencil mol (Lookup::bracket (Y_AXIS, Interval (0, dy), lt, x, lt));
174 mol.translate_axis (heads[LEFT] - sp / 2.0, Y_AXIS);
175 return mol.smobbed_copy ();
178 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
180 Arpeggio::brew_chord_slur (SCM smob)
182 Grob *me = unsmob_grob (smob);
183 SCM dash_definition = me->get_property ("dash-definition");
184 Interval heads = robust_scm2interval (me->get_property ("positions"),
185 Interval ())
186 * Staff_symbol_referencer::staff_space (me);
188 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
189 Real dy = heads.length ();
191 Real height_limit = 1.5;
192 Real ratio = .33;
193 Bezier curve = slur_shape (dy, height_limit, ratio);
194 curve.rotate (M_PI / 2);
196 Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
197 mol.translate_axis (heads[LEFT], Y_AXIS);
198 return mol.smobbed_copy ();
202 We have to do a callback, because print () triggers a
203 vertical alignment if it is cross-staff.
205 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
207 Arpeggio::width (SCM smob)
209 Grob *me = unsmob_grob (smob);
210 return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
213 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
215 Arpeggio::pure_height (SCM smob, SCM, SCM)
217 Grob *me = unsmob_grob (smob);
218 if (to_boolean (me->get_property ("cross-staff")))
219 return ly_interval2scm (Interval ());
221 return Grob::stencil_height (smob);
224 ADD_INTERFACE (Arpeggio,
225 "Functions and settings for drawing an arpeggio symbol.",
227 /* properties */
228 "arpeggio-direction "
229 "positions "
230 "script-priority " // TODO: make around-note-interface
231 "stems "
232 "dash-definition " // TODO: make apply to non-slur arpeggios