% language=us runpath=texruns:manuals/luametafun % \enablemode[check] % corners == boundingbox \environment luametafun-style \startcomponent luametafun-paths \startchapter[title={Paths}] \startsection[title=Introduction] In the end \METAPOST\ is all about creating (beautiful) paths. In this chapter we introduce some extensions to the engine that can be of help when constructing paths. Some relate to combining paths segments, others to generating the points. \stopsection \startsection[title=Cycles] The \type {cycle} commands closes a path: the end gets connected to the start. One way to construct a path stepwise is using a \type {for} loop, as in: \startbuffer \startMPcode draw ( (0,sin(0)) for i=pi/20 step pi/20 until 2pi : .. (i,sin(i)) endfor ) xysized(8cm,2cm) withpen pencircle scaled 1mm withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection This looks kind of ugly because we need to make sure that we only put the \type {..} between points. If we have a closed path we can do this: \startbuffer \startMPcode draw ( for i=0 step pi/20 until 2pi : (i,sin(i)) .. endfor cycle ) xysized(8cm,2cm) withpen pencircle scaled 1mm withcolor "darkblue" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection But that is not what we want here. It is for this reason that we have a different operator, one that closes a path without cycling: \startbuffer \startMPcode draw ( for i=0 step pi/20 until 2pi : (i,sin(i)) .. endfor nocycle ) xysized(8cm,2cm) withpen pencircle scaled 1mm withcolor "darkgreen" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \stopsection \startsection[title=Combining paths] The \type {&} concat operator requires the last point of the previous and the first point of the current path to be the same. This restriction is lifted with the \type {&&}, \type {&&&} and \type {&&&&} commands. \startbuffer \startMPcode def Example(expr p, q) = draw image ( drawpathonly (p && q) shifted ( 0u,0) ; drawpathonly (p &&& q) shifted ( 5u,0) ; drawpathonly (p &&&& q) shifted (10u,0) ; ) ; enddef ; path p[] ; numeric u ; u := 1cm ; p[1] := (0u,0u) -- (1u,0u) -- (1u,1u) ; p[2] := (1u,1u) -- (2u,1u) -- (2u,0u) ; Example(p[1], p[2]) ; Example(p[1] shifted (0u,-2u), p[2] shifted (1u,-2u)) ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection The precise working can be best be seen from what path we get. The single ampersand just does a concat but issues an error when the paths don't touch so we leave that one out. % 0 -> 0 \startbuffer \startMPdefinitions path p, q, r ; p := (0,0) -- (1,0) ; q := (2,0) -- (3,0) ; r := (1,0) -- (3,0) ; vardef Example(expr p) = % show (p); drawpathonly p scaled 4cm ; enddef ; \stopMPdefinitions \stopbuffer \typebuffer[option=TEX] \getbuffer \testpage[4] This gives us: \startlinecorrection \startMPcode Example(p && q) ; \stopMPcode \stoplinecorrection \starttyping (0,0) .. controls (0.33,0) and (0.67,0) .. % p && q (1,0) {end} .. controls (2, 0) and (1, 0) .. (2,0) {begin} .. controls (2.33,0) and (2.67,0) .. (3,0) \stoptyping \testpage[3] \startlinecorrection \startMPcode Example(p && r) ; \stopMPcode \stoplinecorrection \starttyping (0,0) .. controls (0.33,0) and (0.67,0) .. % p && r (1,0) {end} .. controls (1, 0) and (1, 0) .. (1,0) {begin} .. controls (1.67,0) and (2.33,0) .. (3,0) \stoptyping \testpage[3] \startlinecorrection \startMPcode Example(p &&& q) ; \stopMPcode \stoplinecorrection \starttyping (0,0) .. controls (0.33,0) and (0.67,0) .. % p &&& q (1,0) .. controls (2.33,0) and (2.67,0) .. (3,0) \stoptyping \testpage[3] \startlinecorrection \startMPcode Example(p &&& r) ; \stopMPcode \stoplinecorrection \starttyping (0,0) .. controls (0.33,0) and (0.67,0) .. % p &&& r (1,0) .. controls (1.67,0) and (2.33,0) .. (3,0) \stoptyping \testpage[3] \startlinecorrection \startMPcode Example(p &&&& q) ; \stopMPcode \stoplinecorrection \starttyping (0,0) .. controls (0.33,0) and (0.67,0) .. % p &&&& q (1,0) {end} .. controls (2, 0) and (1, 0) .. (2,0) {begin} .. controls (2.33,0) and (2.67,0) .. (3,0) \stoptyping \testpage[3] \startlinecorrection \startMPcode Example(p &&&& r) ; \stopMPcode \stoplinecorrection \starttyping (0,0) .. controls (0.33,0) and (0.67,0) .. % p &&&& r (1,0) {end} .. controls (1, 0) and (1, 0) .. (1,0) {begin} .. controls (1.67,0) and (2.33,0) .. (3,0) \stoptyping If we have one (concat) ampersand we check if the paths touch, error or move on. If we have three (tolerant concat) or four (tolerant append) ampersands we check if the end and begin are the same and if so, we remove one and set the controls points halfway, and then degrade to one (concat) or two (append) ampersands. Finally when (then) we have one ampersand (concat) we connect with some curl magic but when we have two (append) we connect without the curl magic: we let the left and right control points be the points. \startbuffer \startMPcode path p[] ; p[1] := (0,0) -- (100,0) -- (100,100) ; for i=2 upto 5 : p[i] := p[1] ; endfor ; p[1] := p[1] -- cycle ; p[1] := p[1] -- cycle ; p[1] := p[1] -- cycle ; p[2] := p[2] -- cycle ; p[2] := p[2] &&& cycle ; p[2] := p[2] &&& cycle ; p[3] := p[3] -- cycle ; p[3] := p[3] &&&& cycle ; p[3] := p[3] &&&& cycle ; p[4] := p[4] &&& cycle ; p[5] := p[5] &&&& cycle ; for i=1 upto 5 : % show(p[i]) ; fill p[i] shifted (i*110,0) withcolor "middlegray" ; draw p[i] shifted (i*110,0) withcolor "darkred" withpen pencircle scaled 5 ; endfor ; currentpicture := currentpicture xsized TextWidth ; \stopMPcode \stopbuffer Here is another example of usage. Watch how \type {&&&} doesn't influence an already closed curve. \typebuffer \startlinecorrection \getbuffer \stoplinecorrection The paths are, here shown with less precision: \starttyping (0,0) .. controls (33.33,0) and (66.67,-0) .. (100,0) .. controls (100,33.33) and (100,66.67) .. (100,100) .. controls (66.67,66.67) and (33.33,33.33) .. (0,0) .. controls (0,0) and (0,0) .. (0,0) .. controls (0,0) and (0,0) .. cycle (0,0) .. controls (33.33,0) and (66.67,-0) .. (100,0) .. controls (100,33.33) and (100,66.67) .. (100,100) .. controls (66.67,66.67) and (33.33,33.33) .. cycle (0,0) {begin} .. controls (33.33,0) and (66.67,-0) .. (100,0) .. controls (100,33.33) and (100,66.67) .. (100,100) .. controls (66.67,66.67) and (33.33,33.33) .. (0,0) {end} .. controls (0,0) and (0,0) % duplicate {end} is .. (0,0) {end} .. controls (0,0) and (0,0) % sort of an error .. cycle (100,100) .. controls (33.33,0) and (66.67,-0) .. (100,0) .. controls (100,33.33) and (100,66.67) .. cycle (0,0) {begin} .. controls (33.33,0) and (66.67,-0) .. (100,0) .. controls (100,33.33) and (100,66.67) .. (100,100) {end} .. controls (0,0) and (100,100) .. cycle \stoptyping These somewhat complicated rules also relate to the intended application: the backend can apply \type {fill} or \type {eofill} in which case also cycles are involved as the following examples demonstrate: \startbuffer \startMPdefinitions path p, q, r ; p := fullcircle ; q := reverse fullcircle ; r := fullcircle shifted (1/2,0) ; vardef Example(expr p) = image ( eofill p scaled 4cm withcolor "middlegray" ; drawpathonly p scaled 4cm ; ) enddef ; \stopMPdefinitions \stopbuffer \typebuffer[option=TEX] \getbuffer \startbuffer \startMPcode draw Example(p &&& q &&& cycle) ; draw Example(p &&& cycle &&& q &&& cycle) shifted (8cm,0) ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \startbuffer \startMPcode draw Example(p &&& r &&& cycle) ; draw Example(p &&& cycle &&& r &&& cycle) shifted (8cm,0) ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \startbuffer \startMPcode draw Example(p &&&& q &&&& cycle) ; draw Example(p &&&& cycle &&&& q &&&& cycle) shifted (8cm,0) ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \startbuffer \startMPcode draw Example(p &&&& r &&&& cycle) ; draw Example(p &&&& cycle &&&& r &&&& cycle) shifted (8cm,0) ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \stopsection \startsection[title=Implicit points] In the \METAPOST\ library that comes with \LUAMETATEX\ we have a few extensions that relate to paths. You might wonder why we need these but some relate to the fact that paths can be generated programmatically. A prominent operator (or separator) is \type {..} and contrary to what one might expect the frequently used \type {--} is a macro: \starttyping[option=MP] def -- = { curl 1 } .. { curl 1 } enddef ; \stoptyping This involves interpreting nine tokens as part of expanding the macro and in practice that is fast even for huge paths. Nevertheless we now have a \type {--} primitive that involves less interpreting and also avoids some intermediate memory allocation of numbers. Of course you can still define it as macro. When you look at \POSTSCRIPT\ you'll notice that it has operators for relative and absolute positioning in the horizontal, vertical or combined direction. In \LUAMETATEX\ we now have similar operators that we will demonstrate with a few examples. \startbuffer \startMPcode drawarrow origin -- xrelative 300 -- yrelative 20 -- xrelative -300 -- cycle withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection In the next example we show a relative position combined with an absolute and we define them as macros. You basically gets what goes under the name \quote {turtle graphics}: \startbuffer \startMPcode save h ; def h = -- xrelative enddef ; save v ; def v = -- yabsolute enddef ; drawarrow origin h 30 v 20 h 30 v 30 h 30 v 10 h 30 v 50 h 30 v 60 h 30 v 10 withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection When you provide a pair to \type {xabsolute} or \type {yabsolute}, the xpart is the (relative) advance and the second the absolute coordinate. \startbuffer \startMPcode draw origin -- yabsolute(10,30) -- yabsolute(20,20) -- yabsolute(30,10) -- yabsolute(40,20) -- yabsolute(50,30) -- yabsolute(60,20) -- yabsolute(70,10) -- yabsolute(80,20) -- yabsolute(90,30) withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection The \type {xyabsolute} is sort of redundant and is equivalent to just a pair, but maybe there is a use for it. When the two coordinates are the same you can use a numeric. \startbuffer \startMPcode draw origin -- xyabsolute(10, 10) % -- xyabsolute 10 -- xyabsolute(20, 10) -- xyabsolute(30,-10) -- xyabsolute(40,-10) -- xyabsolute(50, 10) -- xyabsolute(60, 10) -- xyabsolute(70,-10) -- xyabsolute(80,-10) withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection The relative variant also can take a pair and numeric, as in: \startbuffer \startMPcode draw origin -- xyrelative 10 -- xyrelative 10 -- xyrelative(10,-10) -- xyrelative(10,-10) -- xyrelative 10 -- xyrelative 10 -- xyrelative(10,-10) -- xyrelative(10,-10) withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection In these examples we used \type {--} but you can mix in \type {..} and control point related operations, although the later is somewhat less intuitive here. \startbuffer \startMPcode draw yabsolute(10,30) .. yabsolute(20,20) .. yabsolute(10,10) .. yabsolute(20,20) .. yabsolute(10,30) .. yabsolute(20,20) .. yabsolute(10,10) .. yabsolute(20,20) .. yabsolute(10,30) withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection And with most features, users will likely find a use for it: \startbuffer \startMPcode draw for i=1 upto 5 : yabsolute(10,30) --- yabsolute(20,20) ... yabsolute(10,10) --- yabsolute(20,20) ... endfor nocycle withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection Here is a more impressive example, the result is shown in \in {figure} [fig:r-a-paths]: \startbuffer \startMPcode for n=10 upto 40 : path p ; p := ( for i = 0 step pi/n until pi : yabsolute(cos(i)^2-sin(i)^2,sin(i)^2-cos(i)^2) -- endfor cycle ) ; draw p withpen pencircle scaled 1/20 withcolor "darkred" withtransparency (1,.25) ; endfor ; currentpicture := currentpicture xysized (TextWidth,.25TextWidth) ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startplacefigure[title=Combined relative x and absolute y positioning,ref=fig:r-a-paths] \getbuffer \stopplacefigure \stopsection \startsection[title=Control points] Most users will create paths by using \type {..}, \type {...}, \type {--} and \type {---} and accept what they get by the looks. If your expectations are more strict you might use \type {tension} or \type {curl} with directions and vectors for the so called control points between connections. In \in {figure} [fig:controlpoints] you see not only \type {controls} in action but also two operators that can be used to set the first and second control point. For the record: if you use \type {controls} without \type {and} the singular pair will be used for both control points. \startbuffer \startMPcode path p, q, r, s ; p = origin {dir 25} .. (80,0) .. controls ( 80, 0) and (100,40) .. (140,30) .. {dir 0} (180,0) ; q = origin {dir 25} .. (80,0) .. controls (100,40) and (140,30) .. (140,30) .. {dir 0} (180,0) ; r = origin {dir 25} .. (80,0) .. secondcontrol (100,40) .. (140,30) .. {dir 0} (180,0) ; s = origin {dir 25} .. (80,0) .. firstcontrol (100,40) .. (140,30) .. {dir 0} (180,0) ; def Example(expr p, t, c) = draw p ; drawpoints p withcolor "middlegray" ; drawcontrollines p withpen pencircle scaled .3 withcolor c ; drawcontrolpoints p withpen pencircle scaled 2 withcolor c ; label.lft("\smallinfofont current", point 1 of p) ; label.top("\smallinfofont next", point 2 of p) ; draw thetextext.rt("\infofont path " & t, (point 3 of p) shifted (5,0)) ; enddef ; draw image ( Example(p, "p", "darkred") ; currentpicture := currentpicture yshifted 50 ; Example(q, "q", "darkblue") ; currentpicture := currentpicture yshifted 50 ; Example(r, "r", "darkred") ; currentpicture := currentpicture yshifted 50 ; Example(s, "s", "darkblue") ; currentpicture := currentpicture yshifted 50 ; ) xsized TextWidth ; \stopMPcode \stopbuffer \typebuffer[option=TEX] % This picture was made by Mikael when we tested these new commands. \startplacefigure[title={Three ways to set the control points.},reference=fig:controlpoints] \getbuffer \stopplacefigure \stopsection \startsection[title=Arcs] In \POSTSCRIPT\ and \SVG\ we have an arc command but not in \METAPOST. In \LMTX\ we provide a macro that does something similar: \startbuffer \startMPcode draw (0,0) -- (arc(0,180) scaled 30 shifted (0,30)) -- cycle withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] The result is not spectacular: \startlinecorrection \getbuffer \stoplinecorrection Instead of a primitive with five arguments and the prescribed line drawn from the current point to the beginning of the arc we just use \type {..}, \type{scaled} for the radius, and \type {shifted} for the origin. It actually permits more advanced trickery. \startbuffer \startMPcode draw (0,0) .. (arc(30,240) xscaled 60 yscaled 30 shifted (0,30)) .. cycle withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] Here time we get smooth connections: \startlinecorrection \getbuffer \stoplinecorrection but because we scale differently also a different kind of arc: it is no longer a circle segment, which is often the intended use of arc. \stopsection \startsection[title=Loops] The \METAPOST\ program is a follow up on \METAFONT, which primary target was to design fonts. The paths that make op glyphs are often not that large and because in most cases we don't know in advance how large a path is they are implemented as linked lists. Now consider a large paths, with say 500 knots. The following assignment: \starttyping[option=MP] pair a ; a := point 359 of p ; \stoptyping has to jump across 358 knots before it reaches the requested point. Let's take an example of drawing a function by (naively) stepping over values: \startbuffer \startMPcode path p ; p := for i=0 step 4pi/500 until 4pi: (i,sin(i)) -- endfor nocycle ; p := p xysized(TextWidth,2cm) ; draw p ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \startbuffer \startMPcode draw p ; for i=0 step 5 until length(p) : drawdot point i of p withpen pencircle scaled 2 ; endfor ; \stopMPcode \stopbuffer Of course we can just calculate the point directly but here we just want to illustrate a problem. \typebuffer[option=TEX] % \startlinecorrection \getbuffer \stoplinecorrection For 500 points, on a modern computer running over the list is rather fast but when we are talking 5000 points is gets noticeable, and given what \METAPOST\ is used for, having many complex graphics calculated at runtime can have some impact on runtime. % \startlinecorrection \getbuffer \stoplinecorrection Of course we can just calculate the point directly but here we just want to illustrate a problem. Where the previous loop takes 0.002 seconds, the second loop needs 0.001 seconds: \startbuffer \startMPcode pair p ; for i within p : if i mod 5 == 0 : drawdot pathpoint withpen pencircle scaled 2 ; fi ; endfor ; \stopMPcode \stopbuffer \typebuffer[option=TEX] % \startlinecorrection \getbuffer \stoplinecorrection These numbers are for assigning the point to a pair variable so that we don't take into account the extra drawing (and backend) overhead. The difference in runtime can be neglected but what if we go to 5000 points? Not unsurprisingly we go down from 0.142 seconds to 0.004 seconds. There are plenty examples where runtime can be impacted, for instance when one first takes the \typ {xpart point i} and then the \typ {ypart point i}. One motivation for adding a more efficient loop for paths is that in generative art one has such long parts and drawing that took tens of minutes or more now can be generated in seconds. Another motivation is in analyzing and manipulating paths. In that case we also need access to the control points and maybe even preceding or succeeding points. In \in {figure} [fig:withinpath] we show the output of the following code: \startbuffer \startMPcode path p ; p := fullcircle scaled 10cm ; fill p withcolor "darkred" ; draw p withpen pencircle scaled 1mm withcolor "middleblue" ; for i within p : draw pathpoint withpen pencircle scaled 4mm withcolor "middlegray" ; draw pathprecontrol withpen pencircle scaled 2mm withcolor "middlegreen" ; draw pathpostcontrol withpen pencircle scaled 2mm withcolor "middlegreen" ; draw textext("\ttbf" & decimal i) shifted .6[deltapoint -2,origin] withcolor white ; draw textext("\ttbf" & decimal i) shifted .4[pathpoint ,origin] withcolor white ; draw textext("\ttbf" & decimal i) shifted .2[deltapoint 2,origin] withcolor white ; endfor ; \stopMPcode \stopbuffer \typebuffer[option=TEX] The \METAPOST\ library in \LUAMETATEX\ uses double linked lists for paths so going back and forward is a rather cheap operation. \startplacefigure[title={Fast looping over paths.},reference=fig:withinpath] \getbuffer \stopplacefigure A nice application of this feature is the following, where we use yet another point property, \typ {pathdirection}: \starttyping[option=MP] vardef dashing (expr pth, shp, stp) = for i within arcpointlist stp of pth : shp rotated angle(pathdirection) shifted pathpoint && endfor nocycle enddef ; \stoptyping With: \startbuffer \startMPcode path p ; p := unitsquare xysized (TextWidth,1cm) ; draw p withpen pencircle scaled .2mm withcolor darkblue ; fill dashing (p, triangle scaled 1mm, 100) && cycle withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] we get: \startlinecorrection \getbuffer \stoplinecorrection It is worth noticing that the path returned by dashing is actually a combined path where the pen gets lifted between the subpaths. This is what the \type {&&} does. The \type {nocycle} is there to intercept the last \quote {connector} (which of course could also have been a \type {--} or \type {..}. So we end up with an open path, which why in case of a fill we need to close it by \type {cycle}. In the next example we show all the accessors: \startbuffer \startMPcode[instance=scaledfun] path p; p := (fullsquare scaled 3 && fullsquare rotated 45 scaled 2 && cycle) ; for i within p : message( "index " & decimal pathindex & ", lastindex " & decimal pathlastindex & ", length " & decimal pathlength & ", first " & if pathfirst : "true" else : "false" fi & ", last " & if pathlast : "true" else : "false" fi & ", state " & decimal pathstate % end/begin subpath & ", point " & ddecimal pathpoint & ", postcontrol " & ddecimal pathprecontrol & ", precontrol " & ddecimal pathpostcontrol & ", direction " & ddecimal pathdirection & ", delta " & ddecimal deltapoint 1 ); endfor ; eofill p xysized (TextWidth, 2cm) withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] If you want to see the messages you need to process it yourself, but this is how the ten point shape looks like: \startlinecorrection \getbuffer \stoplinecorrection \stopsection \startsection[title=Randomized paths] When randomizing a path the points move and when such a path has to bound a specific areas that can result in overlap which what is bounded. \startbuffer \startMPcode path p ; p := fullsquare xyscaled (10cm,2cm) ; fill p withcolor "darkred" ; draw p randomized 3mm withpen pencircle scaled 1mm withcolor "middlegray"; setbounds currentpicture to p ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection Here are two variants that randomize a path but keep the points where they are. They might be better suited for cases where there is text within the area. \startbuffer \startMPcode path p ; p := fullsquare xyscaled (10cm,2cm) ; fill p withcolor "darkblue" ; draw p randomizedcontrols 3mm withpen pencircle scaled 1mm withcolor "middlegray"; setbounds currentpicture to p ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \startbuffer \startMPcode path p ; p := fullsquare xyscaled (10cm,2cm) ; fill p withcolor "darkyellow" ; draw p randomrotatedcontrols 15 withpen pencircle scaled 1mm withcolor "middlegray"; setbounds currentpicture to p ; \stopMPcode \stopbuffer \typebuffer[option=TEX] \startlinecorrection \getbuffer \stoplinecorrection \stopsection \startsection[title=Connecting] In \LUAMETATEX\ the \type {--} operator is a primitive, like \type {..} and when exploring this we came up with this example that demonstrates the difference with (still a macro ) \type {---}. \startbuffer \startMPcode path p[] ; p[1] = origin -- (100, 0) .. (75, 50) .. (50, 100) .. (25, 50) .. cycle ; p[2] = origin --- (100, 0) .. (75, 50) .. (50, 100) .. (25, 50) .. cycle ; p[3] = origin -- (100, 0) ... (75, 50) ... (50, 100) ... (25, 50) ... cycle ; p[4] = origin --- (100, 0) ... (75, 50) ... (50, 100) ... (25, 50) ... cycle ; draw p[1] withpen pencircle scaled 3bp withcolor "darkblue" ; draw p[2] withpen pencircle scaled 2bp withcolor "darkyellow" ; drawpoints p[1] withpen pencircle scaled 3bp withcolor darkred ; draw image ( draw p[1] withpen pencircle scaled 4bp withcolor "darkblue" ; draw p[2] withpen pencircle scaled 3bp withcolor "darkyellow" ; draw p[3] withpen pencircle scaled 2bp withcolor "darkred" ; draw p[4] withpen pencircle scaled 1bp withcolor "darkgreen" ; ) shifted (150,0) ; \stopMPcode \stopbuffer \typebuffer[option=TEX] Where \type {...} makes a more tight curve, \type {---} has consequences for the way a curve gets connected to a straight line segment. \startlinecorrection \getbuffer \stoplinecorrection \stopsection \startsection[title=Curvature] Internally \METAPOST\ only has curves but when a path is output it makes sense to use lines when possible. The \CONTEXT\ backend takes care of that (and further optimizations) but you can check yourself too. \startbuffer \startMPcode def Test(expr p, c) = draw p withpen pencircle scaled 2mm withcolor c ; draw textext("\bf " & if not (subpath(2,3) of p hascurvature 0.02) : "not" else : "" fi & " curved" ) shifted center p ; enddef ; Test(fullcircle scaled 3cm shifted (0cm,0),"darkred"); Test(fullsquare scaled 3cm shifted (4cm,0),"darkblue"); Test(fullsquare scaled 3cm shifted (8cm,0) randomizedcontrols 1cm,"darkgreen"); \stopMPcode \stopbuffer \typebuffer[option=TEX] The \typ {hascurvature} macro is a primary and applies a curvature criterium to a (sub)path. The default tolerance in the backend is \im {131/65536} or \im {\luaexpr[.5N]{131/65536}}. The same default is used for eliminating points that \quote {are the same}. \startlinecorrection \getbuffer \stoplinecorrection In the rare case that the backend decides for straight lines while actually there is a curve, you can use \typ {withcurvature 1} to bypass the check. \stopsection \startsection[title=Joining paths] Say that you have three paths: \starttyping[option=TEX] path p[] ; p[1] := (0,0) -- (100,0) ; p[2] := (101,0) -- (100,100) ; p[3] := (100,101) ; \stoptyping If you join these with: \starttyping[option=TEX] draw p[1] & p[2] & p[3] -- cycle ; \stoptyping You will get an error message telling that the paths don't have common points so that they can't be joined. This can be a problem when your snippets are the result of cutting up a path. In practice the difference between the to be joined coordinates is small, so we provide a way to get around this problem: \startbuffer \startMPcode interim jointolerance := 5eps ; draw (0,0) -- (100,0) & (100+4eps,0) -- (100,20) & (100,20+2eps) -- cycle withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] Up to the tolerance is accepted as difference in either direction, so indeed we get a valid result: \startlinecorrection \getbuffer \stoplinecorrection \startbuffer \startMPcode interim jointolerance := 20 ; draw (0,0) -- (100,0) & (110,10) -- (100,40) & (100,50) -- cycle withpen pencircle scaled 2 withcolor "darkred" ; \stopMPcode \stopbuffer Larger values can give a more noticeable side effect: \typebuffer[option=TEX] It all depends on your need it this is considered okay: \startlinecorrection \getbuffer \stoplinecorrection As with everything \TEX\ and \METAPOST, once you see what is possible it can be abused: \startbuffer \startMPcode interim jointolerance := 20 ; randomseed := 10 ; draw for i=1 upto 200 : (i,50 randomized 10) -- endfor nocycle withpen pencircle scaled .1 ; randomseed := 10 ; draw for i=1 upto 200 : (i,50 randomized 10) if odd i : & else : -- fi endfor nocycle withcolor "darkred" ; \stopMPcode \stopbuffer \typebuffer[option=TEX] We leave it up to the reader to decide how the red line can be interpreted. \startlinecorrection \scale[width=\textwidth]{\getbuffer} \stoplinecorrection Here is another nice example: \startbuffer \startMPcode path p[] ; p[1] := origin -- (100,50) ; p[2] := (200,50) -- (300,0) ; draw p[1] && p[2] withpen pencircle scaled 4 withcolor darkgreen ; draw p[1] -- p[2] withpen pencircle scaled 2 withcolor "orange" ; interim jointolerance := 100 ; draw p[1] & p[2] withpen pencircle scaled 1 withcolor darkblue ; \stopMPcode \stopbuffer \typebuffer[option=TEX] Watch how we get a curve: \startlinecorrection \getbuffer \stoplinecorrection \stopsection \stopchapter \stopcomponent