]> git.donarmstrong.com Git - lilypond.git/commitdiff
* lily/coherent-ligature-engraver.cc,
authorJürgen Reuter <j@web.de>
Sun, 11 May 2003 21:29:27 +0000 (21:29 +0000)
committerJürgen Reuter <j@web.de>
Sun, 11 May 2003 21:29:27 +0000 (21:29 +0000)
lily/gregorian-ligature-engraver.cc,
lily/ligature-bracket-engraver.cc, lily/ligature-engraver.cc,
lily/mensural-ligature-engraver.cc,
lily/vaticana-ligature-engraver.cc,
lily/include/gregorian-ligature-engraver.hh: updated for new
Coherent_ligature_engraver; added comments that describe the basic
design ideas of the ligature implementation

* lily/coherent-ligature-engraver.cc,
lily/include/coherent-ligature-engraver.hh: new file: shared code
between mensural ligatures and Gregorian chant notation ligatures

ChangeLog
lily/coherent-ligature-engraver.cc [new file with mode: 0644]
lily/gregorian-ligature-engraver.cc
lily/include/coherent-ligature-engraver.hh [new file with mode: 0644]
lily/include/gregorian-ligature-engraver.hh
lily/ligature-bracket-engraver.cc
lily/ligature-engraver.cc
lily/mensural-ligature-engraver.cc
lily/vaticana-ligature-engraver.cc

index 8a53823d10dd29e32a004331e39057d021c2bc10..73fcbf5721395a2e7669e158bac5587b13c248d3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2003-05-11  Juergen Reuter  <reuter@ipd.uka.de>
+
+       * lily/coherent-ligature-engraver.cc,
+       lily/gregorian-ligature-engraver.cc,
+       lily/ligature-bracket-engraver.cc, lily/ligature-engraver.cc,
+       lily/mensural-ligature-engraver.cc,
+       lily/vaticana-ligature-engraver.cc,
+       lily/include/gregorian-ligature-engraver.hh: updated for new
+       Coherent_ligature_engraver; added comments that describe the basic
+       design ideas of the ligature implementation
+
+       * lily/coherent-ligature-engraver.cc,
+       lily/include/coherent-ligature-engraver.hh: new file: shared code
+       between mensural ligatures and Gregorian chant notation ligatures
+
 2003-05-11  Heikki Junes  <hjunes@cc.hut.fi>
 
        * lilypond-mode.el: XEmacs fixes: include two definitions for the
diff --git a/lily/coherent-ligature-engraver.cc b/lily/coherent-ligature-engraver.cc
new file mode 100644 (file)
index 0000000..0914031
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+  coherent-ligature-engraver.cc -- implement Coherent_ligature_engraver
+  
+  source file of the GNU LilyPond music typesetter
+  
+  (c)  2003 Juergen Reuter <reuter@ipd.uka.de>
+ */
+
+#include "coherent-ligature-engraver.hh"
+#include "item.hh"
+#include "warn.hh"
+#include "staff-symbol-referencer.hh"
+#include "spanner.hh"
+#include "paper-column.hh"
+
+/*
+ * This abstract class serves as common superclass for all ligature
+ * engravers thet produce a single connected graphical object of fixed
+ * width, consisting of noteheads and other primitives (see class
+ * Ligature_engraver for more information on the interaction between
+ * this class and its superclass).  In particular, it cares for the
+ * following tasks:
+ *
+ * - provide a function for putting all grobs of the ligature into a
+ * single paper column,
+ *
+ * - delegate actual creation of ligature to concrete subclass,
+ *
+ * - collect all accidentals that occur within the ligature and put
+ * them at the left side of the ligature (TODO; see function
+ * collect_accidentals()),
+ *
+ * - collapse superflous space after each ligature (TODO).
+ *
+ * Concrete subclasses must implement function build_ligature (Spanner
+ * *, Array<Grob_info>).  This function is responsible for actually
+ * building the ligature by transforming the array of noteheads.
+ *
+ * Currently, there are two subclasses: Gregorian_ligature_engraver
+ * for Gregorian chant notation (also known as plain song or cantus
+ * planus) and Mensural_ligature_engraver for white mensural notation.
+ * Subclasses for other music notation styles such as modal notation
+ * or ars nova notation may eventually be added.
+ */
+
+/*
+ * TODO: local accidentals: collect accidentals that occur within a
+ * ligature and put them before the ligature.  If an accidental
+ * changes within a ligature, print a warning (user error) and ignore
+ * any further accidental for that pitch within that ligature
+ * (actually, in such a case, the user should split the ligature into
+ * two separate ligatures).  Similarly, any object that, in ordinary
+ * notation, may be put to the left or to the right of a
+ * note-head/ligature-head, should be collected and put before or
+ * after the ligature.
+ *
+ * TODO: make spacing more robust: do not screw up spacing if user
+ * erroneously puts rest in ligature.
+ *
+ * TODO: for each ligature, add Rod that represents the total length
+ * of the ligature (to preemptively avoid collision with adjacent
+ * notes); or maybe just additionally create a
+ * mensural/vaticana/whatever-ligature grob (e.g. via
+ * Mensural_ligature::brew_molecule(SCM)) that just consists of a
+ * bounding box around all primitives of the ligature.
+ *
+ * TODO: Maybe move functions fold_up_primitives() and
+ * join_primitives() from subclasses to here?  N.B. it is not
+ * appropriate to put these into Ligature_engraver, since, for
+ * example, Ligature_bracket_engraver does not share any of this code.
+ */
+
+/*
+ * TODO: Let superflous space after each ligature collapse.  The
+ * following code should help in doing so (though it does not yet
+ * fully work).  Just put the following code into
+ * Spacing_spanner::do_measure().  I put it temporarily here as memo
+ * until it really works and I also get Han-Wen's/Jan's permission to
+ * add it to the spacing spanner code.
+ */
+#if 0 // experimental code to collapse spacing after ligature
+      SCM incr_scm = lc->get_grob_property ("forced-spacing");
+      if (incr_scm != SCM_EOL) /* (Paper_column::musical_b (l)) */
+       {
+         me->warning (_f ("gotcha: ptr=%ul", lc));//debug
+         ly_display_scm (lc->self_scm ());
+         Real distance;
+         if (incr_scm != SCM_EOL)
+           {
+             distance = gh_scm2double (incr_scm);
+           }
+         else
+           {
+             me->warning ("distance undefined, assuming 0.1");
+             distance = 0.1;
+           }
+         me->warning (_f ("distance=%f", distance));//debug
+         Real strength = 1.0;
+         Spaceable_grob::add_spring (lc, rc, distance, strength, false);
+         if (Item *rb = r->find_prebroken_piece (LEFT))
+           Spaceable_grob::add_spring (lc, rb, distance, strength, false);
+
+         continue;
+       }
+#endif
+
+Coherent_ligature_engraver::Coherent_ligature_engraver ()
+{
+}
+
+/*
+ * TODO: move this function to class Item?
+ */
+void
+Coherent_ligature_engraver::get_set_column (Item *item, Paper_column *column)
+{
+  Item *parent = dynamic_cast<Item*> (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))
+           {
+#if 0 // experimental code to collapse spacing after ligature
+             Grob *sibling_parent = sibling->get_parent (X_AXIS);
+             sibling_parent->warning (_f ("Coherent_ligature_engraver: "
+                                          "setting `spacing-increment = "
+                                          "0.01': ptr=%ul", parent));
+             sibling_parent->set_grob_property("forced-spacing",
+                                             gh_double2scm (0.01));
+#endif
+             sibling->set_parent (column, X_AXIS);
+           }
+       }
+    }
+  else
+    {
+      get_set_column (parent, column);
+    }
+}
+
+/*
+ * TODO: This function should collect all accidentals that occur
+ * within the ligature (by scanning through the primitives array) and
+ * place all of them at the left of the ligature.  If there is an
+ * alteration within the ligature (e.g. an "f" followed by a "fis"
+ * somewhere later in the ligature), issue a warning (and maybe create
+ * an additional natural symbol to explicitly make clear that there is
+ * an "f" first?).  The warning should suggest the user to break the
+ * ligature into two or more smaller ligatures such that no alteration
+ * occurs within the broken ligatures any more.
+ */
+void
+Coherent_ligature_engraver::collect_accidentals (Spanner *, Array<Grob_info>)
+{
+  /* TODO */
+}
+
+void
+Coherent_ligature_engraver::build_ligature (Spanner *, Array<Grob_info>)
+{
+  programming_error ("Cohrent_ligature_engraver::build_ligature (): "
+                    "this is an abstract method that should not be called, "
+                    "but overridden by a subclass");
+}
+
+void
+Coherent_ligature_engraver::typeset_ligature (Spanner *ligature,
+                                             Array<Grob_info> primitives)
+{
+  // prepare ligature for typesetting
+  build_ligature (ligature, primitives);
+  collect_accidentals (ligature, primitives);
+
+  // now actually typeset
+  for (int i = 0; i < primitives.size (); i++)
+    {
+      typeset_grob (primitives[i].grob_);
+    }
+}
+
+ENTER_DESCRIPTION (Coherent_ligature_engraver,
+/* descr */       "This is an abstract class.  Subclasses such as Gregorian_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 */       "");
index 8faf888f6b4e7f909ae15b27a88b8ab01d81e968..482b60ba718b0b47c54d55dabd6c676d3de8d3e6 100644 (file)
 #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.
+ * This abstract class is the common superclass for all ligature
+ * engravers for Gregorian chant notation.  It cares for the musical
+ * handling of the neumes, such as checking for valid combinations of
+ * neumes and providing context information.  Notational aspects such
+ * as the glyphs to use or calculating the total width of a ligature,
+ * are left to the concrete subclass.  Currently, there is only a
+ * single subclass, Vaticana_ligature_engraver.  Other ligature
+ * engravers for Gregorian chant will be added in the future, such as
+ * Medicaea_ligature_engraver or Hufnagel_ligature_engraver.
  */
-
 Gregorian_ligature_engraver::Gregorian_ligature_engraver ()
 {
   pes_or_flexa_req_ = 0;
 }
 
-void
-Gregorian_ligature_engraver::transform_heads (Spanner *, Array<Grob_info>)
-{
-  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)
 {
@@ -45,43 +42,6 @@ Gregorian_ligature_engraver::try_music (Music *m)
     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*> (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)
@@ -284,21 +244,24 @@ provide_context_info (Array<Grob_info> primitives)
 }
 
 void
-Gregorian_ligature_engraver::typeset_ligature (Spanner *ligature,
-                                              Array<Grob_info> primitives)
+Gregorian_ligature_engraver::transform_heads (Spanner *, Array<Grob_info>)
+{
+  programming_error ("Gregorian_ligature_engraver::transform_heads (): "
+                    "this is an abstract method that should not be called, "
+                    "but overridden by a subclass");
+}
+
+void
+Gregorian_ligature_engraver::build_ligature (Spanner *ligature,
+                                            Array<Grob_info> primitives)
 {
   // apply style-independent checking and transformation
   check_and_fix_all_prefixes (primitives);
   provide_context_info (primitives);
 
-  // apply style-specific transformation (including line-up)
+  // apply style-specific transformation (including line-up); to be
+  // implemented by subclass
   transform_heads (ligature, primitives);
-
-  // typeset
-  for (int i = 0; i < primitives.size (); i++)
-    {
-      typeset_grob (primitives[i].grob_);
-    }
 }
 
 void
diff --git a/lily/include/coherent-ligature-engraver.hh b/lily/include/coherent-ligature-engraver.hh
new file mode 100644 (file)
index 0000000..8321e72
--- /dev/null
@@ -0,0 +1,31 @@
+/*   
+  coherent-ligature-engraver.hh -- declare Coherent_ligature_engraver
+  
+  source file of the GNU LilyPond music typesetter
+  
+  (c) 2003 Juergen Reuter <reuter@ipd.uka.de>
+  
+ */
+#ifndef COHERENT_LIGATURE_ENGRAVER_HH
+#define COHERENT_LIGATURE_ENGRAVER_HH
+
+#include "ligature-engraver.hh"
+
+class Coherent_ligature_engraver : public Ligature_engraver
+{
+
+public:
+  TRANSLATOR_DECLARATIONS(Coherent_ligature_engraver);
+
+protected:
+  virtual void build_ligature (Spanner *ligature,
+                              Array<Grob_info> primitives); /* abstract */
+  virtual void typeset_ligature (Spanner *ligature,
+                                Array<Grob_info> primitives);
+  virtual void get_set_column (Item *, Paper_column *);
+
+private:
+  void collect_accidentals (Spanner *, Array<Grob_info>);
+};
+
+#endif // COHERENT_LIGATURE_ENGRAVER_HH
index ac6e0d40522214dac23799a85d092b2013361bb7..ec571f6f6477105d4259c4a7e07936ebe15b06ee 100644 (file)
@@ -9,9 +9,9 @@
 #ifndef GREGORIAN_LIGATURE_ENGRAVER_HH
 #define GREGORIAN_LIGATURE_ENGRAVER_HH
 
-#include "ligature-engraver.hh"
+#include "coherent-ligature-engraver.hh"
 
-class Gregorian_ligature_engraver : public Ligature_engraver
+class Gregorian_ligature_engraver : public Coherent_ligature_engraver
 {
   Music *pes_or_flexa_req_;
 
@@ -20,12 +20,10 @@ public:
 
 protected:
   virtual bool try_music (Music *);
-  virtual void typeset_ligature (Spanner *ligature,
-                                Array<Grob_info> primitives);
+  virtual void build_ligature (Spanner *ligature, Array<Grob_info> primitives);
   virtual void transform_heads (Spanner *ligature,
                                Array<Grob_info> primitives); /* abstract method */
   virtual void start_translation_timestep ();
-  void get_set_column (Item *, Paper_column *);
 };
 
 #endif // GREGORIAN_LIGATURE_ENGRAVER_HH
index 5a61c3363ecdaf91f2caf80e539464bfe5678e21..8f1c18a50b6524cfa1fffcd26fee190584b93161 100644 (file)
 #include "tuplet-bracket.hh"
 #include "spanner.hh"
 
+/*
+ * This engraver marks ligatures of any kind by just printing a
+ * horizontal square bracket on top of each ligature.  See class
+ * Ligature_engraver for more information on the interaction between
+ * this class and its superclass.
+ */
 class Ligature_bracket_engraver : public Ligature_engraver
 {
 protected:
index 17414179cb3db1064de6c6e0b3cc102117c3f6d0..3e15bbf5f571fc11d946f3b08582041e6eaae112 100644 (file)
 #include "rest.hh"
 #include "warn.hh"
 
+/*
+ * This abstract class provides the general framework for ligatures of
+ * any kind.  It cares for handling start/stop ligatures requests and
+ * collecting all noteheads inbetween, but delegates creation of a
+ * ligature spanner for each start/stop pair and typesetting of the
+ * ligature spanner to a concrete subclass.
+ *
+ * A concrete ligature engraver must subclass this class and provide
+ * functions create_ligature_spanner () and typeset_ligature
+ * (Spanner *, Array<Grob_info>).  Subclasses of this class basically
+ * fall into two categories.
+ *
+ * The first category consists of engravers that engrave ligatures in
+ * a way that really deserves the name ligature.  That is, they
+ * produce a single connected graphical object of fixed width,
+ * consisting of noteheads and other primitives.  Space may be
+ * inserted only after each ligature, if necessary, but in no case
+ * between the primitives of the ligature.  Accidentals have to be put
+ * to the left of the ligature, and not to the left of individual
+ * noteheads.  Class Coherent_ligature_engraver is the common
+ * superclass for all of these engravers.
+ *
+ * The second category is for engravers that are relaxed in the sense
+ * that they do not require to produce a single connected graphical
+ * object.  For example, in contemporary editions, ligatures are often
+ * marked, but otherwise use contemporary notation and spacing.  In
+ * this category, there is currently only a single class,
+ * Ligature_bracket_engraver, which marks each ligature with a
+ * horizontal sqare bracket, but otherwise leaves the appearance
+ * untouched.
+ */
+
 /*
  * TODO: lyrics/melisma/syllables: there should be at most one
  * syllable of lyrics per ligature (i.e. for the lyrics context, a
index 58134b68712d9c0a8fe9c2ccfbf4cdeb63708d7c..913580a2d34e407add6760056a64e21f1582ff1f 100644 (file)
@@ -7,7 +7,7 @@
  */
 
 #include "mensural-ligature.hh"
-#include "ligature-engraver.hh"
+#include "coherent-ligature-engraver.hh"
 #include "event.hh"
 #include "warn.hh"
 #include "item.hh"
 #include "font-interface.hh"
 
 /*
- * TODO: local accidentals: collect accidentals that occur within a
- * ligature and put them before the ligature.  If an accidental
- * changes within a ligature, print a warning (user error) and ignore
- * any further accidental for that pitch within that ligature
- * (actually, in such a case, the user should split the ligature into
- * two separate ligatures).  Similarly, any object that, in ordinary
- * notation, may be put to the left or to the right of a
- * note-head/ligature-head, should be collected and put before or
- * after the ligature.
- *
- * TODO: make spacing more robust: do not screw up spacing if user
- * erroneously puts rest in ligature.
- *
  * TODO: 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
  *
  * TODO: prohibit multiple voices within a ligature.
  *
- * TODO: for each ligature, add Rod that represents the total length
- * of the ligature (to preemptively avoid collision with adjacent
- * notes); or maybe just additionally create a mensural-ligature grob
- * (via Mensural_ligature::brew_molecule(SCM)) that just consists of a
- * bounding box around all primitives of the ligature.
- *
  * TODO: enhance robustness: in case of an illegal ligature (e.g. the
  * user events for a ligature that contains a minima or STATE_ERROR
  * is reached), automatically break the ligature into smaller, valid
  * pieces.
- *
- * TODO: In the future, there will be further ligature engravers
- * implemented, such as a Vaticana_ligature_engraver.  There will be
- * redundant code between these engravers and the
- * Mensural_ligature_engraver.  In particular these are functions
- * set_column_, fold_up_primitives, join_primitives, and
- * ackowledge_grob; further the code for handling accidentals.  It is
- * not appropriate to put these things into Ligature_engraver, since,
- * for example, Ligature_bracket_engraver does not share any of this
- * code.  Hence, we might to introduce a further subclass of
- * Ligature_engraver which serves as super class for
- * Mensural_ligature_engraver, Vaticana_ligature_engraver, among
- * others.
  */
-class Mensural_ligature_engraver : public Ligature_engraver
+class Mensural_ligature_engraver : public Coherent_ligature_engraver
 {
 
 protected:
   virtual Spanner *create_ligature_spanner ();
-  virtual void typeset_ligature (Spanner *ligature,
-                                Array<Grob_info> primitives);
+  virtual void build_ligature (Spanner *ligature, Array<Grob_info> primitives);
 
 public:
   TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
@@ -96,7 +63,6 @@ private:
   void propagate_properties (Spanner *ligature, Array<Grob_info> primitives);
   void fold_up_primitives (Array<Grob_info> primitives);
   void join_primitives (Array<Grob_info> primitives);
-  void get_set_column (Item *item, Paper_column *new_col);
 };
 
 
@@ -110,43 +76,6 @@ Mensural_ligature_engraver::create_ligature_spanner ()
   return new Spanner (get_property ("MensuralLigature"));
 }
 
-/*
- * TODO: move this function to class Item?
- */
-void
-Mensural_ligature_engraver::get_set_column (Item *item, Paper_column *column)
-{
-  Item *parent = dynamic_cast<Item*> (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);
-    }
-}
-
 /*
  * The following lines implement a finite state automat.  Given a
  * sequence of durations (Longa, Brevis, Semibrevis) or
@@ -528,18 +457,13 @@ Mensural_ligature_engraver::join_primitives (Array<Grob_info> primitives)
 }
 
 void
-Mensural_ligature_engraver::typeset_ligature (Spanner *ligature,
-                                             Array<Grob_info> primitives)
+Mensural_ligature_engraver::build_ligature (Spanner *ligature,
+                                           Array<Grob_info> 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_);
-    }
 }
 
 ENTER_DESCRIPTION (Mensural_ligature_engraver,
index f902e2fda3dba0cdf27e047c91920d29a4626bbc..5c00edafa2dcac8e545cac1b0dd6eae3f8302bc1 100644 (file)
 #include "font-interface.hh"
 #include "warn.hh"
 #include "paper-def.hh"
+#include "paper-column.hh"
 
+/*
+ * This class implements the notation specific aspects of Vaticana
+ * style ligatures for Gregorian chant notation.
+ */
 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
 {
 
@@ -58,6 +63,7 @@ Vaticana_ligature_engraver::finish_primitive (Item *first_primitive,
                                              Real join_thickness,
                                              Real distance)
 {
+  Real next_distance = distance;
   if (primitive)
     {
       // determine width of previous head and x-offset
@@ -128,7 +134,7 @@ Vaticana_ligature_engraver::finish_primitive (Item *first_primitive,
           * Create a small overlap of adjacent heads so that the join
           * can be drawn perfectly between them.
           */
-         distance -= join_thickness;
+         next_distance -= join_thickness;
        }
       else if (!String::compare (glyph_name, ""))
        {
@@ -143,17 +149,19 @@ Vaticana_ligature_engraver::finish_primitive (Item *first_primitive,
          /*
           * Make a small space after a virga.
           */
-         distance += 2 * join_thickness;
+         next_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;
+      primitive->translate_axis (next_distance, X_AXIS);
+      next_distance += head_width;
+
     }
-  return distance;
+
+  return next_distance;
 }
 
 void
@@ -320,9 +328,27 @@ Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
   /*
    * Finish head of last iteration for backend.
    */
-  finish_primitive (first_primitive, prev_primitive,
-                   prev_context_info, prev_glyph_name, prev_pitch_delta,
-                   flexa_width, join_thickness, prev_distance);
+  prev_distance =
+    finish_primitive (first_primitive, prev_primitive,
+                     prev_context_info, prev_glyph_name, prev_pitch_delta,
+                     flexa_width, join_thickness, prev_distance);
+
+  /* TODO: make this cfg'able via SCM */
+  Real padding = join_thickness;
+
+  /* horizontal padding space after ligature */
+  prev_distance += padding;
+
+#if 0 // experimental code to collapse spacing after ligature
+  /* TODO: set to max(old/new spacing-increment), since other
+     voices/staves also may want to set this property. */
+  Paper_column *paper_column = first_primitive->get_column();
+  paper_column->warning (_f ("Vaticana_ligature_engraver: "
+                            "setting `spacing-increment = %f': ptr=%ul",
+                            prev_distance, paper_column));
+  paper_column->
+    set_grob_property("forced-spacing", gh_double2scm (prev_distance));
+#endif
 }