]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem-tremolo.cc
Web-ja: update introduction
[lilypond.git] / lily / stem-tremolo.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 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 "note-collision.hh"
28 #include "note-column.hh"
29 #include "output-def.hh"
30 #include "staff-symbol-referencer.hh"
31 #include "stem.hh"
32 #include "warn.hh"
33
34 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_slope, 1)
35 SCM
36 Stem_tremolo::calc_slope (SCM smob)
37 {
38   Grob *me = unsmob<Grob> (smob);
39   Grob *stem = unsmob<Grob> (me->get_object ("stem"));
40   Spanner *beam = Stem::get_beam (stem);
41
42   SCM style = me->get_property ("style");
43
44   if (beam && !scm_is_eq (style, ly_symbol2scm ("constant")))
45     {
46       Real dy = 0;
47       SCM s = beam->get_property ("quantized-positions");
48       if (is_number_pair (s))
49         dy = - scm_to_double (scm_car (s)) + scm_to_double (scm_cdr (s));
50
51       Grob *s2 = Beam::last_normal_stem (beam);
52       Grob *s1 = Beam::first_normal_stem (beam);
53
54       Grob *common = s1->common_refpoint (s2, X_AXIS);
55       Real dx = s2->relative_coordinate (common, X_AXIS)
56                 - s1->relative_coordinate (common, X_AXIS);
57
58       return scm_from_double (dx ? dy / dx : 0);
59     }
60   else
61     /* down stems with flags should have more sloped trems (helps avoid
62        flag/stem collisions without making the stem very long) */
63     return scm_from_double ((Stem::duration_log (stem) >= 3
64                              && get_grob_direction (me) == DOWN && !beam)
65                             ? 0.40 : 0.25);
66 }
67
68 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_width, 1)
69 SCM
70 Stem_tremolo::calc_width (SCM smob)
71 {
72   Grob *me = unsmob<Grob> (smob);
73   Grob *stem = unsmob<Grob> (me->get_object ("stem"));
74   Direction dir = get_grob_direction (me);
75   bool beam = Stem::get_beam (stem);
76   bool flag = Stem::duration_log (stem) >= 3 && !beam;
77
78   /* beamed stems and up-stems with flags have shorter tremolos */
79   return scm_from_double (((dir == UP && flag) || beam) ? 1.0 : 1.5);
80 }
81
82 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_shape, 1)
83 SCM
84 Stem_tremolo::calc_shape (SCM smob)
85 {
86   Grob *me = unsmob<Grob> (smob);
87   Grob *stem = unsmob<Grob> (me->get_object ("stem"));
88   Direction dir = get_grob_direction (me);
89   bool beam = Stem::get_beam (stem);
90   bool flag = Stem::duration_log (stem) >= 3 && !beam;
91   SCM style = me->get_property ("style");
92
93   return ly_symbol2scm (!scm_is_eq (style, ly_symbol2scm ("constant"))
94                         && ((dir == UP && flag) || beam)
95                         ? "rectangle" : "beam-like");
96 }
97
98 Real
99 Stem_tremolo::get_beam_translation (Grob *me)
100 {
101   Grob *stem = unsmob<Grob> (me->get_object ("stem"));
102   Spanner *beam = Stem::get_beam (stem);
103
104   return (beam && beam->is_live ())
105          ? Beam::get_beam_translation (beam)
106          : (Staff_symbol_referencer::staff_space (me)
107             * robust_scm2double (me->get_property ("length-fraction"), 1.0) * 0.81);
108 }
109
110 Stencil
111 Stem_tremolo::raw_stencil (Grob *me, Real slope, Direction dir)
112 {
113   Real ss = Staff_symbol_referencer::staff_space (me);
114   Real thick = robust_scm2double (me->get_property ("beam-thickness"), 1);
115   Real width = robust_scm2double (me->get_property ("beam-width"), 1);
116   Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
117   SCM shape = me->get_property ("shape");
118   if (!scm_is_symbol (shape))
119     shape = ly_symbol2scm ("beam-like");
120
121   width *= ss;
122   thick *= ss;
123
124   Stencil a;
125   if (scm_is_eq (shape, ly_symbol2scm ("rectangle")))
126     a = Lookup::rotated_box (slope, width, thick, blot);
127   else
128     a = Lookup::beam (slope, width, thick, blot);
129
130   a.align_to (X_AXIS, CENTER);
131   a.align_to (Y_AXIS, CENTER);
132
133   int tremolo_flags = robust_scm2int (me->get_property ("flag-count"), 0);
134   if (!tremolo_flags)
135     {
136       programming_error ("no tremolo flags");
137
138       me->suicide ();
139       return Stencil ();
140     }
141
142   Real beam_translation = get_beam_translation (me);
143
144   Stencil mol;
145   for (int i = 0; i < tremolo_flags; i++)
146     {
147       Stencil b (a);
148       b.translate_axis (beam_translation * i * dir * -1, Y_AXIS);
149       mol.add_stencil (b);
150     }
151   return mol;
152 }
153
154 MAKE_SCHEME_CALLBACK (Stem_tremolo, pure_height, 3);
155 SCM
156 Stem_tremolo::pure_height (SCM smob, SCM, SCM)
157 {
158   Item *me = unsmob<Item> (smob);
159
160   /*
161     Cannot use the real slope, since it looks at the Beam.
162    */
163   Stencil s1 (untranslated_stencil (me, 0.35));
164   Item *stem = unsmob<Item> (me->get_object ("stem"));
165   if (!stem)
166     return ly_interval2scm (s1.extent (Y_AXIS));
167
168   Direction dir = get_grob_direction (me);
169
170   Spanner *beam = Stem::get_beam (stem);
171
172   if (!beam)
173     return ly_interval2scm (s1.extent (Y_AXIS));
174
175   Interval ph = stem->pure_y_extent (stem, 0, INT_MAX);
176   Stem_info si = Stem::get_stem_info (stem);
177   ph[-dir] = si.shortest_y_;
178   int beam_count = Stem::beam_multiplicity (stem).length () + 1;
179   Real beam_translation = get_beam_translation (me);
180
181   ph = ph - dir * max (beam_count, 1) * beam_translation;
182   ph = ph - ph.center ();
183
184   return ly_interval2scm (ph);
185 }
186
187 MAKE_SCHEME_CALLBACK (Stem_tremolo, width, 1);
188 SCM
189 Stem_tremolo::width (SCM smob)
190 {
191   Grob *me = unsmob<Grob> (smob);
192
193   /*
194     Cannot use the real slope, since it looks at the Beam.
195    */
196   Stencil s1 (untranslated_stencil (me, 0.35));
197
198   return ly_interval2scm (s1.extent (X_AXIS));
199 }
200
201 Real
202 Stem_tremolo::vertical_length (Grob *me)
203 {
204   return untranslated_stencil (me, 0.35).extent (Y_AXIS).length ();
205 }
206
207 Stencil
208 Stem_tremolo::untranslated_stencil (Grob *me, Real slope)
209 {
210   Grob *stem = unsmob<Grob> (me->get_object ("stem"));
211   if (!stem)
212     {
213       programming_error ("no stem for stem-tremolo");
214       return Stencil ();
215     }
216
217   Direction dir = get_grob_direction (me);
218
219   bool whole_note = Stem::duration_log (stem) <= 0;
220
221   /* for a whole note, we position relative to the notehead, so we want the
222      stencil aligned on the flag closest to the head */
223   Direction stencil_dir = whole_note ? -dir : dir;
224   return raw_stencil (me, slope, stencil_dir);
225 }
226
227 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_y_offset, 1);
228 SCM
229 Stem_tremolo::calc_y_offset (SCM smob)
230 {
231   Grob *me = unsmob<Grob> (smob);
232   return scm_from_double (y_offset (me, false));
233 }
234
235 MAKE_SCHEME_CALLBACK (Stem_tremolo, pure_calc_y_offset, 3);
236 SCM
237 Stem_tremolo::pure_calc_y_offset (SCM smob,
238                                   SCM, /* start */
239                                   SCM /* end */)
240 {
241   Grob *me = unsmob<Grob> (smob);
242   return scm_from_double (y_offset (me, true));
243 }
244
245 MAKE_SCHEME_CALLBACK (Stem_tremolo, calc_direction, 1);
246 SCM
247 Stem_tremolo::calc_direction (SCM smob)
248 {
249   Item *me = unsmob<Item> (smob);
250
251   Item *stem = unsmob<Item> (me->get_object ("stem"));
252   if (!stem)
253     return scm_from_int (CENTER);
254
255   Direction stemdir = get_grob_direction (stem);
256
257   vector<int> nhp = Stem::note_head_positions (stem);
258   /*
259    * We re-decide stem-dir if there may be collisions with other
260    * note heads in the staff.
261    */
262   Grob *maybe_nc = stem->get_parent (X_AXIS)->get_parent (X_AXIS);
263   bool whole_note = Stem::duration_log (stem) <= 0;
264   if (whole_note && has_interface<Note_collision_interface> (maybe_nc))
265     {
266       Drul_array<bool> avoid_me (false, false);
267       vector<int> all_nhps = Note_collision_interface::note_head_positions (maybe_nc);
268       if (all_nhps[0] < nhp[0])
269         avoid_me[DOWN] = true;
270       if (all_nhps.back () > nhp.back ())
271         avoid_me[UP] = true;
272       if (avoid_me[stemdir])
273         {
274           stemdir = -stemdir;
275           if (avoid_me[stemdir])
276             {
277               me->warning ("Whole-note tremolo may collide with simultaneous notes.");
278               stemdir = -stemdir;
279             }
280         }
281     }
282   return scm_from_int (stemdir);
283 }
284
285 Real
286 Stem_tremolo::y_offset (Grob *me, bool pure)
287 {
288   Item *stem = unsmob<Item> (me->get_object ("stem"));
289   if (!stem)
290     return 0.0;
291
292   Direction dir = get_grob_direction (me);
293
294   Spanner *beam = Stem::get_beam (stem);
295   Real beam_translation = get_beam_translation (me);
296
297   int beam_count = beam ? (Stem::beam_multiplicity (stem).length () + 1) : 0;
298
299   if (pure && beam)
300     {
301       Interval ph = stem->pure_y_extent (stem, 0, INT_MAX);
302       Stem_info si = Stem::get_stem_info (stem);
303       ph[-dir] = si.shortest_y_;
304
305       return (ph - dir * max (beam_count, 1) * beam_translation)[dir] - dir * 0.5 * me->pure_y_extent (me, 0, INT_MAX).length ();
306     }
307
308   Real end_y
309     = (pure
310        ? stem->pure_y_extent (stem, 0, INT_MAX)[dir]
311        : stem->extent (stem, Y_AXIS)[dir])
312       - dir * max (beam_count, 1) * beam_translation
313       - Stem::beam_end_corrective (stem);
314
315   if (!beam && Stem::duration_log (stem) >= 3)
316     {
317       end_y -= dir * (Stem::duration_log (stem) - 2) * beam_translation;
318       if (dir == UP)
319         end_y -= dir * beam_translation * 0.5;
320     }
321
322   bool whole_note = Stem::duration_log (stem) <= 0;
323   if (whole_note || isinf(end_y))
324     {
325       /* we shouldn't position relative to the end of the stem since the stem
326          is invisible */
327       Real ss = Staff_symbol_referencer::staff_space (me);
328       vector<int> nhp = Stem::note_head_positions (stem);
329       Real note_head = (dir == UP ? nhp.back () : nhp[0]) * ss / 2;
330       end_y = note_head + dir * 1.5;
331     }
332
333   return end_y;
334 }
335
336 MAKE_SCHEME_CALLBACK (Stem_tremolo, print, 1);
337 SCM
338 Stem_tremolo::print (SCM grob)
339 {
340   Grob *me = unsmob<Grob> (grob);
341
342   Stencil s = untranslated_stencil (me, robust_scm2double (me->get_property ("slope"), 0.25));
343   return s.smobbed_copy ();
344 }
345
346 ADD_INTERFACE (Stem_tremolo,
347                "A beam slashing a stem to indicate a tremolo.  The property"
348                " @code{shape} can be @code{beam-like} or @code{rectangle}.",
349
350                /* properties */
351                "beam-thickness "
352                "beam-width "
353                "direction "
354                "flag-count "
355                "length-fraction "
356                "stem "
357                "shape "
358                "slope "
359               );