1
2
3\environment luametafunstyle
4
5\startcomponent luametafunenvelopes
6
7\startMPdefinitions
8 loadmodule("misc") ;
9\stopMPdefinitions
10
11\startchapter[title={Envelopes}]
12
13\startsection[title=Introduction]
14
15Envelopes are what \METAPOST\ makes for a non circular path. A circular path is
16supported directly by \POSTSCRIPT\ and \PDF. When such a oath is rotated, it is
17still somewhat easy because \METAPOST\ outputs the shape twice, transformed
18differently, but in the end we have one curve, and filling the right space the
19two curves bound which is native behavior of path filling. When the pen is more
20complex, that is not a transformed basic pencircle, \METAPOST\ will calculate a
21so called envelope. This chapter limits the explanation to what we can observe
22and better explanations about pens can be found in the \METAFONT\ book.
23
24\stopsection
25
26\startsection[title=Pens]
27
28The code involves is non trivial and can only work reliable for paths made from
29straight lines which which is why a pen is always reduced to a path with straight
30lines. Internally the term \quote {convex hull} is used. In \LUAMETATEX\ we have
31that operation as primitive.
32
33\startbuffer
34\startMPcode
35pen mypen ; mypen := makepen (fullcircle);
36draw origin withpen mypen scaled 100 withcolor "darkblue" ;
37\stopMPcode
38\stopbuffer
39
40\typebuffer[option=TEX]
41
42By drawing just one point we see the pen:
43
44\startlinecorrection \getbuffer \stoplinecorrection
45
46Indeed the circle has been simplified here.
47
48\startbuffer
49\startMPcode
50def ShowPaths(expr pth) =
51 path p[] ;
52 p[0] := pth scaled 50;
53 p[1] := uncontrolled p[0] ;
54 p[2] := convexed p[0] ;
55 draw p[0] shifted ( 0,0) withpen pencircle scaled 5 withcolor "darkgreen" ;
56 draw p[1] shifted (100,0) withpen pencircle scaled 5 withcolor "darkred" ;
57 draw p[2] shifted (160,0) withpen pencircle scaled 5 withcolor "darkblue" ;
58 draw p[1] shifted (260,0) withpen pencircle scaled 5 withcolor "darkred" ;
59 draw p[2] shifted (260,0) withpen pencircle scaled 5 withcolor "white" ;
60enddef ;
61
62ShowPaths(fullcircle) ;
63\stopMPcode
64\stopbuffer
65
66\typebuffer[option=TEX]
67
68In this case the straightforward removal of control points gives the same result
69as first calculating the convex hull.
70
71\startlinecorrection \getbuffer \stoplinecorrection
72
73\startbuffer
74\startMPcode
75ShowPaths(fullcircle randomized .1) ;
76\stopMPcode
77\stopbuffer
78
79\typebuffer[option=TEX]
80
81In this example we still seem to get what we expect:
82
83\startlinecorrection \getbuffer \stoplinecorrection
84
85\startbuffer
86\startMPcode
87ShowPaths(fullcircle randomized .4) ;
88\stopMPcode
89\stopbuffer
90
91\typebuffer[option=TEX]
92
93But a bit of exaggeration shows that we dont get the same:
94
95\startlinecorrection \getbuffer \stoplinecorrection
96
97It all has to do with heuristics and nasty border cases when we turn corners. Here is
98what these (not randomized) paths look like, first the \type {uncontrolled}:
99
100\starttyping
101(25,0) .. controls (22.56,5.89) and (20.12,11.79)
102.. (17,68,17,68) .. controls (11.79,20.12) and (5.89,22.56)
103.. (0,25) .. controls (5.89,22.56) and (11.79,20.12)
104.. (17,68,17,68) .. controls (20.12,11.79) and (22.56,5.89)
105.. (25,0) .. controls (22.56,5.89) and (20.12,11.79)
106.. (17,68,17,68) .. controls (11.79,20.12) and (5.89,22.56)
107.. (0,25) .. controls (5.89,22.56) and (11.79,20.12)
108.. (17,68,17,68) .. controls (20.12,11.79) and (22.56,5.89)
109.. cycle
110\stoptyping
111
112and here is the \type {unconvexed}:
113
114\starttyping
115(25,0) .. controls (22.56,5.89) and (20.12,11.79)
116.. (17,68,17,68) .. controls (11.79,20.12) and (5.89,22.56)
117.. (0,25) .. controls (5.89,22.56) and (11.79,20.12)
118.. (17,68,17,68) .. controls (20.12,11.79) and (22.56,5.89)
119.. (25,0) .. controls (22.56,5.89) and (20.12,11.79)
120.. (17,68,17,68) .. controls (11.79,20.12) and (5.89,22.56)
121.. (0,25) .. controls (5.89,22.56) and (11.79,20.12)
122.. (17,68,17,68) .. controls (20.12,11.79) and (22.56,5.89)
123.. cycle
124\stoptyping
125
126Now, in order to see what convexing has to do with pens we also introduce a
127\quote {nep} which is a pen that doesnt get its path convexed. We mainly have
128this variant available for experimenting and documentation purposes. Take these
129definitions:
130
131\startbuffer
132\startMPdefinitions
133path PthP ; PthP := (fullcircle scaled 100) randomized 80 ;
134pen PenP ; PenP := makepen PthP ;
135nep NepP ; NepP := makenep PthP ;
136path ConP ; ConP := convexed PthP ;
137path UncP ; UncP := uncontrolled PthP ;
138\stopMPdefinitions
139\stopbuffer
140
141\typebuffer[option=TEX] \getbuffer
142
143That are used in:
144
145\startbuffer
146\startMPdefinitions
147def Pth =
148 draw PthP ;
149enddef ;
150def Pen =
151 draw origin withpen PenP withcolor "darkred" withtransparency (1,.5) ;
152enddef ;
153def Nep =
154 draw origin withpen NepP withcolor "darkblue" withtransparency (1,.5);
155enddef ;
156def Con =
157 fill ConP withpen pencircle scaled 0 withcolor "darkgreen" withtransparency (1,.5) ;
158enddef ;
159def Unc =
160 fill UncP withpen pencircle scaled 0 withcolor "darkyellow" withtransparency (1,.5) ;
161enddef ;
162\stopMPdefinitions
163\stopbuffer
164
165\typebuffer[option=TEX] \getbuffer
166
167The main reason for showing the differences in \in {figure} [fig:trickyconvex] is that
168one should be aware of possible side effects
169
170\startbuffer[all]
171\startcombination [nx=3,ny=4]
172 {\startMPcode draw image (Pen Nep Pth) ; \stopMPcode} {pen nep}
173 {\startMPcode draw image (Pen Con Pth) ; \stopMPcode} {pen convexed}
174 {\startMPcode draw image (Pen Unc Pth) ; \stopMPcode} {pen uncontrolled}
175 {\startMPcode draw image (Nep Pen Pth) ; \stopMPcode} {nep pen}
176 {\startMPcode draw image (Nep Con Pth) ; \stopMPcode} {nep convexed}
177 {\startMPcode draw image (Nep Unc Pth) ; \stopMPcode} {nep uncontrolled}
178 {\startMPcode draw image (Con Pen Pth) ; \stopMPcode} {convexed pen}
179 {\startMPcode draw image (Con Nep Pth) ; \stopMPcode} {convexed nep}
180 {\startMPcode draw image (Con Unc Pth) ; \stopMPcode} {convexed uncontrolled}
181 {\startMPcode draw image (Unc Pen Pth) ; \stopMPcode} {uncontrolled pen}
182 {\startMPcode draw image (Unc Nep Pth) ; \stopMPcode} {uncontrolled nep}
183 {\startMPcode draw image (Unc Con Pth) ; \stopMPcode} {uncontrolled convexed}
184\stopcombination
185\stopbuffer
186
187\startplacefigure[title={Pens are paths with straight lines.},reference=fig:trickyconvex]
188 \getbuffer[all]
189\stopplacefigure
190
191In case you doubt if all this matters, if we use a not to weird path, were
192fine, as is demonstrated in \in {figure} [fig:okayconvex]; here we used
193
194\starttyping[option=MP]
195PthP := fullcircle yscaled 80 xscaled 140 rotated 45 ;
196\stoptyping
197
198\startbuffer
199\startMPdefinitions
200path PthP ; PthP := fullcircle yscaled 80 xscaled 140 rotated 45 ;
201pen PenP ; PenP := makepen PthP ;
202nep NepP ; NepP := makenep PthP ;
203path ConP ; ConP := convexed PthP ;
204path UncP ; UncP := uncontrolled PthP ;
205\stopMPdefinitions
206\stopbuffer
207
208\getbuffer
209
210\startplacefigure[title={When using decent pens the results will be consistent.},reference=fig:okayconvex]
211 \getbuffer[all]
212\stopplacefigure
213
214And when we use such rather normal (non extreme) paths for pens were ready for
215envelopes.
216
217\page
218
219\startsection[title=Usage]
220
221An envelop is the outline that we get when we run a pen over a path. An envelop
222is (of course) a closed path. Here is a simple example:
223
224\startbuffer
225\startMPcode
226path p ; p := origin -- (100,10) -- cycle ;
227path e ; e := envelope pensquare scaled 10 rotated 45 of p ;
228
229draw e withpen pencircle scaled 2 withcolor "darkred" ;
230draw p withpen pencircle scaled 2 withcolor "darkgray" ;
231
232fill e shifted (120,0) withcolor "darkred" ;
233draw p shifted (120,0) withcolor "lightgray" withpen pencircle scaled 2 ;
234
235fill e shifted (240,0)
236 withshademethod "linear"
237 withshadecolors ("darkred","lightgray") ;
238\stopMPcode
239\stopbuffer
240
241\typebuffer[option=TEX]
242
243This also demonstrates that this way you can apply a shade to a path:
244
245\startlinecorrection
246\getbuffer
247\stoplinecorrection
248
249One problem with envelopes is that you can get unexpected results so lets try to
250explore some details. We start by defining a main path, a pen, a path from the
251pen, and two envelopes.
252
253\startbuffer
254\startMPcode
255path PthP ; PthP := fullcircle xysized(10cm,2cm) ;
256pen PenP ; PenP := pensquare scaled 2mm rotated 45 ;
257path PthU ; PthU := fullsquare scaled 2mm rotated 45 ;
258path PatP ; PatP := makepath PenP ;
259
260path PthI ; PthI := envelope PenP of reverse PthP ;
261path PthO ; PthO := envelope PenP of PthP ;
262
263fill PthI && PthO && cycle withcolor "lightgray" ;
264
265draw PthI withcolor "darkred" ;
266draw PthO withcolor "darkgreen" ;
267draw PthP dashed evenly ;
268\stopMPcode
269\stopbuffer
270
271\typebuffer[option=TEX]
272
273Watch the difference between the two envelopes: one is the result from traveling
274the pen clockwise and one from running anticlockwise:
275
276\startlinecorrection
277\getbuffer
278\stoplinecorrection
279
280We can emulate running the pen over the path:
281
282\startbuffer
283\startMPcode
284fill PthI && PthO && cycle withcolor "darkgray" ;
285fill
286 for i within (arcpointlist 50 of PthP) :
287 PatP shifted pathpoint &&
288 endfor cycle
289 withcolor "middlegray" ;
290\stopMPcode
291\stopbuffer
292
293\typebuffer[option=TEX]
294
295Instead of drawing 50 paths, we draw an efficient single one made from 50
296segments and we get this:
297
298\startlinecorrection
299\getbuffer
300\stoplinecorrection
301
302If you look closely at the first rendering you will notice an artifact in the inner
303envelope.
304
305\startlinecorrection
306\startMPcode
307draw PthI withpen pencircle scaled .4mm withcolor "darkred" ;
308\stopMPcode
309\stoplinecorrection
310
311We can get rid of this with a helper macro:
312
313\startbuffer
314\startMPcode
315draw reducedenvelope(PthI) withpen pencircle scaled .4mm withcolor "darkred" ;
316\stopMPcode
317\stopbuffer
318
319\typebuffer[option=TEX]
320
321Of course you get no guarantees but here it works:
322
323\startlinecorrection
324\getbuffer
325\stoplinecorrection
326
327One reason why the helper is not in the core is that it doesnt catch all cases:
328
329\startbuffer
330\startMPcode
331path p ; p := fullcircle scaled 4cm ;
332pen e ; e := pensquare scaled 3mm ;
333draw envelope e of p ;
334draw envelope e of reverse p ;
335p := p rotated eps shifted (5cm,0) ;
336draw envelope e of p ;
337draw envelope e of reverse p ;
338p := p shifted (5cm,0) ;
339draw p enveloped e ;
340draw (reverse p) enveloped e ;
341\stopMPcode
342\stopbuffer
343
344\typebuffer[option=TEX]
345
346Watch how a tiny rotations rid us of the weird rectangle, and the helper makes three
347extra inflected points go away but were still stuck with an imperfection.
348
349\startlinecorrection
350\getbuffer
351\stoplinecorrection
352
353When we only fill the envelope we dont suffer from thisbecause the artifacts
354stay within the bounds. Sometimes rotating the pen by \type {eps} also helps.
355
356\startbuffer
357\startMPcode
358path p ; p := fullcircle scaled 4cm ;
359pen e ; e := pensquare scaled 3mm ;
360fill
361 (envelope e of p) && (envelope e of reverse p) && cycle
362 withcolor "darkblue" ;
363draw
364 (envelope e of p) && (envelope e of reverse p) && cycle
365 withcolor "white" ;
366\stopMPcode
367\stopbuffer
368
369\typebuffer[option=TEX]
370
371\startlinecorrection
372\getbuffer
373\stoplinecorrection
374
375\stopsection
376
377\startsection[title=Details]
378
379For those who are interested in seeing what goes on behind the scenes, this
380section shows some examples that we made when writing an article about envelopes. We start with a couple of definitions
381
382\startbuffer
383\startMPdefinitions
384loadmodule("misc") ;
385
386path mypaths[] ;
387path mypens[] ;
388
389mypens[ 1] := fullcircle scaled 15mm ;
390mypens[ 2] := fulldiamond scaled 15mm ;
391mypens[ 3] := fulltriangle scaled 15mm ;
392mypens[ 4] := fullsquare scaled 15mm ;
393mypens[ 5] := starring(-13) scaled 15mm ;
394mypens[ 6] := starring(-12) scaled 15mm ;
395mypens[ 7] := starring(eps) scaled 15mm ;
396mypens[ 8] := starring(1) scaled 15mm ;
397mypens[ 9] := starring(12) scaled 15mm ;
398mypens[10] := starring(eps) scaled 15mm ;
399
400mypaths[1] := fullcircle scaled 10cm ;
401mypaths[2] := ((0,0) -- (12,12) -- (22,0)) scaled 10cm ;
402mypaths[3] := ((0,0) -- (12,12) -- (22,0) -- cycle) scaled 10cm ;
403\stopMPdefinitions
404\stopbuffer
405
406\typebuffer[option=TEX] \getbuffer
407
408We are not going to use all these shapes and pens here but you might want to try
409out some yourself. We \in {Figure} [fig:envelope:1] we apply a so called \type
410{pensquare} to the paths. In \in {Figure} [fig:envelope:2] we use a star but
411\METAPOST\ will turn this one into a rectangle. In \in {Figure} [fig:envelope:3]
412we also use star but here the points are used.
413
414\startbuffer
415\startMPcode
416draw showenvelope(mypaths[1], mypens[4]) ;
417draw showenvelope(mypaths[2], mypens[4]) shifted (10cm, 1cm) ;
418draw showenvelope(mypaths[3], mypens[4]) shifted (10cm,-6cm) ;
419\stopMPcode
420\stopbuffer
421
422\typebuffer[option=TEX]
423
424\startplacefigure[title={How pen 4 creates an envelope.},reference=fig:envelope:1]
425 \scale[width=1tw]{\getbuffer}
426\stopplacefigure
427
428\startbuffer
429\startMPcode
430draw showenvelope(mypaths[1], mypens[6]) ;
431draw showenvelope(mypaths[2], mypens[6]) shifted (10cm, 1cm) ;
432draw showenvelope(mypaths[3], mypens[6]) shifted (10cm,-6cm) ;
433\stopMPcode
434\stopMPcode
435\stopbuffer
436
437\typebuffer[option=TEX]
438
439\startplacefigure[title={How pen 6 creates an envelope.},reference=fig:envelope:2]
440 \scale[width=1tw]{\getbuffer}
441\stopplacefigure
442
443\startbuffer
444\startMPcode
445draw showenvelope(mypaths[1], mypens[9]) ;
446draw showenvelope(mypaths[2], mypens[9]) shifted (10cm, 1cm) ;
447draw showenvelope(mypaths[3], mypens[9]) shifted (10cm,-6cm) ;
448\stopMPcode
449\stopbuffer
450
451\typebuffer[option=TEX]
452
453\startplacefigure[title={How pen 9 creates an envelope.},reference=fig:envelope:3]
454 \scale[width=1tw]{\getbuffer}
455\stopplacefigure
456
457\stopsection
458
459\startsection[title=Reducing]
460
461If you watch the third shape in the previous examples, the last figure differs in
462that it has a symmetrical inner envelope. We can actually use this knowledge to
463define a pensquare that is better suited for envelopes. We take this example:
464
465\startbuffer
466\startMPdefinitions
467def ExamplePaths =
468 path PthA ; PthA := fullcircle scaled 5cm ;
469 path PthB ; PthB := triangle scaled 5cm ;
470
471 draw envelope pensquare scaled 10mm of reverse PthA
472 withpen pencircle scaled 2mm
473 withcolor "darkblue"
474 ;
475 draw envelope pensquare scaled 10mm of reverse PthB
476 withpen pencircle scaled 2mm
477 withcolor "darkblue"
478 ;
479
480 draw (reverse PthA) enveloped (pensquare scaled 10mm)
481 withpen pencircle scaled 2mm
482 withcolor "darkred"
483 ;
484 draw (reverse PthB) enveloped (pensquare scaled 10mm)
485 withpen pencircle scaled 2mm
486 withcolor "darkred"
487 ;
488 enddef ;
489\stopMPdefinitions
490\stopbuffer
491
492\typebuffer[option=TEX] \getbuffer
493
494We define two renderings, one with the normal pensquare definition:
495
496\startbuffer[a]
497\startMPcode
498pensquare := makepen(unitsquare shifted (.5,.5)) ; ExamplePaths ;
499\stopMPcode
500\stopbuffer
501
502\typebuffer[a][option=TEX]
503
504and one with an alternative definition where we have middle points on the
505edges that stick out one eps:
506
507\startbuffer[b]
508\startMPcode
509pensquare := makepen((starring(eps) scaled 12)) ; ExamplePaths ;
510\stopMPcode
511\stopbuffer
512
513\typebuffer[b][option=TEX]
514
515This gives \in {figure} [fig:envelope:4]. The blue extensions are what we get
516without clean up but at least the alternative has symmetrical ears.
517
518\startplacefigure[title={An alternative pensquare.},reference=fig:envelope:4]
519 \startcombination[nx=2,ny=1]
520 {\scale[width=.45tw]{\getbuffer[a]}} {default pensquare}
521 {\scale[width=.45tw]{\getbuffer[b]}} {alternative pensquare}
522 \stopcombination
523\stopplacefigure
524
525When you have a somewhat weird envelope the \type {reducedenvelope} macro might
526be able to improve it. The \typ {<pth> enveloped <pen>} primary macro has this
527built in.
528
529\stopsection
530
531\stopchapter
532
533\stopcomponent
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605 |