From: Jürgen Reuter Date: Sun, 11 May 2003 21:29:27 +0000 (+0000) Subject: * lily/coherent-ligature-engraver.cc, X-Git-Tag: release/1.7.20~63 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=011366cd352bf329f9103c099aabd9e54ad92366;p=lilypond.git * 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 --- diff --git a/ChangeLog b/ChangeLog index 8a53823d10..73fcbf5721 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2003-05-11 Juergen Reuter + + * 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 * lilypond-mode.el: XEmacs fixes: include two definitions for the diff --git a/lily/coherent-ligature-engraver.cc b/lily/coherent-ligature-engraver.cc new file mode 100644 index 0000000000..091403154e --- /dev/null +++ b/lily/coherent-ligature-engraver.cc @@ -0,0 +1,202 @@ +/* + coherent-ligature-engraver.cc -- implement Coherent_ligature_engraver + + source file of the GNU LilyPond music typesetter + + (c) 2003 Juergen Reuter + */ + +#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). 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->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) +{ + /* TODO */ +} + +void +Coherent_ligature_engraver::build_ligature (Spanner *, Array) +{ + 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 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 */ ""); diff --git a/lily/gregorian-ligature-engraver.cc b/lily/gregorian-ligature-engraver.cc index 8faf888f6b..482b60ba71 100644 --- a/lily/gregorian-ligature-engraver.cc +++ b/lily/gregorian-ligature-engraver.cc @@ -15,24 +15,21 @@ #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) -{ - 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) { @@ -45,43 +42,6 @@ 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->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) @@ -284,21 +244,24 @@ provide_context_info (Array primitives) } void -Gregorian_ligature_engraver::typeset_ligature (Spanner *ligature, - Array primitives) +Gregorian_ligature_engraver::transform_heads (Spanner *, Array) +{ + 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 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 diff --git a/lily/include/coherent-ligature-engraver.hh b/lily/include/coherent-ligature-engraver.hh new file mode 100644 index 0000000000..8321e727ee --- /dev/null +++ b/lily/include/coherent-ligature-engraver.hh @@ -0,0 +1,31 @@ +/* + coherent-ligature-engraver.hh -- declare Coherent_ligature_engraver + + source file of the GNU LilyPond music typesetter + + (c) 2003 Juergen Reuter + + */ +#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 primitives); /* abstract */ + virtual void typeset_ligature (Spanner *ligature, + Array primitives); + virtual void get_set_column (Item *, Paper_column *); + +private: + void collect_accidentals (Spanner *, Array); +}; + +#endif // COHERENT_LIGATURE_ENGRAVER_HH diff --git a/lily/include/gregorian-ligature-engraver.hh b/lily/include/gregorian-ligature-engraver.hh index ac6e0d4052..ec571f6f64 100644 --- a/lily/include/gregorian-ligature-engraver.hh +++ b/lily/include/gregorian-ligature-engraver.hh @@ -9,9 +9,9 @@ #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_; @@ -20,12 +20,10 @@ public: protected: virtual bool try_music (Music *); - virtual void typeset_ligature (Spanner *ligature, - Array primitives); + virtual void build_ligature (Spanner *ligature, Array primitives); virtual void transform_heads (Spanner *ligature, Array primitives); /* abstract method */ virtual void start_translation_timestep (); - void get_set_column (Item *, Paper_column *); }; #endif // GREGORIAN_LIGATURE_ENGRAVER_HH diff --git a/lily/ligature-bracket-engraver.cc b/lily/ligature-bracket-engraver.cc index 5a61c3363e..8f1c18a50b 100644 --- a/lily/ligature-bracket-engraver.cc +++ b/lily/ligature-bracket-engraver.cc @@ -11,6 +11,12 @@ #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: diff --git a/lily/ligature-engraver.cc b/lily/ligature-engraver.cc index 17414179cb..3e15bbf5f5 100644 --- a/lily/ligature-engraver.cc +++ b/lily/ligature-engraver.cc @@ -14,6 +14,38 @@ #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). 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 diff --git a/lily/mensural-ligature-engraver.cc b/lily/mensural-ligature-engraver.cc index 58134b6871..913580a2d3 100644 --- a/lily/mensural-ligature-engraver.cc +++ b/lily/mensural-ligature-engraver.cc @@ -7,7 +7,7 @@ */ #include "mensural-ligature.hh" -#include "ligature-engraver.hh" +#include "coherent-ligature-engraver.hh" #include "event.hh" #include "warn.hh" #include "item.hh" @@ -22,19 +22,6 @@ #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 @@ -54,37 +41,17 @@ * * 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 primitives); + virtual void build_ligature (Spanner *ligature, Array primitives); public: TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver); @@ -96,7 +63,6 @@ private: void propagate_properties (Spanner *ligature, Array primitives); void fold_up_primitives (Array primitives); void join_primitives (Array primitives); - void get_set_column (Item *item, Paper_column *new_col); }; @@ -110,43 +76,6 @@ Mensural_ligature_engraver::create_ligature_spanner () 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->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 @@ -528,18 +457,13 @@ Mensural_ligature_engraver::join_primitives (Array primitives) } void -Mensural_ligature_engraver::typeset_ligature (Spanner *ligature, - Array primitives) +Mensural_ligature_engraver::build_ligature (Spanner *ligature, + Array 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, diff --git a/lily/vaticana-ligature-engraver.cc b/lily/vaticana-ligature-engraver.cc index f902e2fda3..5c00edafa2 100644 --- a/lily/vaticana-ligature-engraver.cc +++ b/lily/vaticana-ligature-engraver.cc @@ -15,7 +15,12 @@ #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 { @@ -58,6 +63,7 @@ Vaticana_ligature_engraver::finish_primitive (Item *first_primitive, Real join_thickness, Real distance) { + Real next_distance = distance; if (primitive) { // determine width of previous head and x-offset @@ -128,7 +134,7 @@ Vaticana_ligature_engraver::finish_primitive (Item *first_primitive, * 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, "")) { @@ -143,17 +149,19 @@ Vaticana_ligature_engraver::finish_primitive (Item *first_primitive, /* * 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 @@ -320,9 +328,27 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, /* * 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 }