]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature.cc
*** empty log message ***
[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 /*
22  * TODO: move this function to class Lookup?
23  */
24 Molecule
25 vaticana_brew_flexa (Grob *me,
26                      bool solid,
27                      Real thickness,
28                      Direction stem_direction)
29 {
30   Real staff_space = Staff_symbol_referencer::staff_space (me);
31   Molecule molecule = Molecule ();
32   Real right_height = 0.6 * staff_space;
33
34   Real interval;
35   SCM flexa_height_scm = me->get_grob_property ("flexa-height");
36   if (flexa_height_scm != SCM_EOL)
37     {
38       interval = gh_scm2int (flexa_height_scm);
39     }
40   else
41     {
42       me->warning ("Vaticana_ligature: "
43                    "flexa-height undefined; assuming 0");
44       interval = 0.0;
45     }
46
47   if (interval >= 0.0)
48     {
49       me->warning (_ ("ascending vaticana style flexa"));
50     }
51
52   Real width;
53   SCM flexa_width_scm = me->get_grob_property ("flexa-width");
54   if (flexa_width_scm != SCM_EOL)
55     {
56       width = gh_scm2double (flexa_width_scm);
57     }
58   else
59     {
60       me->warning ("Vaticana_ligature:"
61                    "flexa-width undefined; assuming 2.0");
62       width = 2.0 * staff_space;
63     }
64
65   bool add_stem = to_boolean (me->get_grob_property ("add-stem"));
66
67   // Compensate thickness that appears to be smaller in steep section
68   // of bend.
69   Real left_height =
70     right_height +
71     min (0.12 * abs(interval), 0.3) * staff_space;
72
73   if (add_stem)
74     {
75       bool consider_interval =
76         stem_direction * interval > 0.0;
77
78       Interval stem_box_x (0, thickness);
79       Interval stem_box_y;
80
81       if (consider_interval)
82         {
83           Real y_length = max (abs(interval)/2.0*staff_space +
84                                (right_height-left_height),
85                                1.2*staff_space);
86           stem_box_y = Interval (0, y_length);
87         }
88       else
89         stem_box_y = Interval (0, staff_space);
90
91       Real y_correction =
92         (stem_direction == UP) ?
93         +0.5*left_height :
94         -0.5*left_height - stem_box_y.length();
95
96       Box stem_box (stem_box_x, stem_box_y);
97       Molecule stem = Lookup::filledbox (stem_box);
98       stem.translate_axis (y_correction, Y_AXIS);
99       molecule.add_molecule(stem);
100     }
101
102   // Compensate optical illusion regarding vertical position of left
103   // and right endings due to curved shape.
104   Real ypos_correction = -0.1*staff_space * sign(interval);
105   Real interval_correction = 0.2*staff_space * sign(interval);
106   Real corrected_interval = interval*staff_space + interval_correction;
107
108   // middle curve of flexa shape
109   Bezier curve;
110   curve.control_[0] = Offset (0.00 * width, 0.0);
111   curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
112   curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
113   curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
114
115   Bezier top_curve = curve, bottom_curve = curve;
116   for (int i = 0; i < 4; i++)
117     {
118       Real thickness = 0.33 * ((3 - i)*left_height + i*right_height);
119       top_curve.control_[i] += Offset (0, +0.5*thickness);
120       bottom_curve.control_[i] += Offset (0, -0.5*thickness);
121     }
122
123   if (solid)
124     {
125       Molecule solid_head =
126         Lookup::bezier_sandwich (top_curve, bottom_curve);
127       molecule.add_molecule (solid_head);
128     }
129   else // outline
130     {
131       Bezier inner_top_curve = top_curve;
132       inner_top_curve.translate (Offset (0.0, -thickness));
133       Molecule top_edge =
134         Lookup::bezier_sandwich (top_curve, inner_top_curve);
135       molecule.add_molecule(top_edge);
136
137       Bezier inner_bottom_curve = bottom_curve;
138       inner_bottom_curve.translate (Offset (0.0, +thickness));
139       Molecule bottom_edge =
140         Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve);
141       molecule.add_molecule(bottom_edge);
142
143       // TODO: Use horizontal slope with proper slope value rather
144       // than filled box for left edge, since the filled box stands
145       // out from the flexa shape if the interval is big and the line
146       // thickness small.  The difficulty here is to compute a proper
147       // slope value, as it should roughly be equal with the slope of
148       // the left end of the bezier curve.
149       Box left_edge_box (Interval (0, thickness),
150                          Interval (-0.5*left_height, +0.5*left_height));
151       Molecule left_edge = Lookup::filledbox (left_edge_box);
152       molecule.add_molecule(left_edge);
153
154       Box right_edge_box (Interval (-thickness, 0),
155                           Interval (-0.5*right_height, +0.5*right_height));
156       Molecule right_edge = Lookup::filledbox (right_edge_box);
157       right_edge.translate_axis (width, X_AXIS);
158       right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
159       molecule.add_molecule(right_edge);
160     }
161   molecule.translate_axis (ypos_correction, Y_AXIS);
162   return molecule;
163 }
164
165 void
166 vaticana_add_ledger_lines (Grob *me, Molecule *out, int pos, Real offs,
167                            bool ledger_take_space)
168 {
169   int interspaces = Staff_symbol_referencer::line_count (me)-1;
170   if (abs (pos) - interspaces > 1)
171     {
172       Interval hd = out->extent (X_AXIS);
173       Real left_ledger_protusion = hd.length ()/4;
174       Real right_ledger_protusion = left_ledger_protusion;
175
176       Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
177                                      hd[RIGHT] + right_ledger_protusion);
178       Molecule ledger_lines =
179         Note_head::brew_ledger_lines (me, pos, interspaces,
180                                       l_extents,
181                                       ledger_take_space);
182       ledger_lines.translate_axis (offs, Y_AXIS);
183       out->add_molecule (ledger_lines);
184     }
185 }
186
187 Molecule
188 vaticana_brew_primitive (Grob *me, bool ledger_take_space)
189 {
190   SCM glyph_name_scm = me->get_grob_property ("glyph-name");
191   if (glyph_name_scm == SCM_EOL)
192     {
193       programming_error ("Vaticana_ligature:"
194                          "undefined glyph-name -> ignoring grob");
195       return Molecule ();
196     }
197
198   String glyph_name = ly_scm2string (glyph_name_scm);
199   if (!String::compare (glyph_name, ""))
200     {
201       // empty head (typically, this is the right side of flexa shape,
202       // which is already typeset by the associated left side head);
203       // nothing left to do
204       return Molecule ();
205     }
206
207   Molecule out;
208   int flexa_height = 0;
209   Real thickness = 0.0;
210   Real staff_space = Staff_symbol_referencer::staff_space (me);
211
212   SCM thickness_scm = me->get_grob_property ("thickness");
213   if (thickness_scm != SCM_EOL)
214     {
215       thickness = gh_scm2double (thickness_scm);
216     }
217   else
218     {
219       programming_error (_f ("Vaticana_ligature:"
220                              "thickness undefined; assuming 1.4",
221                              me));
222       thickness = 1.4 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
223     }
224
225   Real x_offset = 0.0;
226   SCM x_offset_scm = me->get_grob_property ("x-offset");
227   if (x_offset_scm != SCM_EOL)
228     {
229       x_offset = gh_scm2double (x_offset_scm);
230     }
231   else
232     {
233       programming_error (_f ("Vaticana_ligature:"
234                              "x-offset undefined; assuming 0.0",
235                              me));
236     }
237
238   if (!String::compare (glyph_name, "flexa"))
239     {
240       out = vaticana_brew_flexa (me, true, thickness, DOWN);
241     }
242   else
243     {
244       Molecule mol =
245         Font_interface::get_default_font (me)->
246         find_by_name ("noteheads-" + glyph_name);
247       mol.translate_axis (x_offset, X_AXIS);
248       out.add_molecule (mol);
249     }
250
251   if (to_boolean (me->get_grob_property ("join-left")))
252     {
253       SCM delta_pitch_scm = me->get_grob_property ("delta-pitch");
254       if (delta_pitch_scm != SCM_EOL)
255         {
256           int delta_pitch = gh_scm2int (delta_pitch_scm);
257           if (!delta_pitch)
258             programming_error (_f ("Vaticana_ligature: (delta_pitch == 0)"));
259           Real blotdiameter = (me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter")));
260           Interval x_extent = Interval (0, thickness);
261           Interval y_extent = (delta_pitch > 0) ?
262             Interval (-delta_pitch * 0.5 * staff_space, 0) : // ascending join
263             Interval (0, -delta_pitch * 0.5 * staff_space); // descending join
264           Box stem_box (x_extent, y_extent);
265
266           Molecule stem = Lookup::roundfilledbox (stem_box, blotdiameter);
267           out.add_molecule (stem);
268         }
269       else
270         {
271           programming_error (_f ("Vaticana_ligature:"
272                                  "delta-pitch -> ignoring join",
273                                  me));
274         }
275     }
276
277   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
278   vaticana_add_ledger_lines(me, &out, pos, 0, ledger_take_space);
279   if (!String::compare (glyph_name, "flexa"))
280     {
281       pos += flexa_height;
282       vaticana_add_ledger_lines(me, &out, pos, 0.5*flexa_height, ledger_take_space);
283     }
284
285   return out;
286 }
287
288 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_ligature_primitive, 1);
289 SCM
290 Vaticana_ligature::brew_ligature_primitive (SCM smob)
291 {
292   Grob *me = unsmob_grob (smob);
293   SCM primitive = vaticana_brew_primitive (me, false).smobbed_copy ();
294   return primitive;
295 }
296
297 MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_molecule, 1);
298 SCM
299 Vaticana_ligature::brew_molecule (SCM)
300 {
301   return SCM_EOL;
302 }
303
304 ADD_INTERFACE (Vaticana_ligature, "vaticana-ligature-interface",
305                "A vaticana style gregorian ligature",
306                "glyph-name flexa-height flexa-width thickness join-left "
307                "delta-pitch add-stem x-offset ligature-primitive-callback");