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