]> git.donarmstrong.com Git - lilypond.git/blob - lily/arpeggio.cc
Merge branch 'master' into lilypond/translation
[lilypond.git] / lily / arpeggio.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2011 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 "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"
32
33 static Stencil
34 get_squiggle (Grob *me)
35 {
36   Font_metric *fm = Font_interface::get_default_font (me);
37   Stencil squiggle = fm->find_by_name ("scripts.arpeggio");
38
39   return squiggle;
40 }
41
42 Grob *
43 Arpeggio::get_common_y (Grob *me)
44 {
45   Grob *common = me;
46
47   extract_grob_set (me, "stems", stems);
48   for (vsize i = 0; i < stems.size (); i++)
49     {
50       Grob *stem = stems[i];
51       common = common->common_refpoint (Staff_symbol_referencer::get_staff_symbol (stem),
52                                         Y_AXIS);
53     }
54
55   return common;
56 }
57
58 MAKE_SCHEME_CALLBACK (Arpeggio, calc_positions, 1);
59 SCM
60 Arpeggio::calc_positions (SCM grob)
61 {
62   Grob *me = unsmob_grob (grob);
63   Grob *common = get_common_y (me);
64
65   /*
66     TODO:
67
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.
71   */
72   Interval heads;
73   Real my_y = me->relative_coordinate (common, Y_AXIS);
74
75   extract_grob_set (me, "stems", stems);
76   for (vsize i = 0; i < stems.size (); i++)
77     {
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);
84     }
85
86   heads *= 1 / Staff_symbol_referencer::staff_space (me);
87
88   return ly_interval2scm (heads);
89 }
90
91 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
92 SCM
93 Arpeggio::print (SCM smob)
94 {
95   Grob *me = unsmob_grob (smob);
96   Interval heads = robust_scm2interval (me->get_property ("positions"),
97                                         Interval ())
98                    * Staff_symbol_referencer::staff_space (me);
99
100   if (heads.is_empty () || heads.length () < 0.5)
101     {
102       if (to_boolean (me->get_property ("transparent")))
103         {
104           /*
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.
109           */
110           heads.unite (get_squiggle (me).extent (Y_AXIS));
111         }
112       else
113         {
114           me->warning ("no heads for arpeggio found?");
115           me->suicide ();
116           return SCM_EOL;
117         }
118     }
119
120   SCM ad = me->get_property ("arpeggio-direction");
121   Direction dir = CENTER;
122   if (is_direction (ad))
123     dir = to_dir (ad);
124
125   Stencil mol;
126   Stencil squiggle (get_squiggle (me));
127
128   /*
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.
135   */
136   const Real epsilon = 1e-3;
137
138   Stencil arrow;
139   if (dir)
140     {
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 ();
144     }
145
146   while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
147     mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
148
149   mol.translate_axis (heads[LEFT], Y_AXIS);
150   if (dir)
151     mol.add_at_edge (Y_AXIS, dir, arrow, 0);
152
153   return mol.smobbed_copy ();
154 }
155
156 /* Draws a vertical bracket to the left of a chord
157    Chris Jackson <chris@fluffhouse.org.uk> */
158
159 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
160 SCM
161 Arpeggio::brew_chord_bracket (SCM smob)
162 {
163   Grob *me = unsmob_grob (smob);
164   Interval heads = robust_scm2interval (me->get_property ("positions"),
165                                         Interval ())
166                    * Staff_symbol_referencer::staff_space (me);
167
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;
172
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 ();
176 }
177
178 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
179 SCM
180 Arpeggio::brew_chord_slur (SCM smob)
181 {
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);
187
188   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
189   Real dy = heads.length ();
190
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);
195
196   Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
197   mol.translate_axis (heads[LEFT], Y_AXIS);
198   return mol.smobbed_copy ();
199 }
200
201 /*
202   We have to do a callback, because print () triggers a
203   vertical alignment if it is cross-staff.
204 */
205 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
206 SCM
207 Arpeggio::width (SCM smob)
208 {
209   Grob *me = unsmob_grob (smob);
210   return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
211 }
212
213 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
214 SCM
215 Arpeggio::pure_height (SCM smob, SCM, SCM)
216 {
217   Grob *me = unsmob_grob (smob);
218   if (to_boolean (me->get_property ("cross-staff")))
219     return ly_interval2scm (Interval ());
220
221   return Grob::stencil_height (smob);
222 }
223
224 ADD_INTERFACE (Arpeggio,
225                "Functions and settings for drawing an arpeggio symbol.",
226
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
233               );
234