luametafun-paths.tex /size: 28 Kb    last modification: 2025-02-21 11:03
1% language=us runpath=texruns:manuals/luametafun
2
3% \enablemode[check]
4
5% corners == boundingbox
6
7\environment luametafun-style
8
9\startcomponent luametafun-paths
10
11\startchapter[title={Paths}]
12
13\startsection[title=Introduction]
14
15In the end \METAPOST\ is all about creating (beautiful) paths. In this chapter we
16introduce some extensions to the engine that can be of help when constructing
17paths. Some relate to combining paths segments, others to generating the points.
18
19\stopsection
20
21\startsection[title=Cycles]
22
23The \type {cycle} commands closes a path: the end gets connected to the start. One
24way to construct a path stepwise is using a \type {for} loop, as in:
25
26\startbuffer
27\startMPcode
28draw (
29    (0,sin(0)) for i=pi/20 step pi/20 until 2pi :
30        .. (i,sin(i))
31    endfor
32) xysized(8cm,2cm)
33withpen pencircle scaled 1mm
34withcolor "darkred" ;
35\stopMPcode
36\stopbuffer
37
38\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
39
40This looks kind of ugly because we need to make sure that we only put the
41\type {..} between points. If we have a closed path we can do this:
42
43\startbuffer
44\startMPcode
45draw (
46    for i=0 step pi/20 until 2pi :
47        (i,sin(i)) ..
48    endfor cycle
49) xysized(8cm,2cm)
50withpen pencircle scaled 1mm
51withcolor "darkblue" ;
52\stopMPcode
53\stopbuffer
54
55\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
56
57But that is not what we want here. It is for this reason that we have a different
58operator, one that closes a path without cycling:
59
60\startbuffer
61\startMPcode
62draw (
63    for i=0 step pi/20 until 2pi :
64        (i,sin(i)) ..
65    endfor nocycle
66) xysized(8cm,2cm)
67withpen pencircle scaled 1mm
68withcolor "darkgreen" ;
69\stopMPcode
70\stopbuffer
71
72\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
73
74\stopsection
75
76\startsection[title=Combining paths]
77
78The \type {&} concat operator requires the last point of the previous and the
79first point of the current path to be the same. This restriction is lifted with
80the \type {&&}, \type {&&&} and \type {&&&&} commands.
81
82\startbuffer
83\startMPcode
84def Example(expr p, q) =
85    draw image (
86        drawpathonly (p &&   q) shifted ( 0u,0) ;
87        drawpathonly (p &&&  q) shifted ( 5u,0) ;
88        drawpathonly (p &&&& q) shifted (10u,0) ;
89    ) ;
90enddef ;
91
92path p[] ; numeric u ; u := 1cm ;
93p[1] := (0u,0u) -- (1u,0u) -- (1u,1u) ;
94p[2] := (1u,1u) -- (2u,1u) -- (2u,0u) ;
95
96Example(p[1], p[2]) ;
97
98Example(p[1] shifted (0u,-2u), p[2] shifted (1u,-2u)) ;
99\stopMPcode
100\stopbuffer
101
102\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
103
104The precise working can be best be seen from what path we get. The single
105ampersand just does a concat but issues an error when the paths don't touch so we
106leave that one out.
107
108% 0 -> 0
109
110\startbuffer
111\startMPdefinitions
112path p, q, r ;
113p := (0,0) -- (1,0) ;
114q := (2,0) -- (3,0) ;
115r := (1,0) -- (3,0) ;
116vardef Example(expr p) =
117  % show (p);
118    drawpathonly p scaled 4cm ;
119enddef ;
120\stopMPdefinitions
121\stopbuffer
122
123\typebuffer[option=TEX] \getbuffer
124
125\testpage[4]
126
127This gives us:
128
129\startlinecorrection \startMPcode Example(p && q) ; \stopMPcode \stoplinecorrection
130
131\starttyping
132(0,0)         .. controls (0.33,0) and (0.67,0) .. % p && q
133(1,0) {end}   .. controls (2,   0) and (1,   0) ..
134(2,0) {begin} .. controls (2.33,0) and (2.67,0) ..
135(3,0)
136\stoptyping
137
138\testpage[3]
139
140\startlinecorrection \startMPcode Example(p && r) ; \stopMPcode \stoplinecorrection
141
142\starttyping
143(0,0)         .. controls (0.33,0) and (0.67,0) .. % p && r
144(1,0) {end}   .. controls (1,   0) and (1,   0) ..
145(1,0) {begin} .. controls (1.67,0) and (2.33,0) ..
146(3,0)
147\stoptyping
148
149\testpage[3]
150
151\startlinecorrection \startMPcode Example(p &&& q) ; \stopMPcode \stoplinecorrection
152
153\starttyping
154(0,0)         .. controls (0.33,0) and (0.67,0) .. % p &&& q
155(1,0)         .. controls (2.33,0) and (2.67,0) ..
156(3,0)
157\stoptyping
158
159\testpage[3]
160
161\startlinecorrection \startMPcode Example(p &&& r) ; \stopMPcode \stoplinecorrection
162
163\starttyping
164(0,0)         .. controls (0.33,0) and (0.67,0) .. % p &&& r
165(1,0)         .. controls (1.67,0) and (2.33,0) ..
166(3,0)
167\stoptyping
168
169\testpage[3]
170
171\startlinecorrection \startMPcode Example(p &&&& q) ; \stopMPcode \stoplinecorrection
172
173\starttyping
174(0,0)         .. controls (0.33,0) and (0.67,0) .. % p &&&& q
175(1,0) {end}   .. controls (2,   0) and (1,   0) ..
176(2,0) {begin} .. controls (2.33,0) and (2.67,0) ..
177(3,0)
178\stoptyping
179
180\testpage[3]
181
182\startlinecorrection \startMPcode Example(p &&&& r) ; \stopMPcode \stoplinecorrection
183
184\starttyping
185(0,0)         .. controls (0.33,0) and (0.67,0) .. % p &&&& r
186(1,0) {end}   .. controls (1,   0) and (1,   0) ..
187(1,0) {begin} .. controls (1.67,0) and (2.33,0) ..
188(3,0)
189\stoptyping
190
191If we have one (concat) ampersand we check if the paths touch, error or move on.
192If we have three (tolerant concat) or four (tolerant append) ampersands we check
193if the end and begin are the same and if so, we remove one and set the controls
194points halfway, and then degrade to one (concat) or two (append) ampersands.
195Finally when (then) we have one ampersand (concat) we connect with some curl
196magic but when we have two (append) we connect without the curl magic: we let the
197left and right control points be the points.
198
199\startbuffer
200\startMPcode
201path p[] ;
202
203p[1] := (0,0) -- (100,0) -- (100,100) ; for i=2 upto 5 : p[i] := p[1] ; endfor ;
204
205p[1] := p[1]   -- cycle ; p[1] := p[1]   -- cycle ; p[1] := p[1]   -- cycle ;
206p[2] := p[2]   -- cycle ; p[2] := p[2]  &&& cycle ; p[2] := p[2]  &&& cycle ;
207p[3] := p[3]   -- cycle ; p[3] := p[3] &&&& cycle ; p[3] := p[3] &&&& cycle ;
208p[4] := p[4]  &&& cycle ;
209p[5] := p[5] &&&& cycle ;
210
211for i=1 upto 5 :
212  % show(p[i]) ;
213    fill p[i] shifted (i*110,0) withcolor "middlegray" ;
214    draw p[i] shifted (i*110,0) withcolor "darkred" withpen pencircle scaled 5  ;
215endfor ;
216currentpicture := currentpicture xsized TextWidth ;
217\stopMPcode
218\stopbuffer
219
220Here is another example of usage. Watch how \type {&&&} doesn't influence an
221already closed curve.
222
223\typebuffer
224
225\startlinecorrection \getbuffer \stoplinecorrection
226
227The paths are, here shown with less precision:
228
229\starttyping
230(0,0) .. controls (33.33,0) and (66.67,-0)
231.. (100,0) .. controls (100,33.33) and (100,66.67)
232.. (100,100) .. controls (66.67,66.67) and (33.33,33.33)
233.. (0,0) .. controls (0,0) and (0,0)
234.. (0,0) .. controls (0,0) and (0,0)
235.. cycle
236
237(0,0) .. controls (33.33,0) and (66.67,-0)
238.. (100,0) .. controls (100,33.33) and (100,66.67)
239.. (100,100) .. controls (66.67,66.67) and (33.33,33.33)
240.. cycle
241
242(0,0) {begin} .. controls (33.33,0) and (66.67,-0)
243.. (100,0) .. controls (100,33.33) and (100,66.67)
244.. (100,100) .. controls (66.67,66.67) and (33.33,33.33)
245.. (0,0) {end} .. controls (0,0) and (0,0) % duplicate {end} is
246.. (0,0) {end} .. controls (0,0) and (0,0) % sort of an error
247.. cycle
248
249(100,100) .. controls (33.33,0) and (66.67,-0)
250.. (100,0) .. controls (100,33.33) and (100,66.67)
251.. cycle
252
253(0,0) {begin} .. controls (33.33,0) and (66.67,-0)
254.. (100,0) .. controls (100,33.33) and (100,66.67)
255.. (100,100) {end} .. controls (0,0) and (100,100)
256.. cycle
257\stoptyping
258
259These somewhat complicated rules also relate to the intended application: the
260backend can apply \type {fill} or \type {eofill} in which case also cycles are
261involved as the following examples demonstrate:
262
263\startbuffer
264\startMPdefinitions
265path p, q, r ;
266p := fullcircle ;
267q := reverse fullcircle ;
268r := fullcircle shifted (1/2,0) ;
269vardef Example(expr p) =
270    image (
271        eofill p scaled 4cm withcolor "middlegray" ;
272        drawpathonly p scaled 4cm ;
273    )
274enddef ;
275\stopMPdefinitions
276\stopbuffer
277
278\typebuffer[option=TEX] \getbuffer
279
280\startbuffer
281\startMPcode
282    draw Example(p &&&           q &&& cycle) ;
283    draw Example(p &&& cycle &&& q &&& cycle) shifted (8cm,0) ;
284\stopMPcode
285\stopbuffer
286
287\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
288
289\startbuffer
290\startMPcode
291    draw Example(p &&&           r &&& cycle) ;
292    draw Example(p &&& cycle &&& r &&& cycle) shifted (8cm,0) ;
293\stopMPcode
294\stopbuffer
295
296\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
297
298\startbuffer
299\startMPcode
300    draw Example(p &&&&            q &&&& cycle) ;
301    draw Example(p &&&& cycle &&&& q &&&& cycle) shifted (8cm,0) ;
302\stopMPcode
303\stopbuffer
304
305\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
306
307\startbuffer
308\startMPcode
309    draw Example(p &&&&            r &&&& cycle) ;
310    draw Example(p &&&& cycle &&&& r &&&& cycle) shifted (8cm,0) ;
311\stopMPcode
312\stopbuffer
313
314\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
315
316\stopsection
317
318\startsection[title=Implicit points]
319
320In the \METAPOST\ library that comes with \LUAMETATEX\ we have a few extensions
321that relate to paths. You might wonder why we need these but some relate to the
322fact that paths can be generated programmatically. A prominent operator (or
323separator) is \type {..} and contrary to what one might expect the frequently
324used \type {--} is a macro:
325
326\starttyping[option=MP]
327def -- = { curl 1 } .. { curl 1 } enddef ;
328\stoptyping
329
330This involves interpreting nine tokens as part of expanding the macro and in
331practice that is fast even for huge paths. Nevertheless we now have a \type {--}
332primitive that involves less interpreting and also avoids some intermediate
333memory allocation of numbers. Of course you can still define it as macro.
334
335When you look at \POSTSCRIPT\ you'll notice that it has operators for relative
336and absolute positioning in the horizontal, vertical or combined direction. In
337\LUAMETATEX\ we now have similar operators that we will demonstrate with a few
338examples.
339
340\startbuffer
341\startMPcode
342drawarrow origin
343    -- xrelative  300
344    -- yrelative   20
345    -- xrelative -300
346    -- cycle
347withpen pencircle scaled 2
348withcolor "darkred" ;
349\stopMPcode
350\stopbuffer
351
352\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
353
354In the next example we show a relative position combined with an absolute and we
355define them as macros. You basically gets what goes under the name \quote {turtle
356graphics}:
357
358\startbuffer
359\startMPcode
360save h ; def h = -- xrelative enddef ;
361save v ; def v = -- yabsolute enddef ;
362
363drawarrow origin
364    h 30 v 20 h 30 v 30
365    h 30 v 10 h 30 v 50
366    h 30 v 60 h 30 v 10
367withpen pencircle scaled 2
368withcolor "darkred" ;
369\stopMPcode
370\stopbuffer
371
372\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
373
374When you provide a pair to \type {xabsolute} or \type {yabsolute}, the xpart is
375the (relative) advance and the second the absolute coordinate.
376
377\startbuffer
378\startMPcode
379draw origin
380    -- yabsolute(10,30)
381    -- yabsolute(20,20)
382    -- yabsolute(30,10)
383    -- yabsolute(40,20)
384    -- yabsolute(50,30)
385    -- yabsolute(60,20)
386    -- yabsolute(70,10)
387    -- yabsolute(80,20)
388    -- yabsolute(90,30)
389withpen pencircle scaled 2
390withcolor "darkred" ;
391\stopMPcode
392\stopbuffer
393
394\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
395
396The \type {xyabsolute} is sort of redundant and is equivalent to just a pair, but
397maybe there is a use for it. When the two coordinates are the same you can use
398a numeric.
399
400\startbuffer
401\startMPcode
402draw origin
403    -- xyabsolute(10, 10) % -- xyabsolute 10
404    -- xyabsolute(20, 10)
405    -- xyabsolute(30,-10)
406    -- xyabsolute(40,-10)
407    -- xyabsolute(50, 10)
408    -- xyabsolute(60, 10)
409    -- xyabsolute(70,-10)
410    -- xyabsolute(80,-10)
411withpen pencircle scaled 2
412withcolor "darkred" ;
413\stopMPcode
414\stopbuffer
415
416\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
417
418The relative variant also can take a pair and numeric, as in:
419
420\startbuffer
421\startMPcode
422draw origin
423    -- xyrelative 10
424    -- xyrelative 10
425    -- xyrelative(10,-10)
426    -- xyrelative(10,-10)
427    -- xyrelative 10
428    -- xyrelative 10
429    -- xyrelative(10,-10)
430    -- xyrelative(10,-10)
431withpen pencircle scaled 2
432withcolor "darkred" ;
433\stopMPcode
434\stopbuffer
435
436\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
437
438In these examples we used \type {--} but you can mix in \type {..} and
439control point related operations, although the later is somewhat less
440intuitive here.
441
442\startbuffer
443\startMPcode
444draw   yabsolute(10,30)
445    .. yabsolute(20,20)
446    .. yabsolute(10,10)
447    .. yabsolute(20,20)
448    .. yabsolute(10,30)
449    .. yabsolute(20,20)
450    .. yabsolute(10,10)
451    .. yabsolute(20,20)
452    .. yabsolute(10,30)
453withpen pencircle scaled 2
454withcolor "darkred" ;
455\stopMPcode
456\stopbuffer
457
458\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
459
460And with most features, users will likely find a use for it:
461
462\startbuffer
463\startMPcode
464draw for i=1 upto 5 :
465    yabsolute(10,30) ---
466    yabsolute(20,20) ...
467    yabsolute(10,10) ---
468    yabsolute(20,20) ...
469endfor nocycle
470withpen pencircle scaled 2
471withcolor "darkred" ;
472\stopMPcode
473\stopbuffer
474
475\typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection
476
477Here is a more impressive example, the result is shown in \in {figure}
478[fig:r-a-paths]:
479
480\startbuffer
481\startMPcode
482for n=10 upto 40 :
483    path p ; p := (
484        for i = 0 step pi/n until pi :
485            yabsolute(cos(i)^2-sin(i)^2,sin(i)^2-cos(i)^2) --
486        endfor cycle
487    ) ;
488    draw p
489        withpen pencircle scaled 1/20
490        withcolor "darkred" withtransparency (1,.25) ;
491endfor ;
492currentpicture := currentpicture xysized (TextWidth,.25TextWidth) ;
493\stopMPcode
494\stopbuffer
495
496\typebuffer[option=TEX]
497
498\startplacefigure[title=Combined relative x and absolute y positioning,ref=fig:r-a-paths]
499    \getbuffer
500\stopplacefigure
501
502\stopsection
503
504\startsection[title=Control points]
505
506Most users will create paths by using \type {..}, \type {...}, \type {--} and
507\type {---} and accept what they get by the looks. If your expectations are more
508strict you might use \type {tension} or \type {curl} with directions and vectors
509for the so called control points between connections. In \in {figure}
510[fig:controlpoints] you see not only \type {controls} in action but also two
511operators that can be used to set the first and second control point. For the
512record: if you use \type {controls} without \type {and} the singular pair will
513be used for both control points.
514
515\startbuffer
516\startMPcode
517path p, q, r, s ;
518
519p = origin {dir 25} .. (80,0) ..      controls ( 80, 0) and (100,40) .. (140,30) .. {dir 0} (180,0) ;
520q = origin {dir 25} .. (80,0) ..      controls (100,40) and (140,30) .. (140,30) .. {dir 0} (180,0) ;
521r = origin {dir 25} .. (80,0) .. secondcontrol              (100,40) .. (140,30) .. {dir 0} (180,0) ;
522s = origin {dir 25} .. (80,0) ..  firstcontrol (100,40)              .. (140,30) .. {dir 0} (180,0) ;
523
524def Example(expr p, t, c) =
525    draw p ;
526    drawpoints p withcolor "middlegray" ;
527    drawcontrollines p withpen pencircle scaled .3 withcolor c ;
528    drawcontrolpoints p withpen pencircle scaled 2  withcolor c ;
529    label.lft("\smallinfofont current", point 1 of p) ;
530    label.top("\smallinfofont next", point 2 of p) ;
531    draw thetextext.rt("\infofont path " & t, (point 3 of p) shifted (5,0)) ;
532enddef ;
533
534draw image (
535    Example(p, "p", "darkred")  ; currentpicture := currentpicture yshifted 50 ;
536    Example(q, "q", "darkblue") ; currentpicture := currentpicture yshifted 50 ;
537    Example(r, "r", "darkred")  ; currentpicture := currentpicture yshifted 50 ;
538    Example(s, "s", "darkblue") ; currentpicture := currentpicture yshifted 50 ;
539) xsized TextWidth ;
540\stopMPcode
541\stopbuffer
542
543\typebuffer[option=TEX]
544
545% This picture was made by Mikael when we tested these new commands.
546
547\startplacefigure[title={Three ways to set the control points.},reference=fig:controlpoints]
548    \getbuffer
549\stopplacefigure
550
551\stopsection
552
553\startsection[title=Arcs]
554
555In \POSTSCRIPT\ and \SVG\ we have an arc command but not in \METAPOST. In \LMTX\
556we provide a macro that does something similar:
557
558\startbuffer
559\startMPcode
560draw
561    (0,0) --
562    (arc(0,180) scaled 30 shifted (0,30)) --
563    cycle
564withpen pencircle scaled 2
565withcolor "darkred" ;
566\stopMPcode
567\stopbuffer
568
569\typebuffer[option=TEX]
570
571The result is not spectacular:
572
573\startlinecorrection \getbuffer \stoplinecorrection
574
575Instead of a primitive with five arguments and the prescribed line drawn from the
576current point to the beginning of the arc we just use \type {..}, \type{scaled}
577for the radius, and \type {shifted} for the origin. It actually permits more
578advanced trickery.
579
580\startbuffer
581\startMPcode
582draw
583    (0,0) ..
584    (arc(30,240) xscaled 60 yscaled 30 shifted (0,30)) ..
585    cycle
586withpen pencircle scaled 2
587withcolor "darkred" ;
588\stopMPcode
589\stopbuffer
590
591\typebuffer[option=TEX]
592
593Here time we get smooth connections:
594
595\startlinecorrection \getbuffer \stoplinecorrection
596
597but because we scale differently also a different kind of arc: it is no longer a
598circle segment, which is often the intended use of arc.
599
600\stopsection
601
602\startsection[title=Loops]
603
604The \METAPOST\ program is a follow up on \METAFONT, which primary target was to
605design fonts. The paths that make op glyphs are often not that large and because
606in most cases we don't know in advance how large a path is they are implemented
607as linked lists. Now consider a large paths, with say 500 knots. The following
608assignment:
609
610\starttyping[option=MP]
611pair a ; a := point 359 of p ;
612\stoptyping
613
614has to jump across 358 knots before it reaches the requested point. Let's take an
615example of drawing a function by (naively) stepping over values:
616
617\startbuffer
618\startMPcode
619path p ; p := for i=0 step 4pi/500 until 4pi: (i,sin(i)) -- endfor nocycle ;
620p := p xysized(TextWidth,2cm) ;
621draw p ;
622\stopMPcode
623\stopbuffer
624
625\typebuffer[option=TEX]
626
627\startlinecorrection \getbuffer \stoplinecorrection
628
629\startbuffer
630\startMPcode
631draw p ; for i=0 step 5 until length(p) :
632    drawdot point i of p withpen pencircle scaled 2 ;
633endfor ;
634\stopMPcode
635\stopbuffer
636
637Of course we can just calculate the point directly but here we just
638want to illustrate a problem.
639
640\typebuffer[option=TEX]
641
642% \startlinecorrection \getbuffer \stoplinecorrection
643
644For 500 points, on a modern computer running over the list is rather fast
645but when we are talking 5000 points is gets noticeable, and given what
646\METAPOST\ is used for, having many complex graphics calculated at runtime
647can have some impact on runtime.
648
649% \startlinecorrection \getbuffer \stoplinecorrection
650
651Of course we can just calculate the point directly but here we just
652want to illustrate a problem. Where the previous loop takes 0.002
653seconds, the second loop needs 0.001 seconds:
654
655\startbuffer
656\startMPcode
657pair p ; for i within p :
658   if i mod 5 == 0 :
659       drawdot pathpoint withpen pencircle scaled 2 ;
660   fi ;
661endfor ;
662\stopMPcode
663\stopbuffer
664
665\typebuffer[option=TEX]
666
667% \startlinecorrection \getbuffer \stoplinecorrection
668
669These numbers are for assigning the point to a pair variable so that we don't
670take into account the extra drawing (and backend) overhead. The difference in
671runtime can be neglected but what if we go to 5000 points? Not unsurprisingly we
672go down from 0.142 seconds to 0.004 seconds. There are plenty examples where
673runtime can be impacted, for instance when one first takes the \typ {xpart point
674i} and then the \typ {ypart point i}.
675
676One motivation for adding a more efficient loop for paths is that in generative
677art one has such long parts and drawing that took tens of minutes or more now can
678be generated in seconds. Another motivation is in analyzing and manipulating
679paths. In that case we also need access to the control points and maybe even
680preceding or succeeding points. In \in {figure} [fig:withinpath] we show the
681output of the following code:
682
683\startbuffer
684\startMPcode
685path p ; p := fullcircle scaled 10cm ;
686fill p withcolor "darkred" ;
687draw p withpen pencircle scaled 1mm withcolor "middleblue" ;
688
689for i within p :
690    draw pathpoint       withpen pencircle scaled 4mm withcolor "middlegray" ;
691    draw pathprecontrol  withpen pencircle scaled 2mm withcolor "middlegreen" ;
692    draw pathpostcontrol withpen pencircle scaled 2mm withcolor "middlegreen" ;
693    draw textext("\ttbf" & decimal i) shifted .6[deltapoint -2,origin] withcolor white ;
694    draw textext("\ttbf" & decimal i) shifted .4[pathpoint    ,origin] withcolor white ;
695    draw textext("\ttbf" & decimal i) shifted .2[deltapoint  2,origin] withcolor white ;
696endfor ;
697\stopMPcode
698\stopbuffer
699
700\typebuffer[option=TEX]
701
702The \METAPOST\ library in \LUAMETATEX\ uses double linked lists for paths so going
703back and forward is a rather cheap operation.
704
705\startplacefigure[title={Fast looping over paths.},reference=fig:withinpath]
706    \getbuffer
707\stopplacefigure
708
709A nice application of this feature is the following, where we use yet another
710point property, \typ {pathdirection}:
711
712\starttyping[option=MP]
713vardef dashing (expr pth, shp, stp) =
714    for i within arcpointlist stp of pth :
715        shp
716            rotated angle(pathdirection)
717            shifted pathpoint
718        &&
719    endfor nocycle
720enddef ;
721\stoptyping
722
723With:
724
725\startbuffer
726\startMPcode
727path p ; p := unitsquare xysized (TextWidth,1cm) ;
728draw p withpen pencircle scaled .2mm withcolor darkblue ;
729fill dashing (p, triangle scaled 1mm, 100) && cycle withcolor "darkred" ;
730\stopMPcode
731\stopbuffer
732
733\typebuffer[option=TEX]
734
735we get:
736
737\startlinecorrection
738\getbuffer
739\stoplinecorrection
740
741It is worth noticing that the path returned by dashing is actually a combined
742path where the pen gets lifted between the subpaths. This is what the \type {&&}
743does. The \type {nocycle} is there to intercept the last \quote {connector} (which
744of course could also have been a \type {--} or \type {..}. So we end up with an
745open path, which why in case of a fill we need to close it by \type {cycle}.
746In the next example we show all the accessors:
747
748\startbuffer
749\startMPcode[instance=scaledfun]
750path p; p := (fullsquare scaled 3 && fullsquare rotated 45 scaled 2 && cycle) ;
751
752for i within p :
753    message(
754            "index "       & decimal  pathindex
755        & ", lastindex "   & decimal  pathlastindex
756        & ", length "      & decimal  pathlength
757        & ", first "       & if       pathfirst : "true" else : "false" fi
758        & ", last "        & if       pathlast  : "true" else : "false" fi
759        & ", state "       & decimal  pathstate % end/begin subpath
760        & ", point "       & ddecimal pathpoint
761        & ", postcontrol " & ddecimal pathprecontrol
762        & ", precontrol "  & ddecimal pathpostcontrol
763        & ", direction "   & ddecimal pathdirection
764        & ", delta "       & ddecimal deltapoint 1
765    );
766endfor ;
767
768eofill p xysized (TextWidth, 2cm) withcolor "darkred" ;
769\stopMPcode
770\stopbuffer
771
772\typebuffer[option=TEX]
773
774If you want to see the messages you need to process it yourself, but this is how the
775ten point shape looks like:
776
777\startlinecorrection \getbuffer \stoplinecorrection
778
779\stopsection
780
781\startsection[title=Randomized paths]
782
783When randomizing a path the points move and when such a path has to bound a specific
784areas that can result in overlap which what is bounded.
785
786\startbuffer
787\startMPcode
788path p ; p := fullsquare xyscaled (10cm,2cm) ;
789fill p withcolor "darkred" ;
790draw p randomized 3mm withpen pencircle scaled 1mm withcolor "middlegray";
791setbounds currentpicture to p ;
792\stopMPcode
793\stopbuffer
794
795\typebuffer[option=TEX]
796
797\startlinecorrection \getbuffer \stoplinecorrection
798
799Here are two variants that randomize a path but keep the points where they
800are. They might be better suited for cases where there is text within the
801area.
802
803\startbuffer
804\startMPcode
805path p ; p := fullsquare xyscaled (10cm,2cm) ;
806fill p withcolor "darkblue" ;
807draw p randomizedcontrols 3mm withpen pencircle scaled 1mm withcolor "middlegray";
808setbounds currentpicture to p ;
809\stopMPcode
810\stopbuffer
811
812\typebuffer[option=TEX]
813
814\startlinecorrection \getbuffer \stoplinecorrection
815
816\startbuffer
817\startMPcode
818path p ; p := fullsquare xyscaled (10cm,2cm) ;
819fill p withcolor "darkyellow" ;
820draw p randomrotatedcontrols 15 withpen pencircle scaled 1mm withcolor "middlegray";
821setbounds currentpicture to p ;
822\stopMPcode
823\stopbuffer
824
825\typebuffer[option=TEX]
826
827\startlinecorrection \getbuffer \stoplinecorrection
828
829\stopsection
830
831\startsection[title=Connecting]
832
833In \LUAMETATEX\ the \type {--} operator is a primitive, like \type {..} and when
834exploring this we came up with this example that demonstrates the difference
835with (still a macro ) \type {---}.
836
837\startbuffer
838\startMPcode
839path p[] ;
840p[1] = origin --  (100, 0) ..  (75, 50) ..  (50, 100) ..  (25, 50) ..  cycle ;
841p[2] = origin --- (100, 0) ..  (75, 50) ..  (50, 100) ..  (25, 50) ..  cycle ;
842p[3] = origin --  (100, 0) ... (75, 50) ... (50, 100) ... (25, 50) ... cycle ;
843p[4] = origin --- (100, 0) ... (75, 50) ... (50, 100) ... (25, 50) ... cycle ;
844
845draw p[1] withpen pencircle scaled 3bp withcolor "darkblue" ;
846draw p[2] withpen pencircle scaled 2bp withcolor "darkyellow" ;
847drawpoints p[1] withpen pencircle scaled 3bp withcolor darkred ;
848
849draw image (
850    draw p[1] withpen pencircle scaled 4bp withcolor "darkblue" ;
851    draw p[2] withpen pencircle scaled 3bp withcolor "darkyellow" ;
852    draw p[3] withpen pencircle scaled 2bp withcolor "darkred" ;
853    draw p[4] withpen pencircle scaled 1bp withcolor "darkgreen" ;
854) shifted (150,0) ;
855\stopMPcode
856\stopbuffer
857
858\typebuffer[option=TEX]
859
860Where \type {...} makes a more tight curve, \type {---} has consequences for the
861way a curve gets connected to a straight line segment.
862
863\startlinecorrection \getbuffer \stoplinecorrection
864
865\stopsection
866
867\startsection[title=Curvature]
868
869Internally \METAPOST\ only has curves but when a path is output it makes sense to
870use lines when possible. The \CONTEXT\ backend takes care of that (and further optimizations)
871but you can check yourself too.
872
873\startbuffer
874\startMPcode
875def Test(expr p, c) =
876    draw
877        p
878        withpen pencircle scaled 2mm
879        withcolor c ;
880    draw
881        textext("\bf " & if not (subpath(2,3) of p hascurvature 0.02) : "not" else : "" fi & " curved" )
882        shifted center p ;
883enddef ;
884
885Test(fullcircle scaled 3cm shifted (0cm,0),"darkred");
886Test(fullsquare scaled 3cm shifted (4cm,0),"darkblue");
887Test(fullsquare scaled 3cm shifted (8cm,0) randomizedcontrols 1cm,"darkgreen");
888\stopMPcode
889\stopbuffer
890
891\typebuffer[option=TEX]
892
893The \typ {hascurvature} macro is a primary and applies a curvature criterium to a
894(sub)path. The default tolerance in the backend is \im {131/65536} or \im
895{\luaexpr[.5N]{131/65536}}. The same default is used for eliminating points that \quote
896{are the same}.
897
898\startlinecorrection \getbuffer \stoplinecorrection
899
900In the rare case that the backend decides for straight lines while actually
901there is a curve, you can use \typ {withcurvature 1} to bypass the check.
902
903\stopsection
904
905\startsection[title=Joining paths]
906
907Say that you have three paths:
908
909\starttyping[option=TEX]
910path p[] ;
911p[1] := (0,0) -- (100,0) ;
912p[2] := (101,0) -- (100,100) ;
913p[3] := (100,101) ;
914\stoptyping
915
916If you join these with:
917
918\starttyping[option=TEX]
919draw p[1] & p[2] & p[3] -- cycle ;
920\stoptyping
921
922You will get an error message telling that the paths don't have common points so
923that they can't be joined. This can be a problem when your snippets are the result
924of cutting up a path. In practice the difference between the to be joined coordinates is
925small, so we provide a way to get around this problem:
926
927\startbuffer
928\startMPcode
929    interim jointolerance := 5eps ;
930    draw (0,0) -- (100,0) & (100+4eps,0) -- (100,20) & (100,20+2eps) -- cycle
931        withpen pencircle scaled 2 withcolor "darkred" ;
932\stopMPcode
933\stopbuffer
934
935\typebuffer[option=TEX]
936
937Up to the tolerance is accepted as difference in either direction, so indeed we get
938a valid result:
939
940\startlinecorrection \getbuffer \stoplinecorrection
941
942\startbuffer
943\startMPcode
944    interim jointolerance := 20 ;
945    draw (0,0) -- (100,0) & (110,10) -- (100,40) & (100,50) -- cycle
946        withpen pencircle scaled 2 withcolor "darkred" ;
947\stopMPcode
948\stopbuffer
949
950Larger values can give a more noticeable side effect:
951
952\typebuffer[option=TEX]
953
954It all depends on your need it this is considered okay:
955
956\startlinecorrection \getbuffer \stoplinecorrection
957
958As with everything \TEX\ and \METAPOST, once you see what is possible it can be
959abused:
960
961\startbuffer
962\startMPcode
963    interim jointolerance := 20 ;
964    randomseed := 10 ;
965    draw for i=1 upto 200 :
966       (i,50 randomized 10) --
967    endfor nocycle
968        withpen pencircle scaled .1 ;
969    randomseed := 10 ;
970    draw for i=1 upto 200 :
971        (i,50 randomized 10) if odd i : & else : -- fi
972    endfor nocycle
973        withcolor "darkred" ;
974\stopMPcode
975\stopbuffer
976
977\typebuffer[option=TEX]
978
979We leave it up to the reader to decide how the red line can be interpreted.
980
981\startlinecorrection \scale[width=\textwidth]{\getbuffer} \stoplinecorrection
982
983Here is another nice example:
984
985\startbuffer
986\startMPcode
987    path p[] ;
988    p[1] := origin -- (100,50) ;
989    p[2] := (200,50) -- (300,0) ;
990    draw p[1] && p[2] withpen pencircle scaled 4 withcolor darkgreen ;
991    draw p[1] -- p[2] withpen pencircle scaled 2 withcolor "orange" ;
992    interim jointolerance := 100 ;
993    draw p[1] & p[2]  withpen pencircle scaled 1 withcolor darkblue ;
994\stopMPcode
995\stopbuffer
996
997\typebuffer[option=TEX]
998
999Watch how we get a curve:
1000
1001\startlinecorrection \getbuffer \stoplinecorrection
1002
1003\stopsection
1004
1005\stopchapter
1006
1007\stopcomponent
1008