]> git.donarmstrong.com Git - lilypond.git/blob - lily/arpeggio.cc
Fix 932.
[lilypond.git] / lily / arpeggio.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2009 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 Grob *
34 Arpeggio::get_common_y (Grob *me)
35 {
36   Grob *common = me;
37
38   extract_grob_set (me, "stems", stems);
39   for (vsize i = 0; i < stems.size (); i++)
40     {
41       Grob *stem = stems[i];
42       common = common->common_refpoint (Staff_symbol_referencer::get_staff_symbol (stem),
43                                         Y_AXIS);
44     }
45
46   return common;
47 }
48
49 MAKE_SCHEME_CALLBACK(Arpeggio, calc_positions, 1);
50 SCM
51 Arpeggio::calc_positions (SCM grob)
52 {
53   Grob *me = unsmob_grob (grob);
54   Grob *common = get_common_y (me);
55   
56   /*
57     TODO:
58
59     Using stems here is not very convenient; should store noteheads
60     instead, and also put them into the support. Now we will mess up
61     in vicinity of a collision.
62   */
63   Interval heads;
64   Real my_y = me->relative_coordinate (common, Y_AXIS);
65
66   extract_grob_set (me, "stems", stems);
67   for (vsize i = 0; i < stems.size (); i++)
68     {
69       Grob *stem = stems[i];
70       Grob *ss = Staff_symbol_referencer::get_staff_symbol (stem);
71       Interval iv = Stem::head_positions (stem);
72       iv *= Staff_symbol::staff_space (ss) / 2.0;
73
74       heads.unite (iv + ss->relative_coordinate (common, Y_AXIS)
75                    - my_y);
76     }
77
78   heads *= 1/Staff_symbol_referencer::staff_space(me);
79
80   return ly_interval2scm (heads);
81 }
82
83 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
84 SCM
85 Arpeggio::print (SCM smob)
86 {
87   Grob *me = unsmob_grob (smob);
88   Interval heads = robust_scm2interval (me->get_property ("positions"),
89                                         Interval())
90     * Staff_symbol_referencer::staff_space (me);
91   
92   if (heads.is_empty () || heads.length () < 0.5)
93     {
94       if (!to_boolean (me->get_property ("transparent")))
95         {
96           me->warning ("no heads for arpeggio found?");
97           me->suicide ();
98         }
99       return SCM_EOL;
100     }
101
102   SCM ad = me->get_property ("arpeggio-direction");
103   Direction dir = CENTER;
104   if (is_direction (ad))
105     dir = to_dir (ad);
106
107   Stencil mol;
108   Font_metric *fm = Font_interface::get_default_font (me);
109   Stencil squiggle = fm->find_by_name ("scripts.arpeggio");
110
111   /*
112     Compensate for rounding error which may occur when a chord
113     reaches the center line, resulting in an extra squiggle
114     being added to the arpeggio stencil.  This value is appreciably
115     larger than the rounding error, which is in the region of 1e-16
116     for a global-staff-size of 20, but small enough that it does not
117     interfere with smaller staff sizes.
118   */
119   const Real epsilon = 1e-3;
120
121   Stencil arrow;
122   if (dir)
123     {
124       arrow = fm->find_by_name ("scripts.arpeggio.arrow." + to_string (dir));
125       heads[dir] -= dir * arrow.extent (Y_AXIS).length ();
126     }
127
128   while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
129     {
130       mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
131     }
132
133   mol.translate_axis (heads[LEFT], Y_AXIS);
134   if (dir)
135     mol.add_at_edge (Y_AXIS, dir, arrow, 0);
136
137   return mol.smobbed_copy ();
138 }
139
140 /* Draws a vertical bracket to the left of a chord
141    Chris Jackson <chris@fluffhouse.org.uk> */
142
143 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
144 SCM
145 Arpeggio::brew_chord_bracket (SCM smob)
146 {
147   Grob *me = unsmob_grob (smob);
148   Interval heads = robust_scm2interval (me->get_property ("positions"),
149                                         Interval())
150     * Staff_symbol_referencer::staff_space (me);
151
152   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
153   Real sp = 1.5 * Staff_symbol_referencer::staff_space (me);
154   Real dy = heads.length () + sp;
155   Real x = 0.7;
156
157   Stencil mol (Lookup::bracket (Y_AXIS, Interval (0, dy), lt, x, lt));
158   mol.translate_axis (heads[LEFT] - sp / 2.0, Y_AXIS);
159   return mol.smobbed_copy ();
160 }
161
162 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
163 SCM
164 Arpeggio::brew_chord_slur (SCM smob)
165 {
166   Grob *me = unsmob_grob (smob);
167   SCM dash_definition = me->get_property ("dash-definition");
168   Interval heads = robust_scm2interval (me->get_property ("positions"),
169                                         Interval())
170     * Staff_symbol_referencer::staff_space (me);
171
172   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
173   Real dy = heads.length ();
174
175   Real height_limit = 1.5;
176   Real ratio = .33;
177   Bezier curve = slur_shape (dy, height_limit, ratio);
178   curve.rotate (M_PI / 2);
179
180   Stencil mol (Lookup::slur (curve, lt, lt, dash_definition));
181   mol.translate_axis (heads[LEFT], Y_AXIS);
182   return mol.smobbed_copy ();
183 }
184
185 /*
186   We have to do a callback, because print () triggers a
187   vertical alignment if it is cross-staff.
188 */
189 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
190 SCM
191 Arpeggio::width (SCM smob)
192 {
193   Grob *me = unsmob_grob (smob);
194   Stencil arpeggio = Font_interface::get_default_font (me)->find_by_name ("scripts.arpeggio");
195
196   return ly_interval2scm (arpeggio.extent (X_AXIS));
197 }
198
199 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
200 SCM
201 Arpeggio::pure_height (SCM smob, SCM, SCM)
202 {
203   Grob *me = unsmob_grob (smob);
204   if (to_boolean (me->get_property ("cross-staff")))
205     return ly_interval2scm (Interval ());
206
207   return Grob::stencil_height (smob);
208 }
209
210 ADD_INTERFACE (Arpeggio,
211                "Functions and settings for drawing an arpeggio symbol.",
212
213                /* properties */
214                "arpeggio-direction "
215                "positions "
216                "script-priority " // TODO: make around-note-interface
217                "stems "
218                "dash-definition " // TODO: make apply to non-slur arpeggios
219                );
220