]> git.donarmstrong.com Git - lilypond.git/blob - lily/arpeggio.cc
Issue 5168 Let arpeggio-bracket/slur depend on grob-property thickness
[lilypond.git] / lily / arpeggio.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2015 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_cross_staff, 1);
60 SCM
61 Arpeggio::calc_cross_staff (SCM grob)
62 {
63   Grob *me = unsmob<Grob> (grob);
64
65   extract_grob_set (me, "stems", stems);
66   Grob *vag = 0;
67
68   for (vsize i = 0; i < stems.size (); i++)
69     {
70       if (!i)
71         vag = Grob::get_vertical_axis_group (stems[i]);
72       else
73         {
74           if (vag != Grob::get_vertical_axis_group (stems[i]))
75             return SCM_BOOL_T;
76         }
77     }
78
79   return SCM_BOOL_F;
80 }
81
82 MAKE_SCHEME_CALLBACK (Arpeggio, calc_positions, 1);
83 SCM
84 Arpeggio::calc_positions (SCM grob)
85 {
86   Grob *me = unsmob<Grob> (grob);
87   Grob *common = get_common_y (me);
88
89   /*
90     TODO:
91
92     Using stems here is not very convenient; should store noteheads
93     instead, and also put them into the support. Now we will mess up
94     in vicinity of a collision.
95   */
96   Interval heads;
97   Real my_y = me->relative_coordinate (common, Y_AXIS);
98
99   extract_grob_set (me, "stems", stems);
100   for (vsize i = 0; i < stems.size (); i++)
101     {
102       Grob *stem = stems[i];
103       Grob *ss = Staff_symbol_referencer::get_staff_symbol (stem);
104       Interval iv = Stem::head_positions (stem);
105       iv *= Staff_symbol_referencer::staff_space (me) / 2.0;
106       Real staff_y = ss ? ss->relative_coordinate (common, Y_AXIS) : 0.0;
107       heads.unite (iv + staff_y - my_y);
108     }
109
110   heads *= 1 / Staff_symbol_referencer::staff_space (me);
111
112   return ly_interval2scm (heads);
113 }
114
115 MAKE_SCHEME_CALLBACK (Arpeggio, print, 1);
116 SCM
117 Arpeggio::print (SCM smob)
118 {
119   Grob *me = unsmob<Grob> (smob);
120   Interval heads = robust_scm2interval (me->get_property ("positions"),
121                                         Interval ())
122                    * Staff_symbol_referencer::staff_space (me);
123
124   if (heads.is_empty () || heads.length () < 0.5)
125     {
126       if (to_boolean (me->get_property ("transparent")))
127         {
128           /*
129             This is part of a cross-staff/-voice span-arpeggio,
130             so we need to ensure `heads' is large enough to encompass
131             a single trill-element since the span-arpeggio depends on
132             its children to prevent collisions.
133           */
134           heads.unite (get_squiggle (me).extent (Y_AXIS));
135         }
136       else
137         {
138           me->warning (_ ("no heads for arpeggio found?"));
139           me->suicide ();
140           return SCM_EOL;
141         }
142     }
143
144   SCM ad = me->get_property ("arpeggio-direction");
145   Direction dir = CENTER;
146   if (is_direction (ad))
147     dir = to_dir (ad);
148
149   Stencil mol;
150   Stencil squiggle (get_squiggle (me));
151
152   /*
153     Compensate for rounding error which may occur when a chord
154     reaches the center line, resulting in an extra squiggle
155     being added to the arpeggio stencil.  This value is appreciably
156     larger than the rounding error, which is in the region of 1e-16
157     for a global-staff-size of 20, but small enough that it does not
158     interfere with smaller staff sizes.
159   */
160   const Real epsilon = 1e-3;
161
162   Stencil arrow;
163   if (dir)
164     {
165       Font_metric *fm = Font_interface::get_default_font (me);
166       arrow = fm->find_by_name ("scripts.arpeggio.arrow." + ::to_string (dir));
167       heads[dir] -= dir * arrow.extent (Y_AXIS).length ();
168     }
169
170   while (mol.extent (Y_AXIS).length () + epsilon < heads.length ())
171     mol.add_at_edge (Y_AXIS, UP, squiggle, 0.0);
172
173   mol.translate_axis (heads[LEFT], Y_AXIS);
174   if (dir)
175     mol.add_at_edge (Y_AXIS, dir, arrow, 0);
176
177   return mol.smobbed_copy ();
178 }
179
180 /* Draws a vertical bracket to the left of a chord
181    Chris Jackson <chris@fluffhouse.org.uk> */
182
183 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_bracket, 1);
184 SCM
185 Arpeggio::brew_chord_bracket (SCM smob)
186 {
187   Grob *me = unsmob<Grob> (smob);
188   Interval heads = robust_scm2interval (me->get_property ("positions"),
189                                         Interval ())
190                    * Staff_symbol_referencer::staff_space (me);
191
192   Real th
193     = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"))
194       * robust_scm2double (me->get_property ("thickness"), 1);
195   Real sp = 1.5 * Staff_symbol_referencer::staff_space (me);
196   Real dy = heads.length () + sp;
197   Real x = robust_scm2double (me->get_property ("protrusion"), 0.4);
198
199   Stencil mol (Lookup::bracket (Y_AXIS, Interval (0, dy), th, x, th));
200   mol.translate_axis (heads[LEFT] - sp / 2.0, Y_AXIS);
201   return mol.smobbed_copy ();
202 }
203
204 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
205 SCM
206 Arpeggio::brew_chord_slur (SCM smob)
207 {
208   Grob *me = unsmob<Grob> (smob);
209   SCM dash_definition = me->get_property ("dash-definition");
210   Interval heads = robust_scm2interval (me->get_property ("positions"),
211                                         Interval ())
212                    * Staff_symbol_referencer::staff_space (me);
213
214   Real lt
215     = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"))
216       * robust_scm2double (me->get_property ("line-thickness"), 1.0);
217   Real th
218     = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"))
219       * robust_scm2double (me->get_property ("thickness"), 1.0);
220   Real dy = heads.length ();
221
222   Real height_limit = 1.5;
223   Real ratio = .33;
224   Bezier curve = slur_shape (dy, height_limit, ratio);
225   curve.rotate (90.0);
226
227   Stencil mol (Lookup::slur (curve, th, lt, dash_definition));
228   mol.translate_axis (heads[LEFT], Y_AXIS);
229   return mol.smobbed_copy ();
230 }
231
232 /*
233   We have to do a callback, because print () triggers a
234   vertical alignment if it is cross-staff.
235 */
236 MAKE_SCHEME_CALLBACK (Arpeggio, width, 1);
237 SCM
238 Arpeggio::width (SCM smob)
239 {
240   Grob *me = unsmob<Grob> (smob);
241   return ly_interval2scm (get_squiggle (me).extent (X_AXIS));
242 }
243
244 MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3);
245 SCM
246 Arpeggio::pure_height (SCM smob, SCM, SCM)
247 {
248   Grob *me = unsmob<Grob> (smob);
249   if (to_boolean (me->get_property ("cross-staff")))
250     return ly_interval2scm (Interval ());
251
252   return Grob::stencil_height (smob);
253 }
254
255 ADD_INTERFACE (Arpeggio,
256                "Functions and settings for drawing an arpeggio symbol.",
257
258                /* properties */
259                "arpeggio-direction "
260                "dash-definition " // TODO: make apply to non-slur arpeggios
261                "line-thickness "
262                "positions "
263                "protrusion "
264                "script-priority " // TODO: make around-note-interface
265                "stems "
266                "thickness "
267               );
268