]> git.donarmstrong.com Git - lilypond.git/commitdiff
Add Stencil::stack function and ly:stencil-stack
authorDavid Kastrup <dak@gnu.org>
Fri, 10 May 2013 09:48:21 +0000 (11:48 +0200)
committerDavid Kastrup <dak@gnu.org>
Sun, 26 May 2013 00:33:02 +0000 (02:33 +0200)
This is the base for line-forming and stacking functions.  As opposed
to Stencil::add_at_edge (which is better suited for creating combined
glyphs), the metrics facilitate progress along a line.

lily/include/stencil.hh
lily/stencil-scheme.cc
lily/stencil.cc
scm/safe-lily.scm

index 6b03eb07a0eea1984892f90cba96d7f6a45169c9..6a89e022daba721fe449b708bb5867616c21ccfa 100644 (file)
@@ -71,6 +71,7 @@ public:
      Set dimensions to empty, or to (Interval (0, 0), Interval (0, 0) */
   void set_empty (bool);
   void add_at_edge (Axis a, Direction d, const Stencil &m, Real padding);
+  void stack (Axis a, Direction d, const Stencil &m, Real padding, Real mindist);
   void add_stencil (Stencil const &m);
   void translate (Offset);
   Stencil translated (Offset) const;
index 1afe56cc2df94a5ee716b90a220508213e6939f9..70358cd81e071af4e4034b021d1e11aca7c557a5 100644 (file)
@@ -136,6 +136,57 @@ LY_DEFINE (ly_stencil_combine_at_edge, "ly:stencil-combine-at-edge",
   return result.smobbed_copy ();
 }
 
+LY_DEFINE (ly_stencil_stack, "ly:stencil-stack",
+           4, 2, 0, (SCM first, SCM axis, SCM direction,
+                     SCM second,
+                     SCM padding,
+                     SCM mindist),
+           "Construct a stencil by stacking @var{second} next to @var{first}."
+           "  @var{axis} can be 0 (x-axis) or@tie{}1 (y-axis)."
+           "  @var{direction} can be -1 (left or down) or@tie{}1 (right or"
+           " up).  The stencils are juxtaposed with @var{padding} as extra"
+           " space.  @var{first} and @var{second} may also be @code{'()} or"
+           " @code{#f}.  As opposed to @code{ly:stencil-combine-at-edge},"
+           " metrics are suited for successively accumulating lines of"
+           " stencils.  Also, @var{second} stencil is drawn last.\n\n"
+           "If @var{mindist} is specified, reference points are placed"
+           " apart at least by this distance.  If either of the stencils"
+           " is spacing, @var{padding} and @var{mindist} do not apply.")
+{
+  Stencil *s1 = unsmob_stencil (first);
+  Stencil *s2 = unsmob_stencil (second);
+  Stencil result;
+
+  SCM_ASSERT_TYPE (s1 || first == SCM_BOOL_F || first == SCM_EOL,
+                   first, SCM_ARG1, __FUNCTION__, "Stencil, #f or ()");
+  SCM_ASSERT_TYPE (s2 || second == SCM_BOOL_F || second == SCM_EOL,
+                   second, SCM_ARG4, __FUNCTION__, "Stencil, #f or ()");
+  LY_ASSERT_TYPE (is_axis, axis, 2);
+  LY_ASSERT_TYPE (is_direction, direction, 3);
+
+  Real p = 0.0;
+  if (padding != SCM_UNDEFINED)
+    {
+      LY_ASSERT_TYPE (scm_is_number, padding, 5);
+      p = scm_to_double (padding);
+    }
+  Real d = -infinity_f;
+  if (!SCM_UNBNDP (mindist))
+    {
+      LY_ASSERT_TYPE (scm_is_number, mindist, 6);
+      d = scm_to_double (mindist);
+    }
+
+  if (s1)
+    result = *s1;
+
+  if (s2)
+    result.stack (Axis (scm_to_int (axis)),
+                  Direction (scm_to_int (direction)), *s2, p, d);
+
+  return result.smobbed_copy ();
+}
+
 LY_DEFINE (ly_stencil_add, "ly:stencil-add",
            0, 0, 1, (SCM args),
            "Combine stencils.  Takes any number of arguments.")
index c9105c7e8dcc5a85807dfe85617d82d5c621b378..078ebbb4690f32e091928ab3937f05adb18ff4d9 100644 (file)
@@ -270,7 +270,7 @@ Stencil::add_at_edge (Axis a, Direction d, Stencil const &s, Real padding)
   // Material that is empty in the axis of reference has only limited
   // usefulness for combining.  We still retain as much information as
   // available since there may be uses like setting page links or
-  // background color or watermarks.
+  // background color or watermarks, and off-axis extents.
 
   if (is_empty (a))
     {
@@ -283,6 +283,7 @@ Stencil::add_at_edge (Axis a, Direction d, Stencil const &s, Real padding)
   if (s.is_empty (a))
     {
       Stencil toadd (s);
+      // translation does not affect axis-empty extent box.
       toadd.translate_axis (first_extent[d], a);
       add_stencil (toadd);
       return;
@@ -305,6 +306,108 @@ Stencil::add_at_edge (Axis a, Direction d, Stencil const &s, Real padding)
   add_stencil (toadd);
 }
 
+// Stencil::stack is mainly used for assembling lines or columns
+// of stencils.  For the most common case of adding at the right, the
+// reference point of the added stencil is usually placed at the right
+// edge of the current one, unless the added stencil has a negative
+// left extent in which case its left edge is placed at the right edge
+// of the current one.
+//
+// Spacing is special in that it is applied without padding.  Spacing
+// at the right edge shifts the right edge accordingly.
+//
+// For spacing at the left edge, there are several approaches.  In
+// order to get to predictable behavior, we want to have at least a
+// continuous approach.  An obvious idea is to do a "translate" by the
+// appropriate amount.  Doing that while retaining the nominal left
+// edge seems like the most straightforward way.
+
+void
+Stencil::stack (Axis a, Direction d, Stencil const &s, Real padding, Real mindist)
+{
+  // Material that is empty in the axis of reference can't be sensibly
+  // stacked.  We just revert to add_at_edge behavior then.
+
+  if (is_empty (a))
+    {
+      Stencil toadd (s);
+      toadd.add_stencil (*this);
+      expr_ = toadd.expr ();
+      dim_ = toadd.extent_box ();
+      return;
+    }
+
+  Interval first_extent = extent (a);
+
+  if (s.is_empty (a))
+    {
+      Stencil toadd (s);
+      toadd.translate_axis (first_extent[d], a);
+      toadd.add_stencil (*this);
+      expr_ = toadd.expr ();
+      dim_ = toadd.extent_box ();
+      return;
+    }
+
+  Interval next_extent = s.extent (a);
+
+  // It is somewhat tedious to special-case all spacing, but it turns
+  // out that not doing so makes it astonishingly hard to make the
+  // code do the correct thing.
+
+  // If first is spacing, we translate second accordingly without
+  // letting this affect its backward edge.
+  if (is_empty (other_axis (a)))
+    {
+      Stencil toadd (s);
+      Real offset = d * first_extent.delta ();
+      toadd.translate_axis (offset, a);
+      toadd.add_stencil (*this);
+      expr_ = toadd.expr ();
+      dim_ = toadd.extent_box ();
+      dim_[a][-d] = next_extent[-d];
+      dim_[a][d] = next_extent[d] + offset;
+      return;
+    }
+
+  // If next is spacing, similar action:
+  if (s.is_empty (other_axis (a)))
+    {
+      Stencil toadd (s);
+      Real offset = first_extent [d];
+      toadd.translate_axis (offset, a);
+      toadd.add_stencil (*this);
+      expr_ = toadd.expr ();
+      dim_ = toadd.extent_box ();
+      dim_[a][-d] = first_extent[-d];
+      dim_[a][d] = first_extent[d] + d * next_extent.delta ();
+      return;
+    }
+
+
+  Real offset = first_extent[d];
+
+  // If the added stencil has a backwardly protruding edge, we make
+  // room for it when combining.
+
+  if (d * next_extent [-d] < 0)
+    offset -= next_extent [-d];
+
+  offset += d * padding;
+
+  if (offset * d < mindist)
+    offset = d * mindist;
+
+  Stencil toadd (s);
+  toadd.translate_axis (offset, a);
+  toadd.add_stencil (*this);
+  expr_ = toadd.expr ();
+  dim_ = toadd.extent_box ();
+  dim_[a][-d] = first_extent [-d];
+  dim_[a][d] = next_extent [d] + offset;
+}
+
+
 Stencil
 Stencil::in_color (Real r, Real g, Real b) const
 {
index 202cad688375bf8dab64daf971a2abd0e661f295..cfc088c8b37dd6cd9d069452d66745e9c066094c 100644 (file)
    ly:stencil-combine-at-edge
    ly:stencil-expr
    ly:stencil-extent
+   ly:stencil-stack
    ly:stencil-translate
    ly:stencil-translate-axis
    ly:stencil?