ontarget-envelopes.tex /size: 35 Kb    last modification: 2024-01-16 10:21
1% language=us runpath=texruns:manuals/ontarget
2
3\startcomponent ontarget-envelopes
4
5\environment ontarget-style
6
7\startchapter[title={Pushing the envelope}]
8
9Here I describe the results of some exploration and experiments by Mikael
10Sundqvist and me. We got sidetracked from intersections, arcs and drawing
11functions when we noticed some artifacts with envelopes. But what are envelopes
12actually? Let us start with a simple path:
13
14\startbuffer[testpath]
15\startMPinclusions
16    path TestPath ; TestPath := fullcircle xyscaled (10cm,1cm)
17\stopMPinclusions
18\stopbuffer
19
20\startbuffer[a]
21\startMPcode
22    draw TestPath withpen pencircle scaled 2mm withcolor darkred ;
23\stopMPcode
24\stopbuffer
25
26\startbuffer[b]
27\startMPcode
28    fill TestPath withpen pencircle scaled 2mm withcolor darkred  ;
29\stopMPcode
30\stopbuffer
31
32\startbuffer[c]
33\startMPcode
34    draw TestPath withpen pensquare scaled 2mm withcolor darkblue ;
35\stopMPcode
36\stopbuffer
37
38\startbuffer[d]
39\startMPcode
40    fill TestPath withpen pensquare scaled 2mm withcolor darkblue ;
41\stopMPcode
42\stopbuffer
43
44\startbuffer[e]
45\startMPcode
46    fill TestPath withpen pensquare scaled 2mm withcolor darkgreen
47        withtransparency (1,.5) ;
48\stopMPcode
49\stopbuffer
50
51\typebuffer[testpath]
52
53When we draw this with a circular pen we get this:
54
55\typebuffer[a] \startlinecorrection \getbuffer[testpath,a] \stoplinecorrection
56
57Filling gives:
58
59\typebuffer[b] \startlinecorrection \getbuffer[testpath,b] \stoplinecorrection
60
61When a \type {pencircle} is used \METAPOST\ delegates the work to the backend
62because \POSTSCRIPT\ has a circular pen, otherwise it has to calculate the
63to|-|be|-|filled shape itself. The backend has to do some path juggling in the
64case of \PDF\ because there a pen transform is different from \POSTSCRIPT.
65
66\typebuffer[c] \startlinecorrection \getbuffer[testpath,c] \stoplinecorrection
67
68Here we draw the shape with a square pen while filling gives:
69
70\typebuffer[d] \startlinecorrection \getbuffer[testpath,d] \stoplinecorrection
71
72In most cases this works out well but there are some hidden issues. These get
73exposed when we use a transparency:
74
75\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection
76
77It are these artifacts that we will explore a little. For that we will render
78quite some graphics. We could show numerous more examples but when you are a
79\CONTEXT\ user you will be able to make plenty yourself by looking at these
80examples.
81
82\startbuffer[e]
83\startMPcode
84    fill fullcircle xyscaled (.8TextWidth,2cm)
85        withpen pensquare scaled 8mm
86        withcolor darkgreen
87        withtransparency (1,.5) ;
88\stopMPcode
89\stopbuffer
90
91\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection
92
93When we were playing with the \type {envelope} primitive we noticed these
94artifacts and we spent quite some time looking at the code to see where it comes
95from and if we could prevent this. It was then that we realized that the fill
96actually also uses these envelopes but that it gets delayed till the shapes are
97flushed to the backend. That meant that we could use fills with transparencies
98as simple test cases.
99
100The first thing to get rid of is the weird blob at the right end of the fill in
101this example. Not really understanding all what went on, we explored all kind of
102shapes and temporarily disabled some of the code in the \METAPOST\ library to see
103where it crept in. We decided that touching the code to get rid of for instance
104rounding issues or potential direction related side effects made no sense. In the
105end the solution was simple:
106
107\startbuffer[e]
108\startMPcode
109    pen p ; p := makepen(unitsquare rotated eps) ;
110    fill fullcircle xyscaled (.8TextWidth,2cm)
111        withpen p scaled 8mm
112        withcolor darkgreen
113        withtransparency (1,.5) ;
114\stopMPcode
115\stopbuffer
116
117\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection
118
119We show what the \type {envelope} primitive gives us:
120
121\startbuffer[e]
122\startMPcode
123    pen p ; p := makepen(unitsquare rotated eps) ;
124    path e ; e :=
125        envelope (p scaled 8mm)
126    of
127        (fullcircle xyscaled (.8TextWidth,2cm))
128    ;
129    draw e
130        withpen pencircle scaled 2mm
131        withcolor darkgreen
132        withtransparency (1,.5) ;
133    drawpoints e ;
134\stopMPcode
135\stopbuffer
136
137\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection
138
139This looks okay compared to previous the examples but we have only a simple path
140here, while the fill actually has two:
141
142\startbuffer[e]
143\startMPcode
144    pen p ; p := makepen(unitsquare rotated eps) ;
145    enfill fullcircle xyscaled (.8TextWidth,2cm)
146        withpen p scaled 8mm
147        withcolor darkgreen
148        withtransparency (1,.5) ;
149\stopMPcode
150\stopbuffer
151
152\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection
153
154So how do we get that inner shape? Once you know what a fill actually outputs to
155the backend it is easy! There are two envelopes: the normal one and one made from
156the reverse path (or in internal \METAPOST\ speak: htap). In the previous example
157the \type {enfill} treats the path as a fill but will draw the envelopes instead.
158As with \type {eofill}, \type {eoclip} and path accumulators this is a \METAFUN\
159backend related feature but we introduced \type {enfill} as a new one.
160
161\startbuffer[e]
162\startMPcode
163    pen p ; p := makepen(unitsquare rotated eps) ;
164    draw
165        envelope (p scaled 8mm) of
166        (fullcircle xyscaled (.8TextWidth,2cm))
167        withpen pencircle scaled 2mm
168        withcolor darkgreen
169        withtransparency (1,.5) ;
170    draw
171        envelope (p scaled 8mm) of
172        reverse (fullcircle xyscaled (.8TextWidth,2cm))
173        withpen pencircle scaled 2mm
174        withcolor darkblue
175        withtransparency (1,.5) ;
176\stopMPcode
177\stopbuffer
178
179\typebuffer[e] \startlinecorrection \getbuffer[testpath,e] \stoplinecorrection
180
181We're now ready for the real deal but keep in mind that what we show here is the
182result of stepwise growing insight combined with adding some features to the
183engine that not only makes it possible to illustrate this but also might prove to
184be useful. The used primitives will be explained later, for now we just stick
185to the results.
186
187\startMPextensions
188    def pentest (expr pth, psh, convex, enhance, width) =
189        image (
190            save p, s, w, pp, somepen ;
191            numeric s ; s := width ;
192            numeric w ; w := (15/100)*s ;
193
194            path p, e, pp, somepen ;
195
196            p := pth scaled s ;
197
198            % gets rid of bad end condition (rectangle) (introduced close points)
199
200            pp := psh rotated eps ;
201
202            if convex :
203                pen somepen ; somepen := makepen (pp) ;
204            else :
205                nep somepen ; somepen := makenep (pp) ;
206            fi ;
207
208            if cycle p : fill else : draw fi p
209                withcolor blue
210                withpen somepen scaled w
211                withtransparency (1,.5)
212            ;
213            drawarrow p ;
214            drawpoints p ;
215
216            path ppp ; ppp := pp scaled w;
217            path qqq ; qqq := convexed ppp;
218
219            for i within p :
220                drawarrow  qqq shifted pathpoint withcolor yellow dashed evenly withpen pencircle scaled 1;
221                drawarrow  ppp shifted pathpoint withcolor white;
222                drawpoints ppp shifted pathpoint withcolor white ;
223            endfor ;
224
225          % for i within arcpointlist 50 of p  :
226          %     draw ppp shifted pathpoint withcolor white ;
227          % endfor ;
228
229            defaultscale := .4 ;
230            e := envelope somepen scaled w of p ;
231          % e := e scrutinized 0.01 ;
232            draw            e withcolor darkgreen withpen pencircle scaled 1mm;
233            drawpoints      e ;
234          % drawpointlabels e withcolor green ;
235            e := envelope somepen scaled w of reverse p ;
236          % e := e scrutinized 0.01 ;
237            draw            e withcolor yellow withpen pencircle scaled 1mm;
238            drawpoints      e ;
239          % drawpointlabels e withcolor yellow ;
240
241            if enhance :
242                e := e scrutinized 0.01 ;
243                draw convexed e withcolor darkmagenta withpen pencircle scaled 2mm ;
244            fi ;
245
246        )
247    enddef ;
248\stopMPextensions
249
250\startMPextensions
251    def TestShapesA(expr pth, convex, enhance, dy) =
252        draw pentest (pth, fullcircle,   convex, enhance, .4TextWidth) shifted (          0,          0) ;
253        draw pentest (pth, fulldiamond,  convex, enhance, .4TextWidth) shifted (.5TextWidth,          0) ;
254        draw pentest (pth, fulltriangle, convex, enhance, .4TextWidth) shifted (          0,dy*TextWidth) ;
255        draw pentest (pth, fullsquare,   convex, enhance, .4TextWidth) shifted (.5TextWidth,dy*TextWidth) ;
256    enddef ;
257
258    def TestShapesB(expr pth, convex, enhance, dy) =
259        draw pentest (pth, starring(-1/3), convex, enhance, .35TextWidth) shifted (          0,          0) ;
260        draw pentest (pth, starring(-1/2), convex, enhance, .35TextWidth) shifted (.5TextWidth,          0) ;
261        draw pentest (pth, starring(-eps), convex, enhance, .35TextWidth) shifted (          0,dy*TextWidth) ;
262        draw pentest (pth, starring(-1/2), convex, enhance, .35TextWidth) shifted (.5TextWidth,dy*TextWidth) ;
263    enddef ;
264\stopMPextensions
265
266\startplacefigure[reference=fig:A:T:F:1,title={Using four different relatively large pens on a circle.}]
267    \startMPcode
268        TestShapesA(fullcircle, true, false, .5) ;
269    \stopMPcode
270\stopplacefigure
271
272\in {Figure} [fig:A:T:F:1] shows a circle filled (or enveloped) with pens made from
273\type {fullcircle}, \type {fulldiamond}, \type {fulltriangle} and \type
274{fullsquare}. The paths that we use for the pens are also shown. The outcome can
275be puzzling but after going over the code (in the engine) and trying to reason
276the logic it becomes clear that the unexpected is mostly due to the fact that
277there is no other way to draw the path (read: meet the criteria).
278
279When looking closely at the results (adding labels to the points and zooming in)
280one will notice more side effects. Because we rotate over \type {eps} to get rid
281of the weird end situation we can end up with more points than we like and these
282are so close to each other that one doesn't notice them. For this we can apply
283the scrutinizer:
284
285\starttyping
286e := e scrutinized 0.01 ;
287\stoptyping
288
289When that is done we can wonder if a simplified (inner) path is possible. I tried
290a few solutions using the \LUA\ interface while Mikael (as mathematician)
291followed the more scientific approach but the results largely depend on the pens
292and shapes.
293
294\startplacefigure[reference=fig:B:T:F:1]
295    \startMPcode
296        TestShapesB(fullcircle, true, false, .5) ;
297    \stopMPcode
298\stopplacefigure
299
300Actually when doing all that we used a more complex pen in several variants. This
301is shown in \in {figure} [fig:B:T:F:1]. Notice the dashed lines here. When a pen
302is defined there is some checking going on. One is that circular pens get no
303treatment at all and just pass through the system. Basically any single point
304cycle is considered as elliptical anyway. Then the path turned into a so called
305\quote {convex} path. It also showed us the real pen being used. When out of
306curiosity I commented that bit of code I noticed that we could achieve
307interesting results. The result is that we now have a \type {convexed} primitive.
308After all the code was there so it took only a few lines to add this primitive.
309In \in {figure} [fig:B:F:F:1] you can see the result of a unconvexed pen.
310
311\startplacefigure[reference=fig:B:F:F:1]
312    \startMPcode
313        TestShapesB(fullcircle, false, false, .5) ;
314    \stopMPcode
315\stopplacefigure
316
317We can also calculate envelopes of non|-|cyclic paths which is demonstrated in
318\in {figure} [fig:B:T:F:2] and \in {figure} [fig:B:F:F:2]. There is however some
319trickery involved. Just to make this easier the \METAFUN\ macro package has a
320type {starring} macro that makes such a star:
321
322\starttyping
323path p ; p := starring(-1/2) rotated eps ;
324\stoptyping
325
326This star can become a pen:
327
328\starttyping
329pen somepen ; somepen := makepen (pp) ;
330\stoptyping
331
332And as mentioned pens get convexed by default. Even worse, whenever we transform
333a pen it gets convexed again. When we fill a shape the pen gets attached to that shape
334and the backend will do the enveloping. The easiest way to consistently avoid
335convexing was to introduce a new pen type.
336
337\starttyping
338nep somepen ; somepen := makenep (pp) ;
339\stoptyping
340
341The somewhat weird short \type {nep} perfectly fits the bill as in Dutch it means
342fake. A pen defined this way stays unconvexed. Actually there is another property
343where pens differ from regular paths: they are double linked. In original
344\METAPOST\ that back (prev) link uses a field in a knot record that is not used
345by pen paths. The path that gets pencilled also abuses one of knot fields for
346keeping track of the offset that a point has relative to the current point in the
347pen. It was good moment to also make regular paths double linked lists. That
348comes at the cost of an extra pointer in the knot record but we could also save
349some space by using smaller slots for other fields. Memory is not our biggest
350worry anyway. \footnote {Of course adding code is but when looking in more detail
351at the code involved it was actually possible to simplify the code a bit so there
352we gained}. Double linking meant that there was no need for doing that when
353making pens. \footnote {It makes it possible to get points relative to the
354current point in iterators over paths that we introduced a while ago, which makes
355for high performance path manipulators.}
356
357\startplacefigure[reference=fig:B:T:F:2]
358    \startMPcode
359        TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0), true, false, .35) ;
360    \stopMPcode
361\stopplacefigure
362
363\startplacefigure[reference=fig:B:F:F:2]
364    \startMPcode
365        TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0), false, false, .35) ;
366    \stopMPcode
367\stopplacefigure
368
369We can apply the \type {convexed} primitive to the inner envelope which is demonstrated
370in \in {figure} [fig:B:T:T:3] and \in {figure} [fig:B:F:T:3]. Of course it is debatable
371how useful this is but as with all these \METAPOST\ shapes, it has some charm.
372
373\startplacefigure[reference=fig:B:T:T:3]
374    \startMPcode
375        TestShapesB(fullcircle, true, true, .5) ;
376    \stopMPcode
377\stopplacefigure
378
379\startplacefigure[reference=fig:B:F:T:3]
380    \startMPcode
381        TestShapesB(fullcircle, false, true, .5) ;
382    \stopMPcode
383\stopplacefigure
384
385% \startplacefigure[reference=fig:B:T:F:2]
386%     \startMPcode
387%         TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0) -- cycle, true, false) ;
388%     \stopMPcode
389% \stopplacefigure
390
391% \startplacefigure[reference=fig:B:F:F:2]
392%     \startMPcode
393%         TestShapesB((0,0) -- (1/2,1/2) -- (2/2,0) -- cycle, false, false) ;
394%     \stopMPcode
395% \stopplacefigure
396
397% \startMPpage[offset=1dk]
398%     enfill         fullcircle scaled 2cm withpen makepen(fullsquare) scaled 1pt withcolor darkred ;
399%     enfill         fullcircle scaled 3cm withpen makepen(fullsquare) scaled 1pt withcolor darkblue ;
400%     enfill reverse fullcircle scaled 1cm withpen makepen(fullsquare) scaled 1pt withcolor darkgreen ;
401% \stopMPpage
402
403% \startMPpage[offset=1dk]
404%     draw envelope makepen(fullsquare)         of (        fullcircle scaled 2cm) withpen pencircle scaled 1pt withcolor darkred ;
405%     draw envelope makepen(fullsquare)         of (reverse fullcircle scaled 3cm) withpen pencircle scaled 1pt withcolor darkblue ;
406%     draw envelope makepen(reverse fullsquare) of (        fullcircle scaled 1cm) withpen pencircle scaled 1pt withcolor darkgreen ;
407% \stopMPpage
408
409\page
410
411To what extend does all this influence the output? As long as we don't use
412transparencies we're quite okay unless we use a pen size that introduces the more
413extreme overshoots. If you think these phenomena only relate to \METAPOST\
414output, you're wrong. Over the past decades I've seen various fonts that exhibit
415the same small spikes and other artifacts btu as we often see the shapes at small
416sizes it goes unnoticed. A particular sensitive areas is variable fonts where,
417when the ranges on which the various dimensions operate are too liberal, you can
418also get these effects. After all, glyphs are filled shapes. To that you can also
419add the fact that they are single (connected) paths drawn with \type {eofill}.
420
421The final format a graphics ends up in can be \PDF. Take the following three
422shapes and watch the subtle side effect of rotating either the to be drawn shape
423or the pen.
424
425\startbuffer[e]
426\startMPcode
427    fill fullcircle xyscaled (5cm,3cm)
428        withpen makepen(fullsquare) scaled 2mm
429        withcolor darkred
430        withtransparency (1,.5) ;
431    fill fullcircle xyscaled (5cm,3cm)
432        shifted (6cm,0)
433        withpen makepen(fullsquare rotated eps) scaled 2mm
434        withcolor darkblue
435        withtransparency (1,.5) ;
436    fill fullcircle rotated eps xyscaled (5cm,3cm)
437        shifted (12cm,0)
438        withpen makepen(fullsquare) scaled 2mm
439        withcolor darkgreen
440        withtransparency (1,.5) ;
441\stopMPcode
442\stopbuffer
443
444\typebuffer[e] \startlinecorrection \getbuffer[e] \stoplinecorrection
445
446This produces four filled paths in the \PDF\ file, a normal and a reverse path
447per shape. I show the whole output because you can see how some points of the
448\quote {inside} curve are sort of duplicated: they have the same coordinates but
449can have different control points.
450
451\starttabulate[|lT|lT|lT|]
452\BC        \BC inner                               \BC outer                      \NC \NR
453\BC left   \NC 25=26 31=32 35=36 39=40 43=25       \NC                            \NC \NR
454\BC middle \NC                                     \NC 49=50=51 54=55 58=59 62=63 \NC \NR
455\BC right  \NC 107=108=109 112=113 116=117 120=121 \NC 89=105                     \NC \NR
456\stoptabulate
457
458Here is the output. Each combination is between bound by the transparency
459operators \type {/Tr1} and \type {/Tr0} and has different colors.
460
461\startlinenumbering
462\starttyping
463% mps graphic 1: begin
464q
465/Tr1 gs
4660.625 0 0 rg 0.625 0 0 RG
46710 M
4681 j
469 45.354315 2.83464 m
470 45.354315 14.111559 40.874577 24.926605 32.900591 32.900591 c
471 24.926605 40.874577 14.111559 45.354315 2.83464 45.354315 c
472  2.83464 45.354315 -2.83464 45.354315 -2.83464 45.354315 c
473 -2.83464 45.354315 l
474-14.111559 45.354315 -24.926605 40.874577 -32.900591 32.900591 c
475-40.874577 24.926605 -45.354315 14.111559 -45.354315 2.83464 c
476-45.354315 2.83464 -45.354315 -2.83464 -45.354315 -2.83464 c
477-45.354315 -2.83464 l
478-45.354315 -14.111559 -40.874577 -24.926605 -32.900591 -32.900591 c
479-24.926605 -40.874577 -14.111559 -45.354315 -2.83464 -45.354315 c
480-2.83464 -45.354315 2.83464 -45.354315 2.83464 -45.354315 c
4812.83464 -45.354315 l
48214.111559 -45.354315 24.926605 -40.874577 32.900591 -32.900591 c
48340.874577 -24.926605 45.354315 -14.111559 45.354315 -2.83464 c
48445.354315 -2.83464 45.354315 2.83464 45.354315 2.83464 c
48545.354315 2.83464 l
486h f
48739.685035 -2.83464 m
48839.685035 -2.83464 45.354315 -2.83464 45.354315 -2.83464 c
48945.354315 -2.83464 45.354315 2.83464 45.354315 2.83464 c
49045.354315 2.83464 39.685035 2.83464 39.685035 2.83464 c
49139.685035 -8.442279 35.205297 -19.257325 27.231311 -27.231311 c
49219.257325 -35.205297 8.442279 -39.685035 -2.83464 -39.685035 c
493-2.83464 -39.685035 l
494-2.83464 -39.685035 2.83464 -39.685035 2.83464 -39.685035 c
495-8.442279 -39.685035 -19.257325 -35.205297 -27.231311 -27.231311 c
496-35.205297 -19.257325 -39.685035 -8.442279 -39.685035 2.83464 c
497-39.685035 2.83464 l
498-39.685035 2.83464 -39.685035 -2.83464 -39.685035 -2.83464 c
499-39.685035 8.442279 -35.205297 19.257325 -27.231311 27.231311 c
500-19.257325 35.205297 -8.442279 39.685035 2.83464 39.685035 c
5012.83464 39.685035 l
5022.83464 39.685035 -2.83464 39.685035 -2.83464 39.685035 c
5038.442279 39.685035 19.257325 35.205297 27.231311 27.231311 c
50435.205297 19.257325 39.685035 8.442279 39.685035 -2.83464 c
50539.685035 -2.83464 l
506h f
507/Tr0 gs
5080 g 0 G
509/Tr1 gs
5100 0 0.625 rg 0 0 0.625 RG
511158.740139 -2.834616 m
512158.740139 -2.834252 l
513158.740139 -2.834252 158.740091 2.835028 158.740091 2.835028 c
514158.739994 14.111816 154.260267 24.926715 146.286366 32.900615 c
515138.31238 40.874601 127.497335 45.354339 116.220416 45.354339 c
516116.220052 45.354339 l
517116.220052 45.354339 110.550772 45.354291 110.550772 45.354291 c
51899.273984 45.354194 88.459085 40.874467 80.485185 32.900566 c
51972.511199 24.92658 68.031461 14.111535 68.031461 2.834616 c
52068.031461 2.834252 l
52168.031461 2.834252 68.031509 -2.835028 68.031509 -2.835028 c
52268.031606 -14.111816 72.511333 -24.926715 80.485234 -32.900615 c
52388.45922 -40.874601 99.274265 -45.354339 110.551184 -45.354339 c
524110.551548 -45.354339 l
525110.551548 -45.354339 116.220828 -45.354291 116.220828 -45.354291 c
526127.497616 -45.354194 138.312515 -40.874467 146.286415 -32.900566 c
527154.260401 -24.92658 158.740139 -14.111535 158.740139 -2.834616 c
528h f
529153.070811 2.834616 m
530153.070811 -8.442303 148.591072 -19.257349 140.617086 -27.231335 c
531132.643186 -35.205235 121.828288 -39.684963 110.5515 -39.685059 c
532110.5515 -39.685059 116.22078 -39.685011 116.22078 -39.685011 c
533116.220416 -39.685011 l
534104.943497 -39.685011 94.128451 -35.205272 86.154465 -27.231286 c
53578.180565 -19.257386 73.700837 -8.442488 73.700741 2.8343 c
53673.700741 2.8343 73.700789 -2.83498 73.700789 -2.83498 c
53773.700789 -2.834616 l
53873.700789 8.442303 78.180528 19.257349 86.154514 27.231335 c
53994.128414 35.205235 104.943312 39.684963 116.2201 39.685059 c
540116.2201 39.685059 110.55082 39.685011 110.55082 39.685011 c
541110.551184 39.685011 l
542121.828103 39.685011 132.643149 35.205272 140.617135 27.231286 c
543148.591035 19.257386 153.070763 8.442488 153.070859 -2.8343 c
544153.070859 -2.8343 153.070811 2.83498 153.070811 2.83498 c
545153.070811 2.834616 l
546h f
547/Tr0 gs
5480 g 0 G
549/Tr1 gs
5500 0.625 0 rg 0 0.625 0 RG
551272.125915 2.835004 m
552272.125819 14.111923 267.645988 24.92693 259.671934 32.900848 c
553251.697965 40.87468 240.883028 45.354315 229.606241 45.354315 c
554229.606241 45.354315 223.936961 45.354315 223.936961 45.354315 c
555223.936596 45.354315 l
556212.659677 45.354219 201.84467 40.874388 193.870752 32.900334 c
557185.89692 24.926365 181.417285 14.111428 181.417285 2.834641 c
558181.417285 2.834641 181.417285 -2.834639 181.417285 -2.834639 c
559181.417285 -2.835004 l
560181.417381 -14.111923 185.897212 -24.92693 193.871266 -32.900848 c
561201.845235 -40.87468 212.660172 -45.354315 223.936959 -45.354315 c
562223.936959 -45.354315 229.606239 -45.354315 229.606239 -45.354315 c
563229.606604 -45.354315 l
564240.883523 -45.354219 251.69853 -40.874388 259.672448 -32.900334 c
565267.64628 -24.926365 272.125915 -14.111428 272.125915 -2.834641 c
566272.125915 -2.834641 272.125915 2.834639 272.125915 2.834639 c
567272.125915 2.835004 l
568h f
569266.456635 -2.834276 m
570266.456635 -2.834641 l
571266.456635 -2.834641 266.456635 2.834639 266.456635 2.834639 c
572266.456635 -8.442148 261.977 -19.257085 254.003168 -27.231054 c
573246.02925 -35.205108 235.214243 -39.684939 223.937324 -39.685035 c
574223.936959 -39.685035 l
575223.936959 -39.685035 229.606239 -39.685035 229.606239 -39.685035 c
576218.329452 -39.685035 207.514515 -35.2054 199.540546 -27.231568 c
577191.566492 -19.25765 187.086661 -8.442643 187.086565 2.834276 c
578187.086565 2.834641 l
579187.086565 2.834641 187.086565 -2.834639 187.086565 -2.834639 c
580187.086565 8.442148 191.5662 19.257085 199.540032 27.231054 c
581207.51395 35.205108 218.328957 39.684939 229.605876 39.685035 c
582229.606241 39.685035 l
583229.606241 39.685035 223.936961 39.685035 223.936961 39.685035 c
584235.213748 39.685035 246.028685 35.2054 254.002654 27.231568 c
585261.976708 19.25765 266.456539 8.442643 266.456635 -2.834276 c
586h f
587/Tr0 gs
5880 g 0 G
589Q
590% mps graphic 1: end
591\stoptyping
592\stoplinenumbering
593
594The duplicates differ per variant and as they are effective \type {lineto}
595combine with \type {curveto} we can consider removing the \type {lineto}'s. This
596can either be done in the backend or we can decide to do that in the \METAPOST\
597library during the export. \footnote {In the end I settled on introducing a move
598tolerance in addition to the bend tolerance that we already have in the export.
599The default value of \cldcontext {"\letterpercent .6N", metapost .
600getmovetolerance ()} removes 14 lines from the above \PDF\ code.}
601
602The \type {tracingspecs} flag can help us to see what happens deep down when
603
604envelopes are made. It will show the intermediate path on the console.
605
606\starttyping
607tracingspecs := 1;
608path e ; e := envelope (makepen(fullsquare) scaled 2mm) of (fullcircle scaled 3cm) ;
609show(e);
610\stoptyping
611
612The intermediate path is reported as:
613
614\starttyping
615   % beginning with      offset ( 2.83464, 2.83464)
616   ( 42.51968, 0       ) .. controls ( 42.51968, 11.27742) and ( 38.03908, 22.09160)
617.. ( 30.06534, 30.06534) .. controls ( 22.09160, 38.03908) and ( 11.27742, 42.51968)
618   % counterclockwise to offset (-2.83464, 2.83464)
619.. (  0,       42.51968) .. controls (  0,       42.51968) and (  0,       42.51968)
620.. (  0,       42.51968) .. controls (-11.27742, 42.51968) and (-22.09160, 38.03908)
621.. (-30.06534, 30.06534) ..controls (-38.03908, 22.09160) and (-42.51968, 11.27742)
622   % counterclockwise to offset (-2.83464,-2.83464)
623.. (-42.51968,  0      ) .. controls (-42.51968,  0      ) and (-42.51968,  0      )
624.. (-42.51968,  0      ) .. controls (-42.51968,-11.27742) and (-38.03908,-22.09160)
625.. (-30.06534,-30.06534) .. controls (-22.0916, -38.03908) and (-11.27742,-42.51968)
626   % counterclockwise to offset ( 2.83464,-2.83464)
627.. (  0,      -42.51968) .. controls (  0,      -42.51968) and (  0,      -42.51968)
628.. (  0,      -42.51968) .. controls ( 11.27742,-42.51968) and ( 22.0916, -38.03908)
629.. ( 30.06534,-30.06534) .. controls ( 38.03908,-22.09160) and ( 42.51968,-11.27742)
630   % counterclockwise to offset ( 2.83464, 2.83464)
631.. ( 42.51968,  0      ) .. controls ( 42.51968,  0      ) and ( 42.51968,  0      )
632.. ( 42.51968,  0      )
633&  cycle
634\stoptyping
635
636The result becomes:
637
638\starttyping
639   ( 45.35432,  2.83464) .. controls ( 45.35432, 14.11206) and ( 40.87372, 24.92624)
640.. ( 32.89998, 32.89998) .. controls ( 24.92624, 40.87372) and ( 14.11206, 45.35432)
641.. (  2.83464, 45.35432) .. controls (  2.83464, 45.35432) and ( -2.83464, 45.35432)
642.. ( -2.83464, 45.35432) .. controls ( -2.83464, 45.35432) and ( -2.83464, 45.35432)
643.. ( -2.83464, 45.35432) .. controls (-14.11206, 45.35432) and (-24.92624, 40.87372)
644.. (-32.89998, 32.89998) .. controls (-40.87372, 24.92624) and (-45.35432, 14.11206)
645.. (-45.35432,  2.83464) .. controls (-45.35432,  2.83464) and (-45.35432, -2.83464)
646.. (-45.35432, -2.83464) .. controls (-45.35432, -2.83464) and (-45.35432, -2.83464)
647.. (-45.35432, -2.83464) .. controls (-45.35432,-14.11206) and (-40.87372,-24.92624)
648.. (-32.89998,-32.89998) .. controls (-24.92624,-40.87372) and (-14.11206,-45.35432)
649.. ( -2.83464,-45.35432) .. controls ( -2.83464,-45.35432) and (  2.83464,-45.35432)
650.. (  2.83464,-45.35432) .. controls (  2.83464,-45.35432) and (  2.83464,-45.35432)
651.. (  2.83464,-45.35432) .. controls ( 14.11206,-45.35432) and ( 24.92624,-40.87372)
652.. ( 32.89998,-32.89998) .. controls ( 40.87372,-24.92624) and ( 45.35432,-14.11206)
653.. ( 45.35432, -2.83464) .. controls ( 45.35432, -2.83464) and ( 45.35432,  2.83464)
654.. ( 45.35432,  2.83464) .. controls ( 45.35432,  2.83464) and ( 45.35432,  2.83464)
655.. cycle
656\stoptyping
657
658Numerous experiments by Mikael and me lead to the conclusion that both stages can
659introduce the duplicate points and that any messing with that during envelop
660generation time has negative side effects. However, when we export the path we
661can definitely get rid of them. They are harmless but we're talking quality
662control here and \TEX\ and \METAPOST\ is all about quality!
663
664As usual, playing with mechanisms like this gets one wondering about similar cases,
665for instance variants of dashing.
666
667\startbuffer[e]
668\startMPcode
669vardef dashing (expr pth, shp, stp) =
670    for i within arcpointlist stp of pth :
671        shp
672            rotated angle(pathdirection)
673            shifted pathpoint
674        &&
675    endfor nocycle
676enddef ;
677
678path parrA ; parrA :=
679    (0,0) -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0) -- (2,2) -- (2,1) -- (0,1) -- (0,0)
680;
681path parrB ; parrB :=
682    parrA -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0)
683;
684path p ; p := fullcircle scaled 2cm ;
685
686fill (dashing (p, parrA, 25) && cycle)                 withtransparency (1,.5) ;
687draw (dashing (p, parrA, 25) && cycle)                 withtransparency (1,.5) ;
688fill (dashing (p, parrB, 25) && cycle) shifted (3cm,0) withtransparency (1,.5) ;
689draw (dashing (p, parrB, 25) && cycle) shifted (3cm,0) withtransparency (1,.5) ;
690\stopMPcode
691\stopbuffer
692
693\typebuffer[e]
694
695In \in {figure} [fig:DASHING:1] we see the result. Of course how well if comes out
696depends on the definition but what is special here is that we use the double
697ampersand operator. That one will connect the paths without complaining about the
698end and being point not colliding. I suppose there was a good reason for making
699that a condition in the case of fonts, after all \METAFONT\ is what it came from,
700but there is no real reason for it. It is a cheap extension anyway. At the same
701time I decided to add a native \quote {direction} operator. The number of extra
702bytes in the binary is probably less than what is needed in memory to store the
703macro and the advantage is that we save an extra run over the path to reach the
704point we're consulting. \footnote {In case you wonder, \ this is how the macro
705definition looks like: \typ {vardef direction expr t of p = postcontrol t of p -
706precontrol t of p enddef ;}. Because points are searched from from the start
707there are two lookups needed. Normally this is no problem but Mikael and I are
708playing with really large paths, like those that come from drawing functions.}
709
710\startplacefigure[reference=fig:DASHING:1,title=A somewhat related rendering.]
711    \scale[width=\textwidth]{\getbuffer[e]}
712\stopplacefigure
713
714In case you wonder why we need this feature, here is an argument:
715
716\startbuffer[e]
717\startMPcode
718    path s ; s := fullcircle scaled 4cm ; pickup pencircle scaled 5mm ;
719
720    draw (s shifted (0cm,0) && s shifted (3cm,0) && s shifted (6cm,0))
721        withcolor "darkred" withtransparency (1,.5) ;
722
723    currentpicture := currentpicture shifted (-8cm,0) ;
724
725    draw s shifted (0cm,0)
726        withcolor "darkblue" withtransparency (1,.5) ;
727    draw s shifted (3cm,0)
728        withcolor "darkblue" withtransparency (1,.5) ;
729    draw s shifted (6cm,0)
730        withcolor "darkblue" withtransparency (1,.5) ;
731
732    currentpicture := currentpicture shifted (-8cm,0) ;
733
734    nodraw s shifted (0cm,0) ;
735    nodraw s shifted (3cm,0) ;
736    nodraw s shifted (6cm,0) ;
737    dodraw origin withcolor "darkgreen" withtransparency (1,.5) ;
738
739\stopMPcode
740\stopbuffer
741
742\typebuffer[e]
743
744The results are shown in \in {figure} [fig:DASHING:2]. Which if the alternatives
745you prefer also depends on how you generate the shape. The \type {nodraw} variant
746for instance can be mixed with calculations without the need to revert to \type
747{hide}.
748
749\startplacefigure[reference=fig:DASHING:1,title=Do you see the difference?]
750    \scale[width=\textwidth]{\getbuffer[e]}
751\stopplacefigure
752
753\startbuffer[e]
754\startMPcode
755vardef dashing (expr pth, shp, stp) =
756    for i within arcpointlist stp of pth :
757        shp
758            rotated angle(pathdirection)
759            shifted pathpoint
760        &&
761    endfor nocycle
762enddef ;
763
764path e, p ; numeric n ;
765e := (0,0) -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0) -- (2,2) -- (2,1) -- (0,1) -- (0,0) ;
766n := 10 * bbwidth(e) ;
767p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;
768
769fill (dashing (p, e scaled 2.5, n) && cycle) withcolor .6white    ;
770draw (dashing (p, e scaled 2.5, n) && cycle) withcolor  darkgreen ;
771
772currentpicture := currentpicture shifted (0,-2cm) ;
773
774n := 20 * bbwidth(e) ;
775fill (dashing (p, e, n) && cycle) withcolor .6white   ;
776draw (dashing (p, e, n) && cycle) withcolor  darkblue ;
777\stopMPcode
778\stopbuffer
779
780\in {Figure} [fig:DASHING:3] demonstrates how far we've come. Mikaels fancy
781arrows nicely follow the shape of the function. Of course you need to make sure
782that these arrows are reasonably scaled. The definition of \type {dashing}
783demonstrates a few primitives that permits efficient iteration over a path and
784\type {arcpointlist} is sort of a path.
785
786\startplacefigure[reference=fig:DASHING:3,title=Advanced pseudo dashing.]
787    \scale[width=\textwidth]{\getbuffer[e]}
788\stopplacefigure
789
790\typebuffer[e]
791
792\stopchapter
793
794\stopcomponent
795
796% \startMPpage[offset=1dk]
797% path theta, xaxis, yaxis;
798%
799% xaxis := (-4, 0) -- (5, 0) ;
800% yaxis := ( 0,-2) -- (0, 2) ;
801% theta := (-3, 0) -- (0, 0) &&
802%          ( 0, 1) -- (3, 1) &&
803%          ( 3,-1) -- (5,-1) ;
804%
805% draw theta scaled 2cm
806%     withpen pencircle scaled 2
807%     withcolor darkred ;
808%
809% drawarrow xaxis scaled 2cm ;
810% drawarrow yaxis scaled 2cm ;
811% \stopMPpage
812
813% \startMPpage[offset=1dk]
814%     vardef dashing (expr pth, shp, stp) =
815%         for i within arcpointlist stp of pth :
816%             shp
817%                 rotated angle(pathdirection)
818%                 shifted pathpoint
819%             &&
820%         endfor nocycle
821%     enddef ;
822%
823%     path e, p ; numeric n ;
824%     e := (0,0) -- (0,-1) -- (2,-1) -- (2,-2) -- (4,0) -- (2,2) -- (2,1) -- (0,1) -- (0,0) ;
825%     n := 30 * bbwidth(e) ; % 80 ;
826%     p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;
827
828%   % draw p withpen pencircle scaled .1mm withcolor darkblue ;
829
830%     eofill (dashing (p, e, n) && cycle) withcolor .6white    ; % withtransparency (1,.5) ;
831%     draw   (dashing (p, e, n) && cycle) withcolor  darkgreen ; % withtransparency (1,.5) ;
832% \stopMPpage
833
834% \startMPpage[offset=1dk]
835% path theta, xaxis, yaxis;
836%
837% xaxis := (-4,0) -- (4,0) ;
838% yaxis := (0,-1/4) -- (0,3/2) ;
839% theta := (-3,0) -- (0,0) && (0,1) -- (3,1) ;
840%
841% pickup pencircle scaled 1mm;
842%
843% drawarrow xaxis scaled 2cm ;
844% drawarrow yaxis scaled 2cm ;
845% draw theta scaled 2cm withcolor darkred ;
846%
847% currentpicture := currentpicture shifted (0,5cm) ;
848%
849% drawarrow xaxis scaled 2cm ;
850% drawarrow yaxis scaled 2cm ;
851%
852% draw            (theta && cycle) scaled 2cm withcolor darkyellow ;
853% drawpoints      (theta && cycle) scaled 2cm withcolor darkyellow ;
854% drawpointlabels (theta && cycle) scaled 2cm withcolor darkyellow ;
855% fill            (theta && cycle) scaled 2cm withcolor "orange" withpen pencircle scaled .5mm;
856%
857% show(theta );
858% show(theta && cycle);
859%
860% \stopMPpage
861
862% \startMPpage[offset=1dk]
863%     path theta, xaxis, yaxis, thetaA, thetaB ;
864%
865%     xaxis := (-4,0) -- (4,0) ;
866%     yaxis := (0,-1/4) -- (0,3/2) ;
867%
868%     thetaA := (-3,0) -- (0,0) -- (-2,1) -- cycle ;
869%     thetaB := ( 0,1) -- (3,1) -- ( 2,0) -- cycle ;
870%     theta  := thetaA && thetaB ;
871%
872%     pickup pencircle scaled 1mm;
873%
874%     drawarrow xaxis scaled 2cm ;
875%     drawarrow yaxis scaled 2cm ;
876%     draw theta scaled 2cm withcolor darkred ;
877%
878%     currentpicture := currentpicture shifted (0,5cm) ;
879%
880%     drawarrow xaxis scaled 2cm ;
881%     drawarrow yaxis scaled 2cm ;
882%
883%     draw (theta && cycle) scaled 2cm withcolor darkyellow ;
884%     fill (theta && cycle) scaled 2cm withcolor "orange" withpen pencircle scaled .5mm;
885% \stopMPpage
886
887% \startMPpage
888% picture q ; q := lmt_outline [ text = "\TEX", kind = "path" ] ;
889% path qq ; qq := for i within q :
890%     if picture i : for j within i : if stroked j :
891%         pathpart j &&
892%     fi endfor fi
893% endfor nocycle ;
894%
895% path e, p ; numeric n ;
896% e := qq shifted - center qq xsized 1mm ;
897% n := 30 * bbwidth(e) ;
898% p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;
899%
900% draw (dashing (p, e scaled 2.5, n) && cycle)
901%     withpen pencircle scaled .1
902%     withcolor darkgreen
903% ;
904%
905% path e, p ; numeric n ;
906% e := qq shifted - center qq xsized .1mm ;
907% n := 2000 * bbwidth(e) ;
908% p := function(1,"x","x/4 + sin(x)",epsed(0.1),epsed(4*pi),0.01) scaled 2cm ;
909
910% draw (dashing (p, e scaled 2.5, n) && cycle)
911%     withpen pencircle scaled .01
912%     withcolor darkgreen
913% ;
914% \stopMPpage
915