]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature.cc
3b1c12efe5fd1eae98bda0fef061802938a4e235
[lilypond.git] / lily / vaticana-ligature.cc
1 /*
2   vaticana-ligature.cc -- implement Vaticana_ligature
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2003--2007 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
9 #include "vaticana-ligature.hh"
10
11 #include "bezier.hh"
12 #include "font-interface.hh"
13 #include "international.hh"
14 #include "item.hh"
15 #include "lookup.hh"
16 #include "note-head.hh"
17 #include "output-def.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "warn.hh"
20
21 Stencil
22 vaticana_brew_cauda (Grob *me,
23                      int pos,
24                      int delta_pitch,
25                      Real thickness,
26                      Real blotdiameter)
27 {
28   bool on_staffline = Staff_symbol_referencer::on_line (me, pos);
29   int interspaces = Staff_symbol_referencer::line_count (me) - 1;
30   bool above_staff = pos > interspaces;
31
32   if (delta_pitch > -1)
33     {
34       me->programming_error ("flexa cauda: invalid delta_pitch; assuming -1");
35       delta_pitch = -1;
36     }
37   Real length;
38   if (on_staffline)
39     {
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;
46     }
47   else
48     {
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;
60     }
61   Box cauda_box (Interval (0, thickness), Interval (-length, 0));
62   return Lookup::round_filled_box (cauda_box, blotdiameter);
63 }
64
65 /*
66  * TODO: move this function to class Lookup?
67  */
68 Stencil
69 vaticana_brew_flexa (Grob *me,
70                      bool solid,
71                      Real line_thickness)
72 {
73   Real staff_space = Staff_symbol_referencer::staff_space (me);
74   Stencil stencil;
75   Real right_height = 0.6 * staff_space;
76
77   Real interval;
78   SCM flexa_height_scm = me->get_property ("flexa-height");
79   if (flexa_height_scm != SCM_EOL)
80     interval = scm_to_int (flexa_height_scm);
81   else
82     {
83       me->warning ("Vaticana_ligature: "
84                    + _ ("flexa-height undefined; assuming 0"));
85       interval = 0.0;
86     }
87
88   if (interval >= 0.0)
89     me->warning (_ ("ascending vaticana style flexa"));
90
91   Real width = robust_scm2double (me->get_property ("flexa-width"), 2);
92
93   /*
94    * Compensate curve thickness that appears to be smaller in steep
95    * section of bend.
96    */
97   Real left_height
98     = right_height
99     + min (0.12 * abs (interval), 0.3) * staff_space;
100
101   /*
102    * Compensate optical illusion regarding vertical position of left
103    * and right endings due to curved shape.
104    */
105   Real ypos_correction = -0.1 * staff_space * sign (interval);
106   Real interval_correction = 0.2 * staff_space * sign (interval);
107   Real corrected_interval = interval * staff_space + interval_correction;
108
109   /*
110    * middle curve of flexa shape
111    */
112   Bezier curve;
113   curve.control_[0] = Offset (0.00 * width, 0.0);
114   curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
115   curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
116   curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
117
118   Bezier top_curve = curve, bottom_curve = curve;
119   for (int i = 0; i < 4; i++)
120     {
121       Real curve_thickness = 0.33 * ((3 - i) * left_height + i * right_height);
122       top_curve.control_[i] += Offset (0, 0.5 * curve_thickness);
123       bottom_curve.control_[i] -= Offset (0, 0.5 * curve_thickness);
124     }
125
126   if (solid)
127     {
128       Stencil solid_head
129         = Lookup::bezier_sandwich (top_curve, bottom_curve);
130       stencil.add_stencil (solid_head);
131     }
132   else // outline
133     {
134       Bezier inner_top_curve = top_curve;
135       inner_top_curve.translate (Offset (0.0, -line_thickness));
136       Stencil top_edge
137         = Lookup::bezier_sandwich (top_curve, inner_top_curve);
138       stencil.add_stencil (top_edge);
139
140       Bezier inner_bottom_curve = bottom_curve;
141       inner_bottom_curve.translate (Offset (0.0, +line_thickness));
142       Stencil bottom_edge
143         = Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve);
144       stencil.add_stencil (bottom_edge);
145
146       /*
147        * TODO: Use horizontal slope with proper slope value rather
148        * than filled box for left edge, since the filled box stands
149        * out from the flexa shape if the interval is big and the line
150        * thickness small.  The difficulty here is to compute a proper
151        * slope value, as it should roughly be equal with the slope of
152        * the left end of the bezier curve.
153        */
154       Box left_edge_box (Interval (0, line_thickness),
155                          Interval (-0.5 * left_height, +0.5 * left_height));
156       Stencil left_edge = Lookup::filled_box (left_edge_box);
157       stencil.add_stencil (left_edge);
158
159       Box right_edge_box (Interval (-line_thickness, 0),
160                           Interval (-0.5 * right_height, +0.5 * right_height));
161       Stencil right_edge = Lookup::filled_box (right_edge_box);
162       right_edge.translate_axis (width, X_AXIS);
163       right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
164       stencil.add_stencil (right_edge);
165     }
166   stencil.translate_axis (ypos_correction, Y_AXIS);
167   return stencil;
168 }
169
170 Stencil
171 vaticana_brew_join (Grob *me, int delta_pitch,
172                     Real join_thickness, Real blotdiameter)
173 {
174   Real staff_space = Staff_symbol_referencer::staff_space (me);
175   if (!delta_pitch)
176     {
177       me->programming_error (_ ("Vaticana_ligature: "
178                                 "zero join (delta_pitch == 0)"));
179       return Lookup::blank (Box (Interval (0, 0), Interval (0, 0)));
180     }
181   Interval x_extent = Interval (0, join_thickness);
182   Interval y_extent = (delta_pitch > 0)
183     ? Interval (0, delta_pitch * 0.5 * staff_space) : // ascending join
184     Interval (delta_pitch * 0.5 * staff_space, 0); // descending join
185   Box join_box (x_extent, y_extent);
186   return Lookup::round_filled_box (join_box, blotdiameter);
187 }
188
189 Stencil
190 vaticana_brew_primitive (Grob *me)
191 {
192   SCM glyph_name_scm = me->get_property ("glyph-name");
193   if (glyph_name_scm == SCM_EOL)
194     {
195       me->programming_error ("Vaticana_ligature: "
196                              "undefined glyph-name -> ignoring grob");
197       return Lookup::blank (Box (Interval (0, 0), Interval (0, 0)));
198     }
199
200   string glyph_name = ly_scm2string (glyph_name_scm);
201
202   Stencil out;
203   Real thickness = robust_scm2double (me->get_property ("thickness"), 1);
204
205   Real line_thickness
206     = thickness * me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
207
208   Real blotdiameter
209     = (me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter")));
210
211   int pos = Staff_symbol_referencer::get_rounded_position (me);
212
213   SCM delta_pitch_scm = me->get_property ("delta-position");
214   int delta_pitch;
215   if (delta_pitch_scm != SCM_EOL)
216     delta_pitch = scm_to_int (delta_pitch_scm);
217   else
218     delta_pitch = 0;
219
220   Real x_offset = robust_scm2double (me->get_property ("x-offset"), 0);
221
222   bool add_stem = to_boolean (me->get_property ("add-stem"));
223   bool add_cauda = to_boolean (me->get_property ("add-cauda"));
224   bool add_join = to_boolean (me->get_property ("add-join"));
225
226   if (glyph_name == "")
227     {
228       /*
229        * This is an empty head.  This typically applies for the right
230        * side of a curved flexa shape, which is already typeset by the
231        * associated left side head.  The only possible thing left to
232        * do is to draw a vertical join to the next head.  (Urgh: need
233        * flexa_width.)
234        */
235       Real staff_space = Staff_symbol_referencer::staff_space (me);
236       Real flexa_width = robust_scm2double (me->get_property ("flexa-width"), 2) * staff_space;
237       out
238         = Lookup::blank (Box (Interval (0, 0.5 * flexa_width), Interval (0, 0)));
239     }
240   else if (glyph_name == "flexa")
241     out = vaticana_brew_flexa (me, true, line_thickness);
242   else
243     {
244       out
245         = Font_interface::get_default_font (me)->
246         find_by_name ("noteheads.s" + glyph_name);
247     }
248   out.translate_axis (x_offset, X_AXIS);
249   Real head_width = out.extent (X_AXIS).length ();
250
251   if (add_cauda)
252     {
253       Stencil cauda
254         = vaticana_brew_cauda (me, pos, delta_pitch,
255                                line_thickness, blotdiameter);
256       out.add_stencil (cauda);
257     }
258
259   if (add_stem)
260     {
261       Stencil stem
262         = vaticana_brew_cauda (me, pos, -1,
263                                line_thickness, blotdiameter);
264       stem.translate_axis (head_width - line_thickness, X_AXIS);
265       out.add_stencil (stem);
266     }
267
268   if (add_join)
269     {
270       Stencil join
271         = vaticana_brew_join (me, delta_pitch, line_thickness, blotdiameter);
272       join.translate_axis (head_width - line_thickness, X_AXIS);
273       out.add_stencil (join);
274     }
275
276   return out;
277 }
278
279 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_ligature_primitive, 1);
280 SCM
281 Vaticana_ligature::brew_ligature_primitive (SCM smob)
282 {
283   Grob *me = unsmob_grob (smob);
284   SCM primitive = vaticana_brew_primitive (me).smobbed_copy ();
285   return primitive;
286 }
287
288 MAKE_SCHEME_CALLBACK (Vaticana_ligature, print, 1);
289 SCM
290 Vaticana_ligature::print (SCM)
291 {
292   return SCM_EOL;
293 }
294
295 ADD_INTERFACE (Vaticana_ligature,
296                "A vaticana style Gregorian ligature.",
297
298                /* properties */
299                "glyph-name "
300                "flexa-height "
301                "flexa-width "
302                "thickness "
303                "add-cauda "
304                "add-stem "
305                "add-join "
306                "delta-position "
307                "x-offset "
308                );