]> git.donarmstrong.com Git - lilypond.git/blob - lily/arpeggio.cc
Merge branch 'master' into translation
[lilypond.git] / lily / arpeggio.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2012 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_positions, 1);
60 SCM
61 Arpeggio::calc_positions (SCM grob)
62 {
63   Grob *me = unsmob_grob (grob);
64   Grob *common = get_common_y (me);
65
66   /*
67     TODO:
68
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.
72   */
73   Interval heads;
74   Real my_y = me->relative_coordinate (common, Y_AXIS);
75
76   extract_grob_set (me, "stems", stems);
77   for (vsize i = 0; i < stems.size (); i++)
78     {
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);
85     }
86
87   heads *= 1 / Staff_symbol_referencer::staff_space (me);
88
89   return ly_interval2scm (heads);
90 }
91
92 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
93 SCM
94 Arpeggio::print (SCM smob)
95 {
96   Grob *me = unsmob_grob (smob);
97   Interval heads = robust_scm2interval (me->get_property ("positions"),
98                                         Interval ())
99                    * Staff_symbol_referencer::staff_space (me);
100
101   if (heads.is_empty () || heads.length () < 0.5)
102     {
103       if (to_boolean (me->get_property ("transparent")))
104         {
105           /*
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.
110           */
111           heads.unite (get_squiggle (me).extent (Y_AXIS));
112         }
113       else
114         {
115           me->warning (_ ("no heads for arpeggio found?"));
116           me->suicide ();
117           return SCM_EOL;
118         }
119     }
120
121   SCM ad = me->get_property ("arpeggio-direction");
122   Direction dir = CENTER;
123   if (is_direction (ad))
124     dir = to_dir (ad);
125
126   Stencil mol;
127   Stencil squiggle (get_squiggle (me));
128
129   /*
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.
136   */
137   const Real epsilon = 1e-3;
138
139   Stencil arrow;
140   if (dir)
141     {
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 ();
145     }
146
147   while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
148     mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
149
150   mol.translate_axis (heads[LEFT], Y_AXIS);
151   if (dir)
152     mol.add_at_edge (Y_AXIS, dir, arrow, 0);
153
154   return mol.smobbed_copy ();
155 }
156
157 /* Draws a vertical bracket to the left of a chord
158    Chris Jackson <chris@fluffhouse.org.uk> */
159
160 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
161 SCM
162 Arpeggio::brew_chord_bracket (SCM smob)
163 {
164   Grob *me = unsmob_grob (smob);
165   Interval heads = robust_scm2interval (me->get_property ("positions"),
166                                         Interval ())
167                    * Staff_symbol_referencer::staff_space (me);
168
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;
172   Real x = robust_scm2double (me->get_property ("protrusion"), 0.4);
173
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 ();
177 }
178
179 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
180 SCM
181 Arpeggio::brew_chord_slur (SCM smob)
182 {
183   Grob *me = unsmob_grob (smob);
184   SCM dash_definition = me->get_property ("dash-definition");
185   Interval heads = robust_scm2interval (me->get_property ("positions"),
186                                         Interval ())
187                    * Staff_symbol_referencer::staff_space (me);
188
189   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
190   Real dy = heads.length ();
191
192   Real height_limit = 1.5;
193   Real ratio = .33;
194   Bezier curve = slur_shape (dy, height_limit, ratio);
195   curve.rotate (M_PI / 2);
196
197   Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
198   mol.translate_axis (heads[LEFT], Y_AXIS);
199   return mol.smobbed_copy ();
200 }
201
202 /*
203   We have to do a callback, because print () triggers a
204   vertical alignment if it is cross-staff.
205 */
206 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
207 SCM
208 Arpeggio::width (SCM smob)
209 {
210   Grob *me = unsmob_grob (smob);
211   return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
212 }
213
214 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
215 SCM
216 Arpeggio::pure_height (SCM smob, SCM, SCM)
217 {
218   Grob *me = unsmob_grob (smob);
219   if (to_boolean (me->get_property ("cross-staff")))
220     return ly_interval2scm (Interval ());
221
222   return Grob::stencil_height (smob);
223 }
224
225 ADD_INTERFACE (Arpeggio,
226                "Functions and settings for drawing an arpeggio symbol.",
227
228                /* properties */
229                "arpeggio-direction "
230                "dash-definition " // TODO: make apply to non-slur arpeggios
231                "positions "
232                "protrusion "
233                "script-priority " // TODO: make around-note-interface
234                "stems "
235               );
236