2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 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 "international.hh"
25 #include "line-interface.hh"
26 #include "output-def.hh"
27 #include "paper-column.hh"
28 #include "pointer-group-interface.hh"
30 #include "staff-symbol-referencer.hh"
31 #include "text-interface.hh"
32 #include "note-column.hh"
35 MAKE_SCHEME_CALLBACK (Hairpin, pure_height, 3);
37 Hairpin::pure_height (SCM smob, SCM, SCM)
39 Grob *me = unsmob_grob (smob);
40 Real height = robust_scm2double (me->get_property ("height"), 0.0)
41 * Staff_symbol_referencer::staff_space (me);
43 Real thickness = robust_scm2double (me->get_property ("thickness"), 1)
44 * Staff_symbol_referencer::line_thickness (me);
46 height += thickness / 2;
47 return ly_interval2scm (Interval (-height, height));
50 MAKE_SCHEME_CALLBACK (Hairpin, print, 1);
52 Hairpin::print (SCM smob)
54 Spanner *me = unsmob_spanner (smob);
56 SCM s = me->get_property ("grow-direction");
57 if (!is_direction (s))
63 Direction grow_dir = to_dir (s);
64 Real padding = robust_scm2double (me->get_property ("bound-padding"), 0.5);
66 Drul_array<bool> broken;
67 Drul_array<Item *> bounds;
71 bounds[d] = me->get_bound (d);
72 broken[d] = bounds[d]->break_status_dir () != CENTER;
74 while (flip (&d) != LEFT);
76 broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT);
77 broken[RIGHT] = broken[RIGHT] && me->broken_neighbor (RIGHT)->is_live ();
81 Spanner *next = me->broken_neighbor (RIGHT);
82 Stencil *s = next->get_stencil ();
83 if (!s || s->is_empty ())
84 broken[RIGHT] = false;
87 Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
88 Drul_array<Real> x_points;
91 Use the height and thickness of the hairpin when making a circled tip
93 bool circled_tip = ly_scm2bool (me->get_property ("circled-tip"));
94 Real height = robust_scm2double (me->get_property ("height"), 0.2)
95 * Staff_symbol_referencer::staff_space (me);
97 FIXME: 0.525 is still just a guess...
99 Real rad = height * 0.525;
102 thick = robust_scm2double (me->get_property ("thickness"), 1.0)
103 * Staff_symbol_referencer::line_thickness (me);
108 x_points[d] = b->relative_coordinate (common, X_AXIS);
112 x_points[d] = b->extent (common, X_AXIS)[RIGHT];
116 if (Text_interface::has_interface (b))
118 Interval e = b->extent (common, X_AXIS);
120 x_points[d] = e[-d] - d * padding;
124 bool neighbor_found = false;
126 extract_grob_set (me, "adjacent-spanners", neighbors);
127 for (vsize i = 0; i < neighbors.size (); i++)
130 FIXME: this will fuck up in case of polyphonic
131 notes in other voices. Need to look at note-columns
132 in the current staff/voice.
134 adjacent = dynamic_cast<Spanner *> (neighbors[i]);
136 && (adjacent->get_bound (-d)->get_column ()
137 == b->get_column ()))
139 neighbor_found = true;
144 Interval e = (Axis_group_interface::has_interface (b)
145 ? Axis_group_interface::generic_bound_extent (b, common, X_AXIS)
146 : robust_relative_extent (b, common, X_AXIS));
149 if (Hairpin::has_interface (adjacent))
152 Handle back-to-back hairpins with a circle in the middle
154 if (circled_tip && (grow_dir != d))
155 x_points[d] = e.center () + d * (rad - thick / 2.0);
157 If we're hung on a paper column, that means we're not
158 adjacent to a text-dynamic, and we may move closer. We
159 make the padding a little smaller, here.
162 x_points[d] = e.center () - d * padding / 3;
164 // Our neighbor is a dynamic text spanner, so add the
165 // same amount of padding as for text dynamics
167 x_points[d] = e[-d] - d * padding;
171 if (Note_column::has_interface (b)
172 && Note_column::has_rests (b))
177 Item *bound = me->get_bound (d);
178 if (bound->is_non_musical (bound))
179 x_points[d] -= d * padding;
184 while (flip (&d) != LEFT);
186 Real width = x_points[RIGHT] - x_points[LEFT];
189 me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
190 : "crescendo too small"));
194 bool continued = broken[Direction (-grow_dir)];
201 endh = continued ? height / 2 : 0.0;
205 starth = continued ? height / 2 : 0.0;
210 should do relative to staff-symbol staff-space?
216 Compensate for size of circle
218 Direction tip_dir = -grow_dir;
219 if (circled_tip && !broken[tip_dir])
223 else if (grow_dir < 0)
226 mol = Line_interface::line (me, Offset (x, starth), Offset (width, endh));
227 mol.add_stencil (Line_interface::line (me,
229 Offset (width, -endh)));
232 Support al/del niente notation by putting a circle at the
233 tip of the (de)crescendo.
237 Box extent (Interval (-rad, rad), Interval (-rad, rad));
239 /* Hmmm, perhaps we should have a Lookup::circle () method? */
240 Stencil circle (extent,
241 scm_list_4 (ly_symbol2scm ("circle"),
242 scm_from_double (rad),
243 scm_from_double (thick),
247 don't add another circle if the hairpin is broken
249 if (!broken[tip_dir])
250 mol.add_at_edge (X_AXIS, tip_dir, Stencil (circle), 0);
253 mol.translate_axis (x_points[LEFT]
254 - bounds[LEFT]->relative_coordinate (common, X_AXIS),
256 return mol.smobbed_copy ();
259 ADD_INTERFACE (Hairpin,
260 "A hairpin crescendo or decrescendo.",