X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fmensural-ligature-engraver.cc;h=906ace106ca689f8f93082dae7b5418a541efc26;hb=5b4b0d6e9a197e8f9eb085b7c2ad78b8be3e5cfc;hp=3c8637f04a132d381be31afca20417717cd3a7fb;hpb=d765f3af45be51f15da55cf570a4b172200e1035;p=lilypond.git diff --git a/lily/mensural-ligature-engraver.cc b/lily/mensural-ligature-engraver.cc index 3c8637f04a..906ace106c 100644 --- a/lily/mensural-ligature-engraver.cc +++ b/lily/mensural-ligature-engraver.cc @@ -1,568 +1,433 @@ /* mensural-ligature-engraver.cc -- implement Mensural_ligature_engraver - + source file of the GNU LilyPond music typesetter - - (C) 2002 Juergen Reuter - */ + (c) 2002--2008 Juergen Reuter , + Pal Benko +*/ + +#include "coherent-ligature-engraver.hh" +#include "font-interface.hh" +#include "international.hh" #include "mensural-ligature.hh" -#include "ligature-engraver.hh" -#include "musical-request.hh" -#include "warn.hh" -#include "item.hh" -#include "spanner.hh" -#include "rod.hh" -#include "paper-column.hh" #include "note-column.hh" -#include "rhythmic-head.hh" #include "note-head.hh" +#include "output-def.hh" +#include "paper-column.hh" +#include "pitch.hh" +#include "rhythmic-head.hh" +#include "spanner.hh" #include "staff-symbol-referencer.hh" -#include "paper-def.hh" -#include "font-interface.hh" +#include "stream-event.hh" +#include "warn.hh" + +#include "translator.icc" /* - * 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: accidentals are aligned with the first note; + * they must appear ahead. * - * 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 - * soon as there appear durations other than breves within a ligature - * with more than two heads (ligatura ternaria etc.). Hence, the - * below implementation is an approximation of what I think the rules - * could look like if forced to be non-ambigous. This should be - * further investigated. + * TODO: prohibit ligatures having notes differing only in accidentals + * (like \[ a\breve g as \]) * - * TODO: The automat is quite complicated, and its design is error - * prone (and most probably, it behaves wrong for some very special - * cases). Maybe we can find a better paradigm for modelling Franco - * of Cologne's rules? + * TODO: do something with multiple voices within a ligature. See + * for example: + * Ockeghem: Missa Ecce ancilla domini, bassus part, end of Christe. * - * TODO: dotted heads: when applying Franco of Cologne's mapping, put - * dots *above* (rather than after) affected ligature heads. - * - * 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 requests 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. + * 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. */ -class Mensural_ligature_engraver : public Ligature_engraver + +class Mensural_ligature_engraver : public Coherent_ligature_engraver { - Real distance_; - Array primitives_; protected: - virtual void acknowledge_grob (Grob_info); - virtual void try_stop_ligature (); virtual Spanner *create_ligature_spanner (); - + virtual void build_ligature (Spanner *ligature, vector primitives); + DECLARE_TRANSLATOR_LISTENER (ligature); + public: - TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver); + TRANSLATOR_DECLARATIONS (Mensural_ligature_engraver); private: - int apply_transition (int state, int input, int i); - void transform_heads (); - void propagate_properties (); - void fold_up_primitives (); - void join_primitives (); - void get_set_column (Item *item, Paper_column *new_col); + 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 () { - distance_ = 0; + brew_ligature_primitive_proc = + Mensural_ligature::brew_ligature_primitive_proc; } Spanner * Mensural_ligature_engraver::create_ligature_spanner () { - distance_ = 0; - return new Spanner (get_property ("MensuralLigature")); + return make_spanner ("MensuralLigature", SCM_EOL); } -/* - * TODO: move this function to class Item? - */ void -Mensural_ligature_engraver::get_set_column (Item *item, Paper_column *column) +Mensural_ligature_engraver::transform_heads (vector primitives) { - Item *parent = dynamic_cast (item->get_parent (X_AXIS)); - if (!parent) + if (primitives.size () < 2) { - programming_error ("failed tweaking paper column in ligature"); + warning (_ ("ligature with less than 2 heads -> skipping")); return; } + int prev_pitch = 0; + bool at_beginning = true; - String name = parent->name (); - if (!String::compare (name, "PaperColumn")) + // needed so that we can check whether + // the previous note can be turned into a flexa + bool prev_brevis_shape = false; + + bool prev_semibrevis = false; + Item *prev_primitive = NULL; + + for (vsize i = 0, s = primitives.size (); i < s; i++) { - // 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)) + 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")) { - Item *sibling = unsmob_item (ly_car (tail)); - if ((sibling) && - (Staff_symbol_referencer::get_staff_symbol (sibling) == sl)) + nr->origin ()->warning + (_ ("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) { - sibling->set_parent (column, X_AXIS); + // we can get here after invalid input + nr->origin ()->warning + (_ ("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 + (_ ("prime interval within ligature -> skipping")); + at_beginning = true; + primitive->set_property ("primitive", + scm_from_int (MLP_NONE)); + continue; } } - } - else - { - get_set_column (parent, column); - } -} -/* - * The following lines implement a finite state automat. Given a - * sequence of durations (Longa, Brevis, Semibrevis) or - * end-of-ligature-request as input, the automat outputs a sequence of - * requests for grobs that form a proper ligature. - */ + if (duration_log < -3 // is this possible at all??? + || duration_log > 0) + { + nr->origin ()->warning + (_ ("mensural ligature: duration none of Mx, L, B, S -> skipping")); + primitive->set_property ("primitive", + scm_from_int (MLP_NONE)); + at_beginning = true; + continue; + } -/* - * This enumeration represents the set of possible input values to the - * automat. There may (potentially) be any sequence of Longa, Brevis, - * and Semibrevis duration symbols fed into the automat, with a final - * EndOfLigature symbol to terminate the ligature. Other durations - * are explicitly prohibited. Depending on the note's pitch of the - * preceding and the current input, the melodic line may be ascending - * or descending. Per definition, the melodic line must either ascend - * or descend, because if the pitches were twice the same, the two - * notes would be merged into a single one (as long as not resulting - * in a prohibited duration). In the case of the EndOfLigature - * symbol, the melodic line is undefined (but we still have ascending - * and descending case for the sake of consistency, making the automat - * simpler). - */ -enum Ligature_input -{ - // Ascending/Descending Longa/Brevis/Semibrevis/EndOfLigature - INPUT_AL = 0, - INPUT_DL, - INPUT_AB, - INPUT_DB, - INPUT_AS, - INPUT_DS, - INPUT_AE, - INPUT_DE, -}; + // apply_transition replacement begins + bool general_case = true; -/* - * This enumeration represents all possible internal states of the - * automat. Besides the generic states START, ERROR, and END, the - * remaining states L, B, S, and SS describe pending values from the - * sequence of input values that have not yet been transformed to - * proper output values, including the melodic direction - * (ascending/descending) for state L. - */ -enum Ligature_state -{ - // aL = ascending Longa, dL descending Longa, B = Brevis, S = - // Semibrevis, SS = 2 Semibreves - STATE_START = 0, - STATE_aL, - STATE_dL, - STATE_B, - STATE_S, - STATE_SS, - STATE_ERROR, - STATE_END, -}; + // 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; -/* - * The following array represents the transitions of the automat: - * given some state and input, it maps to a new state, according (with - * the limitations as described above) to the rules of Franco of - * Cologne. - */ -const int/*new state*/ transition_state[/*old state*/][8/*input*/] = -{ - {STATE_aL, STATE_dL, STATE_B, STATE_B, - STATE_S, STATE_S, STATE_ERROR, STATE_ERROR}, // was: STATE_START - {STATE_aL, STATE_dL, STATE_B, STATE_START, - STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_aL - {STATE_aL, STATE_dL, STATE_B, STATE_START, - STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_dL - {STATE_aL, STATE_dL, STATE_B, STATE_START, - STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_B - {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR, - STATE_SS, STATE_SS, STATE_ERROR, STATE_ERROR}, // was: STATE_S - {STATE_aL, STATE_dL, STATE_B, STATE_B, - STATE_S, STATE_S, STATE_END, STATE_END}, // was: STATE_SS - {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR, - STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_ERROR - {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR, - STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_END -}; + 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 + (_ ("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 + (_ ("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 + (_ ("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 + } -/* - * The following array represents the output of the automat while - * switching from one state to another: given some state and input, it - * maps to the output produced when switching to the next state, - * according (with the limitations as described above) to the rules of - * Franco of Cologne. - */ -const int/*output*/ transition_output[/*old state*/][8/*input*/] = -{ - {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE, - MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_START - {MLP_sc, MLP_ss, MLP_sc, MLP_LB, - MLP_NONE, MLP_NONE, MLP_sc, MLP_sc}, // was: STATE_aL - {MLP_sc, MLP_ss, MLP_sc, MLP_LB, - MLP_NONE, MLP_NONE, MLP_ss, MLP_ss}, // was: STATE_dL - {MLP_ss, MLP_cs, MLP_ss, MLP_BB, - MLP_NONE, MLP_NONE, MLP_ss, MLP_ss} , // was: STATE_B - {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE, - MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_S - {MLP_SS, MLP_SS, MLP_SS, MLP_SS, - MLP_SS, MLP_SS, MLP_SS, MLP_SS} , // was: STATE_SS - {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE, - MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_ERROR - {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE, - MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_END -}; + if (general_case) + { + static int const shape[3] = {MLP_MAXIMA, MLP_LONGA, MLP_BREVIS}; -int -Mensural_ligature_engraver::apply_transition (int state, int input, int i) -{ - int output = transition_output[state][input]; - Item *last_last_primitive = (i > 1) ? - dynamic_cast (primitives_[i-2].grob_) : 0; - Item *last_primitive = (i > 0) ? - dynamic_cast (primitives_[i-1].grob_) : 0; - Item *primitive = (i < primitives_.size ()) ? - dynamic_cast (primitives_[i].grob_) : 0; - switch (output) - { - case MLP_NONE: - // skip note head, expecting a primitive with two note heads - break; - case MLP_sc: - case MLP_ss: - case MLP_cs: - // primitive with single note head - if (!last_primitive) - { - programming_error ("last_primitive undefined"); - break; - } - last_primitive->set_grob_property ("primitive", gh_int2scm (output)); - break; - case MLP_BB: - case MLP_LB: - // primitive with two note heads - if (!last_primitive) - { - programming_error ("last_primitive undefined"); - break; - } - if (!primitive) - { - programming_error ("primitive undefined"); - break; - } - last_primitive->set_grob_property ("primitive", gh_int2scm (output)); - primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE)); - break; - case MLP_SS: - // delayed primitive with two note heads - if (!last_last_primitive) - { - programming_error ("last_last_primitive undefined"); - break; - } - if (!last_primitive) - { - programming_error ("last_primitive undefined"); - break; - } - last_last_primitive->set_grob_property ("primitive", gh_int2scm (output)); - last_primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE)); - break; - default: - programming_error (_f ("unexpected case fall-through")); - break; - } - return transition_state[state][input]; -} + primitive->set_property ("primitive", + scm_from_int (shape[duration_log + 3])); + prev_brevis_shape = duration_log == -1; + } -void -Mensural_ligature_engraver::transform_heads () -{ - if (primitives_.size () < 2) - { - warning (_f ("ligature with less than 2 heads -> skipping")); - return; + // 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 } - int state = STATE_START; - Pitch last_pitch, pitch; - bool have_last_pitch = 0, have_pitch = 0; - for (int i = 0; i < primitives_.size (); i++) { - last_pitch = pitch; - have_last_pitch = have_pitch; - Grob_info info = primitives_[i]; - int duration_log = - Note_head::get_balltype (dynamic_cast (info.grob_)); - Note_req *nr = dynamic_cast (info.music_cause ()); - if (!nr) - { - info.music_cause ()->origin ()->warning (_f ("can not determine pitch of ligature primitive -> skipping")); - i++; - state = STATE_START; - have_pitch = 0; - continue; - } - else - { - pitch = *unsmob_pitch (nr->get_mus_property ("pitch")); - have_pitch = 1; - } - - int delta_pitch; - - if (!have_last_pitch) - { - delta_pitch = 0; // first pitch; delta undefined - } - else - { - delta_pitch = (pitch.steps () - last_pitch.steps ()); - if (Pitch::compare (last_pitch, pitch) == 0) - { - info.music_cause ()->origin ()->warning (_f ("prime interval within ligature -> skipping")); - i++; - state = STATE_START; - have_pitch = 0; - continue; - } - } - - if ((duration_log < -2) || (duration_log > 0)) - { - info.music_cause ()->origin ()->warning (_f ("mensural ligature: duration none of L, B, S -> skipping")); - i++; - state = STATE_START; - have_pitch = 0; - continue; - } - - int input = (duration_log + 2) * 2 + ((delta_pitch < 0) ? 1 : 0); - state = apply_transition (state, input, i); - // TODO: if (state == STATE_ERROR) { ... } - } - - state = apply_transition (state, INPUT_AE, primitives_.size ()); - // TODO: if (state == STATE_ERROR) { ... } -} - -void set_delta_pitch (Item *primitive, Grob_info info1, Grob_info info2) -{ - Note_req *nr1 = dynamic_cast (info1.music_cause ()); - Note_req *nr2 = dynamic_cast (info2.music_cause ()); - Pitch pitch1 = *unsmob_pitch (nr1->get_mus_property ("pitch")); - Pitch pitch2 = *unsmob_pitch (nr2->get_mus_property ("pitch")); - int delta_pitch = (pitch2.steps () - pitch1.steps ()); - primitive->set_grob_property ("delta-pitch", gh_int2scm (delta_pitch)); } /* - * A MensuralLigature grob consists of a bunch of LigatureHead grobs - * that are glued together. It (a) does make sense to change + * A MensuralLigature grob consists of a bunch of NoteHead grobs that + * are glued together. It (a) does not make sense to change * properties like thickness or flexa-width from one head to the next * within a ligature (this would totally screw up alignment), and (b) * some of these properties (like flexa-width) are specific to * e.g. the MensuralLigature (as in contrast to e.g. LigatureBracket), - * and therefore should not be handled in the generic LigatureHead - * (which is also used by LigatureBracket). Therefore, we let the - * user control these properties via the concrete Ligature grob (like + * and therefore should not be handled in the NoteHead code (which is + * also used by LigatureBracket). Therefore, we let the user control + * these properties via the concrete Ligature grob (like * MensuralLigature) and then copy these properties as necessary to - * each of the LigatureHead grobs. This is what - * propagate_properties() does. + * each of the NoteHead grobs. This is what + * propagate_properties () does. */ void -Mensural_ligature_engraver::propagate_properties () +Mensural_ligature_engraver::propagate_properties (Spanner *ligature, + vector primitives) { - SCM thickness_scm = - finished_ligature_->get_grob_property ("thickness"); - Real thickness = (thickness_scm != SCM_EOL) ? - gh_scm2double (thickness_scm) : 1.4; - thickness *= finished_ligature_->get_paper ()->get_var ("linethickness"); - - Real head_width = - Font_interface::get_default_font (finished_ligature_)-> - find_by_name ("noteheads--1mensural").extent (X_AXIS).length (); - SCM flexa_width_scm = - finished_ligature_->get_grob_property ("flexa-width"); - Real flexa_width = (flexa_width_scm != SCM_EOL) ? - gh_scm2double (flexa_width_scm) : 2.0; - flexa_width *= Staff_symbol_referencer::staff_space (finished_ligature_); + 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_); - int output = gh_scm2int (primitive->get_grob_property ("primitive")); - primitive->set_grob_property ("thickness", - gh_double2scm (thickness)); - switch (output) { + Item *primitive = dynamic_cast (primitives[i].grob ()); + int output = scm_to_int (primitive->get_property ("primitive")); + primitive->set_property ("thickness", + scm_from_double (thickness)); + + switch (output & MLP_ANY) + { case MLP_NONE: - primitive->set_grob_property ("head-width", - gh_double2scm (half_flexa_width)); + 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_sc: - case MLP_ss: - case MLP_cs: - primitive->set_grob_property ("head-width", - gh_double2scm (head_width)); + case MLP_MAXIMA: + primitive->set_property ("head-width", + scm_from_double (maxima_head_width)); break; - case MLP_BB: - case MLP_LB: - case MLP_SS: - primitive->set_grob_property ("head-width", - gh_double2scm (half_flexa_width)); - primitive->set_grob_property ("flexa-width", - gh_double2scm (flexa_width)); - set_delta_pitch (primitive, - primitives_[i], primitives_[i+1]); + 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")); + programming_error (_ ("unexpected case fall-through")); break; - } + } } } void -Mensural_ligature_engraver::fold_up_primitives () +Mensural_ligature_engraver::fold_up_primitives (vector primitives) { Item *first = 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 ()); - - if (i > 0) - { -#if 0 - Rod r; - r.distance_ = distance_; - r.item_l_drul_[LEFT] = first; - r.item_l_drul_[RIGHT] = current; - r.add_to_cols (); -#endif - current->translate_axis (distance_, X_AXIS); - } + move_related_items_to_column (current, first->get_column (), + distance); - distance_ += - gh_scm2double (current->get_grob_property ("head-width")) - - gh_scm2double (current->get_grob_property ("thickness")); - } -} + distance + += scm_to_double (current->get_property ("head-width")) + - scm_to_double (current->get_property ("thickness")); -void -Mensural_ligature_engraver::join_primitives () -{ - Pitch last_pitch; - for (int i = 0; i < primitives_.size (); i++) - { - Grob_info info = primitives_[i]; - Note_req *nr = dynamic_cast (info.music_cause ()); - Pitch pitch = *unsmob_pitch (nr->get_mus_property ("pitch")); - if (i > 0) - { - Item *primitive = dynamic_cast (info.grob_); - int output = gh_scm2int (primitive->get_grob_property ("primitive")); - if (output & MLP_ANY) + if (Rhythmic_head::dot_count (current) > 0) + // Move dots above/behind the ligature. + { + if (i + 1 < primitives.size ()) + // dot in the midst => move above head { - int delta_pitch = (pitch.steps () - last_pitch.steps ()); - primitive->set_grob_property ("join-left", - gh_int2scm (delta_pitch)); + // 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); } } - last_pitch = pitch; } } void -Mensural_ligature_engraver::try_stop_ligature () +Mensural_ligature_engraver::build_ligature (Spanner *ligature, + vector primitives) { - if (finished_ligature_) - { - transform_heads (); - propagate_properties (); - fold_up_primitives (); - join_primitives (); + transform_heads (primitives); + propagate_properties (ligature, primitives); + fold_up_primitives (primitives); +} - for (int i = 0; i < primitives_.size (); i++) - { - typeset_grob (primitives_[i].grob_); - } +ADD_ACKNOWLEDGER (Mensural_ligature_engraver, rest); +ADD_ACKNOWLEDGER (Mensural_ligature_engraver, note_head); - primitives_.clear (); - finished_ligature_ = 0; - } -} +ADD_TRANSLATOR (Mensural_ligature_engraver, + /* doc */ + "Handle @code{Mensural_ligature_events} by glueing special" + " ligature heads together.", -void -Mensural_ligature_engraver::acknowledge_grob (Grob_info info) -{ - Ligature_engraver::acknowledge_grob (info); - if (ligature_) - { - if (Note_head::has_interface (info.grob_)) - { - primitives_.push (info); - } - } -} + /* create */ + "MensuralLigature ", + + /* read */ + "", -ENTER_DESCRIPTION (Mensural_ligature_engraver, -/* descr */ "Handles Mensural_ligature_requests by glueing special ligature heads together.", -/* creats*/ "MensuralLigature", -/* acks */ "ligature-head-interface note-head-interface rest-interface", -/* reads */ "", -/* write */ ""); + /* write */ + "" + );