]> git.donarmstrong.com Git - lilypond.git/blob - lily/hairpin.cc
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond into...
[lilypond.git] / lily / hairpin.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2010 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 "hairpin.hh"
21
22 #include "dimensions.hh"
23 #include "international.hh"
24 #include "line-interface.hh"
25 #include "output-def.hh"
26 #include "paper-column.hh"
27 #include "pointer-group-interface.hh"
28 #include "spanner.hh"
29 #include "staff-symbol-referencer.hh"
30 #include "text-interface.hh"
31 #include "note-column.hh"
32 #include "warn.hh"
33
34 MAKE_SCHEME_CALLBACK (Hairpin, pure_height, 3);
35 SCM
36 Hairpin::pure_height (SCM smob, SCM, SCM)
37 {
38   Grob *me = unsmob_grob (smob);
39   Real height = robust_scm2double (me->get_property ("height"), 0.0)
40     * Staff_symbol_referencer::staff_space (me);
41
42   Real thickness = robust_scm2double (me->get_property ("thickness"), 1)
43     * Staff_symbol_referencer::line_thickness (me);
44
45   height += thickness / 2;
46   return ly_interval2scm (Interval (-height, height));
47 }
48
49 MAKE_SCHEME_CALLBACK (Hairpin, print, 1);
50 SCM
51 Hairpin::print (SCM smob)
52 {
53   Spanner *me = unsmob_spanner (smob);
54
55   SCM s = me->get_property ("grow-direction");
56   if (!is_direction (s))
57     {
58       me->suicide ();
59       return SCM_EOL;
60     }
61
62   Direction grow_dir = to_dir (s);
63   Real padding = robust_scm2double (me->get_property ("bound-padding"), 0.5);
64
65   Drul_array<bool> broken;
66   Drul_array<Item *> bounds;
67   Direction d = LEFT;
68   do
69     {
70       bounds[d] = me->get_bound (d);
71       broken[d] = bounds[d]->break_status_dir () != CENTER;
72     }
73   while (flip (&d) != LEFT);
74
75   broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT);
76   broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT)->is_live ();
77
78   if (broken[RIGHT])
79     {
80       Spanner *next = me->broken_neighbor (RIGHT);
81       Stencil *s = next->get_stencil ();
82       if (!s || s->is_empty ())
83         broken[RIGHT] = false;
84     }
85
86   Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
87   Drul_array<Real> x_points;
88
89   /*
90     Use the height and thickness of the hairpin when making a circled tip
91   */
92   bool circled_tip = ly_scm2bool (me->get_property ("circled-tip"));
93   Real height = robust_scm2double (me->get_property ("height"), 0.2)
94     * Staff_symbol_referencer::staff_space (me);
95   /*
96     FIXME: 0.525 is still just a guess...
97   */
98   Real rad = height * 0.525;
99   Real thick = 1.0;
100   if (circled_tip)
101     thick = robust_scm2double (me->get_property ("thickness"), 1.0)
102       * Staff_symbol_referencer::line_thickness (me);
103
104   do
105     {
106       Item *b = bounds[d];
107       x_points[d] = b->relative_coordinate (common, X_AXIS);
108       if (broken [d])
109         {
110           if (d == LEFT)
111             x_points[d] = b->extent (common, X_AXIS)[RIGHT];
112         }
113       else
114         {
115           if (Text_interface::has_interface (b))
116             {
117               Interval e = b->extent (common, X_AXIS);
118               if (!e.is_empty ())
119                 x_points[d] = e[-d] - d * padding;
120             }
121           else
122             {
123               bool neighbor_found = false;
124               Spanner *adjacent;
125               extract_grob_set (me, "adjacent-spanners", neighbors);
126               for (vsize i = 0; i < neighbors.size (); i++)
127                 {
128                   /*
129                     FIXME: this will fuck up in case of polyphonic
130                     notes in other voices. Need to look at note-columns
131                     in the current staff/voice.
132                   */
133                   adjacent = dynamic_cast<Spanner *> (neighbors[i]);
134                   if (adjacent
135                       && (adjacent->get_bound (-d)->get_column ()
136                           == b->get_column ()))
137                     {
138                       neighbor_found = true;
139                       break;
140                     }
141                 }
142
143               Interval e = robust_relative_extent (b, common, X_AXIS);
144               if (neighbor_found)
145                 {
146                   if (Hairpin::has_interface (adjacent))
147                     {
148                       /*
149                         Handle back-to-back hairpins with a circle in the middle
150                       */
151                       if (circled_tip && (grow_dir != d))
152                         x_points[d] = e.center () + d * (rad - thick / 2.0);
153                       /*
154                         If we're hung on a paper column, that means we're not
155                         adjacent to a text-dynamic, and we may move closer. We
156                         make the padding a little smaller, here.
157                       */
158                       else
159                         x_points[d] = e.center () - d * padding / 3;
160                     }
161                   // Our neighbor is a dynamic text spanner, so add the
162                   // same amount of padding as for text dynamics
163                   else
164                     x_points[d] = e[-d] - d * padding;
165                 }
166               else
167                 {
168                   if (Note_column::has_interface (b)
169                       && Note_column::has_rests (b))
170                     x_points[d] = e[-d];
171                   else
172                     x_points[d] = e[d];
173
174                   Item *bound = me->get_bound (d);
175                   if (bound->is_non_musical (bound))
176                     x_points[d] -= d * padding;
177                 }
178             }
179         }
180     }
181   while (flip (&d) != LEFT);
182
183   Real width = x_points[RIGHT] - x_points[LEFT];
184   if (width < 0)
185     {
186       me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
187                       : "crescendo too small"));
188       width = 0;
189     }
190
191   bool continued = broken[Direction (-grow_dir)];
192
193   Real starth = 0;
194   Real endh = 0;
195   if (grow_dir < 0)
196     {
197       starth = height;
198       endh = continued ? height / 2 : 0.0;
199     }
200   else
201     {
202       starth = continued ? height / 2 : 0.0;
203       endh = height;
204     }
205
206   /*
207     should do relative to staff-symbol staff-space?
208   */
209   Stencil mol;
210   Real x = 0.0;
211
212   /*
213     Compensate for size of circle
214   */
215   Direction tip_dir = -grow_dir;
216   if (circled_tip && !broken[tip_dir])
217     {
218       if (grow_dir > 0)
219         x = rad * 2.0;
220       else if (grow_dir < 0)
221         width -= rad *2.0;
222     }
223   mol = Line_interface::line (me, Offset (x, starth), Offset (width, endh));
224   mol.add_stencil (Line_interface::line (me,
225                                          Offset (x, -starth),
226                                          Offset (width, -endh)));
227
228   /*
229     Support al/del niente notation by putting a circle at the
230     tip of the (de)crescendo.
231   */
232   if (circled_tip)
233     {
234       Box extent (Interval (-rad, rad), Interval (-rad, rad));
235
236       /* Hmmm, perhaps we should have a Lookup::circle () method? */
237       Stencil circle (extent,
238                       scm_list_4 (ly_symbol2scm ("circle"),
239                                   scm_from_double (rad),
240                                   scm_from_double (thick),
241                                   SCM_BOOL_F));
242
243       /*
244         don't add another circle if the hairpin is broken
245       */
246       if (!broken[tip_dir])
247         mol.add_at_edge (X_AXIS, tip_dir, Stencil (circle), 0);
248     }
249
250   mol.translate_axis (x_points[LEFT]
251                       - bounds[LEFT]->relative_coordinate (common, X_AXIS),
252                       X_AXIS);
253   return mol.smobbed_copy ();
254 }
255
256 ADD_INTERFACE (Hairpin,
257                "A hairpin crescendo or decrescendo.",
258
259                /* properties */
260                "adjacent-spanners "
261                "circled-tip "
262                "bound-padding "
263                "grow-direction "
264                "height "
265                );