]> git.donarmstrong.com Git - lilypond.git/blob - lily/porrectus.cc
release: 1.5.11
[lilypond.git] / lily / porrectus.cc
1 /*
2   porrectus.cc -- implement Porrectus
3
4   Copyright (C) 2001 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 "math.h" // rint
24
25 void
26 Porrectus::set_left_head (Grob *me, Item *left_head)
27 {
28   if (left_head != 0)
29     {
30       me->set_grob_property ("left-head", left_head->self_scm());
31     }
32   else
33     {
34       programming_error (_ ("(left_head == 0)"));
35       me->set_grob_property ("left-head", SCM_EOL);
36     }
37 }
38
39 Item *
40 Porrectus::get_left_head (Grob *me)
41 {
42   SCM left_head_scm = me->get_grob_property ("left-head");
43   if (left_head_scm == SCM_EOL)
44     {
45       programming_error (_ ("undefined left_head"));
46       return 0;
47     }
48   else
49     {
50       Item *left_head = dynamic_cast<Item*> (unsmob_grob (left_head_scm));
51       return left_head;
52     }
53 }
54
55 void
56 Porrectus::set_right_head (Grob *me, Item *right_head)
57 {
58   if (right_head != 0)
59     {
60       me->set_grob_property ("right-head", right_head->self_scm());
61     }
62   else
63     {
64       programming_error (_ ("(right_head == 0)"));
65       me->set_grob_property ("right-head", SCM_EOL);
66     }
67 }
68
69 Item *
70 Porrectus::get_right_head (Grob *me)
71 {
72   SCM right_head_scm = me->get_grob_property ("right-head");
73   if (right_head_scm == SCM_EOL)
74     {
75       programming_error (_ ("undefined right_head"));
76       return 0;
77     }
78   else
79     {
80       Item *right_head = dynamic_cast<Item*> (unsmob_grob (right_head_scm));
81       return right_head;
82     }
83 }
84
85 // Uugh.  The following two functions are almost duplicated code from
86 // custos.cc, which itself is similar to code in note-head.cc.  Maybe
87 // this should be moved to staff-symbol-referencer.cc?
88 Molecule
89 Porrectus::create_ledger_line (Interval x_extent, Grob *me) 
90 {
91   Molecule line;
92   Molecule slice = Font_interface::get_default_font (me)->find_by_name ("noteheads-ledgerending");
93   Interval slice_x_extent = slice.extent (X_AXIS);
94   Interval slice_y_extent = slice.extent (Y_AXIS);
95
96   // Create left ending of ledger line.
97   Molecule left_ending = slice;
98   left_ending.translate_axis (x_extent[LEFT] - slice_x_extent[LEFT], X_AXIS);
99   if (x_extent.length () > slice_x_extent.length ())
100     line.add_molecule (left_ending);
101
102   // Create right ending of ledger line.
103   Molecule right_ending = slice;
104   right_ending.translate_axis (x_extent[RIGHT] - slice_x_extent[RIGHT],
105                                X_AXIS);
106   line.add_molecule (right_ending);
107
108   // Fill out space between left and right ending of ledger line by
109   // lining up a series of slices in a row between them.
110   Molecule fill_out_slice = left_ending;
111   Real thick = slice_y_extent.length ();
112   Real delta_x = slice_x_extent.length () - thick;
113   Real xpos = x_extent [LEFT] + 2*delta_x + thick/2; // TODO: check: thick*2?
114   while (xpos <= x_extent[RIGHT])
115     {
116       fill_out_slice.translate_axis (delta_x, X_AXIS);
117       line.add_molecule (fill_out_slice);
118       xpos += delta_x;
119     }
120
121   return line;
122 }
123
124 Molecule
125 Porrectus::create_streepjes (Grob *me,
126                              int pos,
127                              int interspaces,
128                              Interval extent)
129 {
130   Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
131   int streepjes_i = abs (pos) < interspaces
132     ? 0
133     : (abs (pos) - interspaces) /2;
134   Molecule molecule = Molecule();
135   if (streepjes_i) 
136     {
137       Direction dir = (Direction)sign (pos);
138       Molecule ledger_line (create_ledger_line (extent, me));
139       ledger_line.set_empty (true);
140       Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
141         ? 0.0
142         : -dir * inter_f;
143       for (int i = 0; i < streepjes_i; i++)
144         {
145           Molecule streep (ledger_line);
146           streep.translate_axis (-dir * inter_f * i * 2 + offs,
147                                  Y_AXIS);
148           molecule.add_molecule (streep);
149         }
150     }
151   return molecule;
152 }
153
154 MAKE_SCHEME_CALLBACK (Porrectus,brew_molecule,1);
155 SCM 
156 Porrectus::brew_molecule (SCM smob)
157 {
158   Item *me = (Item *)unsmob_grob (smob);
159
160   SCM scm_style = me->get_grob_property ("style");
161   String style;
162   if ((gh_symbol_p (scm_style)) && (scm_style != SCM_EOL))
163     style = ly_scm2string (scm_symbol_to_string (scm_style));
164   else {
165     warning (_ ("porrectus style undefined; using mensural"));
166     style = "mensural";
167   }
168
169   bool solid = to_boolean (me->get_grob_property ("solid"));
170   bool add_stem = to_boolean (me->get_grob_property ("add-stem"));
171
172   SCM stem_direction_scm = me->get_grob_property ("stem-direction");
173   Direction stem_direction =
174     gh_number_p (stem_direction_scm) ? to_dir (stem_direction_scm) : DOWN;
175   if (!stem_direction)
176     stem_direction = DOWN;
177
178   Item *left_head = get_left_head (me);
179   Item *right_head = get_right_head (me);
180   if (!left_head || !right_head)
181     {
182       warning (_ ("junking lonely porrectus"));
183       me->suicide ();
184       return SCM_EOL;
185     }
186
187   Real left_position_f = Staff_symbol_referencer::position_f (left_head);
188   Real right_position_f = Staff_symbol_referencer::position_f (right_head);
189   Real interval = right_position_f - left_position_f;
190
191   Molecule molecule;
192
193   SCM line_thickness_scm = me->get_grob_property ("line-thickness");
194   Real line_thickness;
195   if (gh_number_p (line_thickness_scm))
196     {
197       line_thickness = gh_scm2double (line_thickness_scm);
198     }
199   else
200     {
201       line_thickness = 1.0;
202     }
203   Real thickness =
204     line_thickness * me->paper_l ()->get_var ("stafflinethickness");
205
206   SCM porrectus_width_scm = me->get_grob_property ("porrectus-width");
207   Real porrectus_width;
208   if (gh_number_p (porrectus_width_scm))
209     {
210       porrectus_width = gh_scm2double (porrectus_width_scm);
211     }
212   else
213     {
214       porrectus_width = 2.4;
215     }
216   Real width = porrectus_width * Staff_symbol_referencer::staff_space (me);
217
218   if (String::compare_i (style, "vaticana") == 0)
219     molecule = brew_vaticana_molecule (me, interval,
220                                        solid, width, thickness,
221                                        add_stem, stem_direction);
222   else if (String::compare_i (style, "mensural") == 0)
223     molecule = brew_mensural_molecule (me, interval,
224                                        solid, width, thickness,
225                                        add_stem, stem_direction);
226   else
227     return SCM_EOL;
228
229   Real space = Staff_symbol_referencer::staff_space (me);
230   Real head_extent = molecule.extent (X_AXIS).length ();
231   Interval extent (-0.2 * head_extent, 1.2 * head_extent);
232   int interspaces = Staff_symbol_referencer::line_count (me)-1;
233
234   molecule.translate_axis (left_position_f * space/2, Y_AXIS);
235
236   Molecule left_head_streepjes =
237     create_streepjes (me, (int)rint (left_position_f), interspaces, extent);
238   left_head_streepjes.translate_axis (left_position_f * space/2, Y_AXIS);
239   molecule.add_molecule (left_head_streepjes);
240
241   Molecule right_head_streepjes =
242     create_streepjes (me, (int)rint (right_position_f), interspaces, extent);
243   right_head_streepjes.translate_axis (right_position_f * space/2, Y_AXIS);
244   molecule.add_molecule (right_head_streepjes);
245
246   return molecule.smobbed_copy();
247 }
248
249 Molecule
250 Porrectus::brew_vaticana_molecule (Item *me,
251                                    Real interval,
252                                    bool solid,
253                                    Real width,
254                                    Real thickness,
255                                    bool add_stem,
256                                    Direction stem_direction)
257 {
258   Real space = Staff_symbol_referencer::staff_space (me);
259   Molecule molecule = Molecule ();
260
261   if (interval >= 0.0)
262     {
263       warning (_ ("ascending vaticana style porrectus"));
264     }
265
266   if (add_stem)
267     {
268       bool consider_interval =
269         stem_direction * interval > 0.0;
270
271       Interval stem_box_x (-thickness/2, +thickness/2);
272       Interval stem_box_y;
273
274       if (consider_interval)
275         {
276           Real y_length = interval / 2.0;
277           if (y_length < 1.2 * space)
278             y_length = 1.2 * space;
279           stem_box_y = Interval (0, y_length);
280         }
281       else
282         stem_box_y = Interval (0, space);
283
284       Real y_correction =
285         (stem_direction == UP) ?
286         0.3 * space :
287         - 0.3 * space - stem_box_y.length();
288
289       Box stem_box (stem_box_x, stem_box_y);
290       Molecule stem = Lookup::filledbox (stem_box);
291       stem.translate_axis (y_correction, Y_AXIS);
292       molecule.add_molecule(stem);
293     }
294
295   Box vertical_edge (Interval (-thickness/2, +thickness/2),
296                      Interval (-4*thickness/2, +4*thickness/2));
297   Molecule left_edge = Lookup::filledbox (vertical_edge);
298   Molecule right_edge = Lookup::filledbox (vertical_edge);
299   right_edge.translate_axis (width, X_AXIS);
300   right_edge.translate_axis (interval / 2.0, Y_AXIS);
301   molecule.add_molecule(left_edge);
302   molecule.add_molecule(right_edge);
303
304   Bezier bezier;
305   bezier.control_[0] = Offset (0.00 * width, 0.0);
306   bezier.control_[1] = Offset (0.33 * width, interval / 2.0);
307   bezier.control_[2] = Offset (0.66 * width, interval / 2.0);
308   bezier.control_[3] = Offset (1.00 * width, interval / 2.0);
309
310   Molecule slice;
311   slice = Lookup::slur (bezier, 0.0, thickness);
312   slice.translate_axis (-3 * thickness/2, Y_AXIS);
313   molecule.add_molecule (slice);
314   if (solid)
315     for (int i = -2; i < +2; i++)
316       {
317         slice = Lookup::slur (bezier, 0.0, thickness);
318         slice.translate_axis (i * thickness/2, Y_AXIS);
319         molecule.add_molecule (slice);
320       }
321   slice = Lookup::slur (bezier, 0.0, thickness);
322   slice.translate_axis (+3 * thickness/2, Y_AXIS);
323   molecule.add_molecule (slice);
324
325   return molecule;
326 }
327
328 Molecule
329 Porrectus::brew_mensural_molecule (Item *me,
330                                    Real interval,
331                                    bool solid,
332                                    Real width,
333                                    Real thickness,
334                                    bool add_stem,
335                                    Direction stem_direction)
336 {
337   Real space = Staff_symbol_referencer::staff_space (me);
338   Molecule molecule = Molecule ();
339
340   if (add_stem)
341     {
342       // Uugh.  This is currently the same as in
343       // brew_vaticana_molecule, but may eventually be changed.
344
345       bool consider_interval =
346         stem_direction * interval > 0.0;
347
348       Interval stem_box_x (0, thickness);
349       Interval stem_box_y;
350
351       if (consider_interval)
352         {
353           Real y_length = interval / 2.0;
354           if (y_length < 1.2 * space)
355             y_length = 1.2 * space;
356           stem_box_y = Interval (0, y_length);
357         }
358       else
359         stem_box_y = Interval (0, space);
360
361       Real y_correction =
362         (stem_direction == UP) ?
363         0.3 * space :
364         - 0.3 * space - stem_box_y.length();
365
366       Box stem_box (stem_box_x, stem_box_y);
367       Molecule stem = Lookup::filledbox (stem_box);
368       stem.translate_axis (y_correction, Y_AXIS);
369       molecule.add_molecule(stem);
370     }
371
372   Real slope = (interval / 2.0) / width;
373
374   // Compensate optical illusion regarding vertical position of left
375   // and right endings due to slope.
376   Real ypos_correction = -0.1*space * sign(slope);
377   Real slope_correction = 0.2*space * sign(slope);
378   Real corrected_slope = slope + slope_correction/width;
379
380   if (solid)
381     {
382       Molecule solid_head =
383         brew_horizontal_slope (width, corrected_slope, 0.6*space);
384       molecule.add_molecule (solid_head);
385     }
386   else
387     {
388       Molecule left_edge =
389           brew_horizontal_slope (thickness, corrected_slope, 0.6*space);
390       molecule.add_molecule(left_edge);
391
392       Molecule right_edge =
393           brew_horizontal_slope (thickness, corrected_slope, 0.6*space);
394       right_edge.translate_axis (width-thickness, X_AXIS);
395       right_edge.translate_axis (corrected_slope * (width-thickness), Y_AXIS);
396       molecule.add_molecule(right_edge);
397
398       Molecule bottom_edge =
399           brew_horizontal_slope (width, corrected_slope, thickness);
400       bottom_edge.translate_axis (-0.3*space, Y_AXIS);
401       molecule.add_molecule (bottom_edge);
402
403       Molecule top_edge =
404           brew_horizontal_slope (width, corrected_slope, thickness);
405       top_edge.translate_axis (+0.3*space, Y_AXIS);
406       molecule.add_molecule (top_edge);
407     }
408   molecule.translate_axis (ypos_correction, Y_AXIS);
409   return molecule;
410 }
411
412 /*
413  * Horizontal Slope:
414  *
415  *            /|   ^
416  *           / |   |
417  *          /  |   | thickness
418  *         /   |   |
419  *        /    |   v
420  *       |    /
421  *       |   /
422  * (0,0) x  /slope=dy/dx
423  *       | /
424  *       |/
425  *
426  *       <----->
427  *        width
428  */
429 Molecule
430 Porrectus::brew_horizontal_slope(Real width, Real slope, Real thickness)
431 {
432   SCM width_scm = gh_double2scm (width);
433   SCM slope_scm = gh_double2scm (slope);
434   SCM thickness_scm = gh_double2scm (thickness);
435   SCM horizontal_slope = gh_list (ly_symbol2scm ("beam"),
436                                   width_scm, slope_scm,
437                                   thickness_scm, SCM_UNDEFINED);
438   Box b (Interval (0, width),
439          Interval (-thickness/2, thickness/2 + width*slope));
440   return Molecule (b, horizontal_slope);
441 }