2 porrectus.cc -- implement Porrectus
4 Copyright (c) 2001--2003 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 "note-head.hh"
24 #include "math.h" // rint
27 Porrectus::set_left_head (Grob
*me
, Item
*left_head
)
31 me
->set_grob_property ("left-head", left_head
->self_scm());
35 programming_error (_ ("(left_head == 0)"));
36 me
->set_grob_property ("left-head", SCM_EOL
);
41 Porrectus::get_left_head (Grob
*me
)
43 SCM left_head_scm
= me
->get_grob_property ("left-head");
44 if (left_head_scm
== SCM_EOL
)
46 programming_error (_ ("undefined left_head"));
51 Item
*left_head
= unsmob_item (left_head_scm
);
57 Porrectus::set_right_head (Grob
*me
, Item
*right_head
)
61 me
->set_grob_property ("right-head", right_head
->self_scm());
65 programming_error (_ ("(right_head == 0)"));
66 me
->set_grob_property ("right-head", SCM_EOL
);
71 Porrectus::get_right_head (Grob
*me
)
73 SCM right_head_scm
= me
->get_grob_property ("right-head");
74 if (right_head_scm
== SCM_EOL
)
76 programming_error (_ ("undefined right_head"));
81 Item
*right_head
= unsmob_item (right_head_scm
);
86 MAKE_SCHEME_CALLBACK (Porrectus
,brew_molecule
,1);
88 Porrectus::brew_molecule (SCM smob
)
90 Item
*me
= (Item
*)unsmob_grob (smob
);
92 Item
*left_head
= get_left_head (me
);
93 Item
*right_head
= get_right_head (me
);
94 if (!left_head
|| !right_head
)
96 me
->warning (_ ("junking lonely porrectus"));
101 SCM scm_style
= me
->get_grob_property ("style");
103 if ((gh_symbol_p (scm_style
)) && (scm_style
!= SCM_EOL
))
104 style
= ly_symbol2string (scm_style
);
106 me
->warning (_ ("porrectus style undefined; using mensural"));
110 bool solid
= to_boolean (me
->get_grob_property ("solid"));
111 bool add_stem
= to_boolean (me
->get_grob_property ("add-stem"));
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.
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
;
122 stem_direction
= DOWN
;
127 bool auto_properties
= to_boolean (me
->get_grob_property ("auto-properties"));
129 // determine add_stem and stem_direction automatically from durations
131 if (String::compare (style
, "mensural") != 0)
132 me
->warning (String("auto-property should be used for\r\n") +
133 String("mensural style porrectus only; trying anyway"));
136 gh_scm2int (left_head
->get_grob_property ("duration-log"));
138 gh_scm2int (right_head
->get_grob_property ("duration-log"));
140 if ((left_duration
== -1) && (right_duration
== -1))
143 // cum proprietate et sine perfectione (c.s.)
145 stem_direction
= DOWN
;
147 else if ((left_duration
== -2) && (right_duration
== -1))
150 // sine proprietate et sine perfectione (s.s.)
153 else if ((left_duration
== 0) && (right_duration
== 0))
155 // semibrevis -- semibrevis:
156 // cum opposita proprietate (c.o.p.)
162 me
->warning (String("auto-property: failed determining porrectus\r\n") +
163 String("properties due to improper durations; ") +
164 String("using user-supplied properties"));
168 Real left_position_f
= Staff_symbol_referencer::get_position (left_head
);
169 Real right_position_f
= Staff_symbol_referencer::get_position (right_head
);
170 Real interval
= right_position_f
- left_position_f
;
174 SCM line_thickness_scm
= me
->get_grob_property ("thickness");
176 if (gh_number_p (line_thickness_scm
))
178 line_thickness
= gh_scm2double (line_thickness_scm
);
182 line_thickness
= 1.0;
185 line_thickness
* me
->get_paper ()->get_var ("linethickness");
187 SCM porrectus_width_scm
= me
->get_grob_property ("width");
188 Real porrectus_width
;
189 if (gh_number_p (porrectus_width_scm
))
191 porrectus_width
= gh_scm2double (porrectus_width_scm
);
195 porrectus_width
= 2.4;
197 Real width
= porrectus_width
* Staff_symbol_referencer::staff_space (me
);
199 if (String::compare (style
, "vaticana") == 0)
200 molecule
= brew_vaticana_molecule (me
, interval
,
201 solid
, width
, thickness
,
202 add_stem
, stem_direction
);
203 else if (String::compare (style
, "mensural") == 0)
204 molecule
= brew_mensural_molecule (me
, interval
,
205 solid
, width
, thickness
,
206 add_stem
, stem_direction
);
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;
215 molecule
.translate_axis (left_position_f
* space
/2, Y_AXIS
);
217 int left_pos
= (int)rint (left_position_f
);
218 if (abs (left_pos
) - interspaces
> 1)
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,
224 molecule
.add_molecule (left_head_ledger_lines
);
227 int right_pos
= (int)rint (right_position_f
);
228 if (abs (right_pos
) - interspaces
> 1)
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,
234 molecule
.add_molecule (right_head_ledger_lines
);
237 return molecule
.smobbed_copy();
241 Porrectus::brew_vaticana_molecule (Item
*me
,
247 Direction stem_direction
)
251 me
->warning (_ ("ascending vaticana style porrectus"));
254 Real space
= Staff_symbol_referencer::staff_space (me
);
255 Molecule molecule
= Molecule ();
256 Real right_height
= 0.6 * space
;
258 // Compensate thickness that appears to be smaller in steep section
260 Real left_height
= right_height
+ min (0.12 * abs(interval
), 0.3) * space
;
264 bool consider_interval
=
265 stem_direction
* interval
> 0.0;
267 Interval
stem_box_x (0, thickness
);
270 if (consider_interval
)
272 Real y_length
= max (abs(interval
)/2.0*space
+
273 (right_height
-left_height
),
275 stem_box_y
= Interval (0, y_length
);
278 stem_box_y
= Interval (0, space
);
281 (stem_direction
== UP
) ?
283 -0.5*left_height
- stem_box_y
.length();
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
);
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
;
297 // middle curve of vaticana style porrectus
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);
304 Bezier top_curve
= curve
, bottom_curve
= curve
;
305 for (int i
= 0; i
< 4; i
++)
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
);
314 Molecule solid_head
=
315 Lookup::bezier_sandwich (top_curve
, bottom_curve
);
316 molecule
.add_molecule (solid_head
);
320 Bezier inner_top_curve
= top_curve
;
321 inner_top_curve
.translate (Offset (0.0, -thickness
));
323 Lookup::bezier_sandwich (top_curve
, inner_top_curve
);
324 molecule
.add_molecule(top_edge
);
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
);
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
);
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
);
350 molecule
.translate_axis (ypos_correction
, Y_AXIS
);
355 Porrectus::brew_mensural_molecule (Item
*me
,
361 Direction stem_direction
)
363 Real space
= Staff_symbol_referencer::staff_space (me
);
364 Real height
= 0.6 * space
;
365 Molecule molecule
= Molecule ();
369 bool consider_interval
=
370 stem_direction
* interval
> 0.0;
372 Interval
stem_box_x (0, thickness
);
375 if (consider_interval
)
377 Real y_length
= max (interval
/2.0*space
, 1.2*space
);
378 stem_box_y
= Interval (0, y_length
);
381 stem_box_y
= Interval (0, space
);
384 (stem_direction
== UP
) ?
386 -0.5*height
- stem_box_y
.length();
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
);
394 Real slope
= (interval
/ 2.0 * space
) / width
;
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
;
404 Molecule solid_head
=
405 Lookup::horizontal_slope (width
, corrected_slope
, height
);
406 molecule
.add_molecule (solid_head
);
411 Lookup::horizontal_slope (thickness
, corrected_slope
, height
);
412 molecule
.add_molecule(left_edge
);
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
);
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
);
426 Lookup::horizontal_slope (width
, corrected_slope
, thickness
);
427 top_edge
.translate_axis (+0.5*height
, Y_AXIS
);
428 molecule
.add_molecule (top_edge
);
430 molecule
.translate_axis (ypos_correction
, Y_AXIS
);
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");