From 21497b2395aa5cc9c7b53942802763d49202a646 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Tue, 7 Jan 2003 20:07:26 +0000 Subject: [PATCH] Juergens patch. --- ChangeLog | 44 +++ input/test/vaticana.ly | 79 +++++ lily/custos-engraver.cc | 9 +- lily/gregorian-ligature-engraver.cc | 309 ++++++++++++++++++ lily/include/gregorian-ligature-engraver.hh | 31 ++ lily/include/gregorian-ligature.hh | 44 +++ lily/include/ligature-engraver.hh | 23 +- lily/include/vaticana-ligature.hh | 22 ++ lily/ligature-bracket-engraver.cc | 12 +- lily/ligature-engraver.cc | 48 ++- lily/mensural-ligature-engraver.cc | 122 +++----- lily/mensural-ligature.cc | 23 +- lily/note-heads-engraver.cc | 9 + lily/vaticana-ligature-engraver.cc | 328 ++++++++++++++++++++ lily/vaticana-ligature.cc | 296 ++++++++++++++++++ ly/gregorian-init.ly | 32 ++ mf/parmesan-heads.mf | 7 +- scm/grob-description.scm | 10 + scm/grob-property-description.scm | 11 + 19 files changed, 1350 insertions(+), 109 deletions(-) create mode 100644 input/test/vaticana.ly create mode 100644 lily/gregorian-ligature-engraver.cc create mode 100644 lily/include/gregorian-ligature-engraver.hh create mode 100644 lily/include/gregorian-ligature.hh create mode 100644 lily/include/vaticana-ligature.hh create mode 100644 lily/vaticana-ligature-engraver.cc create mode 100644 lily/vaticana-ligature.cc create mode 100644 ly/gregorian-init.ly diff --git a/ChangeLog b/ChangeLog index 9e67424fe9..556f19ed2d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,47 @@ + +2003-01-07 Juergen Reuter + + * lily/custos-engraver.cc: added TODO; editorial changes + + * lily/ligature-bracket-engraver.cc: added typeset_ligature () for + consistency with other ligature engravers; removed access on (now) + private variable _ligature of Ligature_engraver + + * lily/include/ligature-engraver.hh, lily/ligature-engraver.cc: + renamed last_bound -> last_bound_; issue programming_error on + abstract method calls; moved primitives code from + Mensural_bracket_engraver to here (because almost ligature + engravers will need it); renamed try_stop_ligature () -> + typeset_ligature (); bugfix: store primitives of finished ligature + in separate variable finished_primitives_ to avoid clash on input + like "\] \["; added current_ligature () to enable access on + private variable _ligature + + * lily/mensural-ligature-engraver.cc: removed primitives_ code + (now in super class); renamed try_stop_ligature () -> + typeset_ligature () + + * lily/mensural-ligature.cc: issue programming_error on abstract + method calls + + * lily/note-heads-engraver.cc: added TODO comment upon bool + in_ligature + + * mf/parmesan-heads.mf: fixed some of set_char_box() + + * scm/grob-description.scm: added VaticanaLigature + + * scm/grob-property-description.scm: added neume prefix properties + + * ly/gregorian-init.ly, lily/gregorian-ligature-engraver.cc, + lily/include/gregorian-ligature-engraver.hh, + lily/include/gregorian-ligature.hh: new files; framework for + gregorian ligature engravers such as vaticana + + * lily/vaticana-ligature.cc, lily/vaticana-ligature-engraver.cc, + lily/include/vaticana-ligature.hh, input/test/vaticana.ly: + vaticana style ligature implementation (still somewhat uncomplete) + 2003-01-07 Han-Wen Nienhuys * VERSION: release 1.7.11 diff --git a/input/test/vaticana.ly b/input/test/vaticana.ly new file mode 100644 index 0000000000..ae51e7f075 --- /dev/null +++ b/input/test/vaticana.ly @@ -0,0 +1,79 @@ +\version "1.7.10" +\header { + title = "vaticana ligature test" + date = "2003" +} + +\include "paper26.ly" +\include "gregorian-init.ly" + +% +% FIXME: custodes and clefs do not show on all staves +% FIXME: some set_char_box() definitions seem to be bad +% + +cantus = \notes \relative c { + \clef "vaticana_fa2" + \[ f \quilisma g \auctum \descendens a \] + \[ \virga a g \pes a \inclinatum f \inclinatum d + c \pes d \quilisma e \pes f \virga g + a \flexa f \pes g \inclinatum f \inclinatum e \] + \[ d \quilisma e f \flexa e \pes f \] + \[ e \flexa d \] +} + +verba = \context Lyrics = verba \lyrics { + Al-4*3 le-4*15 lu-4*5 ia.4*2 +} + +\score { + \context VaticanaStaff < + \context VaticanaVoice < + \cantus + \verba + > + > + \paper { + stafflinethickness = \staffspace / 5.0 + linewidth = 15.0 \cm +% +% FIXME: ragged-right alignment is currently broken +% width = 15.0 \cm +% raggedright = ##t +% + \translator { + \VoiceContext + \name VaticanaVoice + \alias Voice + \remove Ligature_bracket_engraver + \consists Vaticana_ligature_engraver + NoteHead \set #'style = #'vaticana_punctum + Stem \set #'transparent = ##t + } + \translator { + \StaffContext + \name VaticanaStaff + \alias Staff + \accepts VaticanaVoice + \remove Bar_engraver + \consists Custos_engraver + StaffSymbol \set #'line-count = #4 + TimeSignature \set #'transparent = ##t + KeySignature \set #'style = #'vaticana + Accidental \set #'style = #'vaticana + Custos \set #'style = #'vaticana + Custos \set #'neutral-position = #3 + Custos \set #'neutral-direction = #-1 + Custos \set #'adjust-if-on-staffline = ##t + } + \translator { + \HaraKiriStaffContext + \accepts VaticanaVoice + } + \translator { + \ScoreContext + \accepts VaticanaStaff + \remove Bar_number_engraver + } + } +} diff --git a/lily/custos-engraver.cc b/lily/custos-engraver.cc index d2b16c0afe..fd7c315a2d 100644 --- a/lily/custos-engraver.cc +++ b/lily/custos-engraver.cc @@ -3,7 +3,7 @@ source file of the GNU LilyPond music typesetter - (C) 2000 Juergen Reuter , + (C) 2000 Juergen Reuter , Han-Wen Nienhuys @@ -18,8 +18,11 @@ #include "event.hh" /* - This class implements an engraver for custos symbols. -*/ + * This class implements an engraver for custos symbols. + * + * FIXME: note heads inside of ligatures (i.e. ligature heads) are + * sometimes not recognized by this engraver. --jr + */ class Custos_engraver : public Engraver { public: diff --git a/lily/gregorian-ligature-engraver.cc b/lily/gregorian-ligature-engraver.cc new file mode 100644 index 0000000000..f3ba8b9ba4 --- /dev/null +++ b/lily/gregorian-ligature-engraver.cc @@ -0,0 +1,309 @@ +/* + gregorian-ligature-engraver.cc -- implement Gregorian_ligature_engraver + + source file of the GNU LilyPond music typesetter + + (C) 2003 Juergen Reuter + */ + +#include "gregorian-ligature-engraver.hh" +#include "gregorian-ligature.hh" +#include "item.hh" +#include "warn.hh" +#include "staff-symbol-referencer.hh" +#include "spanner.hh" +#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. + */ + +Gregorian_ligature_engraver::Gregorian_ligature_engraver () +{ + porrectus_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) +{ + if (m->is_mus_type ("porrectus-event")) + { + porrectus_req_ = m; + return true; + } + else + 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) +{ + bool current = *current_set & mask; + bool min = min_set & mask; + bool max = max_set & mask; + if (max < min) + { + programming_error ("min_set > max_set"); + return; + } + if (min && !current) + { + primitive->warning (_f ("\\%s ignored", name)); + *current_set &= ~mask; + } + if (!max && current) + { + primitive->warning (_f ("implied \\%s added", name)); + *current_set |= mask; + } +} + +void fix_prefix_set (int *current_set, int min_set, int max_set, Grob *primitive) +{ + fix_prefix ("virga", VIRGA, current_set, min_set, max_set, primitive); + fix_prefix ("stropha", STROPHA, current_set, min_set, max_set, primitive); + fix_prefix ("inclinatum", INCLINATUM, current_set, min_set, max_set, primitive); + fix_prefix ("auctum", AUCTUM, current_set, min_set, max_set, primitive); + fix_prefix ("descendens", DESCENDENS, current_set, min_set, max_set, primitive); + fix_prefix ("ascendens", ASCENDENS, current_set, min_set, max_set, primitive); + fix_prefix ("oriscus", ORISCUS, current_set, min_set, max_set, primitive); + fix_prefix ("quilisma", QUILISMA, current_set, min_set, max_set, primitive); + fix_prefix ("deminutus", DEMINUTUM, current_set, min_set, max_set, primitive); + fix_prefix ("semivocalis", SEMIVOCALIS, current_set, min_set, max_set, primitive); + fix_prefix ("cavum", CAVUM, current_set, min_set, max_set, primitive); + fix_prefix ("linea", LINEA, current_set, min_set, max_set, primitive); + fix_prefix ("pes_or_flexa", LINEA, current_set, min_set, max_set, primitive); +} + +void check_and_fix_all_prefixes (Array primitives) +{ + /* Check for illegal head modifier combinations */ + for (int i = 0; i < primitives.size(); i++) { + Grob *primitive = primitives[i].grob_; + + /* compute head prefix set by inspecting primitive grob properties */ + int prefix_set = + (VIRGA * to_boolean (primitive->get_grob_property ("virga"))) | + (STROPHA * to_boolean (primitive->get_grob_property ("stropha"))) | + (INCLINATUM * to_boolean (primitive->get_grob_property ("inclinatum"))) | + (AUCTUM * to_boolean (primitive->get_grob_property ("auctum"))) | + (DESCENDENS * to_boolean (primitive->get_grob_property ("descendens"))) | + (ASCENDENS * to_boolean (primitive->get_grob_property ("ascendens"))) | + (ORISCUS * to_boolean (primitive->get_grob_property ("oriscus"))) | + (QUILISMA * to_boolean (primitive->get_grob_property ("quilisma"))) | + (DEMINUTUM * to_boolean (primitive->get_grob_property ("deminutum"))) | + (SEMIVOCALIS * to_boolean (primitive->get_grob_property ("semivocalis"))) | + (CAVUM * to_boolean (primitive->get_grob_property ("cavum"))) | + (LINEA * to_boolean (primitive->get_grob_property ("linea"))) | + (PES_OR_FLEXA * to_boolean (primitive->get_grob_property ("pes-or-flexa"))); + + /* check: ascendens and descendens exclude each other; same with + auctum and diminutum */ + if (prefix_set & DESCENDENS) + { + fix_prefix_set (&prefix_set, + prefix_set & ~ASCENDENS, + prefix_set & ~ASCENDENS, + primitive); + } + if (prefix_set & AUCTUM) + { + fix_prefix_set (&prefix_set, + prefix_set & ~DEMINUTUM, + prefix_set & ~DEMINUTUM, + primitive); + } + + /* check: virga, quilisma and oriscus can not be combined with any + other prefix, but may be part of a pes or flexa */ + if (prefix_set & VIRGA) + { + fix_prefix_set (&prefix_set, + VIRGA, + VIRGA | PES_OR_FLEXA, + primitive); + } + if (prefix_set & QUILISMA) + { + fix_prefix_set (&prefix_set, + QUILISMA, + QUILISMA | PES_OR_FLEXA, + primitive); + } + if (prefix_set & ORISCUS) + { + fix_prefix_set (&prefix_set, + ORISCUS, + ORISCUS | PES_OR_FLEXA, + primitive); + } + + /* check: auctum is the only valid optional prefix for stropha */ + if (prefix_set & STROPHA) + { + fix_prefix_set (&prefix_set, + STROPHA, + STROPHA | AUCTUM, + primitive); + } + + /* check: semivocalis must occur in combination with and only with + pes or flexa */ + if (prefix_set & SEMIVOCALIS) + { + fix_prefix_set (&prefix_set, + SEMIVOCALIS | PES_OR_FLEXA, + SEMIVOCALIS | PES_OR_FLEXA, + primitive); + } + + /* check: inclinatum may be prefixed with auctum or diminutum only */ + if (prefix_set & INCLINATUM) + { + fix_prefix_set (&prefix_set, + INCLINATUM, + INCLINATUM | AUCTUM | DEMINUTUM, + primitive); + } + + /* check: cavum and linea (either or both) may be applied only + upon core punctum */ + if (prefix_set & (CAVUM | LINEA)) + { + fix_prefix_set (&prefix_set, + 0, + CAVUM | LINEA, + primitive); + } + + /* all other combinations should be valid (unless I made a + mistake) */ + + primitive->set_grob_property ("prefix-set", gh_int2scm (prefix_set)); + } +} + +/* + * Marks those heads that participate in a pes or flexa. + */ +void +provide_context_info (Array primitives) +{ + Grob *prev_primitive = 0; + int prev_context_info = 0; + int prev_pitch = 0; + for (int i = 0; i < primitives.size(); i++) { + Grob *primitive = primitives[i].grob_; + Music *music_cause = primitives[i].music_cause (); + int context_info = 0; + int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps (); + int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set")); + + if (prefix_set & PES_OR_FLEXA) + if (pitch > prev_pitch) // pes + { + prev_context_info |= PES_LOWER; + context_info |= PES_UPPER; + } + else if (pitch < prev_pitch) // flexa + { + prev_context_info |= FLEXA_LEFT; + context_info |= FLEXA_RIGHT; + } + else // (pitch == prev_pitch) + { + primitive->warning ("may not apply `\\~' on heads with " + "identical pitch; ignoring `\\~'"); + } + if (prev_primitive) + prev_primitive->set_grob_property ("context-info", + gh_int2scm (prev_context_info)); + prev_primitive = primitive; + prev_context_info = context_info; + prev_pitch = pitch; + } + if (prev_primitive) + prev_primitive->set_grob_property ("context-info", + gh_int2scm (prev_context_info)); +} + +void +Gregorian_ligature_engraver::typeset_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) + transform_heads (ligature, primitives); + + // typeset + for (int i = 0; i < primitives.size (); i++) + { + typeset_grob (primitives[i].grob_); + } +} + +void +Gregorian_ligature_engraver::start_translation_timestep () +{ + Ligature_engraver::start_translation_timestep (); + porrectus_req_ = 0; +} + +ENTER_DESCRIPTION (Gregorian_ligature_engraver, +/* descr */ "This is an abstract class. Subclasses such as Vaticana_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/include/gregorian-ligature-engraver.hh b/lily/include/gregorian-ligature-engraver.hh new file mode 100644 index 0000000000..6d8e9a15e4 --- /dev/null +++ b/lily/include/gregorian-ligature-engraver.hh @@ -0,0 +1,31 @@ +/* + gregorian-ligature-engraver.hh -- declare Gregorian_ligature_engraver + + source file of the GNU LilyPond music typesetter + + (c) 2003 Juergen Reuter + + */ +#ifndef GREGORIAN_LIGATURE_ENGRAVER_HH +#define GREGORIAN_LIGATURE_ENGRAVER_HH + +#include "ligature-engraver.hh" + +class Gregorian_ligature_engraver : public Ligature_engraver +{ + Music *porrectus_req_; + +public: + TRANSLATOR_DECLARATIONS(Gregorian_ligature_engraver); + +protected: + virtual bool try_music (Music *); + virtual void typeset_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/include/gregorian-ligature.hh b/lily/include/gregorian-ligature.hh new file mode 100644 index 0000000000..1d95385398 --- /dev/null +++ b/lily/include/gregorian-ligature.hh @@ -0,0 +1,44 @@ +/* + gregorian-ligature.hh -- part of GNU LilyPond + + source file of the GNU LilyPond music typesetter + + (c) 2003 Juergen Reuter +*/ + +#ifndef GREGORIAN_LIGATURE_HH +#define GREGORIAN_LIGATURE_HH + +/* + * Head prefixes: these bit-mask constants are used to represent + * attributes immediately derived from user input (e.g. by the user + * setting a gregorian ligature grob property or using the "\~" + * keyword). If the according bit of the head prefix value is set, + * the attribute applies for this head. The binary opereator "\~" is + * marked only upon the second head (i.e. the note that comes after + * the operator). + */ +#define VIRGA 0x0001 // attribute "\virga" +#define STROPHA 0x0002 // attribute "\stropha" +#define INCLINATUM 0x0004 // attribute "\inclinatum" +#define AUCTUM 0x0008 // attribute "\auctum" +#define DESCENDENS 0x0010 // attribute "\descendens" +#define ASCENDENS 0x0020 // attribute "\ascendens" +#define ORISCUS 0x0040 // attribute "\oriscus" +#define QUILISMA 0x0080 // attribute "\quilisma" +#define DEMINUTUM 0x0100 // attribute "\deminutum" +#define SEMIVOCALIS 0x0100 // attribute "\semivocalis" +#define CAVUM 0x0200 // attribute "\cavum" +#define LINEA 0x0400 // attribute "\linea" +#define PES_OR_FLEXA 0x0800 // operator "\~" + +/* + * Ligature context info: these attributes are derived from the head + * prefixes by considering the current and the following head. + */ +#define PES_LOWER 0x0001 // this is a head before "\~" in an ascending melody +#define PES_UPPER 0x0002 // this is a head after "\~" in an ascending melody +#define FLEXA_LEFT 0x0004 // this is a head before "\~" in a descending melody +#define FLEXA_RIGHT 0x0008 // this is a head after "\~" in a descending melody + +#endif /* GREGORIAN_LIGATURE_HH */ diff --git a/lily/include/ligature-engraver.hh b/lily/include/ligature-engraver.hh index bc5577a022..db4721c6bc 100644 --- a/lily/include/ligature-engraver.hh +++ b/lily/include/ligature-engraver.hh @@ -6,8 +6,8 @@ (c) 2002 Juergen Reuter */ -#ifndef LIGATUREENGRAVER_HH -#define LIGATUREEENGRAVER_HH +#ifndef LIGATURE_ENGRAVER_HH +#define LIGATURE_ENGRAVER_HH #include "engraver.hh" @@ -21,11 +21,10 @@ protected: virtual void acknowledge_grob (Grob_info); virtual bool try_music (Music*); virtual void process_music (); - virtual void try_stop_ligature (); - virtual Spanner *create_ligature_spanner (); - - Spanner *finished_ligature_; - Spanner *ligature_; + virtual Spanner *create_ligature_spanner (); /* abstract method */ + virtual void typeset_ligature (Spanner *ligature, + Array primitives); /* abstract method */ + virtual Spanner *current_ligature (); SCM brew_ligature_primitive_proc; public: @@ -34,13 +33,19 @@ public: private: Drul_array reqs_drul_; + Spanner *ligature_; + Array primitives_; + + Spanner *finished_ligature_; + Array finished_primitives_; + Music *prev_start_req_; // moment where ligature started. Moment ligature_start_mom_; - Grob *last_bound; + Grob *last_bound_; }; -#endif // ENGRAVERGROUP_HH +#endif // LIGATURE_ENGRAVER_HH diff --git a/lily/include/vaticana-ligature.hh b/lily/include/vaticana-ligature.hh new file mode 100644 index 0000000000..feb965d355 --- /dev/null +++ b/lily/include/vaticana-ligature.hh @@ -0,0 +1,22 @@ +/* + vaticana-ligature.hh + + source file of the GNU LilyPond music typesetter + + (C) 2003 Juergen Reuter +*/ + +#ifndef VATICANA_LIGATURE_HH +#define VATICANA_LIGATURE_HH + +#include "lily-guile.hh" +#include "molecule.hh" + +struct Vaticana_ligature +{ + DECLARE_SCHEME_CALLBACK (brew_ligature_primitive, (SCM )); + DECLARE_SCHEME_CALLBACK (brew_molecule, (SCM )); + static bool has_interface (Grob *); +}; + +#endif // VATICANA_LIGATURE_HH diff --git a/lily/ligature-bracket-engraver.cc b/lily/ligature-bracket-engraver.cc index 6f8d9e97cc..9970886bed 100644 --- a/lily/ligature-bracket-engraver.cc +++ b/lily/ligature-bracket-engraver.cc @@ -17,6 +17,7 @@ class Ligature_bracket_engraver : public Ligature_engraver protected: virtual Spanner *create_ligature_spanner (); virtual void acknowledge_grob (Grob_info); + virtual void typeset_ligature (Spanner *ligature, Array); public: TRANSLATOR_DECLARATIONS(Ligature_bracket_engraver); @@ -36,14 +37,21 @@ Ligature_bracket_engraver::create_ligature_spanner () return new Spanner (get_property ("LigatureBracket")); } +void +Ligature_bracket_engraver::typeset_ligature (Spanner *ligature, Array) +{ + typeset_grob (ligature); +} + void Ligature_bracket_engraver::acknowledge_grob (Grob_info info) { - if (ligature_) + if (current_ligature ()) { if (Note_column::has_interface (info.grob_)) { - Tuplet_bracket::add_column (ligature_, dynamic_cast (info.grob_)); + Tuplet_bracket::add_column (current_ligature (), + dynamic_cast (info.grob_)); } else Ligature_engraver::acknowledge_grob (info); } diff --git a/lily/ligature-engraver.cc b/lily/ligature-engraver.cc index 3344e363a6..53da233be8 100644 --- a/lily/ligature-engraver.cc +++ b/lily/ligature-engraver.cc @@ -10,6 +10,7 @@ #include "ligature-head.hh" #include "spanner.hh" #include "score-engraver.hh" +#include "note-head.hh" #include "rest.hh" #include "warn.hh" @@ -37,7 +38,7 @@ Ligature_engraver::Ligature_engraver () finished_ligature_ = 0; reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0; prev_start_req_ = 0; - last_bound = 0; + last_bound_ = 0; brew_ligature_primitive_proc = SCM_EOL; } @@ -64,6 +65,9 @@ Ligature_engraver::try_music (Music *m) Spanner * Ligature_engraver::create_ligature_spanner () { + programming_error ("Ligature_engraver::create_ligature_spanner (): " + "this is an abstract method that should not be called, " + "but overridden by a subclass"); return 0; } @@ -76,20 +80,22 @@ Ligature_engraver::process_music () reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature")); else { - if (!last_bound) + if (!last_bound_) { reqs_drul_[STOP]->origin ()->warning (_ ("no right bound")); } else { - ligature_->set_bound (RIGHT, last_bound); + ligature_->set_bound (RIGHT, last_bound_); } } prev_start_req_ = 0; + finished_primitives_ = primitives_; finished_ligature_ = ligature_; + primitives_.clear (); ligature_ = 0; } - last_bound = unsmob_grob (get_property ("currentMusicalColumn")); + last_bound_ = unsmob_grob (get_property ("currentMusicalColumn")); if (ligature_) { @@ -137,25 +143,33 @@ Ligature_engraver::start_translation_timestep () } void -Ligature_engraver::try_stop_ligature () +Ligature_engraver::typeset_ligature (Spanner *, Array) { - if (finished_ligature_) - { - typeset_grob (finished_ligature_); - finished_ligature_ = 0; - } + programming_error ("Ligature_engraver::typeset_ligature (): " + "this is an abstract method that should not be called, " + "but overridden by a subclass"); } void Ligature_engraver::stop_translation_timestep () { - try_stop_ligature (); + if (finished_ligature_) + { + typeset_ligature (finished_ligature_, finished_primitives_); + finished_primitives_.clear (); + finished_ligature_ = 0; + } } void Ligature_engraver::finalize () { - try_stop_ligature (); + if (finished_ligature_) + { + typeset_ligature (finished_ligature_, finished_primitives_); + finished_primitives_.clear (); + finished_ligature_ = 0; + } if (ligature_) { prev_start_req_->origin ()->warning (_ ("unterminated ligature")); @@ -163,11 +177,21 @@ Ligature_engraver::finalize () } } +Spanner * +Ligature_engraver::current_ligature () +{ + return ligature_; +} + void Ligature_engraver::acknowledge_grob (Grob_info info) { if (ligature_) { + if (Note_head::has_interface (info.grob_)) + { + primitives_.push (info); + } if (Ligature_head::has_interface (info.grob_)) { info.grob_->set_grob_property ("ligature-primitive-callback", diff --git a/lily/mensural-ligature-engraver.cc b/lily/mensural-ligature-engraver.cc index 436e49bf17..4bedcaf499 100644 --- a/lily/mensural-ligature-engraver.cc +++ b/lily/mensural-ligature-engraver.cc @@ -80,36 +80,33 @@ */ class Mensural_ligature_engraver : public 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 typeset_ligature (Spanner *ligature, + Array primitives); public: 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 (); + 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); void get_set_column (Item *item, Paper_column *new_col); }; Mensural_ligature_engraver::Mensural_ligature_engraver () { - distance_ = 0; } Spanner * Mensural_ligature_engraver::create_ligature_spanner () { - distance_ = 0; return new Spanner (get_property ("MensuralLigature")); } @@ -261,15 +258,16 @@ const int/*output*/ transition_output[/*old state*/][8/*input*/] = }; int -Mensural_ligature_engraver::apply_transition (int state, int input, int i) +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; + 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; + dynamic_cast (primitives[i-1].grob_) : 0; + Item *primitive = (i < primitives.size ()) ? + dynamic_cast (primitives[i].grob_) : 0; switch (output) { case MLP_NONE: @@ -325,9 +323,9 @@ Mensural_ligature_engraver::apply_transition (int state, int input, int i) } void -Mensural_ligature_engraver::transform_heads () +Mensural_ligature_engraver::transform_heads (Array primitives) { - if (primitives_.size () < 2) + if (primitives.size () < 2) { warning (_f ("ligature with less than 2 heads -> skipping")); return; @@ -335,18 +333,16 @@ Mensural_ligature_engraver::transform_heads () int state = STATE_START; Pitch last_pitch, pitch; bool have_last_pitch = 0, have_pitch = 0; - for (int i = 0; i < primitives_.size (); i++) { + for (int i = 0; i < primitives.size (); i++) { last_pitch = pitch; have_last_pitch = have_pitch; - Grob_info info = primitives_[i]; + Grob_info info = primitives[i]; int duration_log = Note_head::get_balltype (dynamic_cast (info.grob_)); - Music * nr = info.music_cause (); + Music *nr = info.music_cause (); - /* - ugh. why not simply check for pitch? */ if (!nr->is_mus_type ("note-event")) @@ -392,11 +388,11 @@ Mensural_ligature_engraver::transform_heads () } int input = (duration_log + 2) * 2 + ((delta_pitch < 0) ? 1 : 0); - state = apply_transition (state, input, i); + state = apply_transition (primitives, state, input, i); // TODO: if (state == STATE_ERROR) { ... } } - state = apply_transition (state, INPUT_AE, primitives_.size ()); + state = apply_transition (primitives, state, INPUT_AE, primitives.size ()); // TODO: if (state == STATE_ERROR) { ... } } @@ -410,7 +406,7 @@ void set_delta_pitch (Item *primitive, Grob_info info1, Grob_info info2) /* * A MensuralLigature grob consists of a bunch of LigatureHead grobs - * that are glued together. It (a) does make sense to change + * 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 @@ -423,28 +419,27 @@ void set_delta_pitch (Item *primitive, Grob_info info1, Grob_info info2) * propagate_properties() does. */ void -Mensural_ligature_engraver::propagate_properties () +Mensural_ligature_engraver::propagate_properties (Spanner *ligature, + Array primitives) { - SCM thickness_scm = - finished_ligature_->get_grob_property ("thickness"); + SCM thickness_scm = 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"); + thickness *= ligature->get_paper ()->get_var ("linethickness"); Real head_width = - Font_interface::get_default_font (finished_ligature_)-> + Font_interface::get_default_font (ligature)-> find_by_name ("noteheads--1mensural").extent (X_AXIS).length (); - SCM flexa_width_scm = - finished_ligature_->get_grob_property ("flexa-width"); + SCM flexa_width_scm = 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_); + 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 (int i = 0; i < primitives.size (); i++) { - Item *primitive = dynamic_cast (primitives_[i].grob_); + Item *primitive = dynamic_cast (primitives[i].grob_); int output = gh_scm2int (primitive->get_grob_property ("primitive")); primitive->set_grob_property ("thickness", gh_double2scm (thickness)); @@ -467,7 +462,7 @@ Mensural_ligature_engraver::propagate_properties () primitive->set_grob_property ("flexa-width", gh_double2scm (flexa_width)); set_delta_pitch (primitive, - primitives_[i], primitives_[i+1]); + primitives[i], primitives[i+1]); break; default: programming_error (_f ("unexpected case fall-through")); @@ -477,12 +472,13 @@ Mensural_ligature_engraver::propagate_properties () } void -Mensural_ligature_engraver::fold_up_primitives () +Mensural_ligature_engraver::fold_up_primitives (Array primitives) { Item *first = 0; - for (int i = 0; i < primitives_.size (); i++) + Real distance = 0; + for (int 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; @@ -494,27 +490,27 @@ Mensural_ligature_engraver::fold_up_primitives () { #if 0 Rod r; - r.distance_ = distance_; + 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); + current->translate_axis (distance, X_AXIS); } - distance_ += + distance += gh_scm2double (current->get_grob_property ("head-width")) - gh_scm2double (current->get_grob_property ("thickness")); } } void -Mensural_ligature_engraver::join_primitives () +Mensural_ligature_engraver::join_primitives (Array primitives) { Pitch last_pitch; - for (int i = 0; i < primitives_.size (); i++) + for (int i = 0; i < primitives.size (); i++) { - Grob_info info = primitives_[i]; + Grob_info info = primitives[i]; Pitch pitch = *unsmob_pitch (info.music_cause ()->get_mus_property ("pitch")); if (i > 0) { @@ -532,35 +528,17 @@ Mensural_ligature_engraver::join_primitives () } void -Mensural_ligature_engraver::try_stop_ligature () +Mensural_ligature_engraver::typeset_ligature (Spanner *ligature, + Array primitives) { - if (finished_ligature_) - { - transform_heads (); - propagate_properties (); - fold_up_primitives (); - join_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_); - } - - primitives_.clear (); - finished_ligature_ = 0; - } -} - -void -Mensural_ligature_engraver::acknowledge_grob (Grob_info info) -{ - Ligature_engraver::acknowledge_grob (info); - if (ligature_) + for (int i = 0; i < primitives.size (); i++) { - if (Note_head::has_interface (info.grob_)) - { - primitives_.push (info); - } + typeset_grob (primitives[i].grob_); } } diff --git a/lily/mensural-ligature.cc b/lily/mensural-ligature.cc index ab39523488..574a38ca2a 100644 --- a/lily/mensural-ligature.cc +++ b/lily/mensural-ligature.cc @@ -130,7 +130,8 @@ internal_brew_primitive (Grob *me, bool ledger_take_space) SCM primitive_scm = me->get_grob_property ("primitive"); if (primitive_scm == SCM_EOL) { - programming_error ("Mensural_ligature: undefined primitive -> ignoring grob"); + programming_error ("Mensural_ligature:" + "undefined primitive -> ignoring grob"); return Molecule (); } @@ -149,7 +150,9 @@ internal_brew_primitive (Grob *me, bool ledger_take_space) } else { - programming_error (_f ("Mensural_ligature: thickness undefined on flexa %d; assuming 1.4", primitive)); + programming_error (_f ("Mensural_ligature:" + "thickness undefined on flexa %d; assuming 1.4", + primitive)); thickness = 1.4 * me->get_paper ()->get_var ("linethickness"); } } @@ -163,7 +166,9 @@ internal_brew_primitive (Grob *me, bool ledger_take_space) } else { - programming_error (_f ("Mensural_ligature: delta-pitch undefined on flexa %d; assuming 0", primitive)); + programming_error (_f ("Mensural_ligature:" + "delta-pitch undefined on flexa %d; assuming 0", + primitive)); delta_pitch = 0; } @@ -174,7 +179,9 @@ internal_brew_primitive (Grob *me, bool ledger_take_space) } else { - programming_error (_f ("Mensural_ligature: flexa-width undefined on flexa %d; assuming 2.0", primitive)); + programming_error (_f ("Mensural_ligature:" + "flexa-width undefined on flexa %d; assuming 2.0", + primitive)); flexa_width = 2.0 * staff_space; } } @@ -205,7 +212,8 @@ internal_brew_primitive (Grob *me, bool ledger_take_space) flexa_width, thickness, false, CENTER); break; default: - programming_error (_f ("Mensural_ligature: unexpected case fall-through")); + programming_error (_f ("Mensural_ligature:" + "unexpected case fall-through")); return Molecule (); } @@ -214,7 +222,7 @@ internal_brew_primitive (Grob *me, bool ledger_take_space) { int join_left = gh_scm2int (join_left_scm); if (!join_left) - programming_error (_f ("Menusral_ligature: (join_left == 0)")); + programming_error (_f ("Mensural_ligature: (join_left == 0)")); Real blotdiameter = (me->get_paper ()->get_var ("blotdiameter")); Interval x_extent = Interval (0, thickness); Interval y_extent = (join_left > 0) ? @@ -222,8 +230,7 @@ internal_brew_primitive (Grob *me, bool ledger_take_space) Interval (0, -join_left * 0.5 * staff_space); Box stem_box (x_extent, y_extent); - Molecule stem = - Lookup::roundfilledbox (stem_box, blotdiameter); + Molecule stem = Lookup::roundfilledbox (stem_box, blotdiameter); out.add_molecule (stem); } diff --git a/lily/note-heads-engraver.cc b/lily/note-heads-engraver.cc index 9d19e641d5..3a388a9c4c 100644 --- a/lily/note-heads-engraver.cc +++ b/lily/note-heads-engraver.cc @@ -18,6 +18,15 @@ /** make balls and rests */ + +/* + * TODO: junk bool in_ligature (and all the messy code around it). + * This can be done by also junking LigatureHead in + * scm/grob-description.scm. Instead, NoteHead should be used + * throughout typesetting of ligatures; ligature-(start/stop)-events + * should simply modify NoteHead properties values of + * molecule-callback ligature-primitive-callback. --jr + */ class Note_heads_engraver : public Engraver { Link_array notes_; diff --git a/lily/vaticana-ligature-engraver.cc b/lily/vaticana-ligature-engraver.cc new file mode 100644 index 0000000000..69bf875c9a --- /dev/null +++ b/lily/vaticana-ligature-engraver.cc @@ -0,0 +1,328 @@ +/* + vaticana-ligature-engraver.cc -- implement Vaticana_ligature_engraver + + source file of the GNU LilyPond music typesetter + + (C) 2003 Juergen Reuter + */ + +#include "gregorian-ligature-engraver.hh" +#include "gregorian-ligature.hh" +#include "vaticana-ligature.hh" +#include "item.hh" +#include "spanner.hh" +#include "staff-symbol-referencer.hh" +#include "font-interface.hh" +#include "warn.hh" +#include "paper-def.hh" + +class Vaticana_ligature_engraver : public Gregorian_ligature_engraver +{ + +private: + Real finish_primitive (Item *first_primitive, + Item *primitive, + int context_info, + String head, + int pitch_delta, + Real flexa_width, + Real join_thickness, + Real distance); + +public: + TRANSLATOR_DECLARATIONS(Vaticana_ligature_engraver); + +protected: + virtual Spanner *create_ligature_spanner (); + virtual void transform_heads (Spanner *ligature, + Array primitives); +}; + +Vaticana_ligature_engraver::Vaticana_ligature_engraver () +{ +} + +Spanner * +Vaticana_ligature_engraver::create_ligature_spanner () +{ + return new Spanner (get_property ("VaticanaLigature")); +} + +Real +Vaticana_ligature_engraver::finish_primitive (Item *first_primitive, + Item *primitive, + int context_info, + String head, + int pitch_delta, + Real flexa_width, + Real join_thickness, + Real distance) +{ + if (primitive) + { + // determine width of previous head and x-shift + Real head_width; + Real x_shift; + bool is_stacked; + is_stacked = context_info & PES_UPPER; + if (context_info & FLEXA_LEFT) + is_stacked = false; + if (!String::compare (head, "vaticana_cephalicus") && + !(context_info & PES_LOWER)) + is_stacked = true; + if (context_info & AUCTUM) + is_stacked = false; + if (is_stacked) + { + /* + * This head is stacked upon another one; hence, it does not + * contribute to the total width of the ligature, hence its + * width is assumed to be 0.0. Moreover, it is shifted to + * the left by its width such that the right side of this + * and the other head are horizontally aligned. + */ + head_width = 0.0; + x_shift = join_thickness - + Font_interface::get_default_font (primitive)-> + find_by_name ("noteheads-" + head).extent (X_AXIS).length (); + } + else if (!String::compare (head, "porrectus") || + !String::compare (head, "")) + { + /* + * This head represents either half of a porrectus shape. + * Hence, it is assigned half the width of this shape. + */ + head_width = 0.5 * flexa_width; + x_shift = 0.0; + } + else // retrieve width from corresponding font + { + head_width = + Font_interface::get_default_font (primitive)-> + find_by_name ("noteheads-" + head).extent (X_AXIS).length (); + x_shift = 0.0; + } + + /* + * Save the head's final shape and x-shift. + */ + primitive->set_grob_property ("ligature-head", + ly_symbol2scm (head.to_str0 ())); + primitive->set_grob_property ("x-shift", + gh_double2scm (x_shift)); + + /* + * If the head is the 2nd head of a pes or flexa (but not a + * porrectus), mark this head to be joined with the left-side + * neighbour head (i.e. the previous head) by a vertical beam. + */ + if ((context_info & PES_UPPER) || + ((context_info & FLEXA_RIGHT) && + !(context_info & PES_LOWER))) + { + primitive->set_grob_property ("join-left", + gh_int2scm (pitch_delta)); + + /* + * Create a small overlap of adjacent heads so that the join + * can be drawn perfectly between them. + */ + distance -= join_thickness; + } + else + { + /* + * Make a small space between adjacent notes of a ligature + * that are not directly joined. + */ + 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; + } + return distance; +} + +void +Vaticana_ligature_engraver::transform_heads (Spanner *ligature, + Array primitives) +{ + Real flexa_width; + SCM flexa_width_scm = ligature->get_grob_property ("flexa-width"); + if (flexa_width_scm != SCM_EOL) + { + flexa_width = gh_scm2double (flexa_width_scm); + } + else + { + programming_error ("Vaticana_ligature_engraver:" + "flexa-width undefined; assuming 2.0 staff space"); + flexa_width = + 2.0 * Staff_symbol_referencer::staff_space (ligature); + } + + Real join_thickness; + SCM join_thickness_scm = ligature->get_grob_property ("thickness"); + if (join_thickness_scm != SCM_EOL) + { + join_thickness = gh_scm2double (join_thickness_scm); + } + else + { + programming_error ("Vaticana_ligature_engraver:" + "thickness undefined; assuming 1.4 linethickness"); + join_thickness = 1.4; + } + join_thickness *= ligature->get_paper ()->get_var ("linethickness"); + + Item *first_primitive = 0; + Item *prev_primitive = 0; + int prev_context_info = 0; + int prev_pitch = 0; + int prev_pitch_delta = 0; + String prev_head = ""; + Real prev_distance = 0.0; + for (int i = 0; i < primitives.size(); i++) { + Item *primitive = dynamic_cast (primitives[i].grob_); + Music *music_cause = primitives[i].music_cause (); + int context_info = gh_scm2int (primitive->get_grob_property ("context-info")); + int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps (); + String head; + if (!first_primitive) + first_primitive = primitive; + int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set")); + + /* + * Now determine which head to typeset (this is context sensitive + * information, since it depends on neighbouring heads; therefore, + * this decision must be made here in the engraver rather than in + * the backend). + */ + if (prefix_set & VIRGA) + head = "vaticana_virga"; + else if (prefix_set & QUILISMA) + head = "vaticana_quilisma"; + else if (prefix_set & ORISCUS) + head = "solesmes_oriscus"; + else if (prefix_set & STROPHA) + if (prefix_set & AUCTUM) + head = "solesmes_stropha_aucta"; + else head = "solesmes_stropha"; + else if (prefix_set & SEMIVOCALIS) + if (pitch > prev_pitch) + head = "vaticana_epiphonus"; + else head = "vaticana_cephalicus"; + else if (prefix_set & INCLINATUM) + if (prefix_set & AUCTUM) + head = "solesmes_incl_auctum"; + else if (prefix_set & DEMINUTUM) + head = "solesmes_incl_parvum"; + else + head = "vaticana_inclinatum"; + else if (prefix_set & (CAVUM | LINEA)) + if ((prefix_set & CAVUM) && (prefix_set & LINEA)) + head = "vaticana_linea_punctum_cavum"; + else if (prefix_set & CAVUM) + head = "vaticana_punctum_cavum"; + else + head = "vaticana_linea_punctum"; + else if (prefix_set & AUCTUM) + if (prefix_set & ASCENDENS) + head = "solesmes_auct_asc"; + else + head = "solesmes_auct_desc"; + else if (prefix_set & DEMINUTUM) + head = "vaticana_plica"; + else if ((prefix_set & PES_OR_FLEXA) && + (context_info & PES_LOWER) && + (context_info & FLEXA_RIGHT)) + head = ""; // second head of porrectus + else if (context_info & PES_UPPER) + if (pitch - prev_pitch > 1) + head = "vaticana_upes"; + else + head = "vaticana_vupes"; + else + head = "vaticana_punctum"; + + /* + * May need updating previous head, depending on the current head. + */ + if (prefix_set & PES_OR_FLEXA) + if ((context_info & PES_LOWER) && + (context_info & FLEXA_RIGHT)) // porrectus + { + prev_head = "porrectus"; + prev_primitive->set_grob_property ("porrectus-height", + gh_int2scm (pitch - prev_pitch)); + prev_primitive->set_grob_property ("porrectus-width", + gh_double2scm (flexa_width)); + bool add_stem = + !(prev_context_info & PES_UPPER) && + !(prev_context_info & FLEXA_RIGHT); + prev_primitive->set_grob_property ("add-stem", + gh_bool2scm (add_stem)); + } + else if (context_info & PES_UPPER) + { + if (!String::compare (prev_head, "vaticana_punctum")) + prev_head = "vaticana_lpes"; + } + else // flexa + { + if (!String::compare (prev_head, "vaticana_punctum")) + prev_head = "vaticana_rvirga"; + } + + /* + * In the backend, porrectus and joins need to know about + * thickness. Hence, for simplicity, let's distribute the + * ligature grob's value for thickness to each ligature head (even + * if not all of them need to know). + */ + primitive->set_grob_property ("thickness", gh_double2scm (join_thickness)); + + /* + * The head of the current iteration still may change during the + * next iteration due to the context sensitiveness of the + * transformation. However, the head of the previous iteration is + * now fully attributed and can be prepared for the backend. + */ + + /* + * Finish head of previous iteration for backend. + */ + prev_distance = + finish_primitive (first_primitive, prev_primitive, + prev_context_info, prev_head, prev_pitch_delta, + flexa_width, join_thickness, prev_distance); + + prev_primitive = primitive; + prev_context_info = context_info; + prev_pitch_delta = pitch - prev_pitch; + prev_pitch = pitch; + prev_head = head; + } + + /* + * Finish head of last iteration for backend. + */ + finish_primitive (first_primitive, prev_primitive, + prev_context_info, prev_head, prev_pitch_delta, + flexa_width, join_thickness, prev_distance); +} + + +ENTER_DESCRIPTION (Vaticana_ligature_engraver, +/* descr */ "Handles ligatures by glueing special ligature heads together.", +/* creats*/ "VaticanaLigature", +/* accepts */ "ligature-event abort-event", +/* acks */ "ligature-head-interface note-head-interface rest-interface", +/* reads */ "", +/* write */ ""); diff --git a/lily/vaticana-ligature.cc b/lily/vaticana-ligature.cc new file mode 100644 index 0000000000..1fe4e19dc0 --- /dev/null +++ b/lily/vaticana-ligature.cc @@ -0,0 +1,296 @@ +/* + vaticana-ligature.cc -- implement Vaticana_ligature + + source file of the GNU LilyPond music typesetter + + (c) 2003 Juergen Reuter +*/ + +#include +#include "item.hh" +#include "vaticana-ligature.hh" +#include "font-interface.hh" +#include "molecule.hh" +#include "lookup.hh" +#include "staff-symbol-referencer.hh" +#include "note-head.hh" +#include "paper-def.hh" +#include "bezier.hh" +#include "warn.hh" + +/* + * TODO: move this function to class Lookup? + */ +Molecule +vaticana_brew_flexa (Grob *me, + Real interval, + bool solid, + Real width, + Real thickness, + bool add_stem, + Direction stem_direction) +{ + if (interval >= 0.0) + { + me->warning (_ ("ascending vaticana style flexa")); + } + + Real space = Staff_symbol_referencer::staff_space (me); + Molecule molecule = Molecule (); + Real right_height = 0.6 * space; + + // Compensate thickness that appears to be smaller in steep section + // of bend. + Real left_height = right_height + min (0.12 * abs(interval), 0.3) * space; + + if (add_stem) + { + bool consider_interval = + stem_direction * interval > 0.0; + + Interval stem_box_x (0, thickness); + Interval stem_box_y; + + if (consider_interval) + { + Real y_length = max (abs(interval)/2.0*space + + (right_height-left_height), + 1.2*space); + stem_box_y = Interval (0, y_length); + } + else + stem_box_y = Interval (0, space); + + Real y_correction = + (stem_direction == UP) ? + +0.5*left_height : + -0.5*left_height - stem_box_y.length(); + + Box stem_box (stem_box_x, stem_box_y); + Molecule stem = Lookup::filledbox (stem_box); + stem.translate_axis (y_correction, Y_AXIS); + molecule.add_molecule(stem); + } + + // Compensate optical illusion regarding vertical position of left + // and right endings due to curved shape. + Real ypos_correction = -0.1*space * sign(interval); + Real interval_correction = 0.2*space * sign(interval); + Real corrected_interval = interval*space + interval_correction; + + // middle curve of flexa + Bezier curve; + curve.control_[0] = Offset (0.00 * width, 0.0); + curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0); + curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0); + curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0); + + Bezier top_curve = curve, bottom_curve = curve; + for (int i = 0; i < 4; i++) + { + Real thickness = 0.33 * ((3 - i)*left_height + i*right_height); + top_curve.control_[i] += Offset (0, +0.5*thickness); + bottom_curve.control_[i] += Offset (0, -0.5*thickness); + } + + if (solid) + { + Molecule solid_head = + Lookup::bezier_sandwich (top_curve, bottom_curve); + molecule.add_molecule (solid_head); + } + else // outline + { + Bezier inner_top_curve = top_curve; + inner_top_curve.translate (Offset (0.0, -thickness)); + Molecule top_edge = + Lookup::bezier_sandwich (top_curve, inner_top_curve); + molecule.add_molecule(top_edge); + + Bezier inner_bottom_curve = bottom_curve; + inner_bottom_curve.translate (Offset (0.0, +thickness)); + Molecule bottom_edge = + Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve); + molecule.add_molecule(bottom_edge); + + // TODO: Use horizontal slope with proper slope value rather + // than filled box for left edge, since the filled box stands + // out from the flexa shape if the interval is big and the line + // thickness small. The difficulty here is to compute a proper + // slope value, as it should roughly be equal with the slope of + // the left end of the bezier curve. + Box left_edge_box (Interval (0, thickness), + Interval (-0.5*left_height, +0.5*left_height)); + Molecule left_edge = Lookup::filledbox (left_edge_box); + molecule.add_molecule(left_edge); + + Box right_edge_box (Interval (-thickness, 0), + Interval (-0.5*right_height, +0.5*right_height)); + Molecule right_edge = Lookup::filledbox (right_edge_box); + right_edge.translate_axis (width, X_AXIS); + right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS); + molecule.add_molecule(right_edge); + } + molecule.translate_axis (ypos_correction, Y_AXIS); + return molecule; +} + +void +vaticana_add_ledger_lines (Grob *me, Molecule *out, int pos, Real offs, + bool ledger_take_space) +{ + int interspaces = Staff_symbol_referencer::line_count (me)-1; + if (abs (pos) - interspaces > 1) + { + Interval hd = out->extent (X_AXIS); + Real left_ledger_protusion = hd.length ()/4; + Real right_ledger_protusion = left_ledger_protusion; + + Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion, + hd[RIGHT] + right_ledger_protusion); + Molecule ledger_lines = + Note_head::brew_ledger_lines (me, pos, interspaces, + l_extents, + ledger_take_space); + ledger_lines.translate_axis (offs, Y_AXIS); + out->add_molecule (ledger_lines); + } +} + +Molecule +vaticana_brew_primitive (Grob *me, bool ledger_take_space) +{ + SCM head_scm = me->get_grob_property ("ligature-head"); + if (head_scm == SCM_EOL) + { + programming_error ("Vaticana_ligature:" + "undefined ligature-head -> ignoring grob"); + return Molecule (); + } + + String head = ly_symbol2string (head_scm); + if (!String::compare (head, "")) + { + // empty head (typically, this is the right side of porrectus + // shape, which is already typeset by the associated left side + // head); nothing left to do + return Molecule (); + } + + Molecule out; + int porrectus_height = 0; + Real thickness = 0.0; + Real porrectus_width = 0.0; + Real staff_space = Staff_symbol_referencer::staff_space (me); + + SCM thickness_scm = me->get_grob_property ("thickness"); + if (thickness_scm != SCM_EOL) + { + thickness = gh_scm2double (thickness_scm); + } + else + { + programming_error (_f ("Vaticana_ligature:" + "thickness undefined; assuming 1.4", + me)); + thickness = 1.4 * me->get_paper ()->get_var ("linethickness"); + } + + Real x_shift = 0.0; + SCM x_shift_scm = me->get_grob_property ("x-shift"); + if (x_shift_scm != SCM_EOL) + { + x_shift = gh_scm2double (x_shift_scm); + } + else + { + programming_error (_f ("Vaticana_ligature:" + "x-shift undefined; assuming 0.0", + me)); + } + + if (!String::compare (head, "porrectus")) + { + SCM porrectus_height_scm = me->get_grob_property ("porrectus-height"); + if (porrectus_height_scm != SCM_EOL) + { + porrectus_height = gh_scm2int (porrectus_height_scm); + } + else + { + me->warning ("Vaticana_ligature: " + "porrectus-height undefined; assuming 0"); + } + + SCM porrectus_width_scm = me->get_grob_property ("porrectus-width"); + if (porrectus_width_scm != SCM_EOL) + { + porrectus_width = gh_scm2double (porrectus_width_scm); + } + else + { + me->warning ("Vaticana_ligature:" + "porrectus-width undefined; assuming 2.0"); + porrectus_width = 2.0 * staff_space; + } + + bool add_stem = to_boolean (me->get_grob_property ("add-stem")); + out = vaticana_brew_flexa (me, porrectus_height, true, + porrectus_width, thickness, add_stem, DOWN); + } + else + { + Molecule mol = Font_interface::get_default_font (me)->find_by_name ("noteheads-" + head); + mol.translate_axis (x_shift, X_AXIS); + out.add_molecule (mol); + } + + SCM join_left_scm = me->get_grob_property ("join-left"); + if (join_left_scm != SCM_EOL) + { + int join_left = gh_scm2int (join_left_scm); + if (!join_left) + programming_error (_f ("Vaticana_ligature: (join_left == 0)")); + Real blotdiameter = (me->get_paper ()->get_var ("blotdiameter")); + Interval x_extent = + Interval (-0.5 * thickness, +0.5 * thickness); + Interval y_extent = (join_left > 0) ? + Interval (-join_left * 0.5 * staff_space, 0) : // ascending join + Interval (0, -join_left * 0.5 * staff_space); // descending join + Box stem_box (x_extent, y_extent); + + Molecule stem = Lookup::roundfilledbox (stem_box, blotdiameter); + out.add_molecule (stem); + } + + int pos = (int)rint (Staff_symbol_referencer::get_position (me)); + vaticana_add_ledger_lines(me, &out, pos, 0, ledger_take_space); + if (!String::compare (head, "porrectus")) + { + pos += porrectus_height; + vaticana_add_ledger_lines(me, &out, pos, 0.5*porrectus_height, ledger_take_space); + } + + return out; +} + +MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_ligature_primitive, 1); +SCM +Vaticana_ligature::brew_ligature_primitive (SCM smob) +{ + Grob *me = unsmob_grob (smob); + SCM primitive = vaticana_brew_primitive (me, false).smobbed_copy (); + return primitive; +} + +MAKE_SCHEME_CALLBACK (Vaticana_ligature, brew_molecule, 1); +SCM +Vaticana_ligature::brew_molecule (SCM) +{ + return SCM_EOL; +} + +ADD_INTERFACE (Vaticana_ligature, "vaticana-ligature-interface", + "A vaticana style gregorian ligature", + "ligature-head porrectus-height porrectus-width thickness join-left add-stem x-shift" + "ligature-primitive-callback"); diff --git a/ly/gregorian-init.ly b/ly/gregorian-init.ly new file mode 100644 index 0000000000..794c843200 --- /dev/null +++ b/ly/gregorian-init.ly @@ -0,0 +1,32 @@ +\version "1.7.10" + +% +% declare shortcuts for gregorian chant notation +% + +virga = + \once \property Voice.LigatureHead \override #'virga = ##t +stropha = + \once \property Voice.LigatureHead \override #'stropha = ##t +inclinatum = + \once \property Voice.LigatureHead \override #'inclinatum = ##t +auctum = + \once \property Voice.LigatureHead \override #'auctum = ##t +aucta = + \once \property Voice.LigatureHead \override #'auctum = ##t +descendens = + \once \property Voice.LigatureHead \override #'descendens = ##t +ascendens = + \once \property Voice.LigatureHead \override #'ascendens = ##t +pes = + \once \property Voice.LigatureHead \override #'pes-or-flexa = ##t +flexa = + \once \property Voice.LigatureHead \override #'pes-or-flexa = ##t +semivocalis = + \once \property Voice.LigatureHead \override #'semivocalis = ##t +oriscus = + \once \property Voice.LigatureHead \override #'oriscus = ##t +quilisma = + \once \property Voice.LigatureHead \override #'quilisma = ##t +deminutum = + \once \property Voice.LigatureHead \override #'deminutum = ##t diff --git a/mf/parmesan-heads.mf b/mf/parmesan-heads.mf index d53c2e75ef..3f15e6428b 100644 --- a/mf/parmesan-heads.mf +++ b/mf/parmesan-heads.mf @@ -439,7 +439,7 @@ def inclinatum_char(expr verbose_name, internal_name, mudela_name, 2beta# = ht# * b_h; a# = beta# * a_b; wd# = 2a# / a_w; - set_char_box(0.3wd#, 0.3wd#, 0.5 ht#, 0.5 ht#); + set_char_box(0.2wd#, 0.2wd#, 0.5 ht#, 0.5 ht#); black_notehead_width# := wd#; save za, alpha, size; @@ -572,7 +572,8 @@ fet_beginchar("Ed. Vat. quilisma", "vaticana_quilisma", "vatquilismahead") 2beta# = ht#*b_h; a# = beta#*a_b; wd# = 2a# / a_w; - set_char_box(0.4wd#, 0.00wd#, 0.5 ht#, 0.5 ht#); + set_char_box(0.5stafflinethickness#, 0.40wd# + 0.5stafflinethickness#, + 0.31 ht#, 0.41 ht#); black_notehead_width# := wd#; define_pixels(ht, wd); @@ -633,7 +634,7 @@ fet_beginchar("Solesmes oriscus", "solesmes_oriscus", 2beta# = ht# * b_h; a# = beta# * a_b; wd# = 2a# / a_w; - set_char_box(0.5wd#, 0.0wd#, 0.5ht#, 0.5ht#); + set_char_box(0.0wd#, 0.5wd#, 0.5ht#, 0.5ht#); black_notehead_width# := wd#; save convexity; diff --git a/scm/grob-description.scm b/scm/grob-description.scm index 3fad39566d..1beca51e0a 100644 --- a/scm/grob-description.scm +++ b/scm/grob-description.scm @@ -1168,6 +1168,16 @@ (meta . ((interfaces . (piano-pedal-interface axis-group-interface side-position-interface spanner-interface)))) )) + (VaticanaLigature + . ( + (thickness . 1.0) + (flexa-width . 2.0) + (ligature-primitive-callback . ,Vaticana_ligature::brew_ligature_primitive) + (molecule-callback . ,Vaticana_ligature::brew_molecule) + (font-family . ancient) + (meta . ((interfaces . (mensural-ligature-interface font-interface)))) + )) + (VoltaBracket . ( (molecule-callback . ,Volta_bracket_interface::brew_molecule) diff --git a/scm/grob-property-description.scm b/scm/grob-property-description.scm index f7034298fd..bb569b14ea 100644 --- a/scm/grob-property-description.scm +++ b/scm/grob-property-description.scm @@ -76,6 +76,7 @@ This procedure is called (using dependency resolution) after line breaking. Retu (grob-property-description 'arpeggio ly:grob? "pointer to arpeggio object.") (grob-property-description 'arpeggio-direction ly:dir? "If set, put an arrow on the arpeggio squiggly line.") +(grob-property-description 'ascendens boolean? "is this neume of an ascending?.") (grob-property-description 'attachment pair? "cons of symbols indicating how a slur should be attached at the ends. The format is '(LEFT-TYPE . RIGHT-TYPE), where both TYPEs are symbols. The values of @@ -84,6 +85,7 @@ these symbols may be alongside-stem, stem, head or loose-end.") '(LEFT-offset . RIGHT-offset). This offset is added to the attachments to prevent ugly slurs. [fixme: we need more documentation here]. .") +(grob-property-description 'auctum boolean? "is this neume augmented?.") (grob-property-description 'auto-properties boolean? "if true, as many properties of this grob as possible will be determined automatically from the musical context.") (grob-property-description 'auto-knee-gap number? "If a gap is found between noteheads where a horizontal beam fits that is larger than this number, make a kneed beam.") @@ -147,10 +149,12 @@ square of the inner notes involved.") (grob-property-description 'dash-length number? "the length of a dash.") (grob-property-description 'dash-period number? "the length of one dash + white space.") (grob-property-description 'dashed number? "[FIXME: use dash-period/dash length; see text-spanner] number representing the length of the dashes.") +(grob-property-description 'descendens boolean? "is this neume of a descendent type?.") (grob-property-description 'de-uglify-parameters list? "list of 3 real constants. They define the valid areas for the middle control points. Used in de_uglyfy. They are a bit empirical.") (grob-property-description 'neutral-direction ly:dir? "Where to go if we're on the neutral position of the staff (by default, the middle of the staff; see also grob-property neutral-position). [Ross] has the following to say about this: Some engravers consider the middle line neutral, and take the option of using either up- or down-stems for notes that fall on it. However, more up-to-date engraving no longer permits an option; now a down-stem is always appropriate.") (grob-property-description 'neutral-position number? "Position (in half staff spaces) where to flip the direction of stems: by default, custodes above this position get their stems downwards; custodes below this position get their stems upwards. A value of 0 designates the center of the staff. Use property neutral-direction to control the behaviour of stems on the neutral position itself. (Note: currently, neutral-position is supported only for custodes; for stems of note heads, neutral-position is currently fixed to 0, i.e. the middle of the staff.)") +(grob-property-description 'deminutum boolean? "is this neume deminished?.") (grob-property-description 'dependencies grob-list? "list of score-grob pointers that indicate who to compute first for certain global passes.") (grob-property-description 'details list? "alist of parameters for detailed grob behavior.") (grob-property-description 'dir-function procedure? "function of type (count total)->direction. Default value: beam-dir-majority, also available: beam-dir-mean, beam-dir-median. @@ -262,6 +266,7 @@ name of character within font.") ") (grob-property-description 'horizontal-shift integer? "integer that identifies ranking of note-column for horizontal shifting. This is used by @ref{note-collision-interface}.") (grob-property-description 'ideal-distances list? "(OBJ . (DIST . STRENGTH)) pairs.") +(grob-property-description 'inclinatum boolean? "is this neume an inclinatum?.") (grob-property-description 'interfaces list? "list of symbols indicating the interfaces supported by this object. Is initialized from the @code{meta} field.") (grob-property-description 'inversion list? " musical-pitch, optional.") (grob-property-description 'items-worth-living grob-list? "list of interesting items. If empty in a particular system, clear that system.") @@ -354,6 +359,7 @@ provided in @code{input/regression/molecule-hacking.ly}. (grob-property-description 'note-head-style string? "name of the font character to be used as note heads in the ambitus grob.") (grob-property-description 'note-heads grob-list? "List of note head grobs") (grob-property-description 'old-accidentals list? "list of (pitch, accidental) pairs.") +(grob-property-description 'oriscus boolean? "is this neume an oriscus?.") (grob-property-description 'outer boolean? "whether a text spanner should extend to the outer edge of the spanned notes") (grob-property-description 'padding number? "add this much extra space between objects that are next to each other.") (grob-property-description 'pedal-type symbol? "Style of piano pedal: text, bracket or mixed.") @@ -363,11 +369,13 @@ this column. 10000 or more means forbid linebreak, -10000 or less means force linebreak. Other values influence linebreaking decisions as a real penalty.") +(grob-property-description 'pes-or-flexa boolean? "shall this neume be joined with the previous head?.") (grob-property-description 'pitch-max ly:pitch? "FIXME, JUNKME") (grob-property-description 'pitch-min ly:pitch? "FIXME, JUNKME") (grob-property-description 'pitches list? "list of musical-pitch.") +(grob-property-description 'quilisma boolean? "is this neume a quilisma?.") (grob-property-description 'positions pair? "cons of staff positions (LEFT . RIGHT") (grob-property-description 'raise number? "height for text to be raised (a negative value lowers the text.") (grob-property-description 'ratio number? "Slur parameter. See height-limit.") @@ -385,6 +393,7 @@ reference point. TODO: revise typing.") (grob-property-description 'self-alignment-Y number? "like self-alignment-X but for Y axis.") (grob-property-description 'segments list? "DOCME. ") +(grob-property-description 'semivocalis boolean? "is this neume a lisquescending one?.") (grob-property-description 'shape symbol? "shape of cluster segments. Valid values include 'leftsided-stairs', 'rightsided-stairs', 'centered-stairs', and 'ramp'.") (grob-property-description 'shorten number? "the amount of space that a stem should be shortened (DOCME!)") (grob-property-description 'shorten-pair number-pair? "the length on each side to shorten a text-spanner, for example a pedal bracket") @@ -430,6 +439,7 @@ the Nth element of the list gives the amount stem shortening of a note with N fl ") (grob-property-description 'stem-spacing-correction number? "optical correction amount. [TODO: doco] ") (grob-property-description 'stems grob-list? "list of stem objects, corresponding to the notes that the arpeggio has to be before.") +(grob-property-description 'stropha boolean? "is this neume a stropha?.") (grob-property-description 'style symbol? "a string determining what style of glyph is typeset. Valid choices depend on the function that is reading this property. .") (grob-property-description 'support-head ly:grob? "the note head at one end of the stem.") @@ -531,6 +541,7 @@ of booleans, signifying whether this grob should be transparent and have no extent. ") +(grob-property-description 'virga boolean? "is this neume a virga?.") (grob-property-description 'when ly:moment? "when does this column happen?.") (grob-property-description 'word-space number? "elongate left by this much (FIXME: cumbersome semantics).") (grob-property-description 'alignment number? "alignment of lyrics on notehead, -1 is LEFT, 0 is CENTRE, 1 is RIGHT .") -- 2.39.2