2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
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.
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.
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/>.
22 #include "axis-group-interface.hh"
23 #include "bar-line.hh"
24 #include "dimensions.hh"
25 #include "directional-element-interface.hh"
26 #include "international.hh"
27 #include "line-interface.hh"
28 #include "output-def.hh"
29 #include "paper-column.hh"
30 #include "pointer-group-interface.hh"
32 #include "staff-symbol-referencer.hh"
33 #include "text-interface.hh"
34 #include "note-column.hh"
38 MAKE_SCHEME_CALLBACK (Hairpin, pure_height, 3);
40 Hairpin::pure_height (SCM smob, SCM, SCM)
42 Grob *me = unsmob_grob (smob);
43 Real height = robust_scm2double (me->get_property ("height"), 0.0)
44 * Staff_symbol_referencer::staff_space (me);
46 Real thickness = robust_scm2double (me->get_property ("thickness"), 1)
47 * Staff_symbol_referencer::line_thickness (me);
49 height += thickness / 2;
50 return ly_interval2scm (Interval (-height, height));
53 MAKE_SCHEME_CALLBACK (Hairpin, broken_bound_padding, 1);
55 Hairpin::broken_bound_padding (SCM smob)
57 Spanner *me = unsmob_spanner (smob);
58 Item *r_bound = me->get_bound (RIGHT);
59 if (r_bound->break_status_dir () != -1)
61 me->warning (_ ("Asking for broken bound padding at a non-broken bound."));
62 return scm_from_double (0.0);
65 System *sys = dynamic_cast<System *> (me->get_system ());
66 Direction dir = get_grob_direction (me->get_parent (Y_AXIS));
68 return scm_from_double (0.0);
70 Grob *my_vertical_axis_group = Grob::get_vertical_axis_group (me);
71 Drul_array<Grob *> vertical_axis_groups;
73 vertical_axis_groups[d] = d == dir
74 ? sys->get_neighboring_staff (d, my_vertical_axis_group, Interval_t<int> (me->spanned_rank_interval ()))
75 : my_vertical_axis_group;
77 if (!vertical_axis_groups[dir])
78 return scm_from_double (0.0);
80 Drul_array<Grob *> span_bars (0, 0);
83 extract_grob_set (vertical_axis_groups[d], "elements", elts);
84 for (vsize i = elts.size (); i--;)
85 if (Bar_line::has_interface (elts[i])
86 && dynamic_cast<Item *> (elts[i])->break_status_dir () == -1)
88 SCM hsb = elts[i]->get_property ("has-span-bar");
89 if (!scm_is_pair (hsb))
92 span_bars[d] = unsmob_grob ((d == UP ? scm_car : scm_cdr) (hsb));
96 return scm_from_double (0.0);
99 if (span_bars[DOWN] != span_bars[UP])
100 return scm_from_double (0.0);
102 return scm_from_double (robust_scm2double (me->get_property ("bound-padding"), 0.5)
106 MAKE_SCHEME_CALLBACK (Hairpin, print, 1);
108 Hairpin::print (SCM smob)
110 Spanner *me = unsmob_spanner (smob);
112 SCM s = me->get_property ("grow-direction");
113 if (!is_direction (s))
119 Direction grow_dir = to_dir (s);
120 Real padding = robust_scm2double (me->get_property ("bound-padding"), 0.5);
122 Drul_array<bool> broken;
123 Drul_array<Item *> bounds;
124 for (LEFT_and_RIGHT (d))
126 bounds[d] = me->get_bound (d);
127 broken[d] = bounds[d]->break_status_dir () != CENTER;
130 broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT);
131 broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT)->is_live ();
135 Spanner *next = me->broken_neighbor (RIGHT);
136 Stencil *s = next->get_stencil ();
137 if (!s || s->is_empty ())
138 broken[RIGHT] = false;
141 Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
142 Drul_array<Real> x_points;
145 Use the height and thickness of the hairpin when making a circled tip
147 bool circled_tip = ly_scm2bool (me->get_property ("circled-tip"));
148 Real height = robust_scm2double (me->get_property ("height"), 0.2)
149 * Staff_symbol_referencer::staff_space (me);
151 FIXME: 0.525 is still just a guess...
153 Real rad = height * 0.525;
156 thick = robust_scm2double (me->get_property ("thickness"), 1.0)
157 * Staff_symbol_referencer::line_thickness (me);
159 for (LEFT_and_RIGHT (d))
162 Interval e = Axis_group_interface::generic_bound_extent (b, common, X_AXIS);
164 x_points[d] = b->relative_coordinate (common, X_AXIS);
171 Real broken_bound_padding
172 = robust_scm2double (me->get_property ("broken-bound-padding"), 0.0);
173 extract_grob_set (me, "concurrent-hairpins", chp);
174 for (vsize i = 0; i < chp.size (); i++)
176 Spanner *span_elt = dynamic_cast<Spanner *> (chp[i]);
177 if (span_elt->get_bound (RIGHT)->break_status_dir () == LEFT)
178 broken_bound_padding = max (broken_bound_padding,
179 robust_scm2double (span_elt->get_property ("broken-bound-padding"), 0.0));
181 x_points[d] -= d * broken_bound_padding;
186 if (Text_interface::has_interface (b))
189 x_points[d] = e[-d] - d * padding;
193 bool neighbor_found = false;
194 Spanner *adjacent = NULL;
195 extract_grob_set (me, "adjacent-spanners", neighbors);
196 for (vsize i = 0; i < neighbors.size (); i++)
199 FIXME: this will fuck up in case of polyphonic
200 notes in other voices. Need to look at note-columns
201 in the current staff/voice.
203 adjacent = dynamic_cast<Spanner *> (neighbors[i]);
205 && (adjacent->get_bound (-d)->get_column ()
206 == b->get_column ()))
208 neighbor_found = true;
215 if (Hairpin::has_interface (adjacent))
218 Handle back-to-back hairpins with a circle in the middle
220 if (circled_tip && (grow_dir != d))
221 x_points[d] = e.center () + d * (rad - thick / 2.0);
223 If we're hung on a paper column, that means we're not
224 adjacent to a text-dynamic, and we may move closer. We
225 make the padding a little smaller, here.
228 x_points[d] = e.center () - d * padding / 3;
230 // Our neighbor is a dynamic text spanner.
231 // If we end on the text, pad as for text dynamics
233 x_points[d] = e[-d] - d * padding;
237 if (Note_column::has_interface (b)
238 && Note_column::has_rests (b))
243 if (Item::is_non_musical (b))
244 x_points[d] -= d * padding;
250 Real width = x_points[RIGHT] - x_points[LEFT];
254 me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
255 : "crescendo too small"));
259 bool continued = broken[Direction (-grow_dir)];
260 bool continuing = broken[Direction (grow_dir)];
266 starth = continuing ? 2 * height / 3 : height;
267 endh = continued ? height / 3 : 0.0;
271 starth = continued ? height / 3 : 0.0;
272 endh = continuing ? 2 * height / 3 : height;
276 should do relative to staff-symbol staff-space?
282 Compensate for size of circle
284 Direction tip_dir = -grow_dir;
285 if (circled_tip && !broken[tip_dir])
289 else if (grow_dir < 0)
292 mol = Line_interface::line (me, Offset (x, starth), Offset (width, endh));
293 mol.add_stencil (Line_interface::line (me,
295 Offset (width, -endh)));
298 Support al/del niente notation by putting a circle at the
299 tip of the (de)crescendo.
303 Box extent (Interval (-rad, rad), Interval (-rad, rad));
305 /* Hmmm, perhaps we should have a Lookup::circle () method? */
306 Stencil circle (extent,
307 scm_list_4 (ly_symbol2scm ("circle"),
308 scm_from_double (rad),
309 scm_from_double (thick),
313 don't add another circle if the hairpin is broken
315 if (!broken[tip_dir])
316 mol.add_at_edge (X_AXIS, tip_dir, Stencil (circle), 0);
319 mol.translate_axis (x_points[LEFT]
320 - bounds[LEFT]->relative_coordinate (common, X_AXIS),
322 return mol.smobbed_copy ();
325 ADD_INTERFACE (Hairpin,
326 "A hairpin crescendo or decrescendo.",
331 "concurrent-hairpins "
332 "broken-bound-padding "