From: David Kastrup Date: Fri, 10 May 2013 09:48:21 +0000 (+0200) Subject: Add Stencil::stack function and ly:stencil-stack X-Git-Tag: release/2.17.19-1~5^2~14 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=7cb16a272217fa147b6f130ef7c467288ad208bf;p=lilypond.git Add Stencil::stack function and ly:stencil-stack 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. --- diff --git a/lily/include/stencil.hh b/lily/include/stencil.hh index 6b03eb07a0..6a89e022da 100644 --- a/lily/include/stencil.hh +++ b/lily/include/stencil.hh @@ -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; diff --git a/lily/stencil-scheme.cc b/lily/stencil-scheme.cc index 1afe56cc2d..70358cd81e 100644 --- a/lily/stencil-scheme.cc +++ b/lily/stencil-scheme.cc @@ -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.") diff --git a/lily/stencil.cc b/lily/stencil.cc index c9105c7e8d..078ebbb469 100644 --- a/lily/stencil.cc +++ b/lily/stencil.cc @@ -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 { diff --git a/scm/safe-lily.scm b/scm/safe-lily.scm index 202cad6883..cfc088c8b3 100644 --- a/scm/safe-lily.scm +++ b/scm/safe-lily.scm @@ -135,6 +135,7 @@ ly:stencil-combine-at-edge ly:stencil-expr ly:stencil-extent + ly:stencil-stack ly:stencil-translate ly:stencil-translate-axis ly:stencil?