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 = (Axis_group_interface::has_interface (b)
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 = 0.0;
180 extract_grob_set (me, "concurrent-hairpins", chp);
181 for (vsize i = 0; i < chp.size (); i++)
183 Spanner *span_elt = dynamic_cast<Spanner *> (chp[i]);
184 if (span_elt->get_bound (RIGHT)->break_status_dir () == LEFT)
185 broken_bound_padding = max (broken_bound_padding,
186 robust_scm2double (span_elt->get_property ("broken-bound-padding"), 0.0));
188 x_points[d] -= d * broken_bound_padding;
193 if (Text_interface::has_interface (b))
196 x_points[d] = e[-d] - d * padding;
200 bool neighbor_found = false;
201 Spanner *adjacent = NULL;
202 extract_grob_set (me, "adjacent-spanners", neighbors);
203 for (vsize i = 0; i < neighbors.size (); i++)
206 FIXME: this will fuck up in case of polyphonic
207 notes in other voices. Need to look at note-columns
208 in the current staff/voice.
210 adjacent = dynamic_cast<Spanner *> (neighbors[i]);
212 && (adjacent->get_bound (-d)->get_column ()
213 == b->get_column ()))
215 neighbor_found = true;
222 if (Hairpin::has_interface (adjacent))
225 Handle back-to-back hairpins with a circle in the middle
227 if (circled_tip && (grow_dir != d))
228 x_points[d] = e.center () + d * (rad - thick / 2.0);
230 If we're hung on a paper column, that means we're not
231 adjacent to a text-dynamic, and we may move closer. We
232 make the padding a little smaller, here.
235 x_points[d] = e.center () - d * padding / 3;
237 // Our neighbor is a dynamic text spanner.
238 // If we end on the text, pad as for text dynamics
240 x_points[d] = e[-d] - d * padding;
244 if (Note_column::has_interface (b)
245 && Note_column::has_rests (b))
250 if (Item::is_non_musical (b))
251 x_points[d] -= d * padding;
256 while (flip (&d) != LEFT);
258 Real width = x_points[RIGHT] - x_points[LEFT];
262 me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
263 : "crescendo too small"));
267 bool continued = broken[Direction (-grow_dir)];
274 endh = continued ? height / 2 : 0.0;
278 starth = continued ? height / 2 : 0.0;
283 should do relative to staff-symbol staff-space?
289 Compensate for size of circle
291 Direction tip_dir = -grow_dir;
292 if (circled_tip && !broken[tip_dir])
296 else if (grow_dir < 0)
299 mol = Line_interface::line (me, Offset (x, starth), Offset (width, endh));
300 mol.add_stencil (Line_interface::line (me,
302 Offset (width, -endh)));
305 Support al/del niente notation by putting a circle at the
306 tip of the (de)crescendo.
310 Box extent (Interval (-rad, rad), Interval (-rad, rad));
312 /* Hmmm, perhaps we should have a Lookup::circle () method? */
313 Stencil circle (extent,
314 scm_list_4 (ly_symbol2scm ("circle"),
315 scm_from_double (rad),
316 scm_from_double (thick),
320 don't add another circle if the hairpin is broken
322 if (!broken[tip_dir])
323 mol.add_at_edge (X_AXIS, tip_dir, Stencil (circle), 0);
326 mol.translate_axis (x_points[LEFT]
327 - bounds[LEFT]->relative_coordinate (common, X_AXIS),
329 return mol.smobbed_copy ();
332 ADD_INTERFACE (Hairpin,
333 "A hairpin crescendo or decrescendo.",
338 "concurrent-hairpins "
339 "broken-bound-padding "