(staff_eligible): new function.
[lilypond.git] / lily / vaticana-ligature.cc
blobd712bfcd86ccab4d5bbcb91058773f2ddd5288b1
1 /*
2 vaticana-ligature.cc -- implement Vaticana_ligature
4 source file of the GNU LilyPond music typesetter
6 (c) 2003 Juergen Reuter <reuter@ipd.uka.de>
7 */
9 #include <math.h>
10 #include "item.hh"
11 #include "vaticana-ligature.hh"
12 #include "font-interface.hh"
13 #include "molecule.hh"
14 #include "lookup.hh"
15 #include "staff-symbol-referencer.hh"
16 #include "note-head.hh"
17 #include "paper-def.hh"
18 #include "bezier.hh"
19 #include "warn.hh"
21 Molecule
22 vaticana_brew_cauda (Grob *me,
23 int pos,
24 int delta_pitch,
25 Real thickness,
26 Real blotdiameter)
28 bool on_staffline = Staff_symbol_referencer::on_staffline (me, pos);
29 int interspaces = Staff_symbol_referencer::line_count (me)-1;
30 bool above_staff = pos > interspaces;
32 if (delta_pitch > -1)
34 me->programming_error ("flexa cauda: invalid delta_pitch; assuming -1");
35 delta_pitch = -1;
37 Real length;
38 if (on_staffline)
40 if (delta_pitch >= -1)
41 length = 1.30;
42 else if (delta_pitch >= -2)
43 length = 1.35;
44 else
45 length = 1.85;
47 else
49 if (delta_pitch >= -1)
50 if (above_staff)
51 length = 1.30;
52 else
53 length = 1.00;
54 else if (delta_pitch >= -2)
55 length = 1.35;
56 else if (delta_pitch >= -3)
57 length = 1.50;
58 else
59 length = 1.85;
61 Box cauda_box (Interval (0, thickness), Interval (-length, 0));
62 return Lookup::round_filled_box (cauda_box, blotdiameter);
66 * TODO: move this function to class Lookup?
68 Molecule
69 vaticana_brew_flexa (Grob *me,
70 bool solid,
71 Real line_thickness)
73 Real staff_space = Staff_symbol_referencer::staff_space (me);
74 Molecule molecule = Molecule ();
75 Real right_height = 0.6 * staff_space;
77 Real interval;
78 SCM flexa_height_scm = me->get_grob_property ("flexa-height");
79 if (flexa_height_scm != SCM_EOL)
81 interval = gh_scm2int (flexa_height_scm);
83 else
85 me->warning ("Vaticana_ligature: "
86 "flexa-height undefined; assuming 0");
87 interval = 0.0;
90 if (interval >= 0.0)
92 me->warning (_ ("ascending vaticana style flexa"));
95 Real width;
96 SCM flexa_width_scm = me->get_grob_property ("flexa-width");
97 if (flexa_width_scm != SCM_EOL)
99 width = gh_scm2double (flexa_width_scm);
101 else
103 me->warning ("Vaticana_ligature: "
104 "flexa-width undefined; assuming 2.0");
105 width = 2.0 * staff_space;
109 * Compensate curve thickness that appears to be smaller in steep
110 * section of bend.
112 Real left_height =
113 right_height +
114 min (0.12 * abs(interval), 0.3) * staff_space;
117 * Compensate optical illusion regarding vertical position of left
118 * and right endings due to curved shape.
120 Real ypos_correction = -0.1*staff_space * sign(interval);
121 Real interval_correction = 0.2*staff_space * sign(interval);
122 Real corrected_interval = interval*staff_space + interval_correction;
125 * middle curve of flexa shape
127 Bezier curve;
128 curve.control_[0] = Offset (0.00 * width, 0.0);
129 curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
130 curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
131 curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
133 Bezier top_curve = curve, bottom_curve = curve;
134 for (int i = 0; i < 4; i++)
136 Real curve_thickness = 0.33 * ((3 - i)*left_height + i*right_height);
137 top_curve.control_[i] += Offset (0, 0.5 * curve_thickness);
138 bottom_curve.control_[i] -= Offset (0, 0.5 * curve_thickness);
141 if (solid)
143 Molecule solid_head =
144 Lookup::bezier_sandwich (top_curve, bottom_curve);
145 molecule.add_molecule (solid_head);
147 else // outline
149 Bezier inner_top_curve = top_curve;
150 inner_top_curve.translate (Offset (0.0, -line_thickness));
151 Molecule top_edge =
152 Lookup::bezier_sandwich (top_curve, inner_top_curve);
153 molecule.add_molecule(top_edge);
155 Bezier inner_bottom_curve = bottom_curve;
156 inner_bottom_curve.translate (Offset (0.0, +line_thickness));
157 Molecule bottom_edge =
158 Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve);
159 molecule.add_molecule(bottom_edge);
162 * TODO: Use horizontal slope with proper slope value rather
163 * than filled box for left edge, since the filled box stands
164 * out from the flexa shape if the interval is big and the line
165 * thickness small. The difficulty here is to compute a proper
166 * slope value, as it should roughly be equal with the slope of
167 * the left end of the bezier curve.
169 Box left_edge_box (Interval (0, line_thickness),
170 Interval (-0.5*left_height, +0.5*left_height));
171 Molecule left_edge = Lookup::filledbox (left_edge_box);
172 molecule.add_molecule(left_edge);
174 Box right_edge_box (Interval (-line_thickness, 0),
175 Interval (-0.5*right_height, +0.5*right_height));
176 Molecule right_edge = Lookup::filledbox (right_edge_box);
177 right_edge.translate_axis (width, X_AXIS);
178 right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
179 molecule.add_molecule(right_edge);
181 molecule.translate_axis (ypos_correction, Y_AXIS);
182 return molecule;
185 Molecule
186 vaticana_brew_join (Grob *me, int delta_pitch,
187 Real join_thickness, Real blotdiameter)
189 Real staff_space = Staff_symbol_referencer::staff_space (me);
190 if (!delta_pitch)
192 me->programming_error (_f ("Vaticana_ligature: "
193 "zero join (delta_pitch == 0)"));
194 return Molecule ();
196 Interval x_extent = Interval (0, join_thickness);
197 Interval y_extent = (delta_pitch > 0) ?
198 Interval (0, delta_pitch * 0.5 * staff_space) : // ascending join
199 Interval (delta_pitch * 0.5 * staff_space, 0); // descending join
200 Box join_box (x_extent, y_extent);
201 return Lookup::round_filled_box (join_box, blotdiameter);
204 void
205 vaticana_add_ledger_lines (Grob *me, Molecule *out, int pos, Real offs,
206 bool ledger_take_space)
208 int interspaces = Staff_symbol_referencer::line_count (me)-1;
209 if (abs (pos) - interspaces > 1)
211 Interval hd = out->extent (X_AXIS);
212 Real left_ledger_protusion = hd.length ()/4;
213 Real right_ledger_protusion = left_ledger_protusion;
215 Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
216 hd[RIGHT] + right_ledger_protusion);
217 Molecule ledger_lines =
218 Note_head::brew_ledger_lines (me, pos, interspaces,
219 l_extents,
220 ledger_take_space);
221 ledger_lines.translate_axis (offs, Y_AXIS);
222 out->add_molecule (ledger_lines);
226 Molecule
227 vaticana_brew_primitive (Grob *me, bool ledger_take_space)
229 SCM glyph_name_scm = me->get_grob_property ("glyph-name");
230 if (glyph_name_scm == SCM_EOL)
232 me->programming_error ("Vaticana_ligature: "
233 "undefined glyph-name -> ignoring grob");
234 return Molecule ();
237 String glyph_name = ly_scm2string (glyph_name_scm);
239 Molecule out;
240 int flexa_height = 0;
241 Real thickness;
243 SCM thickness_scm = me->get_grob_property ("thickness");
244 if (thickness_scm != SCM_EOL)
246 thickness = gh_scm2double (thickness_scm);
248 else
250 me->programming_error ("Vaticana_ligature: "
251 "thickness undefined; assuming 1.0");
252 thickness = 1.0;
255 Real line_thickness =
256 thickness * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
258 Real blotdiameter =
259 (me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter")));
261 int pos = (int)rint (Staff_symbol_referencer::get_position (me));
263 SCM delta_pitch_scm = me->get_grob_property ("delta-pitch");
264 int delta_pitch;
265 if (delta_pitch_scm != SCM_EOL)
266 delta_pitch = gh_scm2int (delta_pitch_scm);
267 else
268 delta_pitch = 0;
270 Real x_offset = 0.0;
271 SCM x_offset_scm = me->get_grob_property ("x-offset");
272 if (x_offset_scm != SCM_EOL)
274 x_offset = gh_scm2double (x_offset_scm);
276 else
278 me->programming_error ("Vaticana_ligature: "
279 "x-offset undefined; assuming 0.0");
282 bool add_stem = to_boolean (me->get_grob_property ("add-stem"));
283 bool add_cauda = to_boolean (me->get_grob_property ("add-cauda"));
284 bool add_join = to_boolean (me->get_grob_property ("add-join"));
286 if (!String::compare (glyph_name, ""))
289 * This is an empty head. This typically applies for the right
290 * side of a curved flexa shape, which is already typeset by the
291 * associated left side head. The only possible thing left to
292 * do is to draw a vertical join to the next head. (Urgh: need
293 * flexa_width.)
295 Real staff_space = Staff_symbol_referencer::staff_space (me);
296 Real flexa_width;
297 SCM flexa_width_scm = me->get_grob_property ("flexa-width");
298 if (flexa_width_scm != SCM_EOL)
300 flexa_width = gh_scm2double (flexa_width_scm);
302 else
304 me->warning ("Vaticana_ligature: "
305 "flexa-width undefined; assuming 2.0");
306 flexa_width = 2.0 * staff_space;
308 out =
309 Lookup::blank (Box (Interval (0, 0.5*flexa_width), Interval (0,0)));
311 else if (!String::compare (glyph_name, "flexa"))
313 out = vaticana_brew_flexa (me, true, line_thickness);
315 else
317 out =
318 Font_interface::get_default_font (me)->
319 find_by_name ("noteheads-" + glyph_name);
321 out.translate_axis (x_offset, X_AXIS);
322 Real head_width = out.extent (X_AXIS).length ();
324 if (add_cauda)
326 Molecule cauda =
327 vaticana_brew_cauda (me, pos, delta_pitch,
328 line_thickness, blotdiameter);
329 out.add_molecule (cauda);
332 if (add_stem)
334 Molecule stem =
335 vaticana_brew_cauda (me, pos, -1,
336 line_thickness, blotdiameter);
337 stem.translate_axis (head_width - line_thickness, X_AXIS);
338 out.add_molecule (stem);
341 if (add_join)
343 Molecule join =
344 vaticana_brew_join (me, delta_pitch, line_thickness, blotdiameter);
345 join.translate_axis (head_width - line_thickness, X_AXIS);
346 out.add_molecule (join);
349 vaticana_add_ledger_lines(me, &out, pos, 0, ledger_take_space);
350 if (!String::compare (glyph_name, "flexa"))
352 pos += flexa_height;
353 vaticana_add_ledger_lines(me, &out, pos, 0.5*flexa_height,
354 ledger_take_space);
357 return out;
360 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_ligature_primitive, 1);
362 Vaticana_ligature::brew_ligature_primitive (SCM smob)
364 Grob *me = unsmob_grob (smob);
365 SCM primitive = vaticana_brew_primitive (me, false).smobbed_copy ();
366 return primitive;
369 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_molecule, 1);
371 Vaticana_ligature::brew_molecule (SCM)
373 return SCM_EOL;
376 ADD_INTERFACE (Vaticana_ligature, "vaticana-ligature-interface",
377 "A vaticana style gregorian ligature",
378 "glyph-name flexa-height flexa-width thickness add-cauda "
379 "add-stem add-join delta-pitch x-offset "
380 "ligature-primitive-callback");