]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature.cc
Autobeaming works properly with tuplet recheck (Issue 2408)
[lilypond.git] / lily / vaticana-ligature.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2003--2012 Juergen Reuter <reuter@ipd.uka.de>
5
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.
10
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.
15
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/>.
18 */
19
20 #include "vaticana-ligature.hh"
21
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"
31
32 Stencil
33 vaticana_brew_cauda (Grob *me,
34                      int pos,
35                      int delta_pitch,
36                      Real thickness,
37                      Real blotdiameter)
38 {
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;
42
43   if (delta_pitch > -1)
44     {
45       me->programming_error ("flexa cauda: invalid delta_pitch; assuming -1");
46       delta_pitch = -1;
47     }
48   Real length;
49   if (on_staffline)
50     {
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;
57     }
58   else
59     {
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;
71     }
72   Box cauda_box (Interval (0, thickness), Interval (-length, 0));
73   return Lookup::round_filled_box (cauda_box, blotdiameter);
74 }
75
76 /*
77  * TODO: move this function to class Lookup?
78  */
79 Stencil
80 vaticana_brew_flexa (Grob *me,
81                      bool solid,
82                      Real line_thickness)
83 {
84   Real staff_space = Staff_symbol_referencer::staff_space (me);
85   Stencil stencil;
86   Real right_height = 0.6 * staff_space;
87
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
93     {
94       me->warning ("Vaticana_ligature: "
95                    + _ ("flexa-height undefined; assuming 0"));
96       interval = 0.0;
97     }
98
99   if (interval >= 0.0)
100     me->warning (_ ("ascending vaticana style flexa"));
101
102   Real width = robust_scm2double (me->get_property ("flexa-width"), 2);
103
104   /*
105    * Compensate curve thickness that appears to be smaller in steep
106    * section of bend.
107    */
108   Real left_height
109     = right_height
110       + min (0.12 * abs (interval), 0.3) * staff_space;
111
112   /*
113    * Compensate optical illusion regarding vertical position of left
114    * and right endings due to curved shape.
115    */
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;
119
120   /*
121    * middle curve of flexa shape
122    */
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);
128
129   Bezier top_curve = curve, bottom_curve = curve;
130   for (int i = 0; i < 4; i++)
131     {
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);
135     }
136
137   if (solid)
138     {
139       Stencil solid_head
140         = Lookup::bezier_sandwich (top_curve, bottom_curve, 0.0);
141       stencil.add_stencil (solid_head);
142     }
143   else // outline
144     {
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);
150
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);
156
157       /*
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.
164        */
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);
169
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);
176     }
177   stencil.translate_axis (ypos_correction, Y_AXIS);
178   return stencil;
179 }
180
181 Stencil
182 vaticana_brew_join (Grob *me, int delta_pitch,
183                     Real join_thickness, Real blotdiameter)
184 {
185   Real staff_space = Staff_symbol_referencer::staff_space (me);
186   if (!delta_pitch)
187     {
188       me->programming_error ("Vaticana_ligature: "
189                              "zero join (delta_pitch == 0)");
190       return Lookup::blank (Box (Interval (0, 0), Interval (0, 0)));
191     }
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);
198 }
199
200 Stencil
201 vaticana_brew_primitive (Grob *me)
202 {
203   SCM glyph_name_scm = me->get_property ("glyph-name");
204   if (glyph_name_scm == SCM_EOL)
205     {
206       me->programming_error ("Vaticana_ligature: "
207                              "undefined glyph-name -> ignoring grob");
208       return Lookup::blank (Box (Interval (0, 0), Interval (0, 0)));
209     }
210
211   string glyph_name = ly_scm2string (glyph_name_scm);
212
213   Stencil out;
214   Real thickness = robust_scm2double (me->get_property ("thickness"), 1);
215
216   Real line_thickness
217     = thickness * me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
218
219   Real blotdiameter
220     = (me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter")));
221
222   int pos = Staff_symbol_referencer::get_rounded_position (me);
223
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;
230
231   Real x_offset = robust_scm2double (me->get_property ("x-offset"), 0);
232
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"));
236
237   if (glyph_name == "")
238     {
239       /*
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.)
245        */
246       Real staff_space = Staff_symbol_referencer::staff_space (me);
247       Real flexa_width = robust_scm2double (me->get_property ("flexa-width"), 2) * staff_space;
248       out
249         = Lookup::blank (Box (Interval (0, 0.5 * flexa_width), Interval (0, 0)));
250     }
251   else if (glyph_name == "flexa")
252     out = vaticana_brew_flexa (me, true, line_thickness);
253   else
254     {
255       out
256         = Font_interface::get_default_font (me)->
257           find_by_name ("noteheads.s" + glyph_name);
258     }
259   out.translate_axis (x_offset, X_AXIS);
260   Real head_width = out.extent (X_AXIS).length ();
261
262   if (add_cauda)
263     {
264       Stencil cauda
265         = vaticana_brew_cauda (me, pos, delta_pitch,
266                                line_thickness, blotdiameter);
267       out.add_stencil (cauda);
268     }
269
270   if (add_stem)
271     {
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);
277     }
278
279   if (add_join)
280     {
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);
285     }
286
287   return out;
288 }
289
290 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_ligature_primitive, 1);
291 SCM
292 Vaticana_ligature::brew_ligature_primitive (SCM smob)
293 {
294   Grob *me = unsmob_grob (smob);
295   SCM primitive = vaticana_brew_primitive (me).smobbed_copy ();
296   return primitive;
297 }
298
299 MAKE_SCHEME_CALLBACK (Vaticana_ligature, print, 1);
300 SCM
301 Vaticana_ligature::print (SCM)
302 {
303   return SCM_EOL;
304 }
305
306 ADD_INTERFACE (Vaticana_ligature,
307                "A vaticana style Gregorian ligature.",
308
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 "
319               );