Hi!
Attached is yet another patch for the porrectus engraver:
* Rewrote code for vaticana style porrectus grob; the solid shape is
now drawn as a single bezier sandwich rather than composed from a
couple of misused slurs. Looks now much nicer.
* Added code to forbid line-breaking inbetween a porrectus
(preliminary; to be moved to ligature engraver framework).
* Added auto-properties property for automatic determination of grob
properties add-stem and stem-direction from musical context.
* Small clean-ups.
Greetings,
Juergen
diff -Nur lilypond-1.5.15/input/test/ancient-font.ly
lilypond-1.5.15.NEW/input/test/ancient-font.ly
--- lilypond-1.5.15/input/test/ancient-font.ly Thu Sep 20 11:08:30 2001
+++ lilypond-1.5.15.NEW/input/test/ancient-font.ly Sat Oct 6 03:48:13 2001
@@ -31,6 +31,7 @@
\property Voice.Porrectus \override #'solid = ##t
\property Voice.Porrectus \override #'add-stem = ##t
\property Voice.Porrectus \override #'stem-direction = #-1
+ \property Voice.Porrectus \override #'line-thickness = #0.5
\key es \major
\clef "vaticana_fa2"
c!1 des! e! f! ges!
@@ -44,7 +45,7 @@
a! b!
\property Staff.BarLine \override #'bar-size = #3.0 \bar "|"
\property Voice.NoteHead \override #'style = #'vaticana_virga
- ces' b! ces'! \~ ges! \~ fes!
+ ces' b! des'! \~ ges! \~ fes!
\breathe
\clef "vaticana_fa1"
\property Voice.NoteHead \override #'style = #'vaticana_quilisma
@@ -129,7 +130,8 @@
\property Voice.Porrectus \override #'add-stem = ##t
\property Voice.Porrectus \override #'stem-direction = #1
\property Voice.Porrectus \override #'line-thickness = #0.7
- % \property Voice.Porrectus \override #'porrectus-width = #3.0
+% \property Voice.Porrectus \override #'auto-properties = ##t
+% \property Voice.Porrectus \override #'porrectus-width = #3.0
\key a \major
% IMPORTANT NOTE:
@@ -185,11 +187,6 @@
\clef "mensural_f"
e2 f g
\clef "mensural_g"
-
- % FIXME: In the second and all subsequent lines of score, the
- % stems and accidentals of the junked notes keep visible on
- % porrectus grobs. Is this an initialization bug in the line
- % breaking algorithm?
bes'! \~ as'! \~ cis''!
bes'! \~ fis'! as'! \~ ges'!
diff -Nur lilypond-1.5.15/lily/include/porrectus.hh
lilypond-1.5.15.NEW/lily/include/porrectus.hh
--- lilypond-1.5.15/lily/include/porrectus.hh Thu Sep 20 11:08:30 2001
+++ lilypond-1.5.15.NEW/lily/include/porrectus.hh Sat Oct 6 05:33:03 2001
@@ -30,6 +30,7 @@
static Molecule brew_mensural_molecule (Item *, Real,
bool, Real, Real,
bool, Direction);
+ static Molecule brew_bezier_sandwich (Bezier, Bezier);
static Molecule brew_horizontal_slope (Real, Real, Real);
static Molecule create_ledger_line (Interval, Grob *);
static Molecule create_streepjes (Grob *, int, int, Interval);
diff -Nur lilypond-1.5.15/lily/porrectus-engraver.cc
lilypond-1.5.15.NEW/lily/porrectus-engraver.cc
--- lilypond-1.5.15/lily/porrectus-engraver.cc Mon Sep 24 01:04:29 2001
+++ lilypond-1.5.15.NEW/lily/porrectus-engraver.cc Sat Oct 6 02:48:14 2001
@@ -15,30 +15,19 @@
*
* TODO: Hufnagel support.
*
- * TODO: Fine-tuning of vaticana-style porrectus shape; in particular,
- * ensure solidity if solid is set to #t and thickness is very small.
- *
- * TODO: For white mensural (i.e. #'style=#'mensural, #'solid=##f)
- * porrectus grobs, it is possible to automatically determine all
- * porrectus specific properties (add-stem, stem-direction) solely
- * from the duration of the contributing notes and time-signature.
- * Introduce a boolean grob property called auto-config, so that, if
- * turned on, lily automatically sets the properties add-stem and
- * stem-direction properly.
- *
* TODO: The following issues are currently not handled by this
- * engraver: (1) accidentals placement, (2) avoiding line breaking
- * inbetween porrectus, (3) spacing. (Han-Wen says: for (2), look at
- * beam engraver.) For example, currently only the accidental for the
- * second note (cp. the above FIXME) is printed. These issues should
- * be resolved by some sort of ligature context that encloses use of
- * this engraver, using syntax like: \ligature { e \~ c }.
+ * engraver: (1) accidentals placement, (2) spacing. For example,
+ * currently only the accidental for the second note (cp. the above
+ * FIXME) is printed. These issues should be resolved by some sort of
+ * ligature context that encloses use of this engraver, using syntax
+ * like: \ligature { e \~ c }.
*
* TODO: Do not allow a series of adjacent porrectus requests, as in:
* e \~ d \~ c.
*
* TODO: Junk duplicate (or rather triple) implementation of
- * create_ledger_line in porrectus.cc, custos.cc and note-head.cc. */
+ * create_ledger_line in porrectus.cc, custos.cc and note-head.cc.
+ */
#include "staff-symbol-referencer.hh"
#include "porrectus.hh"
@@ -47,7 +36,9 @@
#include "rhythmic-head.hh"
#include "item.hh"
#include "engraver.hh"
+#include "score-engraver.hh"
#include "pqueue.hh"
+#include "warn.hh"
// TODO: PHead_melodic_tuple is duplicated code from tie-engraver.cc.
// Maybe put this into public class?
@@ -74,6 +65,7 @@
protected:
virtual bool try_music (Music *req_l);
+ virtual void process_music ();
virtual void create_grobs ();
virtual void stop_translation_timestep ();
virtual void start_translation_timestep ();
@@ -102,6 +94,28 @@
}
else
return false;
+}
+
+void
+Porrectus_engraver::process_music ()
+{
+ if (porrectus_req_l_)
+ {
+ // TODO: Move code that forbids breaking into ligature music
+ // wrapper?
+ Score_engraver *engraver = 0;
+ for (Translator *translator = daddy_grav_l ();
+ translator && !engraver;
+ translator = translator->daddy_trans_l_)
+ {
+ engraver = dynamic_cast<Score_engraver*> (translator);
+ }
+
+ if (!engraver)
+ programming_error ("No score engraver!");
+ else
+ engraver->forbid_breaks ();
+ }
}
void
diff -Nur lilypond-1.5.15/lily/porrectus.cc lilypond-1.5.15.NEW/lily/porrectus.cc
--- lilypond-1.5.15/lily/porrectus.cc Sat Sep 22 14:13:06 2001
+++ lilypond-1.5.15.NEW/lily/porrectus.cc Sat Oct 6 05:27:19 2001
@@ -157,6 +157,15 @@
{
Item *me = (Item *)unsmob_grob (smob);
+ Item *left_head = get_left_head (me);
+ Item *right_head = get_right_head (me);
+ if (!left_head || !right_head)
+ {
+ warning (_ ("junking lonely porrectus"));
+ me->suicide ();
+ return SCM_EOL;
+ }
+
SCM scm_style = me->get_grob_property ("style");
String style;
if ((gh_symbol_p (scm_style)) && (scm_style != SCM_EOL))
@@ -175,13 +184,45 @@
if (!stem_direction)
stem_direction = DOWN;
- Item *left_head = get_left_head (me);
- Item *right_head = get_right_head (me);
- if (!left_head || !right_head)
+ SCM auto_properties = to_boolean (me->get_grob_property ("auto-properties"));
+ if (auto_properties)
+ // determine add_stem and stem_direction automatically from durations
{
- warning (_ ("junking lonely porrectus"));
- me->suicide ();
- return SCM_EOL;
+ if (String::compare_i (style, "mensural") != 0)
+ warning (String("auto-property should be used for\r\n") +
+ String("mensural style porrectus only; trying anyway"));
+
+ int left_duration =
+ gh_scm2int (left_head->get_grob_property ("duration-log"));
+ int right_duration =
+ gh_scm2int (right_head->get_grob_property ("duration-log"));
+
+ if ((left_duration == -1) && (right_duration == -1))
+ {
+ // brevis -- brevis:
+ // cum proprietate et sine perfectione (c.s.)
+ add_stem = true;
+ stem_direction = DOWN;
+ }
+ else if ((left_duration == -2) && (right_duration == -1))
+ {
+ // longa -- brevis:
+ // sine proprietate et sine perfectione (s.s.)
+ add_stem = false;
+ }
+ else if ((left_duration == 0) && (right_duration == 0))
+ {
+ // semibrevis -- semibrevis:
+ // cum opposita proprietate (c.o.p.)
+ add_stem = true;
+ stem_direction = UP;
+ }
+ else
+ {
+ warning (String("auto-property: failed determining porrectus\r\n") +
+ String("properties due to improper durations; ") +
+ String("using user-supplied properties"));
+ }
}
Real left_position_f = Staff_symbol_referencer::position_f (left_head);
@@ -255,27 +296,32 @@
bool add_stem,
Direction stem_direction)
{
- Real space = Staff_symbol_referencer::staff_space (me);
- Molecule molecule = Molecule ();
-
if (interval >= 0.0)
{
warning (_ ("ascending vaticana style porrectus"));
}
+ Real space = Staff_symbol_referencer::staff_space (me);
+ Molecule molecule = Molecule ();
+ Real right_height = 0.6 * space;
+
+ // Compensate thickness that appears to be smaller in steep section
+ // of bend.
+ Real left_height = right_height + min (0.12 * abs(interval), 0.3) * space;
+
if (add_stem)
{
bool consider_interval =
stem_direction * interval > 0.0;
- Interval stem_box_x (-thickness/2, +thickness/2);
+ Interval stem_box_x (0, thickness);
Interval stem_box_y;
if (consider_interval)
- {
- Real y_length = interval / 2.0;
- if (y_length < 1.2 * space)
- y_length = 1.2 * space;
+ {
+ Real y_length = max (abs(interval)/2.0*space +
+ (right_height-left_height),
+ 1.2*space);
stem_box_y = Interval (0, y_length);
}
else
@@ -283,8 +329,8 @@
Real y_correction =
(stem_direction == UP) ?
- 0.3 * space :
- - 0.3 * space - stem_box_y.length();
+ +0.5*left_height :
+ -0.5*left_height - stem_box_y.length();
Box stem_box (stem_box_x, stem_box_y);
Molecule stem = Lookup::filledbox (stem_box);
@@ -292,36 +338,66 @@
molecule.add_molecule(stem);
}
- Box vertical_edge (Interval (-thickness/2, +thickness/2),
- Interval (-4*thickness/2, +4*thickness/2));
- Molecule left_edge = Lookup::filledbox (vertical_edge);
- Molecule right_edge = Lookup::filledbox (vertical_edge);
- right_edge.translate_axis (width, X_AXIS);
- right_edge.translate_axis (interval / 2.0, Y_AXIS);
- molecule.add_molecule(left_edge);
- molecule.add_molecule(right_edge);
-
- Bezier bezier;
- bezier.control_[0] = Offset (0.00 * width, 0.0);
- bezier.control_[1] = Offset (0.33 * width, interval / 2.0);
- bezier.control_[2] = Offset (0.66 * width, interval / 2.0);
- bezier.control_[3] = Offset (1.00 * width, interval / 2.0);
-
- Molecule slice;
- slice = Lookup::slur (bezier, 0.0, thickness);
- slice.translate_axis (-3 * thickness/2, Y_AXIS);
- molecule.add_molecule (slice);
+ // Compensate optical illusion regarding vertical position of left
+ // and right endings due to curved shape.
+ Real ypos_correction = -0.1*space * sign(interval);
+ Real interval_correction = 0.2*space * sign(interval);
+ Real corrected_interval = interval*space + interval_correction;
+
+ // middle curve of vaticana style porrectus
+ Bezier curve;
+ curve.control_[0] = Offset (0.00 * width, 0.0);
+ curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
+ curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
+ curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
+
+ Bezier top_curve = curve, bottom_curve = curve;
+ for (int i = 0; i < 4; i++)
+ {
+ Real thickness = 0.33 * ((3 - i)*left_height + i*right_height);
+ top_curve.control_[i] += Offset (0, +0.5*thickness);
+ bottom_curve.control_[i] += Offset (0, -0.5*thickness);
+ }
+
if (solid)
- for (int i = -2; i < +2; i++)
- {
- slice = Lookup::slur (bezier, 0.0, thickness);
- slice.translate_axis (i * thickness/2, Y_AXIS);
- molecule.add_molecule (slice);
- }
- slice = Lookup::slur (bezier, 0.0, thickness);
- slice.translate_axis (+3 * thickness/2, Y_AXIS);
- molecule.add_molecule (slice);
+ {
+ Molecule solid_head =
+ brew_bezier_sandwich (top_curve, bottom_curve);
+ molecule.add_molecule (solid_head);
+ }
+ else // outline
+ {
+ Bezier inner_top_curve = top_curve;
+ inner_top_curve.translate (Offset (0.0, -thickness));
+ Molecule top_edge =
+ brew_bezier_sandwich (top_curve, inner_top_curve);
+ molecule.add_molecule(top_edge);
+
+ Bezier inner_bottom_curve = bottom_curve;
+ inner_bottom_curve.translate (Offset (0.0, +thickness));
+ Molecule bottom_edge =
+ brew_bezier_sandwich (bottom_curve, inner_bottom_curve);
+ molecule.add_molecule(bottom_edge);
+ // TODO: Use horizontal slope with proper slope value rather
+ // than filled box for left edge, since the filled box stands
+ // out from the porrectus shape if the interval is big and the
+ // line thickness small. The difficulty here is to compute a
+ // proper slope value, as it should roughly be equal with the
+ // slope of the left end of the bezier curve.
+ Box left_edge_box (Interval (0, thickness),
+ Interval (-0.5*left_height, +0.5*left_height));
+ Molecule left_edge = Lookup::filledbox (left_edge_box);
+ molecule.add_molecule(left_edge);
+
+ Box right_edge_box (Interval (-thickness, 0),
+ Interval (-0.5*right_height, +0.5*right_height));
+ Molecule right_edge = Lookup::filledbox (right_edge_box);
+ right_edge.translate_axis (width, X_AXIS);
+ right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
+ molecule.add_molecule(right_edge);
+ }
+ molecule.translate_axis (ypos_correction, Y_AXIS);
return molecule;
}
@@ -335,13 +411,11 @@
Direction stem_direction)
{
Real space = Staff_symbol_referencer::staff_space (me);
+ Real height = 0.6 * space;
Molecule molecule = Molecule ();
if (add_stem)
{
- // Uugh. This is currently the same as in
- // brew_vaticana_molecule, but may eventually be changed.
-
bool consider_interval =
stem_direction * interval > 0.0;
@@ -350,9 +424,7 @@
if (consider_interval)
{
- Real y_length = interval / 2.0;
- if (y_length < 1.2 * space)
- y_length = 1.2 * space;
+ Real y_length = max (interval/2.0*space, 1.2*space);
stem_box_y = Interval (0, y_length);
}
else
@@ -360,8 +432,8 @@
Real y_correction =
(stem_direction == UP) ?
- 0.3 * space :
- - 0.3 * space - stem_box_y.length();
+ +0.5*height :
+ -0.5*height - stem_box_y.length();
Box stem_box (stem_box_x, stem_box_y);
Molecule stem = Lookup::filledbox (stem_box);
@@ -369,7 +441,7 @@
molecule.add_molecule(stem);
}
- Real slope = (interval / 2.0) / width;
+ Real slope = (interval / 2.0 * space) / width;
// Compensate optical illusion regarding vertical position of left
// and right endings due to slope.
@@ -380,29 +452,29 @@
if (solid)
{
Molecule solid_head =
- brew_horizontal_slope (width, corrected_slope, 0.6*space);
+ brew_horizontal_slope (width, corrected_slope, height);
molecule.add_molecule (solid_head);
}
- else
+ else // outline
{
Molecule left_edge =
- brew_horizontal_slope (thickness, corrected_slope, 0.6*space);
+ brew_horizontal_slope (thickness, corrected_slope, height);
molecule.add_molecule(left_edge);
Molecule right_edge =
- brew_horizontal_slope (thickness, corrected_slope, 0.6*space);
+ brew_horizontal_slope (thickness, corrected_slope, height);
right_edge.translate_axis (width-thickness, X_AXIS);
right_edge.translate_axis (corrected_slope * (width-thickness), Y_AXIS);
molecule.add_molecule(right_edge);
Molecule bottom_edge =
brew_horizontal_slope (width, corrected_slope, thickness);
- bottom_edge.translate_axis (-0.3*space, Y_AXIS);
+ bottom_edge.translate_axis (-0.5*height, Y_AXIS);
molecule.add_molecule (bottom_edge);
Molecule top_edge =
brew_horizontal_slope (width, corrected_slope, thickness);
- top_edge.translate_axis (+0.3*space, Y_AXIS);
+ top_edge.translate_axis (+0.5*height, Y_AXIS);
molecule.add_molecule (top_edge);
}
molecule.translate_axis (ypos_correction, Y_AXIS);
@@ -410,11 +482,65 @@
}
/*
+ * Bezier Sandwich:
+ *
+ * .|
+ * . |
+ * top . |
+ * . curve |
+ * . |
+ * . |
+ * . |
+ * | |
+ * | .|
+ * | .
+ * | bottom .
+ * | . curve
+ * | .
+ * | .
+ * | .
+ * | .
+ * |.
+ * |
+ *
+ */
+// TODO: Move this to class Lookup?
+Molecule
+Porrectus::brew_bezier_sandwich (Bezier top_curve, Bezier bottom_curve)
+{
+ /*
+ Need the weird order b.o. the way PS want its arguments
+ */
+ SCM list = SCM_EOL;
+ list = gh_cons (ly_offset2scm (bottom_curve.control_[3]), list);
+ list = gh_cons (ly_offset2scm (bottom_curve.control_[0]), list);
+ list = gh_cons (ly_offset2scm (bottom_curve.control_[1]), list);
+ list = gh_cons (ly_offset2scm (bottom_curve.control_[2]), list);
+ list = gh_cons (ly_offset2scm (top_curve.control_[0]), list);
+ list = gh_cons (ly_offset2scm (top_curve.control_[3]), list);
+ list = gh_cons (ly_offset2scm (top_curve.control_[2]), list);
+ list = gh_cons (ly_offset2scm (top_curve.control_[1]), list);
+
+ SCM horizontal_bend = scm_list_n (ly_symbol2scm ("bezier-sandwich"),
+ ly_quote_scm (list),
+ gh_double2scm (0.0),
+ SCM_UNDEFINED);
+
+ Interval x_extent = top_curve.extent (X_AXIS);
+ x_extent.unite (bottom_curve.extent (X_AXIS));
+ Interval y_extent = top_curve.extent (Y_AXIS);
+ y_extent.unite (bottom_curve.extent (Y_AXIS));
+ Box b (x_extent, y_extent);
+
+ return Molecule (b, horizontal_bend);
+}
+
+/*
* Horizontal Slope:
*
* /| ^
* / | |
- * / | | thickness
+ * / | | height
* / | |
* / | v
* | /
@@ -426,16 +552,17 @@
* <----->
* width
*/
+// TODO: Move this to class Lookup?
Molecule
-Porrectus::brew_horizontal_slope(Real width, Real slope, Real thickness)
+Porrectus::brew_horizontal_slope (Real width, Real slope, Real height)
{
SCM width_scm = gh_double2scm (width);
SCM slope_scm = gh_double2scm (slope);
- SCM thickness_scm = gh_double2scm (thickness);
+ SCM height_scm = gh_double2scm (height);
SCM horizontal_slope = scm_list_n (ly_symbol2scm ("beam"),
- width_scm, slope_scm,
- thickness_scm, SCM_UNDEFINED);
+ width_scm, slope_scm,
+ height_scm, SCM_UNDEFINED);
Box b (Interval (0, width),
- Interval (-thickness/2, thickness/2 + width*slope));
+ Interval (-height/2, height/2 + width*slope));
return Molecule (b, horizontal_slope);
}
diff -Nur lilypond-1.5.15/scm/grob-description.scm
lilypond-1.5.15.NEW/scm/grob-description.scm
--- lilypond-1.5.15/scm/grob-description.scm Mon Sep 24 01:04:29 2001
+++ lilypond-1.5.15.NEW/scm/grob-description.scm Sat Oct 6 03:34:28 2001
@@ -323,15 +323,14 @@
(Porrectus . (
(style . mensural)
- (auto-properties . #t)
+ (auto-properties . #f)
(solid . #f)
(porrectus-width . 2.4)
(line-thickness . 1.0)
(add-stem . #t)
(stem-direction . 1)
(molecule-callback . ,Porrectus::brew_molecule)
- (meta . ,(grob-description
- porrectus-interface))
+ (meta . ,(grob-description porrectus-interface))
))
(RehearsalMark . (
diff -Nur lilypond-1.5.15/scm/grob-property-description.scm
lilypond-1.5.15.NEW/scm/grob-property-description.scm
--- lilypond-1.5.15/scm/grob-property-description.scm Thu Oct 4 00:27:40 2001
+++ lilypond-1.5.15.NEW/scm/grob-property-description.scm Sat Oct 6 05:39:09
+2001
@@ -55,6 +55,7 @@
'(LEFT-offset . RIGHT-offset). This offset is added to the
attachments to prevent ugly slurs. [fixme: we need more documentation here].
.")
+(grob-property-description 'auto-properties boolean? "if true, as many properties of
+this grob as possible will be determined automatically from the musical context.")
(grob-property-description 'auto-knee-gap number-or-boolean? "the minimal smallest
gap between two adjacent beamed chords for which beam will create auto-knees. Set to
false for no auto knees." )
(grob-property-description 'axes list? "list of axis numbers.
In the case of alignment grobs, this should contain only one number.")