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 "dimensions.hh"
24 #include "directional-element-interface.hh"
25 #include "international.hh"
26 #include "line-interface.hh"
27 #include "output-def.hh"
28 #include "paper-column.hh"
29 #include "pointer-group-interface.hh"
31 #include "staff-symbol-referencer.hh"
32 #include "text-interface.hh"
33 #include "note-column.hh"
37 MAKE_SCHEME_CALLBACK (Hairpin, pure_height, 3);
39 Hairpin::pure_height (SCM smob, SCM, SCM)
41 Grob *me = unsmob_grob (smob);
42 Real height = robust_scm2double (me->get_property ("height"), 0.0)
43 * Staff_symbol_referencer::staff_space (me);
45 Real thickness = robust_scm2double (me->get_property ("thickness"), 1)
46 * Staff_symbol_referencer::line_thickness (me);
48 height += thickness / 2;
49 return ly_interval2scm (Interval (-height, height));
52 MAKE_SCHEME_CALLBACK (Hairpin, broken_bound_padding, 1);
54 Hairpin::broken_bound_padding (SCM smob)
56 Spanner *me = unsmob_spanner (smob);
57 Item *r_bound = me->get_bound (RIGHT);
58 if (r_bound->break_status_dir () != -1)
60 me->warning (_ ("Asking for broken bound padding at a non-broken bound."));
61 return scm_from_double (0.0);
64 System *sys = dynamic_cast<System *> (me->get_system ());
65 Direction dir = get_grob_direction (me->get_parent (Y_AXIS));
67 return scm_from_double (0.0);
69 Grob *my_vertical_axis_group = Grob::get_vertical_axis_group (me);
70 Drul_array<Grob *> vertical_axis_groups;
72 vertical_axis_groups[d] = d == dir
73 ? sys->get_neighboring_staff (d, my_vertical_axis_group, Interval_t<int> (me->spanned_rank_interval ()))
74 : my_vertical_axis_group;
76 if (!vertical_axis_groups[dir])
77 return scm_from_double (0.0);
79 Drul_array<Grob *> span_bars (0, 0);
82 extract_grob_set (vertical_axis_groups[d], "elements", elts);
83 for (vsize i = elts.size (); i--;)
84 if (elts[i]->internal_has_interface (ly_symbol2scm ("bar-line-interface"))
85 && dynamic_cast<Item *> (elts[i])->break_status_dir () == -1)
87 SCM hsb = elts[i]->get_property ("has-span-bar");
88 if (!scm_is_pair (hsb))
91 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;
132 Spanner *next = me->broken_neighbor (RIGHT);
133 // Hairpin-parts suicide in after-line-breaking if they need not be drawn
136 (void) next->get_property ("after-line-breaking");
137 broken[RIGHT] = next->is_live ();
140 broken[RIGHT] = false;
143 Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
144 Drul_array<Real> x_points;
147 Use the height and thickness of the hairpin when making a circled tip
149 bool circled_tip = ly_scm2bool (me->get_property ("circled-tip"));
150 Real height = robust_scm2double (me->get_property ("height"), 0.2)
151 * Staff_symbol_referencer::staff_space (me);
153 FIXME: 0.525 is still just a guess...
155 Real rad = height * 0.525;
158 thick = robust_scm2double (me->get_property ("thickness"), 1.0)
159 * Staff_symbol_referencer::line_thickness (me);
161 for (LEFT_and_RIGHT (d))
164 Interval e = Axis_group_interface::generic_bound_extent (b, common, X_AXIS);
166 x_points[d] = b->relative_coordinate (common, X_AXIS);
173 Real broken_bound_padding
174 = robust_scm2double (me->get_property ("broken-bound-padding"), 0.0);
175 extract_grob_set (me, "concurrent-hairpins", chp);
176 for (vsize i = 0; i < chp.size (); i++)
178 Spanner *span_elt = dynamic_cast<Spanner *> (chp[i]);
179 if (span_elt->get_bound (RIGHT)->break_status_dir () == LEFT)
180 broken_bound_padding = max (broken_bound_padding,
181 robust_scm2double (span_elt->get_property ("broken-bound-padding"), 0.0));
183 x_points[d] -= d * broken_bound_padding;
188 if (Text_interface::has_interface (b))
191 x_points[d] = e[-d] - d * padding;
195 bool neighbor_found = false;
196 Spanner *adjacent = NULL;
197 extract_grob_set (me, "adjacent-spanners", neighbors);
198 for (vsize i = 0; i < neighbors.size (); i++)
201 FIXME: this will fuck up in case of polyphonic
202 notes in other voices. Need to look at note-columns
203 in the current staff/voice.
205 adjacent = dynamic_cast<Spanner *> (neighbors[i]);
207 && (adjacent->get_bound (-d)->get_column ()
208 == b->get_column ()))
210 neighbor_found = true;
217 if (Hairpin::has_interface (adjacent))
220 Handle back-to-back hairpins with a circle in the middle
222 if (circled_tip && (grow_dir != d))
223 x_points[d] = e.center () + d * (rad - thick / 2.0);
225 If we're hung on a paper column, that means we're not
226 adjacent to a text-dynamic, and we may move closer. We
227 make the padding a little smaller, here.
230 x_points[d] = e.center () - d * padding / 3;
232 // Our neighbor is a dynamic text spanner.
233 // If we end on the text, pad as for text dynamics
235 x_points[d] = e[-d] - d * padding;
239 if (d == RIGHT // end at the left edge of a rest
240 && Note_column::has_interface (b)
241 && Note_column::has_rests (b))
246 if (Item::is_non_musical (b))
247 x_points[d] -= d * padding;
253 Real width = x_points[RIGHT] - x_points[LEFT];
257 me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
258 : "crescendo too small"));
262 bool continued = broken[Direction (-grow_dir)];
263 bool continuing = broken[Direction (grow_dir)];
269 starth = continuing ? 2 * height / 3 : height;
270 endh = continued ? height / 3 : 0.0;
274 starth = continued ? height / 3 : 0.0;
275 endh = continuing ? 2 * height / 3 : height;
279 should do relative to staff-symbol staff-space?
285 Compensate for size of circle
287 Direction tip_dir = -grow_dir;
288 if (circled_tip && !broken[tip_dir])
292 else if (grow_dir < 0)
295 mol = Line_interface::line (me, Offset (x, starth), Offset (width, endh));
296 mol.add_stencil (Line_interface::line (me,
298 Offset (width, -endh)));
301 Support al/del niente notation by putting a circle at the
302 tip of the (de)crescendo.
306 Box extent (Interval (-rad, rad), Interval (-rad, rad));
308 /* Hmmm, perhaps we should have a Lookup::circle () method? */
309 Stencil circle (extent,
310 scm_list_4 (ly_symbol2scm ("circle"),
311 scm_from_double (rad),
312 scm_from_double (thick),
316 don't add another circle if the hairpin is broken
318 if (!broken[tip_dir])
319 mol.add_at_edge (X_AXIS, tip_dir, Stencil (circle), 0);
322 mol.translate_axis (x_points[LEFT]
323 - bounds[LEFT]->relative_coordinate (common, X_AXIS),
325 return mol.smobbed_copy ();
328 ADD_INTERFACE (Hairpin,
329 "A hairpin crescendo or decrescendo.",
334 "concurrent-hairpins "
335 "broken-bound-padding "