lilypond-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Added chromatic ledger and internal ledger lines. Sample test fi


From: Kevin Dalley
Subject: [PATCH] Added chromatic ledger and internal ledger lines. Sample test file is
Date: Mon, 05 Mar 2007 23:59:31 -0800
User-agent: Gnus/5.110006 (No Gnus v0.6) XEmacs/21.4.19 (linux)

This patch includes chromatic staffs which place the each of the 12
semitones of an octave on a different vertical position on the staff.


I have examples of the LilyPond code and output here:
http://www.kelphead.org/chromatic/


This code also allows the placement of ledger lines internal to the
staff, which is in ledger-line-spanner.cc.  

I hope that this patch comes close to meeting your standards.  I
refactored the ledger-line-spanner.cc a bit to reduce code duplication.


>From f12dff06a034c765190ed51e49a0040a7ad74ca7 Mon Sep 17 00:00:00 2001
From: Kevin Dalley <address@hidden>
Date: Mon, 5 Mar 2007 23:43:20 -0800
Subject: [PATCH] Added chromatic ledger and internal ledger lines. Sample test 
file is
input/test/chromatic-scales.ly.
---
 Documentation/topdocs/AUTHORS.texi |    2 +
 THANKS                             |    2 +-
 input/test/chromatic-scales.ly     |   35 ++++
 lily/ledger-line-spanner.cc        |  305 +++++++++++++++++++++++++++++++-----
 lily/note-heads-engraver.cc        |   55 ++++++-
 lily/staff-symbol.cc               |    1 +
 scm/define-context-properties.scm  |    1 +
 scm/define-grob-properties.scm     |    1 +
 8 files changed, 357 insertions(+), 45 deletions(-)

diff --git a/Documentation/topdocs/AUTHORS.texi 
b/Documentation/topdocs/AUTHORS.texi
index db20189..905abf4 100644
--- a/Documentation/topdocs/AUTHORS.texi
+++ b/Documentation/topdocs/AUTHORS.texi
@@ -29,6 +29,8 @@ al-niente hairpins.
    
 @item @email{benkop@@freestart.hu,Pal Benko},
     Ancient notation.
address@hidden @email{kevin@@kelphead.org,Kevin Dalley},
+    Chromatic staff, staff-line-layout, and internal ledger lines.
 @item @email{david.feuer@@gmail.com, David Feuer},
     PS output code refactoring.
 @item @email{bernard@@fong-hurley.org.uk, Bernard Hurley},
diff --git a/THANKS b/THANKS
index 3862361..f750419 100644
--- a/THANKS
+++ b/THANKS
@@ -19,7 +19,7 @@ Erlend Aasland
 Guido Amoruso
 Heikki Junes
 Joe Neeman
-
+Kevin Dalley
 
 SPONSORS
 
diff --git a/input/test/chromatic-scales.ly b/input/test/chromatic-scales.ly
new file mode 100644
index 0000000..dd75c70
--- /dev/null
+++ b/input/test/chromatic-scales.ly
@@ -0,0 +1,35 @@
+\version "2.10"
+\include "english.ly"
+% This shows how to use internal-ledger-lines and staff-line-layout
+% to produce alternative chromatic notation.
+% This notation is 6-6 Tetragram by Richard Parncutt
+% For more information,
+% see http://web.syr.edu/~pwmorris/mnma/gallery/4LineNotations.html
+
+scales = \relative {
+  a, as b c cs d ds e f fs g gs
+  a as b c cs d ds e f fs g gs
+  a as b c cs d ds e f fs g gs
+  a
+}
+
+\new Staff \with {
+  \remove "Accidental_engraver"
+  staff-line-layout = #'semitone
+  middleCPosition = #-2
+  clefGlyph = #"clefs.G"
+  clefPosition = #(+ -2 7)
+}
+{
+  \override Staff.StaffSymbol #'line-count = #8
+  \override Staff.StaffSymbol #'line-positions = #'(-9 -7 -5 -3 3 5 7 9)
+  \override Staff.StaffSymbol #'internal-ledger-lines = #'((-1 1))
+  \time 4/4
+  <<
+    \scales
+    \context NoteNames {
+      \set printOctaveNames= ##f
+      \scales
+    }
+  >>
+}
diff --git a/lily/ledger-line-spanner.cc b/lily/ledger-line-spanner.cc
index bf7444d..8401d2d 100644
--- a/lily/ledger-line-spanner.cc
+++ b/lily/ledger-line-spanner.cc
@@ -7,6 +7,7 @@
 */
 
 #include <map>
+#include <set>
 using namespace std;
 
 #include "item.hh"
@@ -18,6 +19,104 @@ using namespace std;
 #include "pointer-group-interface.hh"
 #include "paper-column.hh"
 
+
+
+struct Ledger_request
+{
+  Interval ledger_extent_;
+  Interval head_extent_;
+  /* non-negative position, direction is removed */
+  int position_;
+  bool excentric_;
+  Ledger_request ()
+  {
+    ledger_extent_.set_empty ();
+    head_extent_.set_empty ();
+    position_ = 0;
+  }
+};
+
+typedef map < int, Drul_array<Ledger_request> > Ledger_requests;
+typedef map <int, Ledger_request> Ledger_requests_internal;
+
+/* This consists of a group of ledger lines internal to the staff.
+   There should be no staff lines between these ledger lines
+ */
+class Internal_ledgers 
+{
+public:
+  Internal_ledgers():
+    halfspace_(1)
+  {
+  }
+  void set_halfspace(const Real& halfspace)
+  {
+    halfspace_ = halfspace;
+  }
+  /** 
+   * add new ledger line at \a new_ledger
+   *
+   * @param newLedger new ledger position
+   * 
+   */
+  void add_ledger(int new_ledger)
+  {
+    ledgers_.insert(new_ledger);
+  }
+  
+  /** must be performed before using contains */
+  void calculate_extent()
+  {
+    ledger_extent_ = Interval(*min_element(ledgers_.begin(),
+                                          ledgers_.end())/halfspace_ - 1,
+                            *max_element(ledgers_.begin(),
+                                         ledgers_.end())/halfspace_ + 1);
+  }
+  /** 
+   * 
+   * 
+   * @param pos 
+   * 
+   * @return true if \a pos is contain in ledger
+   */
+  bool contains(int pos) const
+  {
+    return ledger_extent_.contains(pos/halfspace_);
+  }
+  const set<int>& ledger_set() const
+  {
+    return ledgers_;
+  }
+private:
+  Interval ledger_extent_;
+  set<int> ledgers_;
+  /** set halfspace for rounding ledger extent */
+  Real halfspace_;
+};
+
+  
+/** 
+ * 
+ * 
+ * @param pos 
+ * @param internal_ledgers_container 
+ * 
+ * @return true if \a pos is contained in at least one item in Internal_ledgers
+ */
+bool contains(int pos, const vector<Internal_ledgers>& 
internal_ledgers_container)
+{
+  for (vector<Internal_ledgers>::const_iterator internal_ledgers = 
internal_ledgers_container.begin();
+       internal_ledgers != internal_ledgers_container.end();
+       ++internal_ledgers)
+    {
+      if ((*internal_ledgers).contains(pos))
+       {
+         return true;
+       }
+    }
+  return false;
+}
+
 struct Ledger_line_spanner
 {
   DECLARE_SCHEME_CALLBACK (print, (SCM));
@@ -28,8 +127,21 @@ struct Ledger_line_spanner
                                    Real, Real,
                                    Interval x_extent,
                                    Real left_shorten);
-
+  static Stencil brew_internal_ledger_lines (const Internal_ledgers& 
internal_ledgers,
+                                          Real, Real,
+                                          Interval x_extent,
+                                          Real left_shorten);
   static bool has_interface (Grob *);
+  /* returns ledger_size and left_shorten, given other values
+   * helper method for brew_ledger_lines
+   */
+  static void find_ledger_size(const Item *head, Grob *common[],
+                              Real length_fraction,
+                              Ledger_request& req,
+                              Interval& ledger_size,
+                              Real& left_shorten
+                              );
+
 };
 
 Stencil
@@ -44,6 +156,7 @@ Ledger_line_spanner::brew_ledger_lines (Grob *staff,
   int line_count = (staff_extent.contains (pos)
                    ? 0
                    : sign (pos) * int (rint(pos -  staff_extent[Direction 
(sign (pos))])) / 2);
+
   Stencil stencil;
   if (line_count)
     {
@@ -73,7 +186,40 @@ Ledger_line_spanner::brew_ledger_lines (Grob *staff,
          stencil.add_stencil (ledger_line);
        }
     }
-
+  
+  return stencil;
+}
+/** extra lines for internal ledger lines */ 
+Stencil
+Ledger_line_spanner::brew_internal_ledger_lines (const Internal_ledgers& 
internal_ledgers,
+                                              Real halfspace,
+                                              Real ledgerlinethickness,
+                                              Interval x_extent,
+                                              Real left_shorten)
+{
+  Stencil stencil;
+  /* halfspace is 1/2 of staff-space */
+  Real blotdiameter = ledgerlinethickness;
+  Interval y_extent
+    = Interval (-0.5 * (ledgerlinethickness),
+               +0.5 * (ledgerlinethickness));
+  Stencil proto_ledger_line
+    = Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
+  x_extent[LEFT] += left_shorten;
+  Stencil proto_first_line
+    = Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
+  const set<int>& ledgers = internal_ledgers.ledger_set();
+  for (set<int>::const_iterator currentLedger = ledgers.begin();
+       currentLedger != ledgers.end();
+       ++currentLedger)
+  {
+    int pos = *currentLedger;
+    Real offs = pos * halfspace;
+    Stencil ledger_line(proto_ledger_line);
+    // Stencil ledger_line(proto_first_line);
+    ledger_line.translate_axis(offs, Y_AXIS);
+    stencil.add_stencil (ledger_line);
+  }
   return stencil;
 }
 
@@ -181,26 +327,50 @@ Ledger_line_spanner::set_spacing_rods (SCM smob)
   return SCM_UNSPECIFIED;
 }
 
-struct Ledger_request
+/*
+ * Calculate
+ * ledger_size
+ * left_shorten
+ */
+void
+Ledger_line_spanner::find_ledger_size(const Item *head, Grob *common[],
+                                     Real length_fraction,
+                                     Ledger_request& req,
+                                     Interval& ledger_size,
+                                     Real& left_shorten
+                                     )
 {
-  Interval ledger_extent_;
-  Interval head_extent_;
-  int position_;
-  bool excentric_;
-  Ledger_request ()
-  {
-    ledger_extent_.set_empty ();
-    head_extent_.set_empty ();
-    position_ = 0;
-  }
-};
+  
+  Interval head_size = head->extent (common[X_AXIS], X_AXIS);
+  ledger_size = head_size;
+  ledger_size.widen (ledger_size.length () * length_fraction);
 
-typedef map < int, Drul_array<Ledger_request> > Ledger_requests;
+  Interval max_size = req.ledger_extent_;
 
+  ledger_size.intersect (max_size);
+  left_shorten = 0.0;
+  if (Grob *g = unsmob_grob (head->get_object ("accidental-grob")))
+    {
+      Interval accidental_size = g->extent (common[X_AXIS], X_AXIS);
+      Real d
+       = linear_combination (Drul_array<Real> (accidental_size[RIGHT],
+                                               head_size[LEFT]),
+                             0.0);
+
+      left_shorten = max (-ledger_size[LEFT] + d, 0.0);
+
+      /*
+       TODO: shorten 2 ledger lines for the case natural +
+       downstem.
+      */
+    }
+}
+  
 /*
   TODO: ledger share a lot of info. Lots of room to optimize away
   common use of objects/variables.
 */
+
 MAKE_SCHEME_CALLBACK (Ledger_line_spanner, print, 1);
 SCM
 Ledger_line_spanner::print (SCM smob)
@@ -240,12 +410,42 @@ Ledger_line_spanner::print (SCM smob)
     }
 
   Ledger_requests reqs;
+  Ledger_requests_internal reqs_internal;
+
+  // add internal ledger lines to list
+  vector<Internal_ledgers> internal_ledgers_container;
+  SCM internal_ledgers = staff->get_property("internal-ledger-lines");
+  set<int> ledger_set;
+  if (scm_is_pair(internal_ledgers))
+    {
+      for (SCM s = internal_ledgers; scm_is_pair (s);
+          s = scm_cdr (s))
+       {
+         SCM car = scm_car(s);
+         Internal_ledgers internal_ledgers;
+         internal_ledgers.set_halfspace(halfspace);
+         // ignore if s2 is not car
+         for (SCM s2 = car; scm_is_pair (s2);
+          s2 = scm_cdr (s2))
+           {
+             int pos = scm_to_int (scm_car(s2));
+             //              ledger_set.insert(pos);
+             internal_ledgers.add_ledger(pos);
+           }
+         internal_ledgers.calculate_extent();
+         internal_ledgers_container.push_back(internal_ledgers);
+       }
+      
+    }
+  
+
+  
   for (vsize i = heads.size (); i--;)
     {
       Item *h = dynamic_cast<Item *> (heads[i]);
 
       int pos = Staff_symbol_referencer::get_rounded_position (h);
-      if (pos && !staff_extent.contains (pos))
+      if (pos != 0 && !staff_extent.contains (pos))
        {
          Interval head_extent = h->extent (common[X_AXIS], X_AXIS);
          Interval ledger_extent = head_extent;
@@ -259,6 +459,22 @@ Ledger_line_spanner::print (SCM smob)
          reqs[rank][vdir].position_
            = vdir * max (vdir * reqs[rank][vdir].position_, vdir * pos);
        }
+      
+      if (contains(pos, internal_ledgers_container))
+       {
+         Interval head_extent = h->extent (common[X_AXIS], X_AXIS);
+         Interval ledger_extent = head_extent;
+         ledger_extent.widen (length_fraction * head_extent.length ());
+
+         Direction vdir = Direction (sign (pos));
+         int rank = h->get_column ()->get_rank ();
+
+         reqs_internal[rank].ledger_extent_.unite (ledger_extent);
+         reqs_internal[rank].head_extent_.unite (head_extent);
+         reqs_internal[rank].position_
+           = vdir * max (vdir * reqs_internal[rank].position_, vdir * pos);
+       }
+      
     }
 
   // determine maximum size for non-colliding ledger.
@@ -312,30 +528,14 @@ Ledger_line_spanner::print (SCM smob)
       int pos = Staff_symbol_referencer::get_rounded_position (h);
       if (!staff_extent.contains (pos - sign (pos)))
        {
-         Interval head_size = h->extent (common[X_AXIS], X_AXIS);
-         Interval ledger_size = head_size;
-         ledger_size.widen (ledger_size.length () * length_fraction);
-
-         Interval max_size = reqs[h->get_column ()->get_rank ()]
-           [Direction (sign (pos))].ledger_extent_;
+         Interval ledger_size;
+         Real left_shorten;
 
-         ledger_size.intersect (max_size);
-         Real left_shorten = 0.0;
-         if (Grob *g = unsmob_grob (h->get_object ("accidental-grob")))
-           {
-             Interval accidental_size = g->extent (common[X_AXIS], X_AXIS);
-             Real d
-               = linear_combination (Drul_array<Real> (accidental_size[RIGHT],
-                                                       head_size[LEFT]),
-                                     0.0);
-
-             left_shorten = max (-ledger_size[LEFT] + d, 0.0);
-
-             /*
-               TODO: shorten 2 ledger lines for the case natural +
-               downstem.
-             */
-           }
+         find_ledger_size(h, common, length_fraction,
+                          reqs[h->get_column ()->get_rank ()]
+                          [Direction (sign (pos))],
+                          ledger_size,
+                          left_shorten);
 
          ledgers.add_stencil (brew_ledger_lines (staff, pos, staff_extent,
                                                  halfspace,
@@ -343,6 +543,30 @@ Ledger_line_spanner::print (SCM smob)
                                                  ledger_size,
                                                  left_shorten));
        }
+  
+      /* handle internal ledger lines */
+      for (vector<Internal_ledgers>::const_iterator internal_ledgers = 
internal_ledgers_container.begin();
+          internal_ledgers != internal_ledgers_container.end();
+          ++internal_ledgers)
+       {
+         if ((*internal_ledgers).contains(pos))
+           {
+             Interval ledger_size;
+             Real left_shorten;
+             
+             find_ledger_size(h, common, length_fraction,
+                              reqs_internal[h->get_column ()->get_rank ()],
+                              ledger_size,
+                              left_shorten);
+
+             ledgers.add_stencil (brew_internal_ledger_lines 
(*internal_ledgers,
+                                                            halfspace,
+                                                            
ledgerlinethickness,
+                                                            ledger_size,
+                                                            left_shorten));
+           }
+       }
+      
     }
 
   ledgers.translate_axis (-me->relative_coordinate (common[X_AXIS], X_AXIS),
@@ -363,7 +587,8 @@ ADD_INTERFACE (Ledger_line_spanner,
               "thickness "
               "minimum-length-fraction "
               "length-fraction "
-              "gap");
+              "gap "
+              "internal-ledger-lines");
 
 struct Ledgered_interface
 {
diff --git a/lily/note-heads-engraver.cc b/lily/note-heads-engraver.cc
index 6c065af..4e9b0fe 100644
--- a/lily/note-heads-engraver.cc
+++ b/lily/note-heads-engraver.cc
@@ -22,6 +22,35 @@ using namespace std;
 
 #include "translator.icc"
 
+class Layout_pos 
+{
+public:
+  virtual int pos(Pitch* pit) = 0;
+};
+
+class Layout_pos_traditional : public Layout_pos
+{
+public:
+  virtual int pos(Pitch* pit)
+  {
+    return pit ? pit->steps () : 0;
+  }
+};
+
+static Layout_pos_traditional layout_pos_traditional;
+
+class Layout_pos_semitone : public Layout_pos
+{
+public:
+  virtual int pos(Pitch* pit)
+  {
+    return pit ? pit->semitone_pitch() : 0;
+  }
+};
+
+static Layout_pos_semitone layout_pos_semitone;
+  
+
 class Note_heads_engraver : public Engraver
 {
   vector<Item*> notes_;
@@ -50,6 +79,18 @@ Note_heads_engraver::listen_note (Stream_event *ev)
 void
 Note_heads_engraver::process_music ()
 {
+  Layout_pos *layout_pos = &layout_pos_traditional;
+  SCM layout_property = get_property("staff-line-layout");
+  if (layout_property)
+    {
+      if (ly_is_equal(layout_property, ly_symbol2scm("traditional"))){
+       layout_pos = &layout_pos_traditional;
+      }
+      else if (ly_is_equal(layout_property, ly_symbol2scm("semitone"))){
+       layout_pos = &layout_pos_semitone;
+      }
+    }
+      
   for (vsize i = 0; i < note_evs_.size (); i++)
     {
       Stream_event *ev = note_evs_[i];
@@ -63,11 +104,16 @@ Note_heads_engraver::process_music ()
        ev->origin ()->warning (_ ("NoteEvent without pitch"));
 #endif
 
-      int pos = pit ? pit->steps () : 0;
       SCM c0 = get_property ("middleCPosition");
+      int middleC;
       if (scm_is_number (c0))
-       pos += scm_to_int (c0);
-
+       middleC = scm_to_int(c0);
+      else
+       middleC = 0;
+      int pos = layout_pos->pos(pit);
+           
+      pos += middleC;
+      
       note->set_property ("staff-position", scm_from_int (pos));
 
       /*
@@ -106,5 +152,6 @@ ADD_TRANSLATOR (Note_heads_engraver,
                /* doc */ "Generate noteheads.",
                /* create */
                "NoteHead ",
-               /* read */ "middleCPosition",
+               /* read */ "middleCPosition "
+               "staff-line-layout",
                /* write */ "");
diff --git a/lily/staff-symbol.cc b/lily/staff-symbol.cc
index 6edb1f9..554b2e2 100644
--- a/lily/staff-symbol.cc
+++ b/lily/staff-symbol.cc
@@ -186,4 +186,5 @@ ADD_INTERFACE (Staff_symbol, "staff-symbol-interface",
               "staff-space "
               "thickness "
               "width "
+              "internal-ledger-lines "
               );
diff --git a/scm/define-context-properties.scm 
b/scm/define-context-properties.scm
index fe32b4e..bf781bb 100644
--- a/scm/define-context-properties.scm
+++ b/scm/define-context-properties.scm
@@ -372,6 +372,7 @@ up the interpretation phase. This speeds up debugging large 
scores.")
      (squashedPosition ,integer? " Vertical position of
 squashing for @internalsref{Pitch_squash_engraver}.")
 
+     (staff-line-layout ,symbol? "Layout of staff lines, 'traditional, or 
'semitone.")
      (stringNumberOrientations ,list? "See @code{fingeringOrientations}")
      (strokeFingerOrientations ,list? "See @code{fingeringOrientations}")
      (stringOneTopmost ,boolean? "Whether the 1st string is printed on the
diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm
index dddcef9..4c79a53 100644
--- a/scm/define-grob-properties.scm
+++ b/scm/define-grob-properties.scm
@@ -204,6 +204,7 @@ and slur ignore eachother.")
 set beam/slur quant to this position, and print the respective scores.")
      (inspect-index ,integer? "If debugging is set,
 set beam/slur configuration to this index, and print the respective scores.")
+     (internal-ledger-lines ,list? "Ledger lines placed between first and last 
lines of staff.")
      (implicit ,boolean? "Is this an implicit bass figure?")
      (keep-inside-line ,boolean? "If set, this column cannot have
 things sticking into the margin.")
-- 
1.4.4.4





reply via email to

[Prev in Thread] Current Thread [Next in Thread]