2 porrectus.cc -- implement Porrectus
4 Copyright (c) 2001--2002 Juergen Reuter
6 written for the GNU LilyPond music typesetter
8 TODO: --> see porrectus-engraver.cc
11 #include "staff-symbol-referencer.hh"
12 #include "porrectus.hh"
14 #include "molecule.hh"
18 #include "dimensions.hh"
19 #include "direction.hh"
21 #include "font-interface.hh"
22 #include "paper-def.hh"
23 #include "math.h" // rint
26 Porrectus::set_left_head (Grob *me, Item *left_head)
30 me->set_grob_property ("left-head", left_head->self_scm());
34 programming_error (_ ("(left_head == 0)"));
35 me->set_grob_property ("left-head", SCM_EOL);
40 Porrectus::get_left_head (Grob *me)
42 SCM left_head_scm = me->get_grob_property ("left-head");
43 if (left_head_scm == SCM_EOL)
45 programming_error (_ ("undefined left_head"));
50 Item *left_head = unsmob_item (left_head_scm);
56 Porrectus::set_right_head (Grob *me, Item *right_head)
60 me->set_grob_property ("right-head", right_head->self_scm());
64 programming_error (_ ("(right_head == 0)"));
65 me->set_grob_property ("right-head", SCM_EOL);
70 Porrectus::get_right_head (Grob *me)
72 SCM right_head_scm = me->get_grob_property ("right-head");
73 if (right_head_scm == SCM_EOL)
75 programming_error (_ ("undefined right_head"));
80 Item *right_head = unsmob_item (right_head_scm);
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?
89 Porrectus::create_ledger_line (Interval x_extent, Grob *me)
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);
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);
102 // Create right ending of ledger line.
103 Molecule right_ending = slice;
104 right_ending.translate_axis (x_extent[RIGHT] - slice_x_extent[RIGHT],
106 line.add_molecule (right_ending);
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])
116 fill_out_slice.translate_axis (delta_x, X_AXIS);
117 line.add_molecule (fill_out_slice);
125 Porrectus::create_streepjes (Grob *me,
130 Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
131 int streepjes_i = abs (pos) < interspaces
133 : (abs (pos) - interspaces) /2;
134 Molecule molecule = Molecule();
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))
143 for (int i = 0; i < streepjes_i; i++)
145 Molecule streep (ledger_line);
146 streep.translate_axis (-dir * inter_f * i * 2 + offs,
148 molecule.add_molecule (streep);
154 MAKE_SCHEME_CALLBACK (Porrectus,brew_molecule,1);
156 Porrectus::brew_molecule (SCM smob)
158 Item *me = (Item *)unsmob_grob (smob);
160 Item *left_head = get_left_head (me);
161 Item *right_head = get_right_head (me);
162 if (!left_head || !right_head)
164 me->warning (_ ("junking lonely porrectus"));
169 SCM scm_style = me->get_grob_property ("style");
171 if ((gh_symbol_p (scm_style)) && (scm_style != SCM_EOL))
172 style = ly_scm2string (scm_symbol_to_string (scm_style));
174 me->warning (_ ("porrectus style undefined; using mensural"));
178 bool solid = to_boolean (me->get_grob_property ("solid"));
179 bool add_stem = to_boolean (me->get_grob_property ("add-stem"));
185 ugr. why not called direction?
188 SCM stem_direction_scm = me->get_grob_property ("direction");
189 Direction stem_direction =
190 gh_number_p (stem_direction_scm) ? to_dir (stem_direction_scm) : DOWN;
192 stem_direction = DOWN;
198 bool auto_properties = to_boolean (me->get_grob_property ("auto-properties"));
200 // determine add_stem and stem_direction automatically from durations
202 if (String::compare_i (style, "mensural") != 0)
203 me->warning (String("auto-property should be used for\r\n") +
204 String("mensural style porrectus only; trying anyway"));
207 gh_scm2int (left_head->get_grob_property ("duration-log"));
209 gh_scm2int (right_head->get_grob_property ("duration-log"));
211 if ((left_duration == -1) && (right_duration == -1))
214 // cum proprietate et sine perfectione (c.s.)
216 stem_direction = DOWN;
218 else if ((left_duration == -2) && (right_duration == -1))
221 // sine proprietate et sine perfectione (s.s.)
224 else if ((left_duration == 0) && (right_duration == 0))
226 // semibrevis -- semibrevis:
227 // cum opposita proprietate (c.o.p.)
233 me->warning (String("auto-property: failed determining porrectus\r\n") +
234 String("properties due to improper durations; ") +
235 String("using user-supplied properties"));
239 Real left_position_f = Staff_symbol_referencer::position_f (left_head);
240 Real right_position_f = Staff_symbol_referencer::position_f (right_head);
241 Real interval = right_position_f - left_position_f;
245 SCM line_thickness_scm = me->get_grob_property ("thickness");
247 if (gh_number_p (line_thickness_scm))
249 line_thickness = gh_scm2double (line_thickness_scm);
253 line_thickness = 1.0;
256 line_thickness * me->paper_l ()->get_var ("stafflinethickness");
258 SCM porrectus_width_scm = me->get_grob_property ("width");
259 Real porrectus_width;
260 if (gh_number_p (porrectus_width_scm))
262 porrectus_width = gh_scm2double (porrectus_width_scm);
266 porrectus_width = 2.4;
268 Real width = porrectus_width * Staff_symbol_referencer::staff_space (me);
270 if (String::compare_i (style, "vaticana") == 0)
271 molecule = brew_vaticana_molecule (me, interval,
272 solid, width, thickness,
273 add_stem, stem_direction);
274 else if (String::compare_i (style, "mensural") == 0)
275 molecule = brew_mensural_molecule (me, interval,
276 solid, width, thickness,
277 add_stem, stem_direction);
281 Real space = Staff_symbol_referencer::staff_space (me);
282 Real head_extent = molecule.extent (X_AXIS).length ();
283 Interval extent (-0.2 * head_extent, 1.2 * head_extent);
284 int interspaces = Staff_symbol_referencer::line_count (me)-1;
286 molecule.translate_axis (left_position_f * space/2, Y_AXIS);
288 Molecule left_head_streepjes =
289 create_streepjes (me, (int)rint (left_position_f), interspaces, extent);
290 left_head_streepjes.translate_axis (left_position_f * space/2, Y_AXIS);
291 molecule.add_molecule (left_head_streepjes);
293 Molecule right_head_streepjes =
294 create_streepjes (me, (int)rint (right_position_f), interspaces, extent);
295 right_head_streepjes.translate_axis (right_position_f * space/2, Y_AXIS);
296 molecule.add_molecule (right_head_streepjes);
298 return molecule.smobbed_copy();
302 Porrectus::brew_vaticana_molecule (Item *me,
308 Direction stem_direction)
312 me->warning (_ ("ascending vaticana style porrectus"));
315 Real space = Staff_symbol_referencer::staff_space (me);
316 Molecule molecule = Molecule ();
317 Real right_height = 0.6 * space;
319 // Compensate thickness that appears to be smaller in steep section
321 Real left_height = right_height + min (0.12 * abs(interval), 0.3) * space;
325 bool consider_interval =
326 stem_direction * interval > 0.0;
328 Interval stem_box_x (0, thickness);
331 if (consider_interval)
333 Real y_length = max (abs(interval)/2.0*space +
334 (right_height-left_height),
336 stem_box_y = Interval (0, y_length);
339 stem_box_y = Interval (0, space);
342 (stem_direction == UP) ?
344 -0.5*left_height - stem_box_y.length();
346 Box stem_box (stem_box_x, stem_box_y);
347 Molecule stem = Lookup::filledbox (stem_box);
348 stem.translate_axis (y_correction, Y_AXIS);
349 molecule.add_molecule(stem);
352 // Compensate optical illusion regarding vertical position of left
353 // and right endings due to curved shape.
354 Real ypos_correction = -0.1*space * sign(interval);
355 Real interval_correction = 0.2*space * sign(interval);
356 Real corrected_interval = interval*space + interval_correction;
358 // middle curve of vaticana style porrectus
360 curve.control_[0] = Offset (0.00 * width, 0.0);
361 curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
362 curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
363 curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
365 Bezier top_curve = curve, bottom_curve = curve;
366 for (int i = 0; i < 4; i++)
368 Real thickness = 0.33 * ((3 - i)*left_height + i*right_height);
369 top_curve.control_[i] += Offset (0, +0.5*thickness);
370 bottom_curve.control_[i] += Offset (0, -0.5*thickness);
375 Molecule solid_head =
376 Lookup::bezier_sandwich (top_curve, bottom_curve);
377 molecule.add_molecule (solid_head);
381 Bezier inner_top_curve = top_curve;
382 inner_top_curve.translate (Offset (0.0, -thickness));
384 Lookup::bezier_sandwich (top_curve, inner_top_curve);
385 molecule.add_molecule(top_edge);
387 Bezier inner_bottom_curve = bottom_curve;
388 inner_bottom_curve.translate (Offset (0.0, +thickness));
389 Molecule bottom_edge =
390 Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve);
391 molecule.add_molecule(bottom_edge);
393 // TODO: Use horizontal slope with proper slope value rather
394 // than filled box for left edge, since the filled box stands
395 // out from the porrectus shape if the interval is big and the
396 // line thickness small. The difficulty here is to compute a
397 // proper slope value, as it should roughly be equal with the
398 // slope of the left end of the bezier curve.
399 Box left_edge_box (Interval (0, thickness),
400 Interval (-0.5*left_height, +0.5*left_height));
401 Molecule left_edge = Lookup::filledbox (left_edge_box);
402 molecule.add_molecule(left_edge);
404 Box right_edge_box (Interval (-thickness, 0),
405 Interval (-0.5*right_height, +0.5*right_height));
406 Molecule right_edge = Lookup::filledbox (right_edge_box);
407 right_edge.translate_axis (width, X_AXIS);
408 right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
409 molecule.add_molecule(right_edge);
411 molecule.translate_axis (ypos_correction, Y_AXIS);
416 Porrectus::brew_mensural_molecule (Item *me,
422 Direction stem_direction)
424 Real space = Staff_symbol_referencer::staff_space (me);
425 Real height = 0.6 * space;
426 Molecule molecule = Molecule ();
430 bool consider_interval =
431 stem_direction * interval > 0.0;
433 Interval stem_box_x (0, thickness);
436 if (consider_interval)
438 Real y_length = max (interval/2.0*space, 1.2*space);
439 stem_box_y = Interval (0, y_length);
442 stem_box_y = Interval (0, space);
445 (stem_direction == UP) ?
447 -0.5*height - stem_box_y.length();
449 Box stem_box (stem_box_x, stem_box_y);
450 Molecule stem = Lookup::filledbox (stem_box);
451 stem.translate_axis (y_correction, Y_AXIS);
452 molecule.add_molecule(stem);
455 Real slope = (interval / 2.0 * space) / width;
457 // Compensate optical illusion regarding vertical position of left
458 // and right endings due to slope.
459 Real ypos_correction = -0.1*space * sign(slope);
460 Real slope_correction = 0.2*space * sign(slope);
461 Real corrected_slope = slope + slope_correction/width;
465 Molecule solid_head =
466 Lookup::horizontal_slope (width, corrected_slope, height);
467 molecule.add_molecule (solid_head);
472 Lookup::horizontal_slope (thickness, corrected_slope, height);
473 molecule.add_molecule(left_edge);
475 Molecule right_edge =
476 Lookup::horizontal_slope (thickness, corrected_slope, height);
477 right_edge.translate_axis (width-thickness, X_AXIS);
478 right_edge.translate_axis (corrected_slope * (width-thickness), Y_AXIS);
479 molecule.add_molecule(right_edge);
481 Molecule bottom_edge =
482 Lookup::horizontal_slope (width, corrected_slope, thickness);
483 bottom_edge.translate_axis (-0.5*height, Y_AXIS);
484 molecule.add_molecule (bottom_edge);
487 Lookup::horizontal_slope (width, corrected_slope, thickness);
488 top_edge.translate_axis (+0.5*height, Y_AXIS);
489 molecule.add_molecule (top_edge);
491 molecule.translate_axis (ypos_correction, Y_AXIS);
496 ADD_INTERFACE (Porrectus,"porrectus-interface",
497 "A porrectus ligature, joining two note heads into a single grob.",
498 "left-head right-head width add-stem auto-properties solid direction");