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.")
// 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))
{
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;
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
{