]> git.donarmstrong.com Git - lilypond.git/blob - lily/porrectus.cc
release: 1.5.48
[lilypond.git] / lily / porrectus.cc
1 /*
2   porrectus.cc -- implement Porrectus
3
4   Copyright (c) 2001--2002  Juergen Reuter
5
6   written for the GNU LilyPond music typesetter
7
8   TODO: --> see porrectus-engraver.cc
9 */
10
11 #include "staff-symbol-referencer.hh"
12 #include "porrectus.hh"
13 #include "item.hh"
14 #include "molecule.hh"
15 #include "pitch.hh"
16 #include "lookup.hh"
17 #include "debug.hh"
18 #include "dimensions.hh"
19 #include "direction.hh"
20 #include "bezier.hh"
21 #include "font-interface.hh"
22 #include "paper-def.hh"
23 #include "note-head.hh"
24 #include "math.h" // rint
25
26 void
27 Porrectus::set_left_head (Grob *me, Item *left_head)
28 {
29   if (left_head != 0)
30     {
31       me->set_grob_property ("left-head", left_head->self_scm());
32     }
33   else
34     {
35       programming_error (_ ("(left_head == 0)"));
36       me->set_grob_property ("left-head", SCM_EOL);
37     }
38 }
39
40 Item *
41 Porrectus::get_left_head (Grob *me)
42 {
43   SCM left_head_scm = me->get_grob_property ("left-head");
44   if (left_head_scm == SCM_EOL)
45     {
46       programming_error (_ ("undefined left_head"));
47       return 0;
48     }
49   else
50     {
51       Item *left_head = unsmob_item (left_head_scm);
52       return left_head;
53     }
54 }
55
56 void
57 Porrectus::set_right_head (Grob *me, Item *right_head)
58 {
59   if (right_head != 0)
60     {
61       me->set_grob_property ("right-head", right_head->self_scm());
62     }
63   else
64     {
65       programming_error (_ ("(right_head == 0)"));
66       me->set_grob_property ("right-head", SCM_EOL);
67     }
68 }
69
70 Item *
71 Porrectus::get_right_head (Grob *me)
72 {
73   SCM right_head_scm = me->get_grob_property ("right-head");
74   if (right_head_scm == SCM_EOL)
75     {
76       programming_error (_ ("undefined right_head"));
77       return 0;
78     }
79   else
80     {
81       Item *right_head = unsmob_item (right_head_scm);
82       return right_head;
83     }
84 }
85
86 MAKE_SCHEME_CALLBACK (Porrectus,brew_molecule,1);
87 SCM 
88 Porrectus::brew_molecule (SCM smob)
89 {
90   Item *me = (Item *)unsmob_grob (smob);
91
92   Item *left_head = get_left_head (me);
93   Item *right_head = get_right_head (me);
94   if (!left_head || !right_head)
95     {
96       me->warning (_ ("junking lonely porrectus"));
97       me->suicide ();
98       return SCM_EOL;
99     }
100
101   SCM scm_style = me->get_grob_property ("style");
102   String style;
103   if ((gh_symbol_p (scm_style)) && (scm_style != SCM_EOL))
104     style = ly_scm2string (scm_symbol_to_string (scm_style));
105   else {
106     me->warning (_ ("porrectus style undefined; using mensural"));
107     style = "mensural";
108   }
109
110   bool solid = to_boolean (me->get_grob_property ("solid"));
111   bool add_stem = to_boolean (me->get_grob_property ("add-stem"));
112
113   /*
114    * This property is called stem-direction (rather than direction)
115    * since it only refers to this grob's stem (or, more precisely, its
116    * "cauda"), but not the grob as a whole.
117    */
118   SCM stem_direction_scm = me->get_grob_property ("direction");
119   Direction stem_direction =
120     gh_number_p (stem_direction_scm) ? to_dir (stem_direction_scm) : DOWN;
121   if (!stem_direction)
122     stem_direction = DOWN;
123
124   /*
125     TODO: revise name.
126    */
127   bool auto_properties = to_boolean (me->get_grob_property ("auto-properties"));
128   if (auto_properties)
129       // determine add_stem and stem_direction automatically from durations
130     {
131       if (String::compare_i (style, "mensural") != 0)
132         me->warning (String("auto-property should be used for\r\n") +
133                  String("mensural style porrectus only; trying anyway"));
134
135       int left_duration =
136           gh_scm2int (left_head->get_grob_property ("duration-log"));
137       int right_duration =
138           gh_scm2int (right_head->get_grob_property ("duration-log"));
139
140       if ((left_duration == -1) && (right_duration == -1))
141         {
142           // brevis -- brevis:
143           // cum proprietate et sine perfectione (c.s.)
144           add_stem = true;
145           stem_direction = DOWN;
146         }
147       else if ((left_duration == -2) && (right_duration == -1))
148         {
149           // longa -- brevis:
150           // sine proprietate et sine perfectione (s.s.)
151           add_stem = false;
152         }
153       else if ((left_duration == 0) && (right_duration == 0))
154         {
155           // semibrevis -- semibrevis:
156           // cum opposita proprietate (c.o.p.)
157           add_stem = true;
158           stem_direction = UP;
159         }
160       else
161         {
162           me->warning (String("auto-property: failed determining porrectus\r\n") +
163                    String("properties due to improper durations; ") +
164                    String("using user-supplied properties"));
165         }
166     }
167
168   Real left_position_f = Staff_symbol_referencer::position_f (left_head);
169   Real right_position_f = Staff_symbol_referencer::position_f (right_head);
170   Real interval = right_position_f - left_position_f;
171
172   Molecule molecule;
173
174   SCM line_thickness_scm = me->get_grob_property ("thickness");
175   Real line_thickness;
176   if (gh_number_p (line_thickness_scm))
177     {
178       line_thickness = gh_scm2double (line_thickness_scm);
179     }
180   else
181     {
182       line_thickness = 1.0;
183     }
184   Real thickness =
185     line_thickness * me->paper_l ()->get_var ("stafflinethickness");
186
187   SCM porrectus_width_scm = me->get_grob_property ("width");
188   Real porrectus_width;
189   if (gh_number_p (porrectus_width_scm))
190     {
191       porrectus_width = gh_scm2double (porrectus_width_scm);
192     }
193   else
194     {
195       porrectus_width = 2.4;
196     }
197   Real width = porrectus_width * Staff_symbol_referencer::staff_space (me);
198
199   if (String::compare_i (style, "vaticana") == 0)
200     molecule = brew_vaticana_molecule (me, interval,
201                                        solid, width, thickness,
202                                        add_stem, stem_direction);
203   else if (String::compare_i (style, "mensural") == 0)
204     molecule = brew_mensural_molecule (me, interval,
205                                        solid, width, thickness,
206                                        add_stem, stem_direction);
207   else
208     return SCM_EOL;
209
210   Real space = Staff_symbol_referencer::staff_space (me);
211   Real head_extent = molecule.extent (X_AXIS).length ();
212   Interval extent (-0.2 * head_extent, 1.2 * head_extent);
213   int interspaces = Staff_symbol_referencer::line_count (me)-1;
214
215   molecule.translate_axis (left_position_f * space/2, Y_AXIS);
216
217   int left_pos = (int)rint (left_position_f);
218   if (abs (left_pos) - interspaces > 1)
219     {
220       Molecule left_head_ledger_lines =
221         Note_head::brew_ledger_lines (me, left_pos, interspaces, extent, true);
222       left_head_ledger_lines.translate_axis (left_position_f * space/2,
223                                              Y_AXIS);
224       molecule.add_molecule (left_head_ledger_lines);
225     }
226
227   int right_pos = (int)rint (right_position_f);
228   if (abs (right_pos) - interspaces > 1)
229     {
230       Molecule right_head_ledger_lines =
231         Note_head::brew_ledger_lines (me, right_pos, interspaces, extent, true);
232       right_head_ledger_lines.translate_axis (right_position_f * space/2,
233                                               Y_AXIS);
234       molecule.add_molecule (right_head_ledger_lines);
235     }
236
237   return molecule.smobbed_copy();
238 }
239
240 Molecule
241 Porrectus::brew_vaticana_molecule (Item *me,
242                                    Real interval,
243                                    bool solid,
244                                    Real width,
245                                    Real thickness,
246                                    bool add_stem,
247                                    Direction stem_direction)
248 {
249   if (interval >= 0.0)
250     {
251       me->warning (_ ("ascending vaticana style porrectus"));
252     }
253
254   Real space = Staff_symbol_referencer::staff_space (me);
255   Molecule molecule = Molecule ();
256   Real right_height = 0.6 * space;
257
258   // Compensate thickness that appears to be smaller in steep section
259   // of bend.
260   Real left_height = right_height + min (0.12 * abs(interval), 0.3) * space;
261
262   if (add_stem)
263     {
264       bool consider_interval =
265         stem_direction * interval > 0.0;
266
267       Interval stem_box_x (0, thickness);
268       Interval stem_box_y;
269
270       if (consider_interval)
271         {
272           Real y_length = max (abs(interval)/2.0*space +
273                                (right_height-left_height),
274                                1.2*space);
275           stem_box_y = Interval (0, y_length);
276         }
277       else
278         stem_box_y = Interval (0, space);
279
280       Real y_correction =
281         (stem_direction == UP) ?
282         +0.5*left_height :
283         -0.5*left_height - stem_box_y.length();
284
285       Box stem_box (stem_box_x, stem_box_y);
286       Molecule stem = Lookup::filledbox (stem_box);
287       stem.translate_axis (y_correction, Y_AXIS);
288       molecule.add_molecule(stem);
289     }
290
291   // Compensate optical illusion regarding vertical position of left
292   // and right endings due to curved shape.
293   Real ypos_correction = -0.1*space * sign(interval);
294   Real interval_correction = 0.2*space * sign(interval);
295   Real corrected_interval = interval*space + interval_correction;
296
297   // middle curve of vaticana style porrectus
298   Bezier curve;
299   curve.control_[0] = Offset (0.00 * width, 0.0);
300   curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
301   curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
302   curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
303
304   Bezier top_curve = curve, bottom_curve = curve;
305   for (int i = 0; i < 4; i++)
306     {
307       Real thickness = 0.33 * ((3 - i)*left_height + i*right_height);
308       top_curve.control_[i] += Offset (0, +0.5*thickness);
309       bottom_curve.control_[i] += Offset (0, -0.5*thickness);
310     }
311
312   if (solid)
313     {
314       Molecule solid_head =
315         Lookup::bezier_sandwich (top_curve, bottom_curve);
316       molecule.add_molecule (solid_head);
317     }
318   else // outline
319     {
320       Bezier inner_top_curve = top_curve;
321       inner_top_curve.translate (Offset (0.0, -thickness));
322       Molecule top_edge =
323         Lookup::bezier_sandwich (top_curve, inner_top_curve);
324       molecule.add_molecule(top_edge);
325
326       Bezier inner_bottom_curve = bottom_curve;
327       inner_bottom_curve.translate (Offset (0.0, +thickness));
328       Molecule bottom_edge =
329         Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve);
330       molecule.add_molecule(bottom_edge);
331
332       // TODO: Use horizontal slope with proper slope value rather
333       // than filled box for left edge, since the filled box stands
334       // out from the porrectus shape if the interval is big and the
335       // line thickness small.  The difficulty here is to compute a
336       // proper slope value, as it should roughly be equal with the
337       // slope of the left end of the bezier curve.
338       Box left_edge_box (Interval (0, thickness),
339                          Interval (-0.5*left_height, +0.5*left_height));
340       Molecule left_edge = Lookup::filledbox (left_edge_box);
341       molecule.add_molecule(left_edge);
342
343       Box right_edge_box (Interval (-thickness, 0),
344                           Interval (-0.5*right_height, +0.5*right_height));
345       Molecule right_edge = Lookup::filledbox (right_edge_box);
346       right_edge.translate_axis (width, X_AXIS);
347       right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
348       molecule.add_molecule(right_edge);
349     }
350   molecule.translate_axis (ypos_correction, Y_AXIS);
351   return molecule;
352 }
353
354 Molecule
355 Porrectus::brew_mensural_molecule (Item *me,
356                                    Real interval,
357                                    bool solid,
358                                    Real width,
359                                    Real thickness,
360                                    bool add_stem,
361                                    Direction stem_direction)
362 {
363   Real space = Staff_symbol_referencer::staff_space (me);
364   Real height = 0.6 * space;
365   Molecule molecule = Molecule ();
366
367   if (add_stem)
368     {
369       bool consider_interval =
370         stem_direction * interval > 0.0;
371
372       Interval stem_box_x (0, thickness);
373       Interval stem_box_y;
374
375       if (consider_interval)
376         {
377           Real y_length = max (interval/2.0*space, 1.2*space);
378           stem_box_y = Interval (0, y_length);
379         }
380       else
381         stem_box_y = Interval (0, space);
382
383       Real y_correction =
384         (stem_direction == UP) ?
385         +0.5*height :
386         -0.5*height - stem_box_y.length();
387
388       Box stem_box (stem_box_x, stem_box_y);
389       Molecule stem = Lookup::filledbox (stem_box);
390       stem.translate_axis (y_correction, Y_AXIS);
391       molecule.add_molecule(stem);
392     }
393
394   Real slope = (interval / 2.0 * space) / width;
395
396   // Compensate optical illusion regarding vertical position of left
397   // and right endings due to slope.
398   Real ypos_correction = -0.1*space * sign(slope);
399   Real slope_correction = 0.2*space * sign(slope);
400   Real corrected_slope = slope + slope_correction/width;
401
402   if (solid)
403     {
404       Molecule solid_head =
405         Lookup::horizontal_slope (width, corrected_slope, height);
406       molecule.add_molecule (solid_head);
407     }
408   else // outline
409     {
410       Molecule left_edge =
411         Lookup::horizontal_slope (thickness, corrected_slope, height);
412       molecule.add_molecule(left_edge);
413
414       Molecule right_edge =
415         Lookup::horizontal_slope (thickness, corrected_slope, height);
416       right_edge.translate_axis (width-thickness, X_AXIS);
417       right_edge.translate_axis (corrected_slope * (width-thickness), Y_AXIS);
418       molecule.add_molecule(right_edge);
419
420       Molecule bottom_edge =
421         Lookup::horizontal_slope (width, corrected_slope, thickness);
422       bottom_edge.translate_axis (-0.5*height, Y_AXIS);
423       molecule.add_molecule (bottom_edge);
424
425       Molecule top_edge =
426         Lookup::horizontal_slope (width, corrected_slope, thickness);
427       top_edge.translate_axis (+0.5*height, Y_AXIS);
428       molecule.add_molecule (top_edge);
429     }
430   molecule.translate_axis (ypos_correction, Y_AXIS);
431   return molecule;
432 }
433
434
435 ADD_INTERFACE (Porrectus,"porrectus-interface",
436   "A porrectus ligature, joining two note heads into a single grob.",
437   "left-head right-head width add-stem auto-properties solid direction");