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