Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / vaticana-ligature.cc
blob7a00cff3003b9075d3278a9f11cc72900dd8eaeb
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2003--2011 Juergen Reuter <reuter@ipd.uka.de>
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/>.
20 #include "vaticana-ligature.hh"
22 #include "bezier.hh"
23 #include "font-interface.hh"
24 #include "international.hh"
25 #include "item.hh"
26 #include "lookup.hh"
27 #include "note-head.hh"
28 #include "output-def.hh"
29 #include "staff-symbol-referencer.hh"
30 #include "warn.hh"
32 Stencil
33 vaticana_brew_cauda (Grob *me,
34 int pos,
35 int delta_pitch,
36 Real thickness,
37 Real blotdiameter)
39 bool on_staffline = Staff_symbol_referencer::on_line (me, pos);
40 int interspaces = Staff_symbol_referencer::line_count (me) - 1;
41 bool above_staff = pos > interspaces;
43 if (delta_pitch > -1)
45 me->programming_error ("flexa cauda: invalid delta_pitch; assuming -1");
46 delta_pitch = -1;
48 Real length;
49 if (on_staffline)
51 if (delta_pitch >= -1)
52 length = 1.30;
53 else if (delta_pitch >= -2)
54 length = 1.35;
55 else
56 length = 1.85;
58 else
60 if (delta_pitch >= -1)
61 if (above_staff)
62 length = 1.30;
63 else
64 length = 1.00;
65 else if (delta_pitch >= -2)
66 length = 1.35;
67 else if (delta_pitch >= -3)
68 length = 1.50;
69 else
70 length = 1.85;
72 Box cauda_box (Interval (0, thickness), Interval (-length, 0));
73 return Lookup::round_filled_box (cauda_box, blotdiameter);
77 * TODO: move this function to class Lookup?
79 Stencil
80 vaticana_brew_flexa (Grob *me,
81 bool solid,
82 Real line_thickness)
84 Real staff_space = Staff_symbol_referencer::staff_space (me);
85 Stencil stencil;
86 Real right_height = 0.6 * staff_space;
88 Real interval;
89 SCM flexa_height_scm = me->get_property ("flexa-height");
90 if (flexa_height_scm != SCM_EOL)
91 interval = scm_to_int (flexa_height_scm);
92 else
94 me->warning ("Vaticana_ligature: "
95 + _ ("flexa-height undefined; assuming 0"));
96 interval = 0.0;
99 if (interval >= 0.0)
100 me->warning (_ ("ascending vaticana style flexa"));
102 Real width = robust_scm2double (me->get_property ("flexa-width"), 2);
105 * Compensate curve thickness that appears to be smaller in steep
106 * section of bend.
108 Real left_height
109 = right_height
110 + min (0.12 * abs (interval), 0.3) * staff_space;
113 * Compensate optical illusion regarding vertical position of left
114 * and right endings due to curved shape.
116 Real ypos_correction = -0.1 * staff_space * sign (interval);
117 Real interval_correction = 0.2 * staff_space * sign (interval);
118 Real corrected_interval = interval * staff_space + interval_correction;
121 * middle curve of flexa shape
123 Bezier curve;
124 curve.control_[0] = Offset (0.00 * width, 0.0);
125 curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
126 curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
127 curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
129 Bezier top_curve = curve, bottom_curve = curve;
130 for (int i = 0; i < 4; i++)
132 Real curve_thickness = 0.33 * ((3 - i) * left_height + i * right_height);
133 top_curve.control_[i] += Offset (0, 0.5 * curve_thickness);
134 bottom_curve.control_[i] -= Offset (0, 0.5 * curve_thickness);
137 if (solid)
139 Stencil solid_head
140 = Lookup::bezier_sandwich (top_curve, bottom_curve, 0.0);
141 stencil.add_stencil (solid_head);
143 else // outline
145 Bezier inner_top_curve = top_curve;
146 inner_top_curve.translate (Offset (0.0, -line_thickness));
147 Stencil top_edge
148 = Lookup::bezier_sandwich (top_curve, inner_top_curve, 0.0);
149 stencil.add_stencil (top_edge);
151 Bezier inner_bottom_curve = bottom_curve;
152 inner_bottom_curve.translate (Offset (0.0, +line_thickness));
153 Stencil bottom_edge
154 = Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve, 0.0);
155 stencil.add_stencil (bottom_edge);
158 * TODO: Use horizontal slope with proper slope value rather
159 * than filled box for left edge, since the filled box stands
160 * out from the flexa shape if the interval is big and the line
161 * thickness small. The difficulty here is to compute a proper
162 * slope value, as it should roughly be equal with the slope of
163 * the left end of the bezier curve.
165 Box left_edge_box (Interval (0, line_thickness),
166 Interval (-0.5 * left_height, +0.5 * left_height));
167 Stencil left_edge = Lookup::filled_box (left_edge_box);
168 stencil.add_stencil (left_edge);
170 Box right_edge_box (Interval (-line_thickness, 0),
171 Interval (-0.5 * right_height, +0.5 * right_height));
172 Stencil right_edge = Lookup::filled_box (right_edge_box);
173 right_edge.translate_axis (width, X_AXIS);
174 right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
175 stencil.add_stencil (right_edge);
177 stencil.translate_axis (ypos_correction, Y_AXIS);
178 return stencil;
181 Stencil
182 vaticana_brew_join (Grob *me, int delta_pitch,
183 Real join_thickness, Real blotdiameter)
185 Real staff_space = Staff_symbol_referencer::staff_space (me);
186 if (!delta_pitch)
188 me->programming_error (_ ("Vaticana_ligature: "
189 "zero join (delta_pitch == 0)"));
190 return Lookup::blank (Box (Interval (0, 0), Interval (0, 0)));
192 Interval x_extent = Interval (0, join_thickness);
193 Interval y_extent = (delta_pitch > 0)
194 ? Interval (0, delta_pitch * 0.5 * staff_space) : // ascending join
195 Interval (delta_pitch * 0.5 * staff_space, 0); // descending join
196 Box join_box (x_extent, y_extent);
197 return Lookup::round_filled_box (join_box, blotdiameter);
200 Stencil
201 vaticana_brew_primitive (Grob *me)
203 SCM glyph_name_scm = me->get_property ("glyph-name");
204 if (glyph_name_scm == SCM_EOL)
206 me->programming_error ("Vaticana_ligature: "
207 "undefined glyph-name -> ignoring grob");
208 return Lookup::blank (Box (Interval (0, 0), Interval (0, 0)));
211 string glyph_name = ly_scm2string (glyph_name_scm);
213 Stencil out;
214 Real thickness = robust_scm2double (me->get_property ("thickness"), 1);
216 Real line_thickness
217 = thickness * me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
219 Real blotdiameter
220 = (me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter")));
222 int pos = Staff_symbol_referencer::get_rounded_position (me);
224 SCM delta_pitch_scm = me->get_property ("delta-position");
225 int delta_pitch;
226 if (delta_pitch_scm != SCM_EOL)
227 delta_pitch = scm_to_int (delta_pitch_scm);
228 else
229 delta_pitch = 0;
231 Real x_offset = robust_scm2double (me->get_property ("x-offset"), 0);
233 bool add_stem = to_boolean (me->get_property ("add-stem"));
234 bool add_cauda = to_boolean (me->get_property ("add-cauda"));
235 bool add_join = to_boolean (me->get_property ("add-join"));
237 if (glyph_name == "")
240 * This is an empty head. This typically applies for the right
241 * side of a curved flexa shape, which is already typeset by the
242 * associated left side head. The only possible thing left to
243 * do is to draw a vertical join to the next head. (Urgh: need
244 * flexa_width.)
246 Real staff_space = Staff_symbol_referencer::staff_space (me);
247 Real flexa_width = robust_scm2double (me->get_property ("flexa-width"), 2) * staff_space;
249 = Lookup::blank (Box (Interval (0, 0.5 * flexa_width), Interval (0, 0)));
251 else if (glyph_name == "flexa")
252 out = vaticana_brew_flexa (me, true, line_thickness);
253 else
256 = Font_interface::get_default_font (me)->
257 find_by_name ("noteheads.s" + glyph_name);
259 out.translate_axis (x_offset, X_AXIS);
260 Real head_width = out.extent (X_AXIS).length ();
262 if (add_cauda)
264 Stencil cauda
265 = vaticana_brew_cauda (me, pos, delta_pitch,
266 line_thickness, blotdiameter);
267 out.add_stencil (cauda);
270 if (add_stem)
272 Stencil stem
273 = vaticana_brew_cauda (me, pos, -1,
274 line_thickness, blotdiameter);
275 stem.translate_axis (head_width - line_thickness, X_AXIS);
276 out.add_stencil (stem);
279 if (add_join)
281 Stencil join
282 = vaticana_brew_join (me, delta_pitch, line_thickness, blotdiameter);
283 join.translate_axis (head_width - line_thickness, X_AXIS);
284 out.add_stencil (join);
287 return out;
290 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_ligature_primitive, 1);
292 Vaticana_ligature::brew_ligature_primitive (SCM smob)
294 Grob *me = unsmob_grob (smob);
295 SCM primitive = vaticana_brew_primitive (me).smobbed_copy ();
296 return primitive;
299 MAKE_SCHEME_CALLBACK (Vaticana_ligature, print, 1);
301 Vaticana_ligature::print (SCM)
303 return SCM_EOL;
306 ADD_INTERFACE (Vaticana_ligature,
307 "A vaticana style Gregorian ligature.",
309 /* properties */
310 "glyph-name "
311 "flexa-height "
312 "flexa-width "
313 "thickness "
314 "add-cauda "
315 "add-stem "
316 "add-join "
317 "delta-position "
318 "x-offset "