]> git.donarmstrong.com Git - lilypond.git/blob - lily/porrectus.cc
release: 1.5.28
[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 = unsmob_item (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 = unsmob_item (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   Item *left_head = get_left_head (me);
161   Item *right_head = get_right_head (me);
162   if (!left_head || !right_head)
163     {
164       warning (_ ("junking lonely porrectus"));
165       me->suicide ();
166       return SCM_EOL;
167     }
168
169   SCM scm_style = me->get_grob_property ("style");
170   String style;
171   if ((gh_symbol_p (scm_style)) && (scm_style != SCM_EOL))
172     style = ly_scm2string (scm_symbol_to_string (scm_style));
173   else {
174     warning (_ ("porrectus style undefined; using mensural"));
175     style = "mensural";
176   }
177
178   bool solid = to_boolean (me->get_grob_property ("solid"));
179   bool add_stem = to_boolean (me->get_grob_property ("add-stem"));
180
181   SCM stem_direction_scm = me->get_grob_property ("stem-direction");
182   Direction stem_direction =
183     gh_number_p (stem_direction_scm) ? to_dir (stem_direction_scm) : DOWN;
184   if (!stem_direction)
185     stem_direction = DOWN;
186
187   bool auto_properties = to_boolean (me->get_grob_property ("auto-properties"));
188   if (auto_properties)
189       // determine add_stem and stem_direction automatically from durations
190     {
191       if (String::compare_i (style, "mensural") != 0)
192         warning (String("auto-property should be used for\r\n") +
193                  String("mensural style porrectus only; trying anyway"));
194
195       int left_duration =
196           gh_scm2int (left_head->get_grob_property ("duration-log"));
197       int right_duration =
198           gh_scm2int (right_head->get_grob_property ("duration-log"));
199
200       if ((left_duration == -1) && (right_duration == -1))
201         {
202           // brevis -- brevis:
203           // cum proprietate et sine perfectione (c.s.)
204           add_stem = true;
205           stem_direction = DOWN;
206         }
207       else if ((left_duration == -2) && (right_duration == -1))
208         {
209           // longa -- brevis:
210           // sine proprietate et sine perfectione (s.s.)
211           add_stem = false;
212         }
213       else if ((left_duration == 0) && (right_duration == 0))
214         {
215           // semibrevis -- semibrevis:
216           // cum opposita proprietate (c.o.p.)
217           add_stem = true;
218           stem_direction = UP;
219         }
220       else
221         {
222           warning (String("auto-property: failed determining porrectus\r\n") +
223                    String("properties due to improper durations; ") +
224                    String("using user-supplied properties"));
225         }
226     }
227
228   Real left_position_f = Staff_symbol_referencer::position_f (left_head);
229   Real right_position_f = Staff_symbol_referencer::position_f (right_head);
230   Real interval = right_position_f - left_position_f;
231
232   Molecule molecule;
233
234   SCM line_thickness_scm = me->get_grob_property ("line-thickness");
235   Real line_thickness;
236   if (gh_number_p (line_thickness_scm))
237     {
238       line_thickness = gh_scm2double (line_thickness_scm);
239     }
240   else
241     {
242       line_thickness = 1.0;
243     }
244   Real thickness =
245     line_thickness * me->paper_l ()->get_var ("stafflinethickness");
246
247   SCM porrectus_width_scm = me->get_grob_property ("porrectus-width");
248   Real porrectus_width;
249   if (gh_number_p (porrectus_width_scm))
250     {
251       porrectus_width = gh_scm2double (porrectus_width_scm);
252     }
253   else
254     {
255       porrectus_width = 2.4;
256     }
257   Real width = porrectus_width * Staff_symbol_referencer::staff_space (me);
258
259   if (String::compare_i (style, "vaticana") == 0)
260     molecule = brew_vaticana_molecule (me, interval,
261                                        solid, width, thickness,
262                                        add_stem, stem_direction);
263   else if (String::compare_i (style, "mensural") == 0)
264     molecule = brew_mensural_molecule (me, interval,
265                                        solid, width, thickness,
266                                        add_stem, stem_direction);
267   else
268     return SCM_EOL;
269
270   Real space = Staff_symbol_referencer::staff_space (me);
271   Real head_extent = molecule.extent (X_AXIS).length ();
272   Interval extent (-0.2 * head_extent, 1.2 * head_extent);
273   int interspaces = Staff_symbol_referencer::line_count (me)-1;
274
275   molecule.translate_axis (left_position_f * space/2, Y_AXIS);
276
277   Molecule left_head_streepjes =
278     create_streepjes (me, (int)rint (left_position_f), interspaces, extent);
279   left_head_streepjes.translate_axis (left_position_f * space/2, Y_AXIS);
280   molecule.add_molecule (left_head_streepjes);
281
282   Molecule right_head_streepjes =
283     create_streepjes (me, (int)rint (right_position_f), interspaces, extent);
284   right_head_streepjes.translate_axis (right_position_f * space/2, Y_AXIS);
285   molecule.add_molecule (right_head_streepjes);
286
287   return molecule.smobbed_copy();
288 }
289
290 Molecule
291 Porrectus::brew_vaticana_molecule (Item *me,
292                                    Real interval,
293                                    bool solid,
294                                    Real width,
295                                    Real thickness,
296                                    bool add_stem,
297                                    Direction stem_direction)
298 {
299   if (interval >= 0.0)
300     {
301       warning (_ ("ascending vaticana style porrectus"));
302     }
303
304   Real space = Staff_symbol_referencer::staff_space (me);
305   Molecule molecule = Molecule ();
306   Real right_height = 0.6 * space;
307
308   // Compensate thickness that appears to be smaller in steep section
309   // of bend.
310   Real left_height = right_height + min (0.12 * abs(interval), 0.3) * space;
311
312   if (add_stem)
313     {
314       bool consider_interval =
315         stem_direction * interval > 0.0;
316
317       Interval stem_box_x (0, thickness);
318       Interval stem_box_y;
319
320       if (consider_interval)
321         {
322           Real y_length = max (abs(interval)/2.0*space +
323                                (right_height-left_height),
324                                1.2*space);
325           stem_box_y = Interval (0, y_length);
326         }
327       else
328         stem_box_y = Interval (0, space);
329
330       Real y_correction =
331         (stem_direction == UP) ?
332         +0.5*left_height :
333         -0.5*left_height - stem_box_y.length();
334
335       Box stem_box (stem_box_x, stem_box_y);
336       Molecule stem = Lookup::filledbox (stem_box);
337       stem.translate_axis (y_correction, Y_AXIS);
338       molecule.add_molecule(stem);
339     }
340
341   // Compensate optical illusion regarding vertical position of left
342   // and right endings due to curved shape.
343   Real ypos_correction = -0.1*space * sign(interval);
344   Real interval_correction = 0.2*space * sign(interval);
345   Real corrected_interval = interval*space + interval_correction;
346
347   // middle curve of vaticana style porrectus
348   Bezier curve;
349   curve.control_[0] = Offset (0.00 * width, 0.0);
350   curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
351   curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
352   curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
353
354   Bezier top_curve = curve, bottom_curve = curve;
355   for (int i = 0; i < 4; i++)
356     {
357       Real thickness = 0.33 * ((3 - i)*left_height + i*right_height);
358       top_curve.control_[i] += Offset (0, +0.5*thickness);
359       bottom_curve.control_[i] += Offset (0, -0.5*thickness);
360     }
361
362   if (solid)
363     {
364       Molecule solid_head =
365         brew_bezier_sandwich (top_curve, bottom_curve);
366       molecule.add_molecule (solid_head);
367     }
368   else // outline
369     {
370       Bezier inner_top_curve = top_curve;
371       inner_top_curve.translate (Offset (0.0, -thickness));
372       Molecule top_edge =
373         brew_bezier_sandwich (top_curve, inner_top_curve);
374       molecule.add_molecule(top_edge);
375
376       Bezier inner_bottom_curve = bottom_curve;
377       inner_bottom_curve.translate (Offset (0.0, +thickness));
378       Molecule bottom_edge =
379         brew_bezier_sandwich (bottom_curve, inner_bottom_curve);
380       molecule.add_molecule(bottom_edge);
381
382       // TODO: Use horizontal slope with proper slope value rather
383       // than filled box for left edge, since the filled box stands
384       // out from the porrectus shape if the interval is big and the
385       // line thickness small.  The difficulty here is to compute a
386       // proper slope value, as it should roughly be equal with the
387       // slope of the left end of the bezier curve.
388       Box left_edge_box (Interval (0, thickness),
389                          Interval (-0.5*left_height, +0.5*left_height));
390       Molecule left_edge = Lookup::filledbox (left_edge_box);
391       molecule.add_molecule(left_edge);
392
393       Box right_edge_box (Interval (-thickness, 0),
394                           Interval (-0.5*right_height, +0.5*right_height));
395       Molecule right_edge = Lookup::filledbox (right_edge_box);
396       right_edge.translate_axis (width, X_AXIS);
397       right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
398       molecule.add_molecule(right_edge);
399     }
400   molecule.translate_axis (ypos_correction, Y_AXIS);
401   return molecule;
402 }
403
404 Molecule
405 Porrectus::brew_mensural_molecule (Item *me,
406                                    Real interval,
407                                    bool solid,
408                                    Real width,
409                                    Real thickness,
410                                    bool add_stem,
411                                    Direction stem_direction)
412 {
413   Real space = Staff_symbol_referencer::staff_space (me);
414   Real height = 0.6 * space;
415   Molecule molecule = Molecule ();
416
417   if (add_stem)
418     {
419       bool consider_interval =
420         stem_direction * interval > 0.0;
421
422       Interval stem_box_x (0, thickness);
423       Interval stem_box_y;
424
425       if (consider_interval)
426         {
427           Real y_length = max (interval/2.0*space, 1.2*space);
428           stem_box_y = Interval (0, y_length);
429         }
430       else
431         stem_box_y = Interval (0, space);
432
433       Real y_correction =
434         (stem_direction == UP) ?
435         +0.5*height :
436         -0.5*height - stem_box_y.length();
437
438       Box stem_box (stem_box_x, stem_box_y);
439       Molecule stem = Lookup::filledbox (stem_box);
440       stem.translate_axis (y_correction, Y_AXIS);
441       molecule.add_molecule(stem);
442     }
443
444   Real slope = (interval / 2.0 * space) / width;
445
446   // Compensate optical illusion regarding vertical position of left
447   // and right endings due to slope.
448   Real ypos_correction = -0.1*space * sign(slope);
449   Real slope_correction = 0.2*space * sign(slope);
450   Real corrected_slope = slope + slope_correction/width;
451
452   if (solid)
453     {
454       Molecule solid_head =
455         brew_horizontal_slope (width, corrected_slope, height);
456       molecule.add_molecule (solid_head);
457     }
458   else // outline
459     {
460       Molecule left_edge =
461           brew_horizontal_slope (thickness, corrected_slope, height);
462       molecule.add_molecule(left_edge);
463
464       Molecule right_edge =
465           brew_horizontal_slope (thickness, corrected_slope, height);
466       right_edge.translate_axis (width-thickness, X_AXIS);
467       right_edge.translate_axis (corrected_slope * (width-thickness), Y_AXIS);
468       molecule.add_molecule(right_edge);
469
470       Molecule bottom_edge =
471           brew_horizontal_slope (width, corrected_slope, thickness);
472       bottom_edge.translate_axis (-0.5*height, Y_AXIS);
473       molecule.add_molecule (bottom_edge);
474
475       Molecule top_edge =
476           brew_horizontal_slope (width, corrected_slope, thickness);
477       top_edge.translate_axis (+0.5*height, Y_AXIS);
478       molecule.add_molecule (top_edge);
479     }
480   molecule.translate_axis (ypos_correction, Y_AXIS);
481   return molecule;
482 }
483
484 /*
485  * Bezier Sandwich:
486  *
487  *                               .|
488  *                        .       |
489  *              top .             |
490  *              . curve           |
491  *          .                     |
492  *       .                        |
493  *     .                          |
494  *    |                           |
495  *    |                          .|
496  *    |                     .
497  *    |         bottom .
498  *    |            . curve
499  *    |         .
500  *    |      .
501  *    |   .
502  *    | .
503  *    |.
504  *    |
505  *
506  */
507 // TODO: Move this to class Lookup?
508 Molecule
509 Porrectus::brew_bezier_sandwich (Bezier top_curve, Bezier bottom_curve)
510 {
511   /*
512     Need the weird order b.o. the way PS want its arguments  
513    */
514   SCM list = SCM_EOL;
515   list = gh_cons (ly_offset2scm (bottom_curve.control_[3]), list);
516   list = gh_cons (ly_offset2scm (bottom_curve.control_[0]), list);
517   list = gh_cons (ly_offset2scm (bottom_curve.control_[1]), list);
518   list = gh_cons (ly_offset2scm (bottom_curve.control_[2]), list);
519   list = gh_cons (ly_offset2scm (top_curve.control_[0]), list);
520   list = gh_cons (ly_offset2scm (top_curve.control_[3]), list);
521   list = gh_cons (ly_offset2scm (top_curve.control_[2]), list);
522   list = gh_cons (ly_offset2scm (top_curve.control_[1]), list);
523
524   SCM horizontal_bend = scm_list_n (ly_symbol2scm ("bezier-sandwich"),
525                                     ly_quote_scm (list),
526                                     gh_double2scm (0.0),
527                                     SCM_UNDEFINED);
528
529   Interval x_extent = top_curve.extent (X_AXIS);
530   x_extent.unite (bottom_curve.extent (X_AXIS));
531   Interval y_extent = top_curve.extent (Y_AXIS);
532   y_extent.unite (bottom_curve.extent (Y_AXIS));
533   Box b (x_extent, y_extent);
534
535   return Molecule (b, horizontal_bend);
536 }
537
538 /*
539  * Horizontal Slope:
540  *
541  *            /|   ^
542  *           / |   |
543  *          /  |   | height
544  *         /   |   |
545  *        /    |   v
546  *       |    /
547  *       |   /
548  * (0,0) x  /slope=dy/dx
549  *       | /
550  *       |/
551  *
552  *       <----->
553  *        width
554  */
555 // TODO: Move this to class Lookup?
556 Molecule
557 Porrectus::brew_horizontal_slope (Real width, Real slope, Real height)
558 {
559   SCM width_scm = gh_double2scm (width);
560   SCM slope_scm = gh_double2scm (slope);
561   SCM height_scm = gh_double2scm (height);
562   SCM horizontal_slope = scm_list_n (ly_symbol2scm ("beam"),
563                                      width_scm, slope_scm,
564                                      height_scm, SCM_UNDEFINED);
565   Box b (Interval (0, width),
566          Interval (-height/2, height/2 + width*slope));
567   return Molecule (b, horizontal_slope);
568 }