X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fvaticana-ligature-engraver.cc;h=c812684b423fd6d991d3fa5088d1ced805d7198c;hb=2ff13e6574980c37bb7cd4dd9bf5731805ba6524;hp=bb535d3442ddb1e1ebec42bd4fc4537b310fe77c;hpb=b37e3f652677ae0298423db9fa0e552e5fce0c92;p=lilypond.git diff --git a/lily/vaticana-ligature-engraver.cc b/lily/vaticana-ligature-engraver.cc index bb535d3442..c812684b42 100644 --- a/lily/vaticana-ligature-engraver.cc +++ b/lily/vaticana-ligature-engraver.cc @@ -3,7 +3,7 @@ source file of the GNU LilyPond music typesetter - (c) 2003--2006 Juergen Reuter + (c) 2003--2007 Juergen Reuter */ #include "gregorian-ligature-engraver.hh" @@ -13,11 +13,15 @@ #include "international.hh" #include "output-def.hh" #include "paper-column.hh" +#include "separation-item.hh" #include "spanner.hh" #include "staff-symbol-referencer.hh" +#include "stream-event.hh" #include "vaticana-ligature.hh" #include "warn.hh" - +#include "dot-column.hh" +#include "rhythmic-head.hh" +#include "pitch.hh" #include "translator.icc" /* @@ -25,6 +29,24 @@ * style ligatures for Gregorian chant notation. */ +/* + * TODO: Maybe move handling of dots/mora to + * Gregorian_ligature_engraver? It's probably common for all types of + * Gregorian chant notation that have dotted notes. + * + * FIXME: The horizontal alignment of the mora column is bad (too far + * to the left), if the last dotted note is not the last primitive in + * the ligature. Fortunately, in practice this bug should have no + * negative impact, since dotted notes appear within a ligature + * usually always at the end of the ligature, such that the bug never + * should apply for valid ligatures. + * + * TODO: Graduale Triplex, tempus per annum, hebdomada septima, + * alleluia (page 280) shows a counter-example for collecting dots + * always in a single column behind the ligature. Maybe only the last + * two dots in a ligature should be collected and all other dots put + * behind or on top of the head? + */ class Vaticana_ligature_engraver : public Gregorian_ligature_engraver { @@ -34,9 +56,13 @@ private: int context_info, int delta_pitch); bool is_stacked_head (int prefix_set, int context_info); - Real align_heads (Array primitives, + Real align_heads (vector primitives, Real flexa_width, Real thickness); + void check_for_prefix_loss (Item *primitive); + void check_for_ambiguous_dot_pitch (Grob_info primitive); + void add_mora_column (Paper_column *column); + vector augmented_primitives_; public: TRANSLATOR_DECLARATIONS (Vaticana_ligature_engraver); @@ -44,13 +70,30 @@ public: protected: virtual Spanner *create_ligature_spanner (); virtual void transform_heads (Spanner *ligature, - Array primitives); + vector primitives); + DECLARE_TRANSLATOR_LISTENER (pes_or_flexa); + DECLARE_TRANSLATOR_LISTENER (ligature); }; +IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, pes_or_flexa); +void +Vaticana_ligature_engraver::listen_pes_or_flexa (Stream_event *ev) +{ + Gregorian_ligature_engraver::listen_pes_or_flexa (ev); +} + +IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, ligature); +void +Vaticana_ligature_engraver::listen_ligature (Stream_event *ev) +{ + Ligature_engraver::listen_ligature (ev); +} + Vaticana_ligature_engraver::Vaticana_ligature_engraver () { brew_ligature_primitive_proc = Vaticana_ligature::brew_ligature_primitive_proc; + augmented_primitives_.clear (); } Spanner * @@ -63,41 +106,41 @@ bool Vaticana_ligature_engraver::is_stacked_head (int prefix_set, int context_info) { - bool is_stacked_b; + bool is_stacked; // upper head of pes is stacked upon lower head of pes ... - is_stacked_b = context_info & PES_UPPER; + is_stacked = context_info & PES_UPPER; // ... unless this note starts a flexa if (context_info & FLEXA_LEFT) - is_stacked_b = false; + is_stacked = false; // ... or another pes if (context_info & PES_LOWER) - is_stacked_b = false; + is_stacked = false; // ... or the previous note is a semivocalis or inclinatum if (context_info & AFTER_DEMINUTUM) - is_stacked_b = false; + is_stacked = false; // auctum head is never stacked upon preceding note if (prefix_set & AUCTUM) - is_stacked_b = false; + is_stacked = false; // virga is never stacked upon preceding note if (prefix_set & VIRGA) - is_stacked_b = false; + is_stacked = false; // oriscus is never stacked upon preceding note if (prefix_set & ORISCUS) - is_stacked_b = false; + is_stacked = false; if ((prefix_set & DEMINUTUM) && ! (prefix_set & INCLINATUM) && (context_info & FLEXA_RIGHT)) - is_stacked_b = true; // semivocalis head of deminutus form + is_stacked = true; // semivocalis head of deminutus form - return is_stacked_b; + return is_stacked; } /* @@ -145,7 +188,7 @@ Vaticana_ligature_engraver::need_extra_horizontal_space (int prev_prefix_set, in } Real -Vaticana_ligature_engraver::align_heads (Array primitives, +Vaticana_ligature_engraver::align_heads (vector primitives, Real flexa_width, Real thickness) { @@ -163,7 +206,7 @@ Vaticana_ligature_engraver::align_heads (Array primitives, = dynamic_cast (primitives[0].grob ())->get_column (); Real join_thickness - = thickness * column->layout ()->get_dimension (ly_symbol2scm ("linethickness")); + = thickness * column->layout ()->get_dimension (ly_symbol2scm ("line-thickness")); /* * Amount of extra space two put between some particular @@ -180,7 +223,7 @@ Vaticana_ligature_engraver::align_heads (Array primitives, Item *prev_primitive = 0; int prev_prefix_set = 0; - for (int i = 0; i < primitives.size (); i++) + for (vsize i = 0; i < primitives.size (); i++) { Item *primitive = dynamic_cast (primitives[i].grob ()); int prefix_set @@ -196,23 +239,23 @@ Vaticana_ligature_engraver::align_heads (Array primitives, if (glyph_name_scm == SCM_EOL) { primitive->programming_error ("Vaticana_ligature:" - "undefined glyph-name -> " - "ignoring grob"); + " undefined glyph-name ->" + " ignoring grob"); continue; } - std::string glyph_name = ly_scm2string (glyph_name_scm); + string glyph_name = ly_scm2string (glyph_name_scm); int delta_pitch = 0; if (prev_primitive) /* urgh, need prev_primitive only here */ { - SCM delta_pitch_scm = prev_primitive->get_property ("delta-pitch"); + SCM delta_pitch_scm = prev_primitive->get_property ("delta-position"); if (delta_pitch_scm != SCM_EOL) delta_pitch = scm_to_int (delta_pitch_scm); else { primitive->programming_error ("Vaticana_ligature:" - "delta-pitch undefined -> " - "ignoring grob"); + " delta-position undefined ->" + " ignoring grob"); continue; } } @@ -308,8 +351,7 @@ Vaticana_ligature_engraver::align_heads (Array primitives, /* * Horizontally line-up this head to form a ligature. */ - get_set_column (primitive, column); - primitive->translate_axis (ligature_width, X_AXIS); + move_related_items_to_column (primitive, column, ligature_width); ligature_width += head_width; prev_primitive = primitive; @@ -337,22 +379,89 @@ Vaticana_ligature_engraver::align_heads (Array primitives, * primitive were engraved as a stand-alone head. */ void -check_for_prefix_loss (Item *primitive) +Vaticana_ligature_engraver::check_for_prefix_loss (Item *primitive) { int prefix_set = scm_to_int (primitive->get_property ("prefix-set")); if (prefix_set & ~PES_OR_FLEXA) { - std::string prefs = Gregorian_ligature::prefixes_to_str (primitive); + string prefs = Gregorian_ligature::prefixes_to_str (primitive); primitive->warning (_f ("ignored prefix (es) `%s' of this head according " "to restrictions of the selected ligature style", prefs.c_str ())); } } +void +Vaticana_ligature_engraver::add_mora_column (Paper_column *column) +{ + if (augmented_primitives_.size () == 0) // no dot for column + return; + if (!column) // empty ligature??? + { + augmented_primitives_[0].grob ()-> + programming_error ("no paper column to add dot"); + return; + } + Item *dotcol = make_item ("DotColumn", SCM_EOL); + dotcol->set_parent (column, X_AXIS); + for (vsize i = 0; i < augmented_primitives_.size (); i++) + { + Item *primitive = + dynamic_cast (augmented_primitives_[i].grob ()); + Item *dot = make_item ("Dots", primitive->self_scm ()); + dot->set_property ("dot-count", scm_from_int (1)); + dot->set_parent (primitive, Y_AXIS); + primitive->set_object ("dot", dot->self_scm ()); + Dot_column::add_head (dotcol, primitive); + + // FIXME: why isn't the dot picked up by Paper_column_engraver? + Separation_item::add_item (column, dot); + } +} + +/* + * This function prints a warning, if the given primitive has the same + * pitch as at least one of the primitives already stored in the + * augmented_primitives_ array. + * + * The rationale of this check is, that, if there are two dotted + * primitives with the same pitch, then collecting all dots in a dot + * column behind the ligature leads to a notational ambiguity of to + * which head the corresponding dot refers. + * + * Such a case should be treated as a badly specified ligature. The + * user should split the ligature to make the notation of dots + * unambiguous. + */ +void +Vaticana_ligature_engraver::check_for_ambiguous_dot_pitch (Grob_info primitive) +{ + // TODO: Fix performance, which is currently O (n^2) (since this + // method is called O (n) times and takes O (n) steps in the for + // loop), but could be O (n) (by replacing the for loop by e.g. a + // bitmask based O (1) test); where n= (which is typically small (n<10), though). + Stream_event *new_cause = primitive.event_cause (); + int new_pitch = unsmob_pitch (new_cause->get_property ("pitch"))->steps (); + for (vsize i = 0; i < augmented_primitives_.size (); i++) + { + Stream_event *cause = augmented_primitives_[i].event_cause (); + int pitch = unsmob_pitch (cause->get_property ("pitch"))->steps (); + if (pitch == new_pitch) + { + primitive.grob ()-> + warning ("Ambiguous use of dots in ligature: there are " + "multiple dotted notes with the same pitch. " + "The ligature should be split."); + return; // supress multiple identical warnings + } + } +} + void Vaticana_ligature_engraver::transform_heads (Spanner *ligature, - Array primitives) + vector primitives) { Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2); @@ -362,20 +471,21 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, int prev_prefix_set = 0; int prev_context_info = 0; int prev_delta_pitch = 0; - std::string prev_glyph_name = ""; - for (int i = 0; i < primitives.size (); i++) + string prev_glyph_name = ""; + augmented_primitives_.clear (); + for (vsize i = 0; i < primitives.size (); i++) { Item *primitive = dynamic_cast (primitives[i].grob ()); int delta_pitch; - SCM delta_pitch_scm = primitive->get_property ("delta-pitch"); + SCM delta_pitch_scm = primitive->get_property ("delta-position"); if (delta_pitch_scm != SCM_EOL) delta_pitch = scm_to_int (delta_pitch_scm); else { primitive->programming_error ("Vaticana_ligature:" - "delta-pitch undefined -> " - "ignoring grob"); + " delta-position undefined ->" + " ignoring grob"); continue; } @@ -384,6 +494,27 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, = scm_to_int (primitive->get_property ("prefix-set")); int context_info = scm_to_int (primitive->get_property ("context-info")); + + if (Rhythmic_head::dot_count (primitive) > 0) + // remove dots from primitive and add remember primitive for + // creating a dot column + { + Rhythmic_head::get_dots (primitive)->set_property ("dot-count", + scm_from_int (0)); + // TODO: Maybe completely remove grob "Dots" (dots->suicide + // () ?) rather than setting property "dot-count" to 0. + + check_for_ambiguous_dot_pitch (primitives[i]); + augmented_primitives_.push_back (primitives[i]); + } + else if (augmented_primitives_.size () > 0) + { + primitive->warning ("This ligature has a dotted head followed by " + "a non-dotted head. The ligature should be " + "split after the last dotted head before " + "this head."); + } + if (is_stacked_head (prefix_set, context_info)) { context_info |= STACKED_HEAD; @@ -397,7 +528,7 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, * this decision must be made here in the engraver rather than in * the backend). */ - std::string glyph_name; + string glyph_name; if (prefix_set & VIRGA) { glyph_name = "vaticana.punctum"; @@ -476,8 +607,8 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, else // (prev_delta_pitch == 0) { primitive->programming_error ("Vaticana_ligature:" - "deminutum head must have different " - "pitch -> ignoring grob"); + " deminutum head must have different" + " pitch -> ignoring grob"); } else if (prefix_set & (CAVUM | LINEA)) if ((prefix_set & CAVUM) && (prefix_set & LINEA)) @@ -554,7 +685,7 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, if (prev_primitive) prev_primitive->set_property ("glyph-name", - scm_makfrom0str (prev_glyph_name.c_str ())); + ly_string2scm (prev_glyph_name)); /* * In the backend, flexa shapes and joins need to know about line @@ -572,10 +703,13 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, } prev_primitive->set_property ("glyph-name", - scm_makfrom0str (prev_glyph_name.c_str ())); + ly_string2scm (prev_glyph_name)); align_heads (primitives, flexa_width, thickness); + // append all dots to paper column of ligature's last head + add_mora_column (prev_primitive->get_column ()); + #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. */ @@ -592,8 +726,16 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature, ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, rest); ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, note_head); ADD_TRANSLATOR (Vaticana_ligature_engraver, - /* doc */ "Handles ligatures by glueing special ligature heads together.", - /* create */ "VaticanaLigature", - /* accept */ "ligature-event", - /* read */ "", - /* write */ ""); + /* doc */ + "Handle ligatures by glueing special ligature heads together.", + + /* create */ + "VaticanaLigature " + "DotColumn ", + + /* read */ + "", + + /* write */ + "" + );