]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/mensural-ligature-engraver.cc
new file, move from
[lilypond.git] / lily / mensural-ligature-engraver.cc
index 4678e9bf26d22873590589bc7124d1a5c950170a..8252e5a5697b6db66550a7c7e008e25ce16d4847 100644 (file)
@@ -3,12 +3,12 @@
   
   source file of the GNU LilyPond music typesetter
   
-  (C) 2002 Juergen Reuter <reuter@ipd.uka.de>
+  (c) 2002--2004 Juergen Reuter <reuter@ipd.uka.de>
  */
 
 #include "mensural-ligature.hh"
-#include "ligature-engraver.hh"
-#include "musical-request.hh"
+#include "coherent-ligature-engraver.hh"
+#include "event.hh"
 #include "warn.hh"
 #include "item.hh"
 #include "spanner.hh"
 #include "rhythmic-head.hh"
 #include "note-head.hh"
 #include "staff-symbol-referencer.hh"
-#include "paper-def.hh"
+#include "output-def.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 requests for a ligature that contains a minima or STATE_ERROR
+ * 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
 {
-  Real distance_;
-  Array<Grob_info> primitives_;
 
 protected:
-  virtual void acknowledge_grob (Grob_info);
-  virtual void try_stop_ligature ();
   virtual Spanner *create_ligature_spanner ();
+  virtual void build_ligature (Spanner *ligature, Array<Grob_info> primitives);
 
 public:
-  TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
+  TRANSLATOR_DECLARATIONS (Mensural_ligature_engraver);
 
 private:
-  int apply_transition (int state, int input, int i);
-  void transform_heads ();
-  void propagate_properties ();
-  void fold_up_primitives ();
-  void join_primitives ();
-  void get_set_column (Item *item, Paper_column *new_col);
+  int apply_transition (Array<Grob_info> primitives,
+                       int state, int input, int i);
+  void transform_heads (Array<Grob_info> primitives);
+  void propagate_properties (Spanner *ligature, Array<Grob_info> primitives);
+  void fold_up_primitives (Array<Grob_info> primitives);
+  void join_primitives (Array<Grob_info> primitives);
 };
 
 
 Mensural_ligature_engraver::Mensural_ligature_engraver ()
 {
-  distance_ = 0;
 }
 
 Spanner *
 Mensural_ligature_engraver::create_ligature_spanner ()
 {
-  distance_ = 0;
-  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);
-    }
+  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-request as input, the automat outputs a sequence of
- * requests for grobs that form a proper ligature.
+ * end-of-ligature-event as input, the automat outputs a sequence of
+ * events for grobs that form a proper ligature.
  */
 
 /*
@@ -261,15 +187,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<Grob_info> primitives,
+                                             int state, int input, int i)
 {
   int output = transition_output[state][input];
   Item *last_last_primitive = (i > 1) ?
-    dynamic_cast<Item*> (primitives_[i-2].grob_) : 0;
+    dynamic_cast<Item*> (primitives[i-2].grob_) : 0;
   Item *last_primitive = (i > 0) ?
-    dynamic_cast<Item*> (primitives_[i-1].grob_) : 0;
-  Item *primitive = (i < primitives_.size ()) ?
-    dynamic_cast<Item*> (primitives_[i].grob_) : 0;
+    dynamic_cast<Item*> (primitives[i-1].grob_) : 0;
+  Item *primitive = (i < primitives.size ()) ?
+    dynamic_cast<Item*> (primitives[i].grob_) : 0;
   switch (output)
     {
       case MLP_NONE:
@@ -284,7 +211,7 @@ Mensural_ligature_engraver::apply_transition (int state, int input, int i)
            programming_error ("last_primitive undefined");
            break;
          }
-       last_primitive->set_grob_property ("primitive", gh_int2scm (output));
+       last_primitive->set_property ("primitive", scm_int2num (output));
        break;
       case MLP_BB:
       case MLP_LB:
@@ -299,8 +226,8 @@ Mensural_ligature_engraver::apply_transition (int state, int input, int i)
            programming_error ("primitive undefined");
            break;
          }
-       last_primitive->set_grob_property ("primitive", gh_int2scm (output));
-       primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
+       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
@@ -314,8 +241,8 @@ Mensural_ligature_engraver::apply_transition (int state, int input, int i)
            programming_error ("last_primitive undefined");
            break;
          }
-       last_last_primitive->set_grob_property ("primitive", gh_int2scm (output));
-       last_primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
+       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"));
@@ -325,9 +252,9 @@ Mensural_ligature_engraver::apply_transition (int state, int input, int i)
 }
 
 void
-Mensural_ligature_engraver::transform_heads ()
+Mensural_ligature_engraver::transform_heads (Array<Grob_info> primitives)
 {
-  if (primitives_.size () < 2)
+  if (primitives.size () < 2)
     {
       warning (_f ("ligature with less than 2 heads -> skipping"));
       return;
@@ -335,18 +262,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<Item*> (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"))
@@ -359,7 +284,7 @@ Mensural_ligature_engraver::transform_heads ()
       }
     else
       {
-       pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
+       pitch = *unsmob_pitch (nr->get_property ("pitch"));
        have_pitch = 1;
       }
 
@@ -392,82 +317,67 @@ 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) { ... }
 }
 
-void set_delta_pitch (Item *primitive, Grob_info info1, Grob_info info2)
-{
-  Pitch pitch1 = *unsmob_pitch (info1.music_cause ()->get_mus_property ("pitch"));
-  Pitch pitch2 = *unsmob_pitch (info2.music_cause ()->get_mus_property ("pitch"));
-  int delta_pitch = (pitch2.steps () - pitch1.steps ());
-  primitive->set_grob_property ("delta-pitch", gh_int2scm (delta_pitch));
-}
-
 /*
- * A MensuralLigature grob consists of a bunch of LigatureHead grobs
- * that are glued together.  It (a) does make sense to change
+ * A MensuralLigature grob consists of a bunch of NoteHead grobs that
+ * are glued together.  It (a) does not make sense to change
  * properties like thickness or flexa-width from one head to the next
  * within a ligature (this would totally screw up alignment), and (b)
  * some of these properties (like flexa-width) are specific to
  * e.g. the MensuralLigature (as in contrast to e.g. LigatureBracket),
- * and therefore should not be handled in the generic LigatureHead
- * (which is also used by LigatureBracket).  Therefore, we let the
- * user control these properties via the concrete Ligature grob (like
+ * and therefore should not be handled in the NoteHead code (which is
+ * also used by LigatureBracket).  Therefore, we let the user control
+ * these properties via the concrete Ligature grob (like
  * MensuralLigature) and then copy these properties as necessary to
- * each of the LigatureHead grobs.  This is what
- * propagate_properties() does.
+ * each of the NoteHead grobs.  This is what
+ * propagate_properties () does.
  */
 void
-Mensural_ligature_engraver::propagate_properties ()
+Mensural_ligature_engraver::propagate_properties (Spanner *ligature,
+                                                 Array<Grob_info> primitives)
 {
-  SCM thickness_scm =
-    finished_ligature_->get_grob_property ("thickness");
-  Real thickness = (thickness_scm != SCM_EOL) ?
-    gh_scm2double (thickness_scm) : 1.4;
-  thickness *= finished_ligature_->get_paper ()->get_var ("linethickness");
+  Real thickness = robust_scm2double (ligature->get_property ("thickness"), 1.4);
+  thickness *= ligature->get_paper ()->get_dimension (ly_symbol2scm ("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");
-  Real flexa_width = (flexa_width_scm != SCM_EOL) ?
-    gh_scm2double (flexa_width_scm) : 2.0;
-  flexa_width *= Staff_symbol_referencer::staff_space (finished_ligature_);
+    Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2);
+  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<Item*> (primitives_[i].grob_);
-      int output = gh_scm2int (primitive->get_grob_property ("primitive"));
-      primitive->set_grob_property ("thickness",
-                                   gh_double2scm (thickness));
+      Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
+      int output = ly_scm2int (primitive->get_property ("primitive"));
+      primitive->set_property ("thickness",
+                                   scm_make_real (thickness));
       switch (output) {
        case MLP_NONE:
-         primitive->set_grob_property ("head-width",
-                                       gh_double2scm (half_flexa_width));
+         primitive->set_property ("head-width",
+                                       scm_make_real (half_flexa_width));
          break;
        case MLP_sc:
        case MLP_ss:
        case MLP_cs:
-         primitive->set_grob_property ("head-width",
-                                       gh_double2scm (head_width));
+         primitive->set_property ("head-width",
+                                       scm_make_real (head_width));
          break;
        case MLP_BB:
        case MLP_LB:
        case MLP_SS:
-         primitive->set_grob_property ("head-width",
-                                       gh_double2scm (half_flexa_width));
-         primitive->set_grob_property ("flexa-width",
-                                       gh_double2scm (flexa_width));
-         set_delta_pitch (primitive,
-                          primitives_[i], primitives_[i+1]);
+         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"));
@@ -477,12 +387,13 @@ Mensural_ligature_engraver::propagate_properties ()
 }
 
 void
-Mensural_ligature_engraver::fold_up_primitives ()
+Mensural_ligature_engraver::fold_up_primitives (Array<Grob_info> 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<Item*> (primitives_[i].grob_);
+      Item *current = dynamic_cast<Item*> (primitives[i].grob_);
       if (i == 0)
        {
          first = current;
@@ -492,39 +403,32 @@ Mensural_ligature_engraver::fold_up_primitives ()
 
       if (i > 0)
        {
-#if 0
-         Rod r;
-         r.distance_ = distance_;
-         r.item_l_drul_[LEFT] = first;
-         r.item_l_drul_[RIGHT] = current;
-         r.add_to_cols ();
-#endif
-         current->translate_axis (distance_, X_AXIS);
+         current->translate_axis (distance, X_AXIS);
        }
 
-      distance_ +=
-       gh_scm2double (current->get_grob_property ("head-width")) -
-       gh_scm2double (current->get_grob_property ("thickness"));
+      distance +=
+       ly_scm2double (current->get_property ("head-width")) -
+       ly_scm2double (current->get_property ("thickness"));
     }
 }
 
 void
-Mensural_ligature_engraver::join_primitives ()
+Mensural_ligature_engraver::join_primitives (Array<Grob_info> 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];
-      Pitch pitch = *unsmob_pitch (info.music_cause ()->get_mus_property ("pitch"));
+      Grob_info info = primitives[i];
+      Pitch pitch = *unsmob_pitch (info.music_cause ()->get_property ("pitch"));
       if (i > 0)
         {
          Item *primitive = dynamic_cast<Item*> (info.grob_);
-         int output = gh_scm2int (primitive->get_grob_property ("primitive"));
+         int output = ly_scm2int (primitive->get_property ("primitive"));
          if (output & MLP_ANY)
            {
              int delta_pitch = (pitch.steps () - last_pitch.steps ());
-             primitive->set_grob_property ("join-left",
-                                           gh_int2scm (delta_pitch));
+             primitive->set_property ("join-left-amount",
+                                           scm_int2num (delta_pitch));
            }
        }
       last_pitch = pitch;
@@ -532,42 +436,19 @@ Mensural_ligature_engraver::join_primitives ()
 }
 
 void
-Mensural_ligature_engraver::try_stop_ligature ()
-{
-  if (finished_ligature_)
-    {
-      transform_heads ();
-      propagate_properties ();
-      fold_up_primitives ();
-      join_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)
+Mensural_ligature_engraver::build_ligature (Spanner *ligature,
+                                           Array<Grob_info> primitives)
 {
-  Ligature_engraver::acknowledge_grob (info);
-  if (ligature_)
-    {
-      if (Note_head::has_interface (info.grob_))
-       {
-         primitives_.push (info);
-       }
-    }
+  transform_heads (primitives);
+  propagate_properties (ligature, primitives);
+  fold_up_primitives (primitives);
+  join_primitives (primitives);
 }
 
 ENTER_DESCRIPTION (Mensural_ligature_engraver,
-/* descr */       "Handles Mensural_ligature_requests by glueing special ligature heads together.",
+/* descr */       "Handles Mensural_ligature_events by glueing special ligature heads together.",
 /* creats*/       "MensuralLigature",
-/* accepts */     "general-music",
-/* acks  */      "ligature-head-interface note-head-interface rest-interface",
+/* accepts */     "ligature-event",
+/* acks  */      "note-head-interface rest-interface",
 /* reads */       "",
 /* write */       "");