]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem-tremolo.cc
Update source file headers. Fixes using standard GNU package conventions.
[lilypond.git] / lily / stem-tremolo.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
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 "stem-tremolo.hh"
21
22 #include "spanner.hh"
23 #include "beam.hh"
24 #include "directional-element-interface.hh"
25 #include "item.hh"
26 #include "lookup.hh"
27 #include "output-def.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "stem.hh"
30 #include "warn.hh"
31
32 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_slope, 1)
33 SCM
34 Stem_tremolo::calc_slope (SCM smob)
35 {
36   Grob *me = unsmob_grob (smob);
37   Grob *stem = unsmob_grob (me->get_object ("stem"));
38   Spanner *beam = Stem::get_beam (stem);
39
40   if (beam)
41     {
42       Real dy = 0;
43       SCM s = beam->get_property ("quantized-positions");
44       if (is_number_pair (s))
45         dy = - scm_to_double (scm_car (s)) + scm_to_double (scm_cdr (s));
46
47       Grob *s2 = Beam::last_normal_stem (beam);
48       Grob *s1 = Beam::first_normal_stem (beam);
49       
50       Grob *common = s1->common_refpoint (s2, X_AXIS);
51       Real dx = s2->relative_coordinate (common, X_AXIS) -
52         s1->relative_coordinate (common, X_AXIS);
53
54       return scm_from_double (dx ? dy / dx : 0);
55     }
56   else
57     /* down stems with flags should have more sloped trems (helps avoid
58        flag/stem collisions without making the stem very long) */
59     return scm_from_double (
60         (Stem::duration_log (stem) >= 3 && get_grob_direction (stem) == DOWN) ?
61           0.40 : 0.25);
62 }
63
64 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_width, 1)
65 SCM
66 Stem_tremolo::calc_width (SCM smob)
67 {
68   Grob *me = unsmob_grob (smob);
69   Grob *stem = unsmob_grob (me->get_object ("stem"));
70   Direction stemdir = get_grob_direction (stem);
71   bool beam = Stem::get_beam (stem);
72   bool flag = Stem::duration_log (stem) >= 3 && !beam;
73
74   /* beamed stems and up-stems with flags have shorter tremolos */
75   return scm_from_double (((stemdir == UP && flag) || beam)? 1.0 : 1.5);
76 }
77
78 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_style, 1)
79 SCM
80 Stem_tremolo::calc_style (SCM smob)
81 {
82   Grob *me = unsmob_grob (smob);
83   Grob *stem = unsmob_grob (me->get_object ("stem"));
84   Direction stemdir = get_grob_direction (stem);
85   bool beam = Stem::get_beam (stem);
86   bool flag = Stem::duration_log (stem) >= 3 && !beam;
87
88   return ly_symbol2scm (((stemdir == UP && flag) || beam) ? "rectangle" : "default");
89 }
90
91 Real
92 Stem_tremolo::get_beam_translation (Grob *me)
93 {
94   Grob *stem = unsmob_grob (me->get_object ("stem"));
95   Spanner *beam = Stem::get_beam (stem);
96
97   return (beam && beam->is_live ())
98     ? Beam::get_beam_translation (beam)
99     : (Staff_symbol_referencer::staff_space (me)
100        * robust_scm2double (me->get_property ("length-fraction"), 1.0) * 0.81);
101 }
102
103 Stencil
104 Stem_tremolo::raw_stencil (Grob *me, Real slope, Direction stemdir)
105 {
106   Real ss = Staff_symbol_referencer::staff_space (me);
107   Real thick = robust_scm2double (me->get_property ("beam-thickness"), 1);
108   Real width = robust_scm2double (me->get_property ("beam-width"), 1);
109   Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
110   SCM style = me->get_property ("style");
111   if (!scm_is_symbol (style))
112     style = ly_symbol2scm ("default");
113
114   width *= ss;
115   thick *= ss;
116
117   Stencil a;
118   if (style == ly_symbol2scm ("rectangle"))
119     a = Lookup::rotated_box (slope, width, thick, blot);
120   else
121     a = Lookup::beam (slope, width, thick, blot);
122
123   a.align_to (X_AXIS, CENTER);
124   a.align_to (Y_AXIS, CENTER);
125
126   int tremolo_flags = robust_scm2int (me->get_property ("flag-count"), 0);
127   if (!tremolo_flags)
128     {
129       programming_error ("no tremolo flags");
130
131       me->suicide ();
132       return Stencil ();
133     }
134
135   Real beam_translation = get_beam_translation (me);
136
137   Stencil mol;
138   for (int i = 0; i < tremolo_flags; i++)
139     {
140       Stencil b (a);
141       b.translate_axis (beam_translation * i * stemdir * -1, Y_AXIS);
142       mol.add_stencil (b);
143     }
144   return mol;
145 }
146
147
148
149 MAKE_SCHEME_CALLBACK (Stem_tremolo, height, 1);
150 SCM
151 Stem_tremolo::height (SCM smob)
152 {
153   Grob *me = unsmob_grob (smob);
154
155   /*
156     Cannot use the real slope, since it looks at the Beam.
157    */
158   Stencil s1 (translated_stencil (me, 0.35));
159
160   return ly_interval2scm (s1.extent (Y_AXIS));
161 }
162
163 MAKE_SCHEME_CALLBACK (Stem_tremolo, width, 1);
164 SCM
165 Stem_tremolo::width (SCM smob)
166 {
167   Grob *me = unsmob_grob (smob);
168
169   /*
170     Cannot use the real slope, since it looks at the Beam.
171    */
172   Stencil s1 (untranslated_stencil (me, 0.35));
173
174   return ly_interval2scm (s1.extent (X_AXIS));
175 }
176
177 Real
178 Stem_tremolo::vertical_length (Grob *me)
179 {
180   return untranslated_stencil (me, 0.35).extent (Y_AXIS).length ();
181 }
182   
183 Stencil
184 Stem_tremolo::untranslated_stencil (Grob *me, Real slope)
185 {
186   Grob *stem = unsmob_grob (me->get_object ("stem"));
187   if (!stem)
188     {
189       programming_error ("no stem for stem-tremolo");
190       return Stencil ();
191     }
192
193   Direction stemdir = get_grob_direction (stem);
194   if (!stemdir)
195     stemdir = UP;
196
197   bool whole_note = Stem::duration_log (stem) <= 0;
198
199   /* for a whole note, we position relative to the notehead, so we want the
200      stencil aligned on the flag closest to the head */
201   Direction stencil_dir = whole_note ? -stemdir : stemdir;
202   return raw_stencil (me, slope, stencil_dir);
203 }
204
205   
206 Stencil
207 Stem_tremolo::translated_stencil (Grob *me, Real slope)
208 {
209   Stencil mol = untranslated_stencil (me, slope);
210
211   Grob *stem = unsmob_grob (me->get_object ("stem"));
212   if (!stem)
213     return Stencil ();
214   
215   Direction stemdir = get_grob_direction (stem);
216   if (stemdir == 0)
217     stemdir = UP;
218
219   Spanner *beam = Stem::get_beam (stem);
220   Real beam_translation = get_beam_translation (me);
221
222   int beam_count = beam ? (Stem::beam_multiplicity (stem).length () + 1) : 0;
223   Real ss = Staff_symbol_referencer::staff_space (me);
224
225   Real end_y
226     = Stem::stem_end_position (stem) * ss / 2
227     - stemdir * max (beam_count, 1) * beam_translation;
228
229   if (!beam && Stem::duration_log (stem) >= 3)
230     {
231       end_y -= stemdir * (Stem::duration_log (stem) - 2) * beam_translation;
232       if (stemdir == UP)
233         end_y -= stemdir * beam_translation * 0.5;
234     }
235
236   bool whole_note = Stem::duration_log (stem) <= 0;
237   if (whole_note)
238     {
239       /* we shouldn't position relative to the end of the stem since the stem
240          is invisible */
241       vector<int> nhp = Stem::note_head_positions (stem);
242       Real note_head = (stemdir == UP ? nhp.back () : nhp[0]) * ss / 2;
243       end_y = note_head + stemdir * 1.5;
244     }
245   mol.translate_axis (end_y, Y_AXIS);
246
247   return mol;
248 }
249
250 MAKE_SCHEME_CALLBACK (Stem_tremolo, print, 1);
251 SCM
252 Stem_tremolo::print (SCM grob)
253 {
254   Grob *me = unsmob_grob (grob);
255   
256   Stencil s = translated_stencil (me, robust_scm2double (me->get_property ("slope"), 0.25));
257   return s.smobbed_copy ();
258 }
259
260 ADD_INTERFACE (Stem_tremolo,
261                "A beam slashing a stem to indicate a tremolo.  The property"
262                " @code{style} can be @code{default} or @code{rectangle}.",
263
264                /* properties */
265                "beam-thickness "
266                "beam-width "
267                "flag-count "
268                "length-fraction "
269                "stem "
270                "style "
271                "slope "
272                );