]> git.donarmstrong.com Git - lilypond.git/blob - lily/arpeggio.cc
95da047a067bd5ced57ba29f53a0a2ee93405bbe
[lilypond.git] / lily / arpeggio.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2015 Jan Nieuwenhuizen <janneke@gnu.org>
5
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.
10
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.
15
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/>.
18 */
19
20 #include "arpeggio.hh"
21
22 #include "bezier.hh"
23 #include "font-interface.hh"
24 #include "grob.hh"
25 #include "international.hh"
26 #include "lookup.hh"
27 #include "output-def.hh"
28 #include "pointer-group-interface.hh"
29 #include "staff-symbol-referencer.hh"
30 #include "staff-symbol.hh"
31 #include "stem.hh"
32 #include "warn.hh"
33
34 static Stencil
35 get_squiggle (Grob *me)
36 {
37   Font_metric *fm = Font_interface::get_default_font (me);
38   Stencil squiggle = fm->find_by_name ("scripts.arpeggio");
39
40   return squiggle;
41 }
42
43 Grob *
44 Arpeggio::get_common_y (Grob *me)
45 {
46   Grob *common = me;
47
48   extract_grob_set (me, "stems", stems);
49   for (vsize i = 0; i < stems.size (); i++)
50     {
51       Grob *stem = stems[i];
52       common = common->common_refpoint (Staff_symbol_referencer::get_staff_symbol (stem),
53                                         Y_AXIS);
54     }
55
56   return common;
57 }
58
59 MAKE_SCHEME_CALLBACK (Arpeggio, calc_cross_staff, 1);
60 SCM
61 Arpeggio::calc_cross_staff (SCM grob)
62 {
63   Grob *me = unsmob<Grob> (grob);
64
65   extract_grob_set (me, "stems", stems);
66   Grob *vag = 0;
67
68   for (vsize i = 0; i < stems.size (); i++)
69     {
70       if (!i)
71         vag = Grob::get_vertical_axis_group (stems[i]);
72       else
73         {
74           if (vag != Grob::get_vertical_axis_group (stems[i]))
75             return SCM_BOOL_T;
76         }
77     }
78
79   return SCM_BOOL_F;
80 }
81
82 MAKE_SCHEME_CALLBACK (Arpeggio, calc_positions, 1);
83 SCM
84 Arpeggio::calc_positions (SCM grob)
85 {
86   Grob *me = unsmob<Grob> (grob);
87   Grob *common = get_common_y (me);
88
89   /*
90     TODO:
91
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.
95   */
96   Interval heads;
97   Real my_y = me->relative_coordinate (common, Y_AXIS);
98
99   extract_grob_set (me, "stems", stems);
100   for (vsize i = 0; i < stems.size (); i++)
101     {
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);
108     }
109
110   heads *= 1 / Staff_symbol_referencer::staff_space (me);
111
112   return ly_interval2scm (heads);
113 }
114
115 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
116 SCM
117 Arpeggio::print (SCM smob)
118 {
119   Grob *me = unsmob<Grob> (smob);
120   Interval heads = robust_scm2interval (me->get_property ("positions"),
121                                         Interval ())
122                    * Staff_symbol_referencer::staff_space (me);
123
124   if (heads.is_empty () || heads.length () < 0.5)
125     {
126       if (to_boolean (me->get_property ("transparent")))
127         {
128           /*
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.
133           */
134           heads.unite (get_squiggle (me).extent (Y_AXIS));
135         }
136       else
137         {
138           me->warning (_ ("no heads for arpeggio found?"));
139           me->suicide ();
140           return SCM_EOL;
141         }
142     }
143
144   SCM ad = me->get_property ("arpeggio-direction");
145   Direction dir = CENTER;
146   if (is_direction (ad))
147     dir = to_dir (ad);
148
149   Stencil mol;
150   Stencil squiggle (get_squiggle (me));
151
152   /*
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.
159   */
160   const Real epsilon = 1e-3;
161
162   Stencil arrow;
163   if (dir)
164     {
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 ();
168     }
169
170   while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
171     mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
172
173   mol.translate_axis (heads[LEFT], Y_AXIS);
174   if (dir)
175     mol.add_at_edge (Y_AXIS, dir, arrow, 0);
176
177   return mol.smobbed_copy ();
178 }
179
180 /* Draws a vertical bracket to the left of a chord
181    Chris Jackson <chris@fluffhouse.org.uk> */
182
183 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
184 SCM
185 Arpeggio::brew_chord_bracket (SCM smob)
186 {
187   Grob *me = unsmob<Grob> (smob);
188   Interval heads = robust_scm2interval (me->get_property ("positions"),
189                                         Interval ())
190                    * Staff_symbol_referencer::staff_space (me);
191
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);
196
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 ();
200 }
201
202 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
203 SCM
204 Arpeggio::brew_chord_slur (SCM smob)
205 {
206   Grob *me = unsmob<Grob> (smob);
207   SCM dash_definition = me->get_property ("dash-definition");
208   Interval heads = robust_scm2interval (me->get_property ("positions"),
209                                         Interval ())
210                    * Staff_symbol_referencer::staff_space (me);
211
212   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
213   Real dy = heads.length ();
214
215   Real height_limit = 1.5;
216   Real ratio = .33;
217   Bezier curve = slur_shape (dy, height_limit, ratio);
218   curve.rotate (90.0);
219
220   Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
221   mol.translate_axis (heads[LEFT], Y_AXIS);
222   return mol.smobbed_copy ();
223 }
224
225 /*
226   We have to do a callback, because print () triggers a
227   vertical alignment if it is cross-staff.
228 */
229 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
230 SCM
231 Arpeggio::width (SCM smob)
232 {
233   Grob *me = unsmob<Grob> (smob);
234   return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
235 }
236
237 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
238 SCM
239 Arpeggio::pure_height (SCM smob, SCM, SCM)
240 {
241   Grob *me = unsmob<Grob> (smob);
242   if (to_boolean (me->get_property ("cross-staff")))
243     return ly_interval2scm (Interval ());
244
245   return Grob::stencil_height (smob);
246 }
247
248 ADD_INTERFACE (Arpeggio,
249                "Functions and settings for drawing an arpeggio symbol.",
250
251                /* properties */
252                "arpeggio-direction "
253                "dash-definition " // TODO: make apply to non-slur arpeggios
254                "positions "
255                "protrusion "
256                "script-priority " // TODO: make around-note-interface
257                "stems "
258               );
259