]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature.cc
* lily/beam.cc (brew_molecule): rewrite debug output: split up scores.
[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 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
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"
20
21 Molecule
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_staffline (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 Molecule
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   Molecule molecule = Molecule ();
75   Real right_height = 0.6 * staff_space;
76
77   Real interval;
78   SCM flexa_height_scm = me->get_grob_property ("flexa-height");
79   if (flexa_height_scm != SCM_EOL)
80     {
81       interval = gh_scm2int (flexa_height_scm);
82     }
83   else
84     {
85       me->warning ("Vaticana_ligature: "
86                    "flexa-height undefined; assuming 0");
87       interval = 0.0;
88     }
89
90   if (interval >= 0.0)
91     {
92       me->warning (_ ("ascending vaticana style flexa"));
93     }
94
95   Real width;
96   SCM flexa_width_scm = me->get_grob_property ("flexa-width");
97   if (flexa_width_scm != SCM_EOL)
98     {
99       width = gh_scm2double (flexa_width_scm);
100     }
101   else
102     {
103       me->warning ("Vaticana_ligature: "
104                    "flexa-width undefined; assuming 2.0");
105       width = 2.0 * staff_space;
106     }
107
108   /*
109    * Compensate curve thickness that appears to be smaller in steep
110    * section of bend.
111    */
112   Real left_height =
113     right_height +
114     min (0.12 * abs(interval), 0.3) * staff_space;
115
116   /*
117    * Compensate optical illusion regarding vertical position of left
118    * and right endings due to curved shape.
119    */
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;
123
124   /*
125    * middle curve of flexa shape
126    */
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);
132
133   Bezier top_curve = curve, bottom_curve = curve;
134   for (int i = 0; i < 4; i++)
135     {
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);
139     }
140
141   if (solid)
142     {
143       Molecule solid_head =
144         Lookup::bezier_sandwich (top_curve, bottom_curve);
145       molecule.add_molecule (solid_head);
146     }
147   else // outline
148     {
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);
154
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);
160
161       /*
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.
168        */
169       Box left_edge_box (Interval (0, line_thickness),
170                          Interval (-0.5*left_height, +0.5*left_height));
171       Molecule left_edge = Lookup::filled_box (left_edge_box);
172       molecule.add_molecule(left_edge);
173
174       Box right_edge_box (Interval (-line_thickness, 0),
175                           Interval (-0.5*right_height, +0.5*right_height));
176       Molecule right_edge = Lookup::filled_box (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);
180     }
181   molecule.translate_axis (ypos_correction, Y_AXIS);
182   return molecule;
183 }
184
185 Molecule
186 vaticana_brew_join (Grob *me, int delta_pitch,
187                     Real join_thickness, Real blotdiameter)
188 {
189   Real staff_space = Staff_symbol_referencer::staff_space (me);
190   if (!delta_pitch)
191     {
192       me->programming_error (_f ("Vaticana_ligature: "
193                                  "zero join (delta_pitch == 0)"));
194       return Molecule ();
195     }
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);
202 }
203
204 void
205 vaticana_add_ledger_lines (Grob *me, Molecule *out, int pos, Real offs,
206                            bool ledger_take_space)
207 {
208   int interspaces = Staff_symbol_referencer::line_count (me)-1;
209   if (abs (pos) - interspaces > 1)
210     {
211       Interval hd = out->extent (X_AXIS);
212       Real left_ledger_protusion = hd.length ()/4;
213       Real right_ledger_protusion = left_ledger_protusion;
214
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);
223     }
224 }
225
226 Molecule
227 vaticana_brew_primitive (Grob *me, bool ledger_take_space)
228 {
229   SCM glyph_name_scm = me->get_grob_property ("glyph-name");
230   if (glyph_name_scm == SCM_EOL)
231     {
232       me->programming_error ("Vaticana_ligature: "
233                              "undefined glyph-name -> ignoring grob");
234       return Molecule ();
235     }
236
237   String glyph_name = ly_scm2string (glyph_name_scm);
238
239   Molecule out;
240   int flexa_height = 0;
241   Real thickness;
242
243   SCM thickness_scm = me->get_grob_property ("thickness");
244   if (thickness_scm != SCM_EOL)
245     {
246       thickness = gh_scm2double (thickness_scm);
247     }
248   else
249     {
250       me->programming_error ("Vaticana_ligature: "
251                              "thickness undefined; assuming 1.0");
252       thickness = 1.0;
253     }
254
255   Real line_thickness =
256     thickness * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
257
258   Real blotdiameter =
259     (me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter")));
260
261   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
262
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;
269
270   Real x_offset = 0.0;
271   SCM x_offset_scm = me->get_grob_property ("x-offset");
272   if (x_offset_scm != SCM_EOL)
273     {
274       x_offset = gh_scm2double (x_offset_scm);
275     }
276   else
277     {
278       me->programming_error ("Vaticana_ligature: "
279                              "x-offset undefined; assuming 0.0");
280     }
281
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"));
285
286   if (!String::compare (glyph_name, ""))
287     {
288       /*
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.)
294        */
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)
299         {
300           flexa_width = gh_scm2double (flexa_width_scm);
301         }
302       else
303         {
304           me->warning ("Vaticana_ligature: "
305                        "flexa-width undefined; assuming 2.0");
306           flexa_width = 2.0 * staff_space;
307         }
308       out =
309         Lookup::blank (Box (Interval (0, 0.5*flexa_width), Interval (0,0)));
310     }
311   else if (!String::compare (glyph_name, "flexa"))
312     {
313       out = vaticana_brew_flexa (me, true, line_thickness);
314     }
315   else
316     {
317       out =
318         Font_interface::get_default_font (me)->
319         find_by_name ("noteheads-" + glyph_name);
320     }
321   out.translate_axis (x_offset, X_AXIS);
322   Real head_width = out.extent (X_AXIS).length ();
323
324   if (add_cauda)
325     {
326       Molecule cauda =
327         vaticana_brew_cauda (me, pos, delta_pitch,
328                              line_thickness, blotdiameter);
329       out.add_molecule (cauda);
330     }
331
332   if (add_stem)
333     {
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);
339     }
340
341   if (add_join)
342     {
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);
347     }
348
349   vaticana_add_ledger_lines(me, &out, pos, 0, ledger_take_space);
350   if (!String::compare (glyph_name, "flexa"))
351     {
352       pos += flexa_height;
353       vaticana_add_ledger_lines(me, &out, pos, 0.5*flexa_height,
354                                 ledger_take_space);
355     }
356
357   return out;
358 }
359
360 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_ligature_primitive, 1);
361 SCM
362 Vaticana_ligature::brew_ligature_primitive (SCM smob)
363 {
364   Grob *me = unsmob_grob (smob);
365   SCM primitive = vaticana_brew_primitive (me, false).smobbed_copy ();
366   return primitive;
367 }
368
369 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_molecule, 1);
370 SCM
371 Vaticana_ligature::brew_molecule (SCM)
372 {
373   return SCM_EOL;
374 }
375
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");