% language=us runpath=texruns:manuals/ontarget

\startcomponent ontarget-envelopes

\environment ontarget-style

\startchapter[title={Pushing the envelope}]

Here I describe the results of some exploration and experiments by Mikael
Sundqvist and me. We got sidetracked from intersections, arcs and drawing
functions when we noticed some artifacts with envelopes. But what are envelopes
actually? Let us start with a simple path:

\startbuffer[testpath]
\startMPinclusions
    path TestPath ; TestPath := fullcircle xyscaled (10cm,1cm)
\stopMPinclusions
\stopbuffer

\startbuffer[a]
\startMPcode
    draw TestPath withpen pencircle scaled 2mm withcolor darkred ;
\stopMPcode
\stopbuffer

\startbuffer[b]
\startMPcode
    fill TestPath withpen pencircle scaled 2mm withcolor darkred  ;
\stopMPcode
\stopbuffer

\startbuffer[c]
\startMPcode
    draw TestPath withpen pensquare scaled 2mm withcolor darkblue ;
\stopMPcode
\stopbuffer

\startbuffer[d]
\startMPcode
    fill TestPath withpen pensquare scaled 2mm withcolor darkblue ;
\stopMPcode
\stopbuffer

\startbuffer[e]
\startMPcode
    fill TestPath withpen pensquare scaled 2mm withcolor darkgreen
        withtransparency (1,.5) ;
\stopMPcode
\stopbuffer

\typebuffer[testpath]

When we draw this with a circular pen we get this:

\typebuffer[a] \startlinecorrection \getbuffer[testpath,a] \stoplinecorrection

Filling gives:

\typebuffer[b] \startlinecorrection \getbuffer[testpath,b] \stoplinecorrection

When a \type {pencircle} is used \METAPOST\ delegates the work to the backend
because \POSTSCRIPT\ has a circular pen, otherwise it has to calculate the
to|-|be|-|filled shape itself. The backend has to do some path juggling in the
case of \PDF\ because there a pen transform is different from \POSTSCRIPT.

\typebuffer[c] \startlinecorrection \getbuffer[testpath,c] \stoplinecorrection

Here we draw the shape with a square pen while filling gives:

\typebuffer[d] \startlinecorrection \getbuffer[testpath,d] \stoplinecorrection

In most cases this works out well but there are some hidden issues. These get
exposed when we use a transparency:

\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection

It are these artifacts that we will explore a little. For that we will render
quite some graphics. We could show numerous more examples but when you are a
\CONTEXT\ user you will be able to make plenty yourself by looking at these
examples.

\startbuffer[e]
\startMPcode
    fill fullcircle xyscaled (.8TextWidth,2cm)
        withpen pensquare scaled 8mm
        withcolor darkgreen
        withtransparency (1,.5) ;
\stopMPcode
\stopbuffer

\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection

When we were playing with the \type {envelope} primitive we noticed these
artifacts and we spent quite some time looking at the code to see where it comes
from and if we could prevent this. It was then that we realized that the fill
actually also uses these envelopes but that it gets delayed till the shapes are
flushed to the backend. That meant that we could use fills with transparencies
as simple test cases.

The first thing to get rid of is the weird blob at the right end of the fill in
this example. Not really understanding all what went on, we explored all kind of
shapes and temporarily disabled some of the code in the \METAPOST\ library to see
where it crept in. We decided that touching the code to get rid of for instance
rounding issues or potential direction related side effects made no sense. In the
end the solution was simple:

\startbuffer[e]
\startMPcode
    pen p ; p := makepen(unitsquare rotated eps) ;
    fill fullcircle xyscaled (.8TextWidth,2cm)
        withpen p scaled 8mm
        withcolor darkgreen
        withtransparency (1,.5) ;
\stopMPcode
\stopbuffer

\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection

We show what the \type {envelope} primitive gives us:

\startbuffer[e]
\startMPcode
    pen p ; p := makepen(unitsquare rotated eps) ;
    path e ; e :=
        envelope (p scaled 8mm)
    of
        (fullcircle xyscaled (.8TextWidth,2cm))
    ;
    draw e
        withpen pencircle scaled 2mm
        withcolor darkgreen
        withtransparency (1,.5) ;
    drawpoints e ;
\stopMPcode
\stopbuffer

\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection

This looks okay compared to previous the examples but we have only a simple path
here, while the fill actually has two:

\startbuffer[e]
\startMPcode
    pen p ; p := makepen(unitsquare rotated eps) ;
    enfill fullcircle xyscaled (.8TextWidth,2cm)
        withpen p scaled 8mm
        withcolor darkgreen
        withtransparency (1,.5) ;
\stopMPcode
\stopbuffer

\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection

So how do we get that inner shape? Once you know what a fill actually outputs to
the backend it is easy! There are two envelopes: the normal one and one made from
the reverse path (or in internal \METAPOST\ speak: htap). In the previous example
the \type {enfill} treats the path as a fill but will draw the envelopes instead.
As with \type {eofill}, \type {eoclip} and path accumulators this is a \METAFUN\
backend related feature but we introduced \type {enfill} as a new one.

\startbuffer[e]
\startMPcode
    pen p ; p := makepen(unitsquare rotated eps) ;
    draw
        envelope (p scaled 8mm) of
        (fullcircle xyscaled (.8TextWidth,2cm))
        withpen pencircle scaled 2mm
        withcolor darkgreen
        withtransparency (1,.5) ;
    draw
        envelope (p scaled 8mm) of
        reverse (fullcircle xyscaled (.8TextWidth,2cm))
        withpen pencircle scaled 2mm
        withcolor darkblue
        withtransparency (1,.5) ;
\stopMPcode
\stopbuffer

\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection

We're now ready for the real deal but keep in mind that what we show here is the
result of stepwise growing insight combined with adding some features to the
engine that not only makes it possible to illustrate this but also might prove to
be useful. The used primitives will be explained later, for now we just stick
to the results.

\startMPextensions
    def pentest (expr pth, psh, convex, enhance, width) =
        image (
            save p, s, w, pp, somepen ;
            numeric s ; s := width ;
            numeric w ; w := (15/100)*s ;

            path p, e, pp, somepen ;

            p := pth scaled s ;

            % gets rid of bad end condition (rectangle) (introduced close points)

            pp := psh rotated eps ;

            if convex :
                pen somepen ; somepen := makepen (pp) ;
            else :
                nep somepen ; somepen := makenep (pp) ;
            fi ;

            if cycle p : fill else : draw fi p
                withcolor blue
                withpen somepen scaled w
                withtransparency (1,.5)
            ;
            drawarrow p ;
            drawpoints p ;

            path ppp ; ppp := pp scaled w;
            path qqq ; qqq := convexed ppp;

            for i within p :
                drawarrow  qqq shifted pathpoint withcolor yellow dashed evenly withpen pencircle scaled 1;
                drawarrow  ppp shifted pathpoint withcolor white;
                drawpoints ppp shifted pathpoint withcolor white ;
            endfor ;

          % for i within arcpointlist 50 of p  :
          %     draw ppp shifted pathpoint withcolor white ;
          % endfor ;

            defaultscale := .4 ;
            e := envelope somepen scaled w of p ;
          % e := e scrutinized 0.01 ;
            draw            e withcolor darkgreen withpen pencircle scaled 1mm;
            drawpoints      e ;
          % drawpointlabels e withcolor green ;
            e := envelope somepen scaled w of reverse p ;
          % e := e scrutinized 0.01 ;
            draw            e withcolor yellow withpen pencircle scaled 1mm;
            drawpoints      e ;
          % drawpointlabels e withcolor yellow ;

            if enhance :
                e := e scrutinized 0.01 ;
                draw convexed e withcolor darkmagenta withpen pencircle scaled 2mm ;
            fi ;

        )
    enddef ;
\stopMPextensions

\startMPextensions
    def TestShapesA(expr pth, convex, enhance, dy) =
        draw pentest (pth, fullcircle,   convex, enhance, .4TextWidth) shifted (          0,          0) ;
        draw pentest (pth, fulldiamond,  convex, enhance, .4TextWidth) shifted (.5TextWidth,          0) ;
        draw pentest (pth, fulltriangle, convex, enhance, .4TextWidth) shifted (          0,dy*TextWidth) ;
        draw pentest (pth, fullsquare,   convex, enhance, .4TextWidth) shifted (.5TextWidth,dy*TextWidth) ;
    enddef ;

    def TestShapesB(expr pth, convex, enhance, dy) =
        draw pentest (pth, starring(-1/3), convex, enhance, .35TextWidth) shifted (          0,          0) ;
        draw pentest (pth, starring(-1/2), convex, enhance, .35TextWidth) shifted (.5TextWidth,          0) ;
        draw pentest (pth, starring(-eps), convex, enhance, .35TextWidth) shifted (          0,dy*TextWidth) ;
        draw pentest (pth, starring(-1/2), convex, enhance, .35TextWidth) shifted (.5TextWidth,dy*TextWidth) ;
    enddef ;
\stopMPextensions

\startplacefigure[reference=fig:A:T:F:1,title={Using four different relatively large pens on a circle.}]
    \startMPcode
        TestShapesA(fullcircle, true, false, .5) ;
    \stopMPcode
\stopplacefigure

\in {Figure} [fig:A:T:F:1] shows a circle filled (or enveloped) with pens made from
\type {fullcircle}, \type {fulldiamond}, \type {fulltriangle} and \type
{fullsquare}. The paths that we use for the pens are also shown. The outcome can
be puzzling but after going over the code (in the engine) and trying to reason
the logic it becomes clear that the unexpected is mostly due to the fact that
there is no other way to draw the path (read: meet the criteria).

When looking closely at the results (adding labels to the points and zooming in)
one will notice more side effects. Because we rotate over \type {eps} to get rid
of the weird end situation we can end up with more points than we like and these
are so close to each other that one doesn't notice them. For this we can apply
the scrutinizer:

\starttyping
e := e scrutinized 0.01 ;
\stoptyping

When that is done we can wonder if a simplified (inner) path is possible. I tried
a few solutions using the \LUA\ interface while Mikael (as mathematician)
followed the more scientific approach but the results largely depend on the pens
and shapes.

\startplacefigure[reference=fig:B:T:F:1]
    \startMPcode
        TestShapesB(fullcircle, true, false, .5) ;
    \stopMPcode
\stopplacefigure

Actually when doing all that we used a more complex pen in several variants. This
is shown in \in {figure} [fig:B:T:F:1]. Notice the dashed lines here. When a pen
is defined there is some checking going on. One is that circular pens get no
treatment at all and just pass through the system. Basically any single point
cycle is considered as elliptical anyway. Then the path turned into a so called
\quote {convex} path. It also showed us the real pen being used. When out of
curiosity I commented that bit of code I noticed that we could achieve
interesting results. The result is that we now have a \type {convexed} primitive.
After all the code was there so it took only a few lines to add this primitive.
In \in {figure} [fig:B:F:F:1] you can see the result of a unconvexed pen.

\startplacefigure[reference=fig:B:F:F:1]
    \startMPcode
        TestShapesB(fullcircle, false, false, .5) ;
    \stopMPcode
\stopplacefigure

We can also calculate envelopes of non|-|cyclic paths which is demonstrated in
\in {figure} [fig:B:T:F:2] and \in {figure} [fig:B:F:F:2]. There is however some
trickery involved. Just to make this easier the \METAFUN\ macro package has a
type {starring} macro that makes such a star:

\starttyping
path p ; p := starring(-1/2) rotated eps ;
\stoptyping

This star can become a pen:

\starttyping
pen somepen ; somepen := makepen (pp) ;
\stoptyping

And as mentioned pens get convexed by default. Even worse, whenever we transform
a pen it gets convexed again. When we fill a shape the pen gets attached to that shape
and the backend will do the enveloping. The easiest way to consistently avoid
convexing was to introduce a new pen type.

\starttyping
nep somepen ; somepen := makenep (pp) ;
\stoptyping

The somewhat weird short \type {nep} perfectly fits the bill as in Dutch it means
fake. A pen defined this way stays unconvexed. Actually there is another property
where pens differ from regular paths: they are double linked. In original
\METAPOST\ that back (prev) link uses a field in a knot record that is not used
by pen paths. The path that gets pencilled also abuses one of knot fields for
keeping track of the offset that a point has relative to the current point in the
pen. It was good moment to also make regular paths double linked lists. That
comes at the cost of an extra pointer in the knot record but we could also save
some space by using smaller slots for other fields. Memory is not our biggest
worry anyway. \footnote {Of course adding code is but when looking in more detail
at the code involved it was actually possible to simplify the code a bit so there
we gained}. Double linking meant that there was no need for doing that when
making pens. \footnote {It makes it possible to get points relative to the
current point in iterators over paths that we introduced a while ago, which makes
for high performance path manipulators.}

\startplacefigure[reference=fig:B:T:F:2]
    \startMPcode
        TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0), true, false, .35) ;
    \stopMPcode
\stopplacefigure

\startplacefigure[reference=fig:B:F:F:2]
    \startMPcode
        TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0), false, false, .35) ;
    \stopMPcode
\stopplacefigure

We can apply the \type {convexed} primitive to the inner envelope which is demonstrated
in \in {figure} [fig:B:T:T:3] and \in {figure} [fig:B:F:T:3]. Of course it is debatable
how useful this is but as with all these \METAPOST\ shapes, it has some charm.

\startplacefigure[reference=fig:B:T:T:3]
    \startMPcode
        TestShapesB(fullcircle, true, true, .5) ;
    \stopMPcode
\stopplacefigure

\startplacefigure[reference=fig:B:F:T:3]
    \startMPcode
        TestShapesB(fullcircle, false, true, .5) ;
    \stopMPcode
\stopplacefigure

% \startplacefigure[reference=fig:B:T:F:2]
%     \startMPcode
%         TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0) -- cycle, true, false) ;
%     \stopMPcode
% \stopplacefigure

% \startplacefigure[reference=fig:B:F:F:2]
%     \startMPcode
%         TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0) -- cycle, false, false) ;
%     \stopMPcode
% \stopplacefigure

% \startMPpage[offset=1dk]
%     enfill         fullcircle scaled 2cm withpen makepen(fullsquare) scaled 1pt withcolor darkred ;
%     enfill         fullcircle scaled 3cm withpen makepen(fullsquare) scaled 1pt withcolor darkblue ;
%     enfill reverse fullcircle scaled 1cm withpen makepen(fullsquare) scaled 1pt withcolor darkgreen ;
% \stopMPpage

% \startMPpage[offset=1dk]
%     draw envelope makepen(fullsquare)         of (        fullcircle scaled 2cm) withpen pencircle scaled 1pt withcolor darkred ;
%     draw envelope makepen(fullsquare)         of (reverse fullcircle scaled 3cm) withpen pencircle scaled 1pt withcolor darkblue ;
%     draw envelope makepen(reverse fullsquare) of (        fullcircle scaled 1cm) withpen pencircle scaled 1pt withcolor darkgreen ;
% \stopMPpage

\page

To what extend does all this influence the output? As long as we don't use
transparencies we're quite okay unless we use a pen size that introduces the more
extreme overshoots. If you think these phenomena only relate to \METAPOST\
output, you're wrong. Over the past decades I've seen various fonts that exhibit
the same small spikes and other artifacts btu as we often see the shapes at small
sizes it goes unnoticed. A particular sensitive areas is variable fonts where,
when the ranges on which the various dimensions operate are too liberal, you can
also get these effects. After all, glyphs are filled shapes. To that you can also
add the fact that they are single (connected) paths drawn with \type {eofill}.

The final format a graphics ends up in can be \PDF. Take the following three
shapes and watch the subtle side effect of rotating either the to be drawn shape
or the pen.

\startbuffer[e]
\startMPcode
    fill fullcircle xyscaled (5cm,3cm)
        withpen makepen(fullsquare) scaled 2mm
        withcolor darkred
        withtransparency (1,.5) ;
    fill fullcircle xyscaled (5cm,3cm)
        shifted (6cm,0)
        withpen makepen(fullsquare rotated eps) scaled 2mm
        withcolor darkblue
        withtransparency (1,.5) ;
    fill fullcircle rotated eps xyscaled (5cm,3cm)
        shifted (12cm,0)
        withpen makepen(fullsquare) scaled 2mm
        withcolor darkgreen
        withtransparency (1,.5) ;
\stopMPcode
\stopbuffer

\typebuffer[e] \startlinecorrection \getbuffer[e] \stoplinecorrection

This produces four filled paths in the \PDF\ file, a normal and a reverse path
per shape. I show the whole output because you can see how some points of the
\quote {inside} curve are sort of duplicated: they have the same coordinates but
can have different control points.

\starttabulate[|lT|lT|lT|]
\BC        \BC inner                               \BC outer                      \NC \NR
\BC left   \NC 25=26 31=32 35=36 39=40 43=25       \NC                            \NC \NR
\BC middle \NC                                     \NC 49=50=51 54=55 58=59 62=63 \NC \NR
\BC right  \NC 107=108=109 112=113 116=117 120=121 \NC 89=105                     \NC \NR
\stoptabulate

Here is the output. Each combination is between bound by the transparency
operators \type {/Tr1} and \type {/Tr0} and has different colors.

\startlinenumbering
\starttyping
% mps graphic 1: begin
q
/Tr1 gs
0.625 0 0 rg 0.625 0 0 RG
10 M
1 j
 45.354315 2.83464 m
 45.354315 14.111559 40.874577 24.926605 32.900591 32.900591 c
 24.926605 40.874577 14.111559 45.354315 2.83464 45.354315 c
  2.83464 45.354315 -2.83464 45.354315 -2.83464 45.354315 c
 -2.83464 45.354315 l
-14.111559 45.354315 -24.926605 40.874577 -32.900591 32.900591 c
-40.874577 24.926605 -45.354315 14.111559 -45.354315 2.83464 c
-45.354315 2.83464 -45.354315 -2.83464 -45.354315 -2.83464 c
-45.354315 -2.83464 l
-45.354315 -14.111559 -40.874577 -24.926605 -32.900591 -32.900591 c
-24.926605 -40.874577 -14.111559 -45.354315 -2.83464 -45.354315 c
-2.83464 -45.354315 2.83464 -45.354315 2.83464 -45.354315 c
2.83464 -45.354315 l
14.111559 -45.354315 24.926605 -40.874577 32.900591 -32.900591 c
40.874577 -24.926605 45.354315 -14.111559 45.354315 -2.83464 c
45.354315 -2.83464 45.354315 2.83464 45.354315 2.83464 c
45.354315 2.83464 l
h f
39.685035 -2.83464 m
39.685035 -2.83464 45.354315 -2.83464 45.354315 -2.83464 c
45.354315 -2.83464 45.354315 2.83464 45.354315 2.83464 c
45.354315 2.83464 39.685035 2.83464 39.685035 2.83464 c
39.685035 -8.442279 35.205297 -19.257325 27.231311 -27.231311 c
19.257325 -35.205297 8.442279 -39.685035 -2.83464 -39.685035 c
-2.83464 -39.685035 l
-2.83464 -39.685035 2.83464 -39.685035 2.83464 -39.685035 c
-8.442279 -39.685035 -19.257325 -35.205297 -27.231311 -27.231311 c
-35.205297 -19.257325 -39.685035 -8.442279 -39.685035 2.83464 c
-39.685035 2.83464 l
-39.685035 2.83464 -39.685035 -2.83464 -39.685035 -2.83464 c
-39.685035 8.442279 -35.205297 19.257325 -27.231311 27.231311 c
-19.257325 35.205297 -8.442279 39.685035 2.83464 39.685035 c
2.83464 39.685035 l
2.83464 39.685035 -2.83464 39.685035 -2.83464 39.685035 c
8.442279 39.685035 19.257325 35.205297 27.231311 27.231311 c
35.205297 19.257325 39.685035 8.442279 39.685035 -2.83464 c
39.685035 -2.83464 l
h f
/Tr0 gs
0 g 0 G
/Tr1 gs
0 0 0.625 rg 0 0 0.625 RG
158.740139 -2.834616 m
158.740139 -2.834252 l
158.740139 -2.834252 158.740091 2.835028 158.740091 2.835028 c
158.739994 14.111816 154.260267 24.926715 146.286366 32.900615 c
138.31238 40.874601 127.497335 45.354339 116.220416 45.354339 c
116.220052 45.354339 l
116.220052 45.354339 110.550772 45.354291 110.550772 45.354291 c
99.273984 45.354194 88.459085 40.874467 80.485185 32.900566 c
72.511199 24.92658 68.031461 14.111535 68.031461 2.834616 c
68.031461 2.834252 l
68.031461 2.834252 68.031509 -2.835028 68.031509 -2.835028 c
68.031606 -14.111816 72.511333 -24.926715 80.485234 -32.900615 c
88.45922 -40.874601 99.274265 -45.354339 110.551184 -45.354339 c
110.551548 -45.354339 l
110.551548 -45.354339 116.220828 -45.354291 116.220828 -45.354291 c
127.497616 -45.354194 138.312515 -40.874467 146.286415 -32.900566 c
154.260401 -24.92658 158.740139 -14.111535 158.740139 -2.834616 c
h f
153.070811 2.834616 m
153.070811 -8.442303 148.591072 -19.257349 140.617086 -27.231335 c
132.643186 -35.205235 121.828288 -39.684963 110.5515 -39.685059 c
110.5515 -39.685059 116.22078 -39.685011 116.22078 -39.685011 c
116.220416 -39.685011 l
104.943497 -39.685011 94.128451 -35.205272 86.154465 -27.231286 c
78.180565 -19.257386 73.700837 -8.442488 73.700741 2.8343 c
73.700741 2.8343 73.700789 -2.83498 73.700789 -2.83498 c
73.700789 -2.834616 l
73.700789 8.442303 78.180528 19.257349 86.154514 27.231335 c
94.128414 35.205235 104.943312 39.684963 116.2201 39.685059 c
116.2201 39.685059 110.55082 39.685011 110.55082 39.685011 c
110.551184 39.685011 l
121.828103 39.685011 132.643149 35.205272 140.617135 27.231286 c
148.591035 19.257386 153.070763 8.442488 153.070859 -2.8343 c
153.070859 -2.8343 153.070811 2.83498 153.070811 2.83498 c
153.070811 2.834616 l
h f
/Tr0 gs
0 g 0 G
/Tr1 gs
0 0.625 0 rg 0 0.625 0 RG
272.125915 2.835004 m
272.125819 14.111923 267.645988 24.92693 259.671934 32.900848 c
251.697965 40.87468 240.883028 45.354315 229.606241 45.354315 c
229.606241 45.354315 223.936961 45.354315 223.936961 45.354315 c
223.936596 45.354315 l
212.659677 45.354219 201.84467 40.874388 193.870752 32.900334 c
185.89692 24.926365 181.417285 14.111428 181.417285 2.834641 c
181.417285 2.834641 181.417285 -2.834639 181.417285 -2.834639 c
181.417285 -2.835004 l
181.417381 -14.111923 185.897212 -24.92693 193.871266 -32.900848 c
201.845235 -40.87468 212.660172 -45.354315 223.936959 -45.354315 c
223.936959 -45.354315 229.606239 -45.354315 229.606239 -45.354315 c
229.606604 -45.354315 l
240.883523 -45.354219 251.69853 -40.874388 259.672448 -32.900334 c
267.64628 -24.926365 272.125915 -14.111428 272.125915 -2.834641 c
272.125915 -2.834641 272.125915 2.834639 272.125915 2.834639 c
272.125915 2.835004 l
h f
266.456635 -2.834276 m
266.456635 -2.834641 l
266.456635 -2.834641 266.456635 2.834639 266.456635 2.834639 c
266.456635 -8.442148 261.977 -19.257085 254.003168 -27.231054 c
246.02925 -35.205108 235.214243 -39.684939 223.937324 -39.685035 c
223.936959 -39.685035 l
223.936959 -39.685035 229.606239 -39.685035 229.606239 -39.685035 c
218.329452 -39.685035 207.514515 -35.2054 199.540546 -27.231568 c
191.566492 -19.25765 187.086661 -8.442643 187.086565 2.834276 c
187.086565 2.834641 l
187.086565 2.834641 187.086565 -2.834639 187.086565 -2.834639 c
187.086565 8.442148 191.5662 19.257085 199.540032 27.231054 c
207.51395 35.205108 218.328957 39.684939 229.605876 39.685035 c
229.606241 39.685035 l
229.606241 39.685035 223.936961 39.685035 223.936961 39.685035 c
235.213748 39.685035 246.028685 35.2054 254.002654 27.231568 c
261.976708 19.25765 266.456539 8.442643 266.456635 -2.834276 c
h f
/Tr0 gs
0 g 0 G
Q
% mps graphic 1: end
\stoptyping
\stoplinenumbering

The duplicates differ per variant and as they are effective \type {lineto}
combine with \type {curveto} we can consider removing the \type {lineto}'s. This
can either be done in the backend or we can decide to do that in the \METAPOST\
library during the export. \footnote {In the end I settled on introducing a move
tolerance in addition to the bend tolerance that we already have in the export.
The default value of \cldcontext {"\letterpercent .6N", metapost .
getmovetolerance ()} removes 14 lines from the above \PDF\ code.}

The \type {tracingspecs} flag can help us to see what happens deep down when

envelopes are made. It will show the intermediate path on the console.

\starttyping
tracingspecs := 1;
path e ; e := envelope (makepen(fullsquare) scaled 2mm) of (fullcircle scaled 3cm) ;
show(e);
\stoptyping

The intermediate path is reported as:

\starttyping
   % beginning with      offset ( 2.83464, 2.83464)
   ( 42.51968, 0       ) .. controls ( 42.51968, 11.27742) and ( 38.03908, 22.09160)
.. ( 30.06534, 30.06534) .. controls ( 22.09160, 38.03908) and ( 11.27742, 42.51968)
   % counterclockwise to offset (-2.83464, 2.83464)
.. (  0,       42.51968) .. controls (  0,       42.51968) and (  0,       42.51968)
.. (  0,       42.51968) .. controls (-11.27742, 42.51968) and (-22.09160, 38.03908)
.. (-30.06534, 30.06534) ..controls (-38.03908, 22.09160) and (-42.51968, 11.27742)
   % counterclockwise to offset (-2.83464,-2.83464)
.. (-42.51968,  0      ) .. controls (-42.51968,  0      ) and (-42.51968,  0      )
.. (-42.51968,  0      ) .. controls (-42.51968,-11.27742) and (-38.03908,-22.09160)
.. (-30.06534,-30.06534) .. controls (-22.0916, -38.03908) and (-11.27742,-42.51968)
   % counterclockwise to offset ( 2.83464,-2.83464)
.. (  0,      -42.51968) .. controls (  0,      -42.51968) and (  0,      -42.51968)
.. (  0,      -42.51968) .. controls ( 11.27742,-42.51968) and ( 22.0916, -38.03908)
.. ( 30.06534,-30.06534) .. controls ( 38.03908,-22.09160) and ( 42.51968,-11.27742)
   % counterclockwise to offset ( 2.83464, 2.83464)
.. ( 42.51968,  0      ) .. controls ( 42.51968,  0      ) and ( 42.51968,  0      )
.. ( 42.51968,  0      )
&  cycle
\stoptyping

The result becomes:

\starttyping
   ( 45.35432,  2.83464) .. controls ( 45.35432, 14.11206) and ( 40.87372, 24.92624)
.. ( 32.89998, 32.89998) .. controls ( 24.92624, 40.87372) and ( 14.11206, 45.35432)
.. (  2.83464, 45.35432) .. controls (  2.83464, 45.35432) and ( -2.83464, 45.35432)
.. ( -2.83464, 45.35432) .. controls ( -2.83464, 45.35432) and ( -2.83464, 45.35432)
.. ( -2.83464, 45.35432) .. controls (-14.11206, 45.35432) and (-24.92624, 40.87372)
.. (-32.89998, 32.89998) .. controls (-40.87372, 24.92624) and (-45.35432, 14.11206)
.. (-45.35432,  2.83464) .. controls (-45.35432,  2.83464) and (-45.35432, -2.83464)
.. (-45.35432, -2.83464) .. controls (-45.35432, -2.83464) and (-45.35432, -2.83464)
.. (-45.35432, -2.83464) .. controls (-45.35432,-14.11206) and (-40.87372,-24.92624)
.. (-32.89998,-32.89998) .. controls (-24.92624,-40.87372) and (-14.11206,-45.35432)
.. ( -2.83464,-45.35432) .. controls ( -2.83464,-45.35432) and (  2.83464,-45.35432)
.. (  2.83464,-45.35432) .. controls (  2.83464,-45.35432) and (  2.83464,-45.35432)
.. (  2.83464,-45.35432) .. controls ( 14.11206,-45.35432) and ( 24.92624,-40.87372)
.. ( 32.89998,-32.89998) .. controls ( 40.87372,-24.92624) and ( 45.35432,-14.11206)
.. ( 45.35432, -2.83464) .. controls ( 45.35432, -2.83464) and ( 45.35432,  2.83464)
.. ( 45.35432,  2.83464) .. controls ( 45.35432,  2.83464) and ( 45.35432,  2.83464)
.. cycle
\stoptyping

Numerous experiments by Mikael and me lead to the conclusion that both stages can
introduce the duplicate points and that any messing with that during envelop
generation time has negative side effects. However, when we export the path we
can definitely get rid of them. They are harmless but we're talking quality
control here and \TEX\ and \METAPOST\ is all about quality!

As usual, playing with mechanisms like this gets one wondering about similar cases,
for instance variants of dashing.

\startbuffer[e]
\startMPcode
vardef dashing (expr pth, shp, stp) =
    for i within arcpointlist stp of pth :
        shp
            rotated angle(pathdirection)
            shifted pathpoint
        &&
    endfor nocycle
enddef ;

path parrA ; parrA :=
    (0,0) -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0) -- (2,2) -- (2,1) -- (0,1) -- (0,0)
;
path parrB ; parrB :=
    parrA -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0)
;
path p ; p := fullcircle scaled 2cm ;

fill (dashing (p, parrA, 25) && cycle)                 withtransparency (1,.5) ;
draw (dashing (p, parrA, 25) && cycle)                 withtransparency (1,.5) ;
fill (dashing (p, parrB, 25) && cycle) shifted (3cm,0) withtransparency (1,.5) ;
draw (dashing (p, parrB, 25) && cycle) shifted (3cm,0) withtransparency (1,.5) ;
\stopMPcode
\stopbuffer

\typebuffer[e]

In \in {figure} [fig:DASHING:1] we see the result. Of course how well if comes out
depends on the definition but what is special here is that we use the double
ampersand operator. That one will connect the paths without complaining about the
end and being point not colliding. I suppose there was a good reason for making
that a condition in the case of fonts, after all \METAFONT\ is what it came from,
but there is no real reason for it. It is a cheap extension anyway. At the same
time I decided to add a native \quote {direction} operator. The number of extra
bytes in the binary is probably less than what is needed in memory to store the
macro and the advantage is that we save an extra run over the path to reach the
point we're consulting. \footnote {In case you wonder, \ this is how the macro
definition looks like: \typ {vardef direction expr t of p = postcontrol t of p -
precontrol t of p enddef ;}. Because points are searched from from the start
there are two lookups needed. Normally this is no problem but Mikael and I are
playing with really large paths, like those that come from drawing functions.}

\startplacefigure[reference=fig:DASHING:1,title=A somewhat related rendering.]
    \scale[width=\textwidth]{\getbuffer[e]}
\stopplacefigure

In case you wonder why we need this feature, here is an argument:

\startbuffer[e]
\startMPcode
    path s ; s := fullcircle scaled 4cm ; pickup pencircle scaled 5mm ;

    draw (s shifted (0cm,0) && s shifted (3cm,0) && s shifted (6cm,0))
        withcolor "darkred" withtransparency (1,.5) ;

    currentpicture := currentpicture shifted (-8cm,0) ;

    draw s shifted (0cm,0)
        withcolor "darkblue" withtransparency (1,.5) ;
    draw s shifted (3cm,0)
        withcolor "darkblue" withtransparency (1,.5) ;
    draw s shifted (6cm,0)
        withcolor "darkblue" withtransparency (1,.5) ;

    currentpicture := currentpicture shifted (-8cm,0) ;

    nodraw s shifted (0cm,0) ;
    nodraw s shifted (3cm,0) ;
    nodraw s shifted (6cm,0) ;
    dodraw origin withcolor "darkgreen" withtransparency (1,.5) ;

\stopMPcode
\stopbuffer

\typebuffer[e]

The results are shown in \in {figure} [fig:DASHING:2]. Which if the alternatives
you prefer also depends on how you generate the shape. The \type {nodraw} variant
for instance can be mixed with calculations without the need to revert to \type
{hide}.

\startplacefigure[reference=fig:DASHING:1,title=Do you see the difference?]
    \scale[width=\textwidth]{\getbuffer[e]}
\stopplacefigure

\startbuffer[e]
\startMPcode
vardef dashing (expr pth, shp, stp) =
    for i within arcpointlist stp of pth :
        shp
            rotated angle(pathdirection)
            shifted pathpoint
        &&
    endfor nocycle
enddef ;

path e, p ; numeric n ;
e := (0,0) -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0) -- (2,2) -- (2,1) -- (0,1) -- (0,0) ;
n := 10 * bbwidth(e) ;
p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;

fill (dashing (p, e scaled 2.5, n) && cycle) withcolor .6white    ;
draw (dashing (p, e scaled 2.5, n) && cycle) withcolor  darkgreen ;

currentpicture := currentpicture shifted (0,-2cm) ;

n := 20 * bbwidth(e) ;
fill (dashing (p, e, n) && cycle) withcolor .6white   ;
draw (dashing (p, e, n) && cycle) withcolor  darkblue ;
\stopMPcode
\stopbuffer

\in {Figure} [fig:DASHING:3] demonstrates how far we've come. Mikaels fancy
arrows nicely follow the shape of the function. Of course you need to make sure
that these arrows are reasonably scaled. The definition of \type {dashing}
demonstrates a few primitives that permits efficient iteration over a path and
\type {arcpointlist} is sort of a path.

\startplacefigure[reference=fig:DASHING:3,title=Advanced pseudo dashing.]
    \scale[width=\textwidth]{\getbuffer[e]}
\stopplacefigure

\typebuffer[e]

\stopchapter

\stopcomponent

% \startMPpage[offset=1dk]
% path theta, xaxis, yaxis;
%
% xaxis := (-4, 0) -- (5, 0) ;
% yaxis := ( 0,-2) -- (0, 2) ;
% theta := (-3, 0) -- (0, 0) &&
%          ( 0, 1) -- (3, 1) &&
%          ( 3,-1) -- (5,-1) ;
%
% draw theta scaled 2cm
%     withpen pencircle scaled 2
%     withcolor darkred ;
%
% drawarrow xaxis scaled 2cm ;
% drawarrow yaxis scaled 2cm ;
% \stopMPpage

% \startMPpage[offset=1dk]
%     vardef dashing (expr pth, shp, stp) =
%         for i within arcpointlist stp of pth :
%             shp
%                 rotated angle(pathdirection)
%                 shifted pathpoint
%             &&
%         endfor nocycle
%     enddef ;
%
%     path e, p ; numeric n ;
%     e := (0,0) -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0) -- (2,2) -- (2,1) -- (0,1) -- (0,0) ;
%     n := 30 * bbwidth(e) ; % 80 ;
%     p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;

%   % draw p withpen pencircle scaled .1mm withcolor darkblue ;

%     eofill (dashing (p, e, n) && cycle) withcolor .6white    ; % withtransparency (1,.5) ;
%     draw   (dashing (p, e, n) && cycle) withcolor  darkgreen ; % withtransparency (1,.5) ;
% \stopMPpage

% \startMPpage[offset=1dk]
% path theta, xaxis, yaxis;
%
% xaxis := (-4,0) -- (4,0) ;
% yaxis := (0,-1/4) -- (0,3/2) ;
% theta := (-3,0) -- (0,0) && (0,1) -- (3,1) ;
%
% pickup pencircle scaled 1mm;
%
% drawarrow xaxis scaled 2cm ;
% drawarrow yaxis scaled 2cm ;
% draw theta scaled 2cm withcolor darkred ;
%
% currentpicture := currentpicture shifted (0,5cm) ;
%
% drawarrow xaxis scaled 2cm ;
% drawarrow yaxis scaled 2cm ;
%
% draw            (theta && cycle) scaled 2cm withcolor darkyellow ;
% drawpoints      (theta && cycle) scaled 2cm withcolor darkyellow ;
% drawpointlabels (theta && cycle) scaled 2cm withcolor darkyellow ;
% fill            (theta && cycle) scaled 2cm withcolor "orange" withpen pencircle scaled .5mm;
%
% show(theta );
% show(theta && cycle);
%
% \stopMPpage

% \startMPpage[offset=1dk]
%     path theta, xaxis, yaxis, thetaA, thetaB ;
%
%     xaxis := (-4,0) -- (4,0) ;
%     yaxis := (0,-1/4) -- (0,3/2) ;
%
%     thetaA := (-3,0) -- (0,0) -- (-2,1) -- cycle ;
%     thetaB := ( 0,1) -- (3,1) -- ( 2,0) -- cycle ;
%     theta  := thetaA && thetaB ;
%
%     pickup pencircle scaled 1mm;
%
%     drawarrow xaxis scaled 2cm ;
%     drawarrow yaxis scaled 2cm ;
%     draw theta scaled 2cm withcolor darkred ;
%
%     currentpicture := currentpicture shifted (0,5cm) ;
%
%     drawarrow xaxis scaled 2cm ;
%     drawarrow yaxis scaled 2cm ;
%
%     draw (theta && cycle) scaled 2cm withcolor darkyellow ;
%     fill (theta && cycle) scaled 2cm withcolor "orange" withpen pencircle scaled .5mm;
% \stopMPpage

% \startMPpage
% picture q ; q := lmt_outline [ text = "\TEX", kind = "path" ] ;
% path qq ; qq := for i within q :
%     if picture i : for j within i : if stroked j :
%         pathpart j &&
%     fi endfor fi
% endfor nocycle ;
%
% path e, p ; numeric n ;
% e := qq shifted - center qq xsized 1mm ;
% n := 30 * bbwidth(e) ;
% p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;
%
% draw (dashing (p, e scaled 2.5, n) && cycle)
%     withpen pencircle scaled .1
%     withcolor darkgreen
% ;
%
% path e, p ; numeric n ;
% e := qq shifted - center qq xsized .1mm ;
% n := 2000 * bbwidth(e) ;
% p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;

% draw (dashing (p, e scaled 2.5, n) && cycle)
%     withpen pencircle scaled .01
%     withcolor darkgreen
% ;
% \stopMPpage

