+2003-05-11 Juergen Reuter <reuter@ipd.uka.de>
+
+ * lily/coherent-ligature-engraver.cc,
+ lily/gregorian-ligature-engraver.cc,
+ lily/ligature-bracket-engraver.cc, lily/ligature-engraver.cc,
+ lily/mensural-ligature-engraver.cc,
+ lily/vaticana-ligature-engraver.cc,
+ lily/include/gregorian-ligature-engraver.hh: updated for new
+ Coherent_ligature_engraver; added comments that describe the basic
+ design ideas of the ligature implementation
+
+ * lily/coherent-ligature-engraver.cc,
+ lily/include/coherent-ligature-engraver.hh: new file: shared code
+ between mensural ligatures and Gregorian chant notation ligatures
+
2003-05-11 Heikki Junes <hjunes@cc.hut.fi>
* lilypond-mode.el: XEmacs fixes: include two definitions for the
--- /dev/null
+/*
+ coherent-ligature-engraver.cc -- implement Coherent_ligature_engraver
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2003 Juergen Reuter <reuter@ipd.uka.de>
+ */
+
+#include "coherent-ligature-engraver.hh"
+#include "item.hh"
+#include "warn.hh"
+#include "staff-symbol-referencer.hh"
+#include "spanner.hh"
+#include "paper-column.hh"
+
+/*
+ * This abstract class serves as common superclass for all ligature
+ * engravers thet produce a single connected graphical object of fixed
+ * width, consisting of noteheads and other primitives (see class
+ * Ligature_engraver for more information on the interaction between
+ * this class and its superclass). In particular, it cares for the
+ * following tasks:
+ *
+ * - provide a function for putting all grobs of the ligature into a
+ * single paper column,
+ *
+ * - delegate actual creation of ligature to concrete subclass,
+ *
+ * - collect all accidentals that occur within the ligature and put
+ * them at the left side of the ligature (TODO; see function
+ * collect_accidentals()),
+ *
+ * - collapse superflous space after each ligature (TODO).
+ *
+ * Concrete subclasses must implement function build_ligature (Spanner
+ * *, Array<Grob_info>). This function is responsible for actually
+ * building the ligature by transforming the array of noteheads.
+ *
+ * Currently, there are two subclasses: Gregorian_ligature_engraver
+ * for Gregorian chant notation (also known as plain song or cantus
+ * planus) and Mensural_ligature_engraver for white mensural notation.
+ * Subclasses for other music notation styles such as modal notation
+ * or ars nova notation may eventually be added.
+ */
+
+/*
+ * TODO: local accidentals: collect accidentals that occur within a
+ * ligature and put them before the ligature. If an accidental
+ * changes within a ligature, print a warning (user error) and ignore
+ * any further accidental for that pitch within that ligature
+ * (actually, in such a case, the user should split the ligature into
+ * two separate ligatures). Similarly, any object that, in ordinary
+ * notation, may be put to the left or to the right of a
+ * note-head/ligature-head, should be collected and put before or
+ * after the ligature.
+ *
+ * TODO: make spacing more robust: do not screw up spacing if user
+ * erroneously puts rest in ligature.
+ *
+ * TODO: for each ligature, add Rod that represents the total length
+ * of the ligature (to preemptively avoid collision with adjacent
+ * notes); or maybe just additionally create a
+ * mensural/vaticana/whatever-ligature grob (e.g. via
+ * Mensural_ligature::brew_molecule(SCM)) that just consists of a
+ * bounding box around all primitives of the ligature.
+ *
+ * TODO: Maybe move functions fold_up_primitives() and
+ * join_primitives() from subclasses to here? N.B. it is not
+ * appropriate to put these into Ligature_engraver, since, for
+ * example, Ligature_bracket_engraver does not share any of this code.
+ */
+
+/*
+ * TODO: Let superflous space after each ligature collapse. The
+ * following code should help in doing so (though it does not yet
+ * fully work). Just put the following code into
+ * Spacing_spanner::do_measure(). I put it temporarily here as memo
+ * until it really works and I also get Han-Wen's/Jan's permission to
+ * add it to the spacing spanner code.
+ */
+#if 0 // experimental code to collapse spacing after ligature
+ SCM incr_scm = lc->get_grob_property ("forced-spacing");
+ if (incr_scm != SCM_EOL) /* (Paper_column::musical_b (l)) */
+ {
+ me->warning (_f ("gotcha: ptr=%ul", lc));//debug
+ ly_display_scm (lc->self_scm ());
+ Real distance;
+ if (incr_scm != SCM_EOL)
+ {
+ distance = gh_scm2double (incr_scm);
+ }
+ else
+ {
+ me->warning ("distance undefined, assuming 0.1");
+ distance = 0.1;
+ }
+ me->warning (_f ("distance=%f", distance));//debug
+ Real strength = 1.0;
+ Spaceable_grob::add_spring (lc, rc, distance, strength, false);
+ if (Item *rb = r->find_prebroken_piece (LEFT))
+ Spaceable_grob::add_spring (lc, rb, distance, strength, false);
+
+ continue;
+ }
+#endif
+
+Coherent_ligature_engraver::Coherent_ligature_engraver ()
+{
+}
+
+/*
+ * TODO: move this function to class Item?
+ */
+void
+Coherent_ligature_engraver::get_set_column (Item *item, Paper_column *column)
+{
+ Item *parent = dynamic_cast<Item*> (item->get_parent (X_AXIS));
+ if (!parent)
+ {
+ programming_error ("failed tweaking paper column in ligature");
+ return;
+ }
+
+ String name = parent->name ();
+ if (!String::compare (name, "PaperColumn"))
+ {
+ // Change column not only for targeted item (NoteColumn), but
+ // also for all associated grobs (NoteSpacing, SeparationItem).
+ Grob *sl = Staff_symbol_referencer::get_staff_symbol (item);
+ for (SCM tail = parent->get_grob_property ("elements");
+ gh_pair_p (tail);
+ tail = ly_cdr (tail))
+ {
+ Item *sibling = unsmob_item (ly_car (tail));
+ if ((sibling) &&
+ (Staff_symbol_referencer::get_staff_symbol (sibling) == sl))
+ {
+#if 0 // experimental code to collapse spacing after ligature
+ Grob *sibling_parent = sibling->get_parent (X_AXIS);
+ sibling_parent->warning (_f ("Coherent_ligature_engraver: "
+ "setting `spacing-increment = "
+ "0.01': ptr=%ul", parent));
+ sibling_parent->set_grob_property("forced-spacing",
+ gh_double2scm (0.01));
+#endif
+ sibling->set_parent (column, X_AXIS);
+ }
+ }
+ }
+ else
+ {
+ get_set_column (parent, column);
+ }
+}
+
+/*
+ * TODO: This function should collect all accidentals that occur
+ * within the ligature (by scanning through the primitives array) and
+ * place all of them at the left of the ligature. If there is an
+ * alteration within the ligature (e.g. an "f" followed by a "fis"
+ * somewhere later in the ligature), issue a warning (and maybe create
+ * an additional natural symbol to explicitly make clear that there is
+ * an "f" first?). The warning should suggest the user to break the
+ * ligature into two or more smaller ligatures such that no alteration
+ * occurs within the broken ligatures any more.
+ */
+void
+Coherent_ligature_engraver::collect_accidentals (Spanner *, Array<Grob_info>)
+{
+ /* TODO */
+}
+
+void
+Coherent_ligature_engraver::build_ligature (Spanner *, Array<Grob_info>)
+{
+ programming_error ("Cohrent_ligature_engraver::build_ligature (): "
+ "this is an abstract method that should not be called, "
+ "but overridden by a subclass");
+}
+
+void
+Coherent_ligature_engraver::typeset_ligature (Spanner *ligature,
+ Array<Grob_info> primitives)
+{
+ // prepare ligature for typesetting
+ build_ligature (ligature, primitives);
+ collect_accidentals (ligature, primitives);
+
+ // now actually typeset
+ for (int i = 0; i < primitives.size (); i++)
+ {
+ typeset_grob (primitives[i].grob_);
+ }
+}
+
+ENTER_DESCRIPTION (Coherent_ligature_engraver,
+/* descr */ "This is an abstract class. Subclasses such as Gregorian_ligature_engraver handle ligatures by glueing special ligature heads together.",
+/* creats*/ "",
+/* accepts */ "ligature-event abort-event",
+/* acks */ "ligature-head-interface note-head-interface rest-interface",
+/* reads */ "",
+/* write */ "");
#include "paper-column.hh"
/*
- * TODO: This class shares some code with Mensural_ligature_engraver.
- * Maybe we should create a common super class "Rod_ligature_engraver"
- * and derive all shared code from it.
+ * This abstract class is the common superclass for all ligature
+ * engravers for Gregorian chant notation. It cares for the musical
+ * handling of the neumes, such as checking for valid combinations of
+ * neumes and providing context information. Notational aspects such
+ * as the glyphs to use or calculating the total width of a ligature,
+ * are left to the concrete subclass. Currently, there is only a
+ * single subclass, Vaticana_ligature_engraver. Other ligature
+ * engravers for Gregorian chant will be added in the future, such as
+ * Medicaea_ligature_engraver or Hufnagel_ligature_engraver.
*/
-
Gregorian_ligature_engraver::Gregorian_ligature_engraver ()
{
pes_or_flexa_req_ = 0;
}
-void
-Gregorian_ligature_engraver::transform_heads (Spanner *, Array<Grob_info>)
-{
- programming_error ("Gregorian_ligature_engraver::transform_heads (): "
- "this is an abstract method that should not be called, "
- "but overridden by a subclass");
-}
-
bool
Gregorian_ligature_engraver::try_music (Music *m)
{
return Ligature_engraver::try_music (m);
}
-/*
- * TODO: move this function to class Item?
- */
-void
-Gregorian_ligature_engraver::get_set_column (Item *item, Paper_column *column)
-{
- Item *parent = dynamic_cast<Item*> (item->get_parent (X_AXIS));
- if (!parent)
- {
- programming_error ("failed tweaking paper column in ligature");
- return;
- }
-
- String name = parent->name ();
- if (!String::compare (name, "PaperColumn"))
- {
- // Change column not only for targeted item (NoteColumn), but
- // also for all associated grobs (NoteSpacing, SeparationItem).
- Grob *sl = Staff_symbol_referencer::get_staff_symbol (item);
- for (SCM tail = parent->get_grob_property ("elements");
- gh_pair_p (tail);
- tail = ly_cdr (tail))
- {
- Item *sibling = unsmob_item (ly_car (tail));
- if ((sibling) &&
- (Staff_symbol_referencer::get_staff_symbol (sibling) == sl))
- {
- sibling->set_parent (column, X_AXIS);
- }
- }
- }
- else
- {
- get_set_column (parent, column);
- }
-}
-
void fix_prefix (char *name, int mask,
int *current_set, int min_set, int max_set,
Grob *primitive)
}
void
-Gregorian_ligature_engraver::typeset_ligature (Spanner *ligature,
- Array<Grob_info> primitives)
+Gregorian_ligature_engraver::transform_heads (Spanner *, Array<Grob_info>)
+{
+ programming_error ("Gregorian_ligature_engraver::transform_heads (): "
+ "this is an abstract method that should not be called, "
+ "but overridden by a subclass");
+}
+
+void
+Gregorian_ligature_engraver::build_ligature (Spanner *ligature,
+ Array<Grob_info> primitives)
{
// apply style-independent checking and transformation
check_and_fix_all_prefixes (primitives);
provide_context_info (primitives);
- // apply style-specific transformation (including line-up)
+ // apply style-specific transformation (including line-up); to be
+ // implemented by subclass
transform_heads (ligature, primitives);
-
- // typeset
- for (int i = 0; i < primitives.size (); i++)
- {
- typeset_grob (primitives[i].grob_);
- }
}
void
--- /dev/null
+/*
+ coherent-ligature-engraver.hh -- declare Coherent_ligature_engraver
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2003 Juergen Reuter <reuter@ipd.uka.de>
+
+ */
+#ifndef COHERENT_LIGATURE_ENGRAVER_HH
+#define COHERENT_LIGATURE_ENGRAVER_HH
+
+#include "ligature-engraver.hh"
+
+class Coherent_ligature_engraver : public Ligature_engraver
+{
+
+public:
+ TRANSLATOR_DECLARATIONS(Coherent_ligature_engraver);
+
+protected:
+ virtual void build_ligature (Spanner *ligature,
+ Array<Grob_info> primitives); /* abstract */
+ virtual void typeset_ligature (Spanner *ligature,
+ Array<Grob_info> primitives);
+ virtual void get_set_column (Item *, Paper_column *);
+
+private:
+ void collect_accidentals (Spanner *, Array<Grob_info>);
+};
+
+#endif // COHERENT_LIGATURE_ENGRAVER_HH
#ifndef GREGORIAN_LIGATURE_ENGRAVER_HH
#define GREGORIAN_LIGATURE_ENGRAVER_HH
-#include "ligature-engraver.hh"
+#include "coherent-ligature-engraver.hh"
-class Gregorian_ligature_engraver : public Ligature_engraver
+class Gregorian_ligature_engraver : public Coherent_ligature_engraver
{
Music *pes_or_flexa_req_;
protected:
virtual bool try_music (Music *);
- virtual void typeset_ligature (Spanner *ligature,
- Array<Grob_info> primitives);
+ virtual void build_ligature (Spanner *ligature, Array<Grob_info> primitives);
virtual void transform_heads (Spanner *ligature,
Array<Grob_info> primitives); /* abstract method */
virtual void start_translation_timestep ();
- void get_set_column (Item *, Paper_column *);
};
#endif // GREGORIAN_LIGATURE_ENGRAVER_HH
#include "tuplet-bracket.hh"
#include "spanner.hh"
+/*
+ * This engraver marks ligatures of any kind by just printing a
+ * horizontal square bracket on top of each ligature. See class
+ * Ligature_engraver for more information on the interaction between
+ * this class and its superclass.
+ */
class Ligature_bracket_engraver : public Ligature_engraver
{
protected:
#include "rest.hh"
#include "warn.hh"
+/*
+ * This abstract class provides the general framework for ligatures of
+ * any kind. It cares for handling start/stop ligatures requests and
+ * collecting all noteheads inbetween, but delegates creation of a
+ * ligature spanner for each start/stop pair and typesetting of the
+ * ligature spanner to a concrete subclass.
+ *
+ * A concrete ligature engraver must subclass this class and provide
+ * functions create_ligature_spanner () and typeset_ligature
+ * (Spanner *, Array<Grob_info>). Subclasses of this class basically
+ * fall into two categories.
+ *
+ * The first category consists of engravers that engrave ligatures in
+ * a way that really deserves the name ligature. That is, they
+ * produce a single connected graphical object of fixed width,
+ * consisting of noteheads and other primitives. Space may be
+ * inserted only after each ligature, if necessary, but in no case
+ * between the primitives of the ligature. Accidentals have to be put
+ * to the left of the ligature, and not to the left of individual
+ * noteheads. Class Coherent_ligature_engraver is the common
+ * superclass for all of these engravers.
+ *
+ * The second category is for engravers that are relaxed in the sense
+ * that they do not require to produce a single connected graphical
+ * object. For example, in contemporary editions, ligatures are often
+ * marked, but otherwise use contemporary notation and spacing. In
+ * this category, there is currently only a single class,
+ * Ligature_bracket_engraver, which marks each ligature with a
+ * horizontal sqare bracket, but otherwise leaves the appearance
+ * untouched.
+ */
+
/*
* TODO: lyrics/melisma/syllables: there should be at most one
* syllable of lyrics per ligature (i.e. for the lyrics context, a
*/
#include "mensural-ligature.hh"
-#include "ligature-engraver.hh"
+#include "coherent-ligature-engraver.hh"
#include "event.hh"
#include "warn.hh"
#include "item.hh"
#include "font-interface.hh"
/*
- * TODO: local accidentals: collect accidentals that occur within a
- * ligature and put them before the ligature. If an accidental
- * changes within a ligature, print a warning (user error) and ignore
- * any further accidental for that pitch within that ligature
- * (actually, in such a case, the user should split the ligature into
- * two separate ligatures). Similarly, any object that, in ordinary
- * notation, may be put to the left or to the right of a
- * note-head/ligature-head, should be collected and put before or
- * after the ligature.
- *
- * TODO: make spacing more robust: do not screw up spacing if user
- * erroneously puts rest in ligature.
- *
* TODO: My resources on Franco of Cologne's rules claim that his
* rules map ligature<->mensural timing in a non-ambigous way, but in
* fact, as presented in these resources, the rules become ambigous as
*
* TODO: prohibit multiple voices within a ligature.
*
- * TODO: for each ligature, add Rod that represents the total length
- * of the ligature (to preemptively avoid collision with adjacent
- * notes); or maybe just additionally create a mensural-ligature grob
- * (via Mensural_ligature::brew_molecule(SCM)) that just consists of a
- * bounding box around all primitives of the ligature.
- *
* TODO: enhance robustness: in case of an illegal ligature (e.g. the
* user events for a ligature that contains a minima or STATE_ERROR
* is reached), automatically break the ligature into smaller, valid
* pieces.
- *
- * TODO: In the future, there will be further ligature engravers
- * implemented, such as a Vaticana_ligature_engraver. There will be
- * redundant code between these engravers and the
- * Mensural_ligature_engraver. In particular these are functions
- * set_column_, fold_up_primitives, join_primitives, and
- * ackowledge_grob; further the code for handling accidentals. It is
- * not appropriate to put these things into Ligature_engraver, since,
- * for example, Ligature_bracket_engraver does not share any of this
- * code. Hence, we might to introduce a further subclass of
- * Ligature_engraver which serves as super class for
- * Mensural_ligature_engraver, Vaticana_ligature_engraver, among
- * others.
*/
-class Mensural_ligature_engraver : public Ligature_engraver
+class Mensural_ligature_engraver : public Coherent_ligature_engraver
{
protected:
virtual Spanner *create_ligature_spanner ();
- virtual void typeset_ligature (Spanner *ligature,
- Array<Grob_info> primitives);
+ virtual void build_ligature (Spanner *ligature, Array<Grob_info> primitives);
public:
TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
void propagate_properties (Spanner *ligature, Array<Grob_info> primitives);
void fold_up_primitives (Array<Grob_info> primitives);
void join_primitives (Array<Grob_info> primitives);
- void get_set_column (Item *item, Paper_column *new_col);
};
return new Spanner (get_property ("MensuralLigature"));
}
-/*
- * TODO: move this function to class Item?
- */
-void
-Mensural_ligature_engraver::get_set_column (Item *item, Paper_column *column)
-{
- Item *parent = dynamic_cast<Item*> (item->get_parent (X_AXIS));
- if (!parent)
- {
- programming_error ("failed tweaking paper column in ligature");
- return;
- }
-
- String name = parent->name ();
- if (!String::compare (name, "PaperColumn"))
- {
- // Change column not only for targeted item (NoteColumn), but
- // also for all associated grobs (NoteSpacing, SeparationItem).
- Grob *sl = Staff_symbol_referencer::get_staff_symbol (item);
- for (SCM tail = parent->get_grob_property ("elements");
- gh_pair_p (tail);
- tail = ly_cdr (tail))
- {
- Item *sibling = unsmob_item (ly_car (tail));
- if ((sibling) &&
- (Staff_symbol_referencer::get_staff_symbol (sibling) == sl))
- {
- sibling->set_parent (column, X_AXIS);
- }
- }
- }
- else
- {
- get_set_column (parent, column);
- }
-}
-
/*
* The following lines implement a finite state automat. Given a
* sequence of durations (Longa, Brevis, Semibrevis) or
}
void
-Mensural_ligature_engraver::typeset_ligature (Spanner *ligature,
- Array<Grob_info> primitives)
+Mensural_ligature_engraver::build_ligature (Spanner *ligature,
+ Array<Grob_info> primitives)
{
transform_heads (primitives);
propagate_properties (ligature, primitives);
fold_up_primitives (primitives);
join_primitives (primitives);
-
- for (int i = 0; i < primitives.size (); i++)
- {
- typeset_grob (primitives[i].grob_);
- }
}
ENTER_DESCRIPTION (Mensural_ligature_engraver,
#include "font-interface.hh"
#include "warn.hh"
#include "paper-def.hh"
+#include "paper-column.hh"
+/*
+ * This class implements the notation specific aspects of Vaticana
+ * style ligatures for Gregorian chant notation.
+ */
class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
{
Real join_thickness,
Real distance)
{
+ Real next_distance = distance;
if (primitive)
{
// determine width of previous head and x-offset
* Create a small overlap of adjacent heads so that the join
* can be drawn perfectly between them.
*/
- distance -= join_thickness;
+ next_distance -= join_thickness;
}
else if (!String::compare (glyph_name, ""))
{
/*
* Make a small space after a virga.
*/
- distance += 2 * join_thickness;
+ next_distance += 2 * join_thickness;
}
/*
* Horizontally line-up this head to form a ligature.
*/
get_set_column (primitive, first_primitive->get_column ());
- primitive->translate_axis (distance, X_AXIS);
- distance += head_width;
+ primitive->translate_axis (next_distance, X_AXIS);
+ next_distance += head_width;
+
}
- return distance;
+
+ return next_distance;
}
void
/*
* Finish head of last iteration for backend.
*/
- finish_primitive (first_primitive, prev_primitive,
- prev_context_info, prev_glyph_name, prev_pitch_delta,
- flexa_width, join_thickness, prev_distance);
+ prev_distance =
+ finish_primitive (first_primitive, prev_primitive,
+ prev_context_info, prev_glyph_name, prev_pitch_delta,
+ flexa_width, join_thickness, prev_distance);
+
+ /* TODO: make this cfg'able via SCM */
+ Real padding = join_thickness;
+
+ /* horizontal padding space after ligature */
+ prev_distance += padding;
+
+#if 0 // experimental code to collapse spacing after ligature
+ /* TODO: set to max(old/new spacing-increment), since other
+ voices/staves also may want to set this property. */
+ Paper_column *paper_column = first_primitive->get_column();
+ paper_column->warning (_f ("Vaticana_ligature_engraver: "
+ "setting `spacing-increment = %f': ptr=%ul",
+ prev_distance, paper_column));
+ paper_column->
+ set_grob_property("forced-spacing", gh_double2scm (prev_distance));
+#endif
}