]> git.donarmstrong.com Git - lilypond.git/blob - lily/arpeggio.cc
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond into...
[lilypond.git] / lily / arpeggio.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2010 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::staff_space (ss) / 2.0;
82
83       heads.unite (iv + ss->relative_coordinate (common, Y_AXIS)
84                    - 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     {
149       mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
150     }
151
152   mol.translate_axis (heads[LEFT], Y_AXIS);
153   if (dir)
154     mol.add_at_edge (Y_AXIS, dir, arrow, 0);
155
156   return mol.smobbed_copy ();
157 }
158
159 /* Draws a vertical bracket to the left of a chord
160    Chris Jackson <chris@fluffhouse.org.uk> */
161
162 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
163 SCM
164 Arpeggio::brew_chord_bracket (SCM smob)
165 {
166   Grob *me = unsmob_grob (smob);
167   Interval heads = robust_scm2interval (me->get_property ("positions"),
168                                         Interval())
169     * Staff_symbol_referencer::staff_space (me);
170
171   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
172   Real sp = 1.5 * Staff_symbol_referencer::staff_space (me);
173   Real dy = heads.length () + sp;
174   Real x = 0.7;
175
176   Stencil mol (Lookup::bracket (Y_AXIS, Interval (0, dy), lt, x, lt));
177   mol.translate_axis (heads[LEFT] - sp / 2.0, Y_AXIS);
178   return mol.smobbed_copy ();
179 }
180
181 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
182 SCM
183 Arpeggio::brew_chord_slur (SCM smob)
184 {
185   Grob *me = unsmob_grob (smob);
186   SCM dash_definition = me->get_property ("dash-definition");
187   Interval heads = robust_scm2interval (me->get_property ("positions"),
188                                         Interval())
189     * Staff_symbol_referencer::staff_space (me);
190
191   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
192   Real dy = heads.length ();
193
194   Real height_limit = 1.5;
195   Real ratio = .33;
196   Bezier curve = slur_shape (dy, height_limit, ratio);
197   curve.rotate (M_PI / 2);
198
199   Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
200   mol.translate_axis (heads[LEFT], Y_AXIS);
201   return mol.smobbed_copy ();
202 }
203
204 /*
205   We have to do a callback, because print () triggers a
206   vertical alignment if it is cross-staff.
207 */
208 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
209 SCM
210 Arpeggio::width (SCM smob)
211 {
212   Grob *me = unsmob_grob (smob);
213   return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
214 }
215
216 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
217 SCM
218 Arpeggio::pure_height (SCM smob, SCM, SCM)
219 {
220   Grob *me = unsmob_grob (smob);
221   if (to_boolean (me->get_property ("cross-staff")))
222     return ly_interval2scm (Interval ());
223
224   return Grob::stencil_height (smob);
225 }
226
227 ADD_INTERFACE (Arpeggio,
228                "Functions and settings for drawing an arpeggio symbol.",
229
230                /* properties */
231                "arpeggio-direction "
232                "positions "
233                "script-priority " // TODO: make around-note-interface
234                "stems "
235                "dash-definition " // TODO: make apply to non-slur arpeggios
236                );
237