From: Jürgen Reuter Date: Mon, 21 Feb 2005 01:05:47 +0000 (+0000) Subject: * lily/mensural-ligature-engraver.cc: X-Git-Tag: release/2.5.14~106 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=5af8aac76ee3c9934d1721f9ea939820c326b839;p=lilypond.git * lily/mensural-ligature-engraver.cc: * lily/mensural-ligature.cc: * lily/include/mensural-ligature.hh: new algorithm implemented * scm/define-grob-properties.scm: join-left killed (add-join may be used); join-left-amount changed to join-right-amount --- diff --git a/ChangeLog b/ChangeLog index f250bc759b..37b6f8529e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2005-02-21 Pal Benko + + * lily/mensural-ligature-engraver.cc: + * lily/mensural-ligature.cc: + * lily/include/mensural-ligature.hh: new algorithm implemented + + * scm/define-grob-properties.scm: join-left killed (add-join may + be used); join-left-amount changed to join-right-amount + 2005-02-21 Graham Percival * Documentation/user/notation.itely: small but urgent fix. diff --git a/lily/include/mensural-ligature.hh b/lily/include/mensural-ligature.hh index ea92631124..2626134ecd 100644 --- a/lily/include/mensural-ligature.hh +++ b/lily/include/mensural-ligature.hh @@ -3,7 +3,8 @@ source file of the GNU LilyPond music typesetter - (c) 2002--2005 Juergen Reuter + (c) 2002--2005 Juergen Reuter , + Pal Benko */ #ifndef MENSURAL_LIGATURE_HH @@ -15,19 +16,17 @@ /* * These are all possible mensural ligature primitives. */ -#define MLP_BB 0x01 // flexa with left downward cauda (for Brevis-Brevis) -#define MLP_sc 0x02 // last head of asc. sine proprietate cum perfectione - // (i.e. brevis head with downward right cauda) -#define MLP_ss 0x04 // last head of asc. sine proprietate sine perfectione - // (i.e. pure brevis head) -#define MLP_cs 0x08 // last head of asc. cum proprietate sine perfectione - // (i.e. brevis head with downward left cauda) -#define MLP_SS 0x10 // flexa with left upward cauda (for Semibr.-Semibr.) -#define MLP_LB 0x20 // core flexa (for Longa-Brevis) - -#define MLP_NONE 0x00 // no output -#define MLP_SINGLE_HEAD (MLP_sc | MLP_ss | MLP_cs) -#define MLP_FLEXA (MLP_BB | MLP_SS | MLP_LB) + +#define MLP_NONE 0x00 // no output +#define MLP_UP 0x01 // ligatura ascendens +#define MLP_DOWN 0x02 // ligatura descendens +#define MLP_BREVIS 0x04 // mensural brevis head +#define MLP_LONGA 0x08 // mensural brevis head with right cauda +#define MLP_MAXIMA 0x10 // mensural maxima head without stem +#define MLP_FLEXA 0x20 + +#define MLP_STEM (MLP_UP | MLP_DOWN) +#define MLP_SINGLE_HEAD (MLP_BREVIS | MLP_LONGA | MLP_MAXIMA) #define MLP_ANY (MLP_FLEXA | MLP_SINGLE_HEAD) struct Mensural_ligature diff --git a/lily/mensural-ligature-engraver.cc b/lily/mensural-ligature-engraver.cc index 33d42b17d2..27327e12bf 100644 --- a/lily/mensural-ligature-engraver.cc +++ b/lily/mensural-ligature-engraver.cc @@ -3,7 +3,8 @@ source file of the GNU LilyPond music typesetter - (c) 2002--2005 Juergen Reuter + (c) 2002--2005 Juergen Reuter , + Pal Benko */ #include "coherent-ligature-engraver.hh" @@ -21,30 +22,25 @@ #include "font-interface.hh" /* - * 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: accidentals are aligned with the first note; + * they must appear ahead. * - * 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: prohibit ligatures having notes differing only in accidentals + * (like \[ a\breve g as \]) * - * TODO: dotted heads: when applying Franco of Cologne's mapping, put - * dots *above* (rather than after) affected ligature heads. + * TODO: dotted heads: avoid next note colliding with the dot, e.g. by + * putting it *above* (rather than after) the affected ligature head. * - * TODO: prohibit multiple voices within a ligature. + * 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 - * user events for a ligature that contains a minima or STATE_ERROR - * is reached), automatically break the ligature into smaller, valid - * pieces. + * 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 Coherent_ligature_engraver { @@ -56,12 +52,9 @@ public: TRANSLATOR_DECLARATIONS (Mensural_ligature_engraver); private: - int apply_transition (Array primitives, - int state, int input, int i); void transform_heads (Array primitives); void propagate_properties (Spanner *ligature, Array primitives); void fold_up_primitives (Array primitives); - void join_primitives (Array primitives); }; @@ -75,181 +68,6 @@ Mensural_ligature_engraver::create_ligature_spanner () return make_spanner ("MensuralLigature", SCM_EOL); } -/* - * The following lines implement a finite state automat. Given a - * sequence of durations (Longa, Brevis, Semibrevis) or - * end-of-ligature-event as input, the automat outputs a sequence of - * events for grobs that form a proper ligature. - */ - -/* - * 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, -}; - -/* - * 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, -}; - -/* - * 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 -}; - -/* - * 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 -}; - -int -Mensural_ligature_engraver::apply_transition (Array primitives, - 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_property ("primitive", scm_int2num (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_property ("primitive", scm_int2num (output)); - primitive->set_property ("primitive", scm_int2num (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_property ("primitive", scm_int2num (output)); - last_primitive->set_property ("primitive", scm_int2num (MLP_NONE)); - break; - default: - programming_error (_f ("unexpected case fall-through")); - break; - } - return transition_state[state][input]; -} - void Mensural_ligature_engraver::transform_heads (Array primitives) { @@ -258,70 +76,206 @@ Mensural_ligature_engraver::transform_heads (Array primitives) warning (_f ("ligature with less than 2 heads -> skipping")); return; } - 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; + int prev_pitch = 0; + bool at_beginning = true; + + // 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 (int i = 0, s = primitives.size (); i < s; i++) { Grob_info info = primitives[i]; - int duration_log = - Note_head::get_balltype (dynamic_cast (info.grob_)); + 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? - */ + ugh. why not simply check for pitch? + */ if (!nr->is_mus_type ("note-event")) { - info.music_cause ()->origin ()->warning (_f ("can not determine pitch of ligature primitive -> skipping")); - i++; - state = STATE_START; - have_pitch = 0; + 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 { - pitch = *unsmob_pitch (nr->get_property ("pitch")); - have_pitch = 1; + 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; } - int delta_pitch; + // apply_transition replacement begins + bool general_case = true; - if (!have_last_pitch) + // first check special cases + // 1. beginning + if (at_beginning) { - delta_pitch = 0; // first pitch; delta undefined + // 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; + } } - else + // 2. initial semibrevis must be followed by another one + else if (prev_semibrevis) { - delta_pitch = (pitch.steps () - last_pitch.steps ()); - if (Pitch::compare (last_pitch, pitch) == 0) + prev_semibrevis = false; + if (duration_log == 0) { - info.music_cause ()->origin ()->warning (_f ("prime interval within ligature -> skipping")); - i++; - state = STATE_START; - have_pitch = 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; } } - - if ((duration_log < -2) || (duration_log > 0)) + // 3. semibreves are otherwise not allowed + else if (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; + 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 + } - int input = (duration_log + 2) * 2 + ((delta_pitch < 0) ? 1 : 0); - state = apply_transition (primitives, state, input, i); - // TODO: if (state == STATE_ERROR) { ... } - } + 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; + } - state = apply_transition (primitives, state, INPUT_AE, primitives.size ()); - // TODO: if (state == STATE_ERROR) { ... } + // 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 + } } /* @@ -342,13 +296,20 @@ void Mensural_ligature_engraver::propagate_properties (Spanner *ligature, Array primitives) { - Real thickness = robust_scm2double (ligature->get_property ("thickness"), 1.4); - thickness *= ligature->get_layout ()->get_dimension (ly_symbol2scm ("linethickness")); + 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 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 (); + flexa_width *= Staff_symbol_referencer::staff_space (ligature); Real half_flexa_width = 0.5 * (flexa_width + thickness); @@ -358,29 +319,31 @@ Mensural_ligature_engraver::propagate_properties (Spanner *ligature, 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) { - case MLP_NONE: - primitive->set_property ("head-width", - scm_make_real (half_flexa_width)); - break; - case MLP_sc: - case MLP_ss: - case MLP_cs: - primitive->set_property ("head-width", - scm_make_real (head_width)); - break; - case MLP_BB: - case MLP_LB: - case MLP_SS: - 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_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; } } } @@ -411,29 +374,6 @@ Mensural_ligature_engraver::fold_up_primitives (Array primitives) } } -void -Mensural_ligature_engraver::join_primitives (Array primitives) -{ - Pitch last_pitch; - for (int i = 0; i < primitives.size (); i++) - { - Grob_info info = primitives[i]; - Pitch pitch = *unsmob_pitch (info.music_cause ()->get_property ("pitch")); - if (i > 0) - { - Item *primitive = dynamic_cast (info.grob_); - int output = scm_to_int (primitive->get_property ("primitive")); - if (output & MLP_ANY) - { - int delta_pitch = (pitch.steps () - last_pitch.steps ()); - primitive->set_property ("join-left-amount", - scm_int2num (delta_pitch)); - } - } - last_pitch = pitch; - } -} - void Mensural_ligature_engraver::build_ligature (Spanner *ligature, Array primitives) @@ -441,7 +381,6 @@ Mensural_ligature_engraver::build_ligature (Spanner *ligature, transform_heads (primitives); propagate_properties (ligature, primitives); fold_up_primitives (primitives); - join_primitives (primitives); } ADD_TRANSLATOR (Mensural_ligature_engraver, diff --git a/lily/mensural-ligature.cc b/lily/mensural-ligature.cc index 0375d5d1aa..781c5b3cc3 100644 --- a/lily/mensural-ligature.cc +++ b/lily/mensural-ligature.cc @@ -3,7 +3,8 @@ source file of the GNU LilyPond music typesetter - (c) 2002--2005 Juergen Reuter + (c) 2002--2005 Juergen Reuter , + Pal Benko */ #include "mensural-ligature.hh" @@ -28,41 +29,12 @@ brew_flexa (Grob *me, Real interval, bool solid, Real width, - Real thickness, - bool add_cauda, - Direction cauda_direction) + Real thickness) { Real staff_space = Staff_symbol_referencer::staff_space (me); Real height = 0.6 * staff_space; Stencil stencil; - if (add_cauda) - { - bool consider_interval = - cauda_direction * interval > 0.0; - - Interval cauda_box_x (0, thickness); - Interval cauda_box_y; - - if (consider_interval) - { - Real y_length = max (interval/2.0*staff_space, 1.2*staff_space); - cauda_box_y = Interval (0, y_length); - } - else - cauda_box_y = Interval (0, staff_space); - - Real y_correction = - (cauda_direction == UP) ? - +0.5*height : - -0.5*height - cauda_box_y.length (); - - Box cauda_box (cauda_box_x, cauda_box_y); - Stencil cauda = Lookup::filled_box (cauda_box); - cauda.translate_axis (y_correction, Y_AXIS); - stencil.add_stencil (cauda); - } - Real slope = (interval / 2.0 * staff_space) / width; // Compensate optical illusion regarding vertical position of left @@ -71,7 +43,7 @@ brew_flexa (Grob *me, Real slope_correction = 0.2*staff_space * sign (slope); Real corrected_slope = slope + slope_correction/width; - if (solid) + if (solid) // this will come handy for colorated flexae { Stencil solid_head = Lookup::beam (corrected_slope, width, height, 0.0); @@ -109,83 +81,118 @@ internal_brew_primitive (Grob *me) SCM primitive_scm = me->get_property ("primitive"); if (primitive_scm == SCM_EOL) { - programming_error ("Mensural_ligature:" + programming_error ("Mensural_ligature: " "undefined primitive -> ignoring grob"); return Stencil (); } + int primitive = scm_to_int (primitive_scm); Stencil out; - int primitive = scm_to_int (primitive_scm); int delta_pitch = 0; Real thickness = 0.0; - Real flexa_width = 0.0; + Real width = 0.0; Real staff_space = Staff_symbol_referencer::staff_space (me); if (primitive & MLP_ANY) { - thickness = robust_scm2double ( me->get_property ("thickness"), .14); + thickness = robust_scm2double (me->get_property ("thickness"), .14); } if (primitive & MLP_FLEXA) { delta_pitch = robust_scm2int (me->get_property ("delta-pitch"), 0); - - flexa_width = robust_scm2double (me->get_property ("flexa-width"), 2.0 * staff_space); + width = + robust_scm2double (me->get_property ("flexa-width"), 2.0 * staff_space); + } + if (primitive & MLP_SINGLE_HEAD) + { + width = robust_scm2double (me->get_property ("head-width"), staff_space); } - switch (primitive) + switch (primitive & MLP_ANY) { - case MLP_NONE: - return Stencil (); - case MLP_BB: - out = brew_flexa (me, delta_pitch, false, - flexa_width, thickness, true, DOWN); - break; - case MLP_sc: // mensural brevis head with right cauda - out = Font_interface::get_default_font (me)->find_by_name ("noteheads.s-2mensural"); - break; - case MLP_ss: // mensural brevis head - out = Font_interface::get_default_font (me)->find_by_name ("noteheads.s-1mensural"); - break; - case MLP_cs: // mensural brevis head with left cauda - out = Font_interface::get_default_font (me)->find_by_name ("noteheads.slmensural"); - break; - case MLP_SS: - out = brew_flexa (me, delta_pitch, false, - flexa_width, thickness, true, UP); - break; - case MLP_LB: - out = brew_flexa (me, delta_pitch, false, - flexa_width, thickness, false, CENTER); - break; - default: - programming_error (_f ("Mensural_ligature:" - "unexpected case fall-through")); - return Stencil (); + case MLP_NONE: + return Stencil (); + case MLP_LONGA: // mensural brevis head with right cauda + out = Font_interface::get_default_font (me)->find_by_name + ("noteheads.s-2mensural"); + break; + case MLP_BREVIS: // mensural brevis head + out = Font_interface::get_default_font (me)->find_by_name + ("noteheads.s-1mensural"); + break; + case MLP_MAXIMA: // should be mensural maxima head without stem + out = Font_interface::get_default_font (me)->find_by_name + ("noteheads.s-1neomensural"); + break; + case MLP_FLEXA: + out = brew_flexa (me, delta_pitch, false, width, thickness); + break; + default: + programming_error (_f ("Mensural_ligature: " + "unexpected case fall-through")); + return Stencil (); } - SCM join_left_scm = me->get_property ("join-left-amount"); - if (join_left_scm != SCM_EOL) + Real blotdiameter = + (me->get_layout ()->get_dimension (ly_symbol2scm ("blotdiameter"))); + + if (primitive & MLP_STEM) { - int join_left = scm_to_int (join_left_scm); - if (!join_left) - programming_error (_f ("Mensural_ligature: (join_left == 0)")); - Real blotdiameter = (me->get_layout ()->get_dimension (ly_symbol2scm ("blotdiameter"))); - Interval x_extent = Interval (0, thickness); - Interval y_extent = (join_left > 0) ? - Interval (-join_left * 0.5 * staff_space, 0) : - Interval (0, -join_left * 0.5 * staff_space); + // assume MLP_UP + Real y_bottom = 0.0, y_top = 3.0 * staff_space; + + if (primitive & MLP_DOWN) + { + y_bottom = -y_top; + y_top = 0.0; + } + + Interval x_extent (0, thickness); + Interval y_extent (y_bottom, y_top); Box join_box (x_extent, y_extent); Stencil join = Lookup::round_filled_box (join_box, blotdiameter); out.add_stencil (join); } + SCM join_right_scm = me->get_property ("join-right-amount"); + + if (join_right_scm != SCM_EOL) + { + int join_right = scm_to_int (join_right_scm); + if (join_right) + { + Real y_top = join_right * 0.5 * staff_space; + Real y_bottom = 0.0; + + if (y_top < 0.0) + { + y_bottom = y_top; + y_top = 0.0; + } + + Interval x_extent (width - thickness, width); + Interval y_extent (y_bottom, y_top); + Box join_box (x_extent, y_extent); + Stencil join = Lookup::round_filled_box (join_box, blotdiameter); + + out.add_stencil (join); + } + else + { + programming_error (_f ("Mensural_ligature: (join_right == 0)")); + } + } + +#if 0 // what happend with the ledger lines? int pos = Staff_symbol_referencer::get_rounded_position (me); if (primitive & MLP_FLEXA) { pos += delta_pitch; + add_ledger_lines(me, &out, pos, 0.5*delta_pitch, ledger_take_space); } +#endif return out; } @@ -207,5 +214,5 @@ Mensural_ligature::print (SCM) ADD_INTERFACE (Mensural_ligature, "mensural-ligature-interface", "A mensural ligature", - "delta-pitch flexa-width head-width join-left join-left-amount " + "delta-pitch flexa-width head-width join-right-amount " // "add-join " "ligature-primitive-callback primitive thickness"); diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index 23fb59f287..f723e580b0 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -600,8 +600,9 @@ debugging") (position-callbacks ,list? "list of functions set spanner positions.") -;;; Junk me, replace it by add-join. - (join-left-amount ,number? "") +;;; add-join would be enough if in Mensural_ligature::brew_ligature_primitive +;;; the next note could be seen + (join-right-amount ,number? "") (delta-pitch ,number? "the interval between this and the next note, or, more precisely, their vertical distance; this is used in ligatures for calculation of the height of vertical joins flexa shapes") (head-width ,ly:dimension? "width of this ligature head") @@ -642,7 +643,6 @@ than a whole rest.") (add-stem ,boolean? "is this ligature head a virga and therefore needs an additional stem on the right side?") - (join-left ,boolean? "is this ligature head joined with the previous one by a vertical line?") (context-info ,integer? "DOCME") (inclinatum ,boolean? "is this neume an inclinatum?") (oriscus ,boolean? "is this neume an oriscus?")