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;
74 vertical_axis_groups[d] = d == dir
75 ? sys->get_neighboring_staff (d, my_vertical_axis_group, Interval_t<int> (me->spanned_rank_interval ()))
76 : my_vertical_axis_group;
77 while (flip (&d) != DOWN);
79 if (!vertical_axis_groups[dir])
80 return scm_from_double (0.0);
82 Drul_array<Grob *> span_bars (0, 0);
86 extract_grob_set (vertical_axis_groups[d], "elements", elts);
87 for (vsize i = elts.size (); i--;)
88 if (Bar_line::has_interface (elts[i])
89 && dynamic_cast<Item *> (elts[i])->break_status_dir () == -1)
91 SCM hsb = elts[i]->get_property ("has-span-bar");
92 if (!scm_is_pair (hsb))
95 span_bars[d] = unsmob_grob ((d == UP ? scm_car : scm_cdr) (hsb));
99 return scm_from_double (0.0);
101 while (flip (&d) != DOWN);
103 if (span_bars[DOWN] != span_bars[UP])
104 return scm_from_double (0.0);
106 return scm_from_double (robust_scm2double (me->get_property ("bound-padding"), 0.5)
110 MAKE_SCHEME_CALLBACK (Hairpin, print, 1);
112 Hairpin::print (SCM smob)
114 Spanner *me = unsmob_spanner (smob);
116 SCM s = me->get_property ("grow-direction");
117 if (!is_direction (s))
123 Direction grow_dir = to_dir (s);
124 Real padding = robust_scm2double (me->get_property ("bound-padding"), 0.5);
126 Drul_array<bool> broken;
127 Drul_array<Item *> bounds;
131 bounds[d] = me->get_bound (d);
132 broken[d] = bounds[d]->break_status_dir () != CENTER;
134 while (flip (&d) != LEFT);
136 broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT);
137 broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT)->is_live ();
141 Spanner *next = me->broken_neighbor (RIGHT);
142 Stencil *s = next->get_stencil ();
143 if (!s || s->is_empty ())
144 broken[RIGHT] = false;
147 Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
148 Drul_array<Real> x_points;
151 Use the height and thickness of the hairpin when making a circled tip
153 bool circled_tip = ly_scm2bool (me->get_property ("circled-tip"));
154 Real height = robust_scm2double (me->get_property ("height"), 0.2)
155 * Staff_symbol_referencer::staff_space (me);
157 FIXME: 0.525 is still just a guess...
159 Real rad = height * 0.525;
162 thick = robust_scm2double (me->get_property ("thickness"), 1.0)
163 * Staff_symbol_referencer::line_thickness (me);
168 Interval e = (Paper_column::has_interface (b) && b->break_status_dir ())
169 ? Axis_group_interface::generic_bound_extent (b, common, X_AXIS)
170 : robust_relative_extent (b, common, X_AXIS);
172 x_points[d] = b->relative_coordinate (common, X_AXIS);
179 Real broken_bound_padding
180 = robust_scm2double (me->get_property ("broken-bound-padding"), 0.0);
181 extract_grob_set (me, "concurrent-hairpins", chp);
182 for (vsize i = 0; i < chp.size (); i++)
184 Spanner *span_elt = dynamic_cast<Spanner *> (chp[i]);
185 if (span_elt->get_bound (RIGHT)->break_status_dir () == LEFT)
186 broken_bound_padding = max (broken_bound_padding,
187 robust_scm2double (span_elt->get_property ("broken-bound-padding"), 0.0));
189 x_points[d] -= d * broken_bound_padding;
194 if (Text_interface::has_interface (b))
197 x_points[d] = e[-d] - d * padding;
201 bool neighbor_found = false;
202 Spanner *adjacent = NULL;
203 extract_grob_set (me, "adjacent-spanners", neighbors);
204 for (vsize i = 0; i < neighbors.size (); i++)
207 FIXME: this will fuck up in case of polyphonic
208 notes in other voices. Need to look at note-columns
209 in the current staff/voice.
211 adjacent = dynamic_cast<Spanner *> (neighbors[i]);
213 && (adjacent->get_bound (-d)->get_column ()
214 == b->get_column ()))
216 neighbor_found = true;
223 if (Hairpin::has_interface (adjacent))
226 Handle back-to-back hairpins with a circle in the middle
228 if (circled_tip && (grow_dir != d))
229 x_points[d] = e.center () + d * (rad - thick / 2.0);
231 If we're hung on a paper column, that means we're not
232 adjacent to a text-dynamic, and we may move closer. We
233 make the padding a little smaller, here.
236 x_points[d] = e.center () - d * padding / 3;
238 // Our neighbor is a dynamic text spanner.
239 // If we end on the text, pad as for text dynamics
241 x_points[d] = e[-d] - d * padding;
245 if (Note_column::has_interface (b)
246 && Note_column::has_rests (b))
251 if (Item::is_non_musical (b))
252 x_points[d] -= d * padding;
257 while (flip (&d) != LEFT);
259 Real width = x_points[RIGHT] - x_points[LEFT];
263 me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
264 : "crescendo too small"));
268 bool continued = broken[Direction (-grow_dir)];
269 bool continuing = broken[Direction (grow_dir)];
275 starth = continuing ? 2 * height / 3 : height;
276 endh = continued ? height / 3 : 0.0;
280 starth = continued ? height / 3 : 0.0;
281 endh = continuing ? 2 * height / 3 : height;
285 should do relative to staff-symbol staff-space?
291 Compensate for size of circle
293 Direction tip_dir = -grow_dir;
294 if (circled_tip && !broken[tip_dir])
298 else if (grow_dir < 0)
301 mol = Line_interface::line (me, Offset (x, starth), Offset (width, endh));
302 mol.add_stencil (Line_interface::line (me,
304 Offset (width, -endh)));
307 Support al/del niente notation by putting a circle at the
308 tip of the (de)crescendo.
312 Box extent (Interval (-rad, rad), Interval (-rad, rad));
314 /* Hmmm, perhaps we should have a Lookup::circle () method? */
315 Stencil circle (extent,
316 scm_list_4 (ly_symbol2scm ("circle"),
317 scm_from_double (rad),
318 scm_from_double (thick),
322 don't add another circle if the hairpin is broken
324 if (!broken[tip_dir])
325 mol.add_at_edge (X_AXIS, tip_dir, Stencil (circle), 0);
328 mol.translate_axis (x_points[LEFT]
329 - bounds[LEFT]->relative_coordinate (common, X_AXIS),
331 return mol.smobbed_copy ();
334 ADD_INTERFACE (Hairpin,
335 "A hairpin crescendo or decrescendo.",
340 "concurrent-hairpins "
341 "broken-bound-padding "