X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fmensural-ligature-engraver.cc;h=993e4ff5c97b7b5823941748944a3bd933f2404a;hb=9e338fb9a890089a44a3c36ae3c95653027718d5;hp=1fe6e0730e4b69aacebefc3825003856f7c1ba3c;hpb=0d6207e894224577f9879f4036ef8cab4925c4c7;p=lilypond.git diff --git a/lily/mensural-ligature-engraver.cc b/lily/mensural-ligature-engraver.cc index 1fe6e0730e..993e4ff5c9 100644 --- a/lily/mensural-ligature-engraver.cc +++ b/lily/mensural-ligature-engraver.cc @@ -1,24 +1,28 @@ /* mensural-ligature-engraver.cc -- implement Mensural_ligature_engraver - + source file of the GNU LilyPond music typesetter - - (c) 2002--2005 Juergen Reuter , - Pal Benko - */ + + (c) 2002--2007 Juergen Reuter , + Pal Benko +*/ #include "coherent-ligature-engraver.hh" +#include "font-interface.hh" +#include "international.hh" #include "mensural-ligature.hh" -#include "event.hh" -#include "warn.hh" -#include "spanner.hh" -#include "paper-column.hh" #include "note-column.hh" -#include "rhythmic-head.hh" #include "note-head.hh" -#include "staff-symbol-referencer.hh" #include "output-def.hh" -#include "font-interface.hh" +#include "paper-column.hh" +#include "pitch.hh" +#include "rhythmic-head.hh" +#include "spanner.hh" +#include "staff-symbol-referencer.hh" +#include "stream-event.hh" +#include "warn.hh" + +#include "translator.icc" /* * TODO: accidentals are aligned with the first note; @@ -27,14 +31,11 @@ * TODO: prohibit ligatures having notes differing only in accidentals * (like \[ a\breve g as \]) * - * TODO: dotted heads: avoid next note colliding with the dot, e.g. by - * putting it *above* (rather than after) the affected ligature head. - * * TODO: do something with multiple voices within a ligature. See * for example: * Ockeghem: Missa Ecce ancilla domini, bassus part, end of Christe. * - * TODO: enhance robustness: in case of an illegal ligature (e.g. the + * TODO: enhance robustness: in case of an invalid ligature (e.g. the * input specifies a ligature that contains a minima), automatically * break the ligature into smaller, valid pieces. Such a piece may be * a single note. @@ -45,20 +46,29 @@ class Mensural_ligature_engraver : public Coherent_ligature_engraver protected: virtual Spanner *create_ligature_spanner (); - virtual void build_ligature (Spanner *ligature, Array primitives); - + virtual void build_ligature (Spanner *ligature, vector primitives); + DECLARE_TRANSLATOR_LISTENER (ligature); + public: TRANSLATOR_DECLARATIONS (Mensural_ligature_engraver); private: - void transform_heads (Array primitives); - void propagate_properties (Spanner *ligature, Array primitives); - void fold_up_primitives (Array primitives); + void transform_heads (vector primitives); + void propagate_properties (Spanner *ligature, vector primitives); + void fold_up_primitives (vector primitives); }; +IMPLEMENT_TRANSLATOR_LISTENER (Mensural_ligature_engraver, ligature); +void +Mensural_ligature_engraver::listen_ligature (Stream_event *ev) +{ + Ligature_engraver::listen_ligature (ev); +} Mensural_ligature_engraver::Mensural_ligature_engraver () { + brew_ligature_primitive_proc = + Mensural_ligature::brew_ligature_primitive_proc; } Spanner * @@ -68,7 +78,7 @@ Mensural_ligature_engraver::create_ligature_spanner () } void -Mensural_ligature_engraver::transform_heads (Array primitives) +Mensural_ligature_engraver::transform_heads (vector primitives) { if (primitives.size () < 2) { @@ -85,196 +95,197 @@ Mensural_ligature_engraver::transform_heads (Array primitives) bool prev_semibrevis = false; Item *prev_primitive = NULL; - for (int i = 0, s = primitives.size (); i < s; i++) { - Grob_info info = primitives[i]; - Item *primitive = dynamic_cast (info.grob_); - int duration_log = Note_head::get_balltype (primitive); - - Music *nr = info.music_cause (); - - /* - ugh. why not simply check for pitch? - */ - if (!nr->is_mus_type ("note-event")) - { - nr->origin ()->warning - (_f ("can not determine pitch of ligature primitive -> skipping")); - at_beginning = true; - continue; - } - - int pitch = unsmob_pitch (nr->get_property ("pitch"))->steps (); - int delta_pitch = 0; - - if (at_beginning) - { - if (i == s - 1) - { - // we can get here after invalid input - nr->origin ()->warning - (_f ("single note ligature - skipping")); - break; - } - prev_semibrevis = prev_brevis_shape = false; - prev_primitive = NULL; - } - else - { - delta_pitch = pitch - prev_pitch; - if (delta_pitch == 0) - { - nr->origin ()->warning - (_f ("prime interval within ligature -> skipping")); - at_beginning = true; - primitive->set_property ("primitive", - scm_int2num (MLP_NONE)); - continue; - } - } - - if (duration_log < -3 // is this possible at all??? - || duration_log > 0) - { - nr->origin ()->warning - (_f ("mensural ligature: duration none of Mx, L, B, S -> skipping")); - primitive->set_property ("primitive", - scm_int2num (MLP_NONE)); - at_beginning = true; - continue; - } - - // apply_transition replacement begins - bool general_case = true; - - // first check special cases - // 1. beginning - if (at_beginning) - { - // a. semibreves - if (duration_log == 0) - { - primitive->set_property ("primitive", - scm_int2num (MLP_UP | MLP_BREVIS)); - prev_semibrevis = prev_brevis_shape = true; - general_case = false; - } - // b. descendens longa or brevis - else if (i < s - 1 - && (unsmob_pitch (primitives[i + 1].music_cause () - ->get_property ("pitch"))->steps () < pitch) - && duration_log > -3) - { - int left_stem = duration_log == -1 ? MLP_DOWN : 0; - - primitive->set_property ("primitive", - scm_int2num (left_stem | MLP_BREVIS)); - prev_brevis_shape = true; - prev_semibrevis = general_case = false; - } - } - // 2. initial semibrevis must be followed by another one - else if (prev_semibrevis) - { - prev_semibrevis = false; - if (duration_log == 0) - { - primitive->set_property ("primitive", scm_int2num(MLP_BREVIS)); - general_case = false; - } - else - { - nr->origin ()->warning - (_f ("semibrevis must be followed by another one -> skipping")); - primitive->set_property ("primitive", - scm_int2num (MLP_NONE)); - at_beginning = true; - continue; - } - } - // 3. semibreves are otherwise not allowed - else if (duration_log == 0) - { - nr->origin ()->warning - (_f ("semibreves can only appear at the beginning of a ligature,\n" - "and there may be only zero or two of them")); - primitive->set_property ("primitive", - scm_int2num (MLP_NONE)); - at_beginning = true; - continue; - } - // 4. end, descendens - else if (i == s - 1 && delta_pitch < 0) - { - // brevis; previous note must be turned into flexa - if (duration_log == -1) - { - if (prev_brevis_shape) - { - prev_primitive->set_property - ("primitive", - scm_int2num - (MLP_FLEXA - | (scm_to_int (prev_primitive->get_property ("primitive")) - & MLP_DOWN))); - primitive->set_property ("primitive", scm_int2num (MLP_NONE)); - break; // no more notes, no join - } - else - { - nr->origin ()->warning - (_f ("invalid ligatura ending:\n" - "when the last note is a descending brevis,\n" - "the penultimate note must be another one,\n" - "or the ligatura must be LB or SSB")); - primitive->set_property ("primitive", scm_int2num (MLP_NONE)); - break; - } - } - // longa - else if (duration_log == -2) - { - primitive->set_property ("primitive", scm_int2num (MLP_BREVIS)); - general_case = false; - } - // else maxima; fall through regular case below - } - - if (general_case) - { - static int const shape[3] = {MLP_MAXIMA, MLP_LONGA, MLP_BREVIS}; - - primitive->set_property ("primitive", - scm_int2num (shape[duration_log + 3])); - prev_brevis_shape = duration_log == -1; - } - - // join_primitives replacement - if (!at_beginning) - { - /* - if the previous note is longa-shaped and this note is lower, - then the joining line may hide the stem, so it is made longer - to serve as stem as well - */ - if (delta_pitch < 0 - && (scm_to_int (prev_primitive->get_property ("primitive")) - & MLP_LONGA)) - { - delta_pitch -= 6; - // instead of number 6 - // the legth of the longa stem should be queried something like - // Font_interface::get_default_font (ligature)->find_by_name - // ("noteheads.s-2mensural").extent (Y_AXIS).length () - } - prev_primitive->set_property ("join-right-amount", - scm_int2num (delta_pitch)); - // perhaps set add-join as well - } - at_beginning = false; - prev_primitive = primitive; - prev_pitch = pitch; - // apply_transition replacement ends - } + for (vsize i = 0, s = primitives.size (); i < s; i++) + { + Grob_info info = primitives[i]; + Item *primitive = dynamic_cast (info.grob ()); + int duration_log = Rhythmic_head::duration_log (primitive); + + Stream_event *nr = info.event_cause (); + + /* + ugh. why not simply check for pitch? + */ + if (!nr->in_event_class ("note-event")) + { + nr->origin ()->warning + (_f ("cannot determine pitch of ligature primitive -> skipping")); + at_beginning = true; + continue; + } + + int pitch = unsmob_pitch (nr->get_property ("pitch"))->steps (); + int delta_pitch = 0; + + if (at_beginning) + { + if (i == s - 1) + { + // we can get here after invalid input + nr->origin ()->warning + (_f ("single note ligature - skipping")); + break; + } + prev_semibrevis = prev_brevis_shape = false; + prev_primitive = NULL; + } + else + { + delta_pitch = pitch - prev_pitch; + if (delta_pitch == 0) + { + nr->origin ()->warning + (_f ("prime interval within ligature -> skipping")); + at_beginning = true; + primitive->set_property ("primitive", + scm_from_int (MLP_NONE)); + continue; + } + } + + if (duration_log < -3 // is this possible at all??? + || duration_log > 0) + { + nr->origin ()->warning + (_f ("mensural ligature: duration none of Mx, L, B, S -> skipping")); + primitive->set_property ("primitive", + scm_from_int (MLP_NONE)); + at_beginning = true; + continue; + } + + // apply_transition replacement begins + bool general_case = true; + + // first check special cases + // 1. beginning + if (at_beginning) + { + // a. semibreves + if (duration_log == 0) + { + primitive->set_property ("primitive", + scm_from_int (MLP_UP | MLP_BREVIS)); + prev_semibrevis = prev_brevis_shape = true; + general_case = false; + } + // b. descendens longa or brevis + else if (i < s - 1 + && (unsmob_pitch (primitives[i + 1].event_cause () + ->get_property ("pitch"))->steps () < pitch) + && duration_log > -3) + { + int left_stem = duration_log == -1 ? MLP_DOWN : 0; + + primitive->set_property ("primitive", + scm_from_int (left_stem | MLP_BREVIS)); + prev_brevis_shape = true; + prev_semibrevis = general_case = false; + } + } + // 2. initial semibrevis must be followed by another one + else if (prev_semibrevis) + { + prev_semibrevis = false; + if (duration_log == 0) + { + primitive->set_property ("primitive", scm_from_int (MLP_BREVIS)); + general_case = false; + } + else + { + nr->origin ()->warning + (_f ("semibrevis must be followed by another one -> skipping")); + primitive->set_property ("primitive", + scm_from_int (MLP_NONE)); + at_beginning = true; + continue; + } + } + // 3. semibreves are otherwise not allowed + else if (duration_log == 0) + { + nr->origin ()->warning + (_f ("semibreves can only appear at the beginning of a ligature,\n" + "and there may be only zero or two of them")); + primitive->set_property ("primitive", + scm_from_int (MLP_NONE)); + at_beginning = true; + continue; + } + // 4. end, descendens + else if (i == s - 1 && delta_pitch < 0) + { + // brevis; previous note must be turned into flexa + if (duration_log == -1) + { + if (prev_brevis_shape) + { + prev_primitive->set_property + ("primitive", + scm_from_int + (MLP_FLEXA + | (scm_to_int (prev_primitive->get_property ("primitive")) + & MLP_DOWN))); + primitive->set_property ("primitive", scm_from_int (MLP_NONE)); + break; // no more notes, no join + } + else + { + nr->origin ()->warning + (_f ("invalid ligatura ending:\n" + "when the last note is a descending brevis,\n" + "the penultimate note must be another one,\n" + "or the ligatura must be LB or SSB")); + primitive->set_property ("primitive", scm_from_int (MLP_NONE)); + break; + } + } + // longa + else if (duration_log == -2) + { + primitive->set_property ("primitive", scm_from_int (MLP_BREVIS)); + general_case = false; + } + // else maxima; fall through regular case below + } + + if (general_case) + { + static int const shape[3] = {MLP_MAXIMA, MLP_LONGA, MLP_BREVIS}; + + primitive->set_property ("primitive", + scm_from_int (shape[duration_log + 3])); + prev_brevis_shape = duration_log == -1; + } + + // join_primitives replacement + if (!at_beginning) + { + /* + if the previous note is longa-shaped and this note is lower, + then the joining line may hide the stem, so it is made longer + to serve as stem as well + */ + if (delta_pitch < 0 + && (scm_to_int (prev_primitive->get_property ("primitive")) + & MLP_LONGA)) + { + delta_pitch -= 6; + // instead of number 6 + // the legth of the longa stem should be queried something like + // Font_interface::get_default_font (ligature)->find_by_name + // ("noteheads.sM2mensural").extent (Y_AXIS).length () + } + prev_primitive->set_property ("join-right-amount", + scm_from_int (delta_pitch)); + // perhaps set add-join as well + } + at_beginning = false; + prev_primitive = primitive; + prev_pitch = pitch; + // apply_transition replacement ends + } } /* @@ -293,99 +304,130 @@ Mensural_ligature_engraver::transform_heads (Array primitives) */ void Mensural_ligature_engraver::propagate_properties (Spanner *ligature, - Array primitives) + vector primitives) { - Real thickness = - robust_scm2double (ligature->get_property ("thickness"), 1.4); - thickness *= - ligature->get_layout ()->get_dimension (ly_symbol2scm ("linethickness")); - - Real head_width = - Font_interface::get_default_font (ligature)-> - find_by_name ("noteheads.s-1mensural").extent (X_AXIS).length (); - Real flexa_width = - robust_scm2double (ligature->get_property ("flexa-width"), 2); - Real maxima_head_width = - Font_interface::get_default_font (ligature)-> - find_by_name ("noteheads.s-1neomensural").extent (X_AXIS).length (); + Real thickness + = robust_scm2double (ligature->get_property ("thickness"), 1.4); + thickness + *= ligature->layout ()->get_dimension (ly_symbol2scm ("line-thickness")); + + Real head_width + = Font_interface::get_default_font (ligature)-> + find_by_name ("noteheads.sM1mensural").extent (X_AXIS).length (); + Real flexa_width + = robust_scm2double (ligature->get_property ("flexa-width"), 2); + Real maxima_head_width + = Font_interface::get_default_font (ligature)-> + find_by_name ("noteheads.sM1neomensural").extent (X_AXIS).length (); flexa_width *= Staff_symbol_referencer::staff_space (ligature); Real half_flexa_width = 0.5 * (flexa_width + thickness); - for (int i = 0; i < primitives.size (); i++) + for (vsize i = 0; i < primitives.size (); i++) { - Item *primitive = dynamic_cast (primitives[i].grob_); + Item *primitive = dynamic_cast (primitives[i].grob ()); int output = scm_to_int (primitive->get_property ("primitive")); primitive->set_property ("thickness", - scm_make_real (thickness)); - - switch (output & MLP_ANY) { - case MLP_NONE: - primitive->set_property ("head-width", - scm_make_real (half_flexa_width)); - break; - case MLP_BREVIS: - case MLP_LONGA: - primitive->set_property ("head-width", - scm_make_real (head_width)); - break; - case MLP_MAXIMA: - primitive->set_property ("head-width", - scm_make_real (maxima_head_width)); - break; - case MLP_FLEXA: - primitive->set_property ("head-width", - scm_make_real (half_flexa_width)); - primitive->set_property ("flexa-width", - scm_make_real (flexa_width)); - break; - default: - programming_error (_f ("unexpected case fall-through")); - break; - } + scm_from_double (thickness)); + + switch (output & MLP_ANY) + { + case MLP_NONE: + primitive->set_property ("head-width", + scm_from_double (half_flexa_width)); + break; + case MLP_BREVIS: + case MLP_LONGA: + primitive->set_property ("head-width", + scm_from_double (head_width)); + break; + case MLP_MAXIMA: + primitive->set_property ("head-width", + scm_from_double (maxima_head_width)); + break; + case MLP_FLEXA: + primitive->set_property ("head-width", + scm_from_double (half_flexa_width)); + primitive->set_property ("flexa-width", + scm_from_double (flexa_width)); + break; + default: + programming_error (_f ("unexpected case fall-through")); + break; + } } } void -Mensural_ligature_engraver::fold_up_primitives (Array primitives) +Mensural_ligature_engraver::fold_up_primitives (vector primitives) { Item *first = 0; - Real distance = 0; - for (int i = 0; i < primitives.size (); i++) + Real distance = 0.0; + Real dot_shift = 0.0; + for (vsize i = 0; i < primitives.size (); i++) { - Item *current = dynamic_cast (primitives[i].grob_); + Item *current = dynamic_cast (primitives[i].grob ()); if (i == 0) { first = current; + dot_shift = 1.5 * Staff_symbol_referencer::staff_space (first); } - get_set_column (current, first->get_column ()); + move_related_items_to_column (current, first->get_column (), + distance); - if (i > 0) + distance + += scm_to_double (current->get_property ("head-width")) + - scm_to_double (current->get_property ("thickness")); + + if (Rhythmic_head::dot_count (current) > 0) + // Move dots above/behind the ligature. { - current->translate_axis (distance, X_AXIS); + if (i + 1 < primitives.size ()) + // dot in the midst => move above head + { + // FIXME: Amount of vertical dot-shift should depend on + // pitch. + // + // FIXME: dot placement is horizontally slightly off. + Rhythmic_head::get_dots (current)->translate_axis (dot_shift, Y_AXIS); + } + else + // trailing dot => move behind head + { + double head_width = + scm_to_double (current->get_property ("head-width")); + Rhythmic_head::get_dots (current)-> + translate_axis (head_width, X_AXIS); + } } - - distance += - scm_to_double (current->get_property ("head-width")) - - scm_to_double (current->get_property ("thickness")); } } void Mensural_ligature_engraver::build_ligature (Spanner *ligature, - Array primitives) + vector primitives) { transform_heads (primitives); propagate_properties (ligature, primitives); fold_up_primitives (primitives); } +ADD_ACKNOWLEDGER (Mensural_ligature_engraver, rest); +ADD_ACKNOWLEDGER (Mensural_ligature_engraver, note_head); + ADD_TRANSLATOR (Mensural_ligature_engraver, -/* descr */ "Handles Mensural_ligature_events by glueing special ligature heads together.", -/* creats*/ "MensuralLigature", -/* accepts */ "ligature-event", -/* acks */ "note-head-interface rest-interface", -/* reads */ "", -/* write */ ""); + /* doc */ + "Handle @code{Mensural_ligature_events} by glueing special" + " ligature heads together.", + + /* create */ + "MensuralLigature ", + + /* read */ + "", + + /* write */ + "" + );