diff options
Diffstat (limited to 'doc/context/sources/general/manuals/ontarget/ontarget-metapost.tex')
-rw-r--r-- | doc/context/sources/general/manuals/ontarget/ontarget-metapost.tex | 755 |
1 files changed, 755 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/ontarget/ontarget-metapost.tex b/doc/context/sources/general/manuals/ontarget/ontarget-metapost.tex new file mode 100644 index 000000000..521b90b63 --- /dev/null +++ b/doc/context/sources/general/manuals/ontarget/ontarget-metapost.tex @@ -0,0 +1,755 @@ +% language=us runpath=texruns:manuals/ontarget + +% Musical timestamp: while adding this to the engine and I listened to many John +% Medeski (Martin and Wood) concert videos, many several times ... it kept me in +% the flow! +% +% This wrapup was written with Dave Matthews Band - Seek Up - LIVE, 12/2/2018 in +% the background, as I like these live acts. +% +% This is just a summary from my point of view. A real article is written by +% Mikael Sundqvist who also explains how arctime and its companions actually +% work. + +\startcomponent ontarget-metapost + +\environment ontarget-style + +\startchapter[title={To the point}] + +In the 2022 \NTG\ \MAPS\ 53 there is a visual very attractive article about +generative graphics with \METAPOST\ by Fabrice Larribe. These graphics actually +use very little \METAPOST\ code that use randomized paths and points and the +magic is in getting the parameters right. This means that one has to process them +a lot to figure out what looks best. Here is an example of such a graphic +definition. I will show more variants so a rendering happens later on. + +\startbuffer +\startMPdefinitions + vardef agitate_a(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel, rlength ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + R := for i=1 upto nbpoints: + point (i/nbpoints) along R + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +I will not explain the working of this because there is the article. Instead, I +will focus on something that came up when the \MAPS\ was prepared: performance. +Not only are these graphics large (which is no real problem) but they also take a +while to render (which is something that does matter when one wants to find the +best a parameters). For the first few variants we keep the same names of +variables as in the article. + +\startbuffer[final:definition] +\startMPdefinitions + vardef agitator(expr pth, iterations, points, pointfactor, noise, noisefactor) = + save currentpath, currentpoints, currentnoise ; path currentpath ; + currentpath := pth ; + currentpoints := points ; + currentnoise := noise ; + for step = 1 upto iterations : + currentpath := arcpointlist currentpoints of currentpath ; + if currentnoise <> 0 : + currentpath := + for i within currentpath : + pathpoint + randomized currentnoise + .. + endfor + cycle ; + fi + currentnoise := currentnoise * noisefactor ; + currentpoints := currentpoints * pointfactor ; + endfor ; + currentpath + enddef ; +\stopMPdefinitions +\stopbuffer + +\startbuffer[final:graphic] +\startMPcode + path pth ; + nofcircles := 15 ; iterations := 10 ; + points := 10 ; pointfactor := 1.3 ; + noise := 5 ; noisefactor := 0.8 ; + + nofcircles := 5 ; iterations := 10 ; + points := 5 ; pointfactor := 1.3 ; + +% for c = nofcircles downto 1 : +% pth := fullcircle scaled (c * 6.5) scaled 3 ; +% points := floor(arclength(pth) * 0.5) ; +% pth := agitator(pth, iterations, points, pointfactor, noise, noisefactor) ; +% eofill pth +% withcolor darkred +% withtransparency(1,4/nofcircles) ; +% draw pth +% withpen pencircle scaled 0.1 +% withtransparency(1,4/nofcircles) ; +% endfor ; + +% currentpicture := currentpicture xsized TextWidth ; + + for c = nofcircles downto 1 : + pth := fullcircle scaled (c * 6.5) scaled 3 ; + points := floor(arclength(pth) * 0.5) ; + pth := agitator(pth, iterations, points, pointfactor, noise, noisefactor) ; + draw pth + withpen pencircle scaled 1 + withcolor (c/nofcircles)[darkgreen,darkred] ; + endfor ; + + currentpicture := currentpicture xsized .5TextWidth ; +\stopMPcode +\stopbuffer + +\startplacefigure + [title={Fabrice's agitated circles, with reduced properties to keep this file small (see source).}, + reference=fig:agitated] + \getbuffer[final:definition,final:graphic] +\stopplacefigure + +In \in {figure} [fig:agitated] we show the (kind of) graphic that we are dealing +with. Such an agitator is used in a loop so that we agitate multiple circles, +where we go from large to small with for instance 4868, 4539, 4221, 3892, 3564, +3245, 2917, 2599, 2270, 1941, 1623, 1294, 966, 647 and 319 points. The article +uses a definition like below for the graphic where you can see the agitator being +applied to each of the circles. + +\starttyping[option=mp,color=] +path P ; numeric NbCircles, S, nzero, fn, tzero, ft ; + +randomseed := 10 ; +defaultscale := .05 ; + +NbCircles := 15 ; S := 10 ; nzero := 10 ; fn := 1.3 ; tzero := 5 ; ft := 0.8 ; + +for c = NbCircles downto 1 : + P := fullcircle scaled (c*6.5) scaled 3 ; + P := agitate_a(P, S, nzero, fn, tzero, ft) ; + eofill P + withcolor transparent(1,4/NbCircles,col) ; + draw P + withpen pencircle scaled 0.1 + transparent(1,4/NbCircles,.90[black,col]) ; +endfor ; +\stoptyping + +The first we noticed is that the graphics processes faster when double mode is +used: we gain 40--50\percent\ and the reason for this is that modern processors +are very good at handling doubles while \METAPOST\ in scaled mode has to do a lot +of juggling with pseudo fractions. In the timings shown later we leave that +improvement out. Also, because of this observation \CONTEXT\ \LMTX\ now defaults +its \METAPOST\ instances to method double. + +When I stared at the agitator code I noticed that the \type {along} macro was +used. That macro returns a point at given percentage along a path. In order to do +that the macro calculates the length of the path and then locates that point. The +primitive operations involved are \type {arclength}, \type {arctime of} and \type +{point of} and each these takes some time to complete. A first improvement is to +inline the \type {along} and hoist the length calculation outside the loop. + +\startbuffer +\startMPdefinitions + vardef agitate_b(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel, rlength ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + rlength := (arclength R) / nbpoints; + R := for i=1 upto nbpoints: + (point (arctime (i * rlength) of R) of R) + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +There is not that much that we can improve here but because Mikael Sundqvist and +I had just extended \METAPOST\ with some intersection improvements, it made sense +to see what we could do in the engine. In the next variant the \type {arcpoint} +combines \type {arctime of} and \type {point of}. The reason this is much +faster is that we are already on the right spot when we got the time, and we save +a sequential \type {point of} lookup, something that takes more time when paths +are longer. + +\startbuffer +\startMPdefinitions + vardef agitate_c(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel, rlength ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + rlength := (arclength R) / nbpoints; + R := for i=1 upto nbpoints: + (arcpoint (i * rlength) of R) + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +At that stage we wondered if we could come up with a primitive like \typ +{intersectiontimelist} for these points; here a list refers to a path in which we +collect the points. Now, as with the intersection primitives, \METAPOST\ loops +over the segments of a path and works within such a segment. That is why the +following variant has an explicit start at point zero: we can now use offsets +(discrete points). + +\startbuffer +\startMPdefinitions + vardef agitate_d(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel, rlength ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + rlength := (arclength R) / nbpoints; + R := for i=1 upto nbpoints: + (arcpoint (0, i * rlength) of R) + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +During an evening zooming Mikael and I figured out, by closely looking at the +source, how the arc functions work and how we could indeed come up with a list +primitive. The main issue was to use the right information. Mikael sat down to +make a pure \METAPOST\ variant and I hacked the engine. Mikael came up with a +first variant similar to the following, where we use a new primitive \typ +{subarclength}. + +\startbuffer +\startMPdefinitions + vardef arcpoints_a(expr thepath, cnt) = + save len, seg, tot, tim, stp, acc ; + numeric len ; len := length thepath ; + numeric seg ; seg := 0 ; + numeric tot ; tot := 0 ; + numeric tim ; tim := 0 ; + % + numeric acc[] ; acc[0] := 0 ; + for i = 1 upto len: + acc[i] := acc[i-1] + subarclength (i-1,i) of thepath ; + endfor; + % + numeric stp ; stp := acc[len] / cnt; + % + point 0 of thepath + for tot = stp step stp until acc[len] : + hide( + forever : + exitif ((tim < tot) and (tot < acc[seg+1])) ; + seg := seg + 1 ; + tim := acc[seg] ; + endfor ; + ) + -- (arcpoint (seg,tot-tim) of thepath) + endfor if cycle thepath : -- cycle fi + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +Getting points of a path is somewhat complicated by the fact that the length of a +closed path is different from that of an open path even if they have the same +number of so-called knots. Internally a path is always a closed loop. That way, +when \METAPOST\ runs over a path, it can easily access the first point when it's +at the end, something that is handy when that point has to be taken into account. +Therefore, the end condition of a loop over a path is the arrival at the +beginning. In the next graphic we show a bit how these first (zero) and last +points are located. One reason why the previous macros start at point one and not +at zero is that \type {arclength} can overflow due to the randomly growing path +otherwise. + +\startlinecorrection +\startMPcode + path p ; p := ((0,0) -- (1,0) -- (1,1) -- (0,1)) xyscaled (20EmWidth,5LineHeight) ; + path q ; q := p -- cycle ; + + draw p withpen pencircle scaled 4mm withcolor .2white ; + draw q withpen pencircle scaled 2mm withcolor .6white ; + + draw point length(p) of p withpen pencircle scaled 6mm withcolor green; + draw point length(q) of q withpen pencircle scaled 6mm withcolor blue ; + + draw point 0 of p withpen pencircle scaled 4mm withcolor red ; + draw point 0 of q withpen pencircle scaled 2mm withcolor yellow ; + + draw textext.lft("\strut\tttf point length of p") shifted (point length(p) of p) shifted (-6mm,0) ; + draw textext.lft("\strut\tttf point length of q") shifted (point length(q) of q) shifted (-6mm,LineHeight) ; + draw textext.lft("\strut\tttf point 0 of p") shifted (point 0 of p) shifted (-6mm,0) ; + draw textext.lft("\strut\tttf point 0 of q") shifted (point 0 of q) shifted (-6mm,-LineHeight) ; + + draw textext("\strut\tttf p is open (dark)") shifted (center p) shifted (0, LineHeight/2) ; + draw textext("\strut\tttf q is closed (light)") shifted (center q) shifted (0,-LineHeight/2) ; +\stopMPcode +\stoplinecorrection + +The difference between starting at zero or one for a cycle is show below, we get +more and more points! + +\startlinecorrection +\startMPcode + path p ; p := fullsquare ; path q ; + + for i=1 upto 100 : + q := for j=1 upto length(p) : (point j of p) -- endfor cycle ; + p := q ; + endfor ; + + draw p scaled 2cm withpen pencircle scaled 1mm withcolor "darkred" ; + drawpointlabels p scaled 2cm ; + + path p ; p := fullsquare shifted (3/2,0) ; path q ; + + for i=1 upto 100 : + q := for j=0 upto length(p) : (point j of p) -- endfor cycle ; + p := q ; + endfor ; + + draw p scaled 2cm withpen pencircle scaled 1mm withcolor "darkred" ; + drawpointlabels p scaled 2cm ; +\stopMPcode +\stoplinecorrection + +% point of +% +% if (mp_left_type(p) == mp_endpoint_knot) { +% set_number_to_unity(n); +% number_negate(n); +% } else { +% set_number_to_zero(n); +% } +% do { +% p = mp_next_knot(p); +% number_add(n, unity_t); +% } while (p != cur_exp_knot); + +% length: +% +% mp_knot p = cur_exp_knot; +% int l = mp_left_type(p) == mp_endpoint_knot ? -1 : 0; +% do { +% p = mp_next_knot(p); +% ++l; +% } while (p != cur_exp_knot); +% set_number_from_int(*n, l); + +In the next variants we will not loop over points but step to the \type +{arclength}. Watch the new \type {subarclength} primitive that starts at an +offset. This is much faster than taking a \type {subpath of}. We can move the +accumulator loop into the main loop: + +\startbuffer +\startMPdefinitions + vardef arcpoints_b(expr thepath, cnt) = + save len, aln, seg, tot, tim, stp, acc ; + numeric len ; len := length thepath ; + numeric aln ; aln := arclength thepath ; + numeric seg ; seg := 0 ; + numeric tot ; tot := 0 ; + numeric tim ; tim := 0 ; + numeric stp ; stp := aln / cnt; + numeric acc ; acc := subarclength (0,1) of thepath ; + % + point 0 of thepath + for tot = stp step stp until aln : + hide( + forever : + exitif tot < acc ; + seg := seg + 1 ; + tim := acc ; + acc := acc + subarclength (seg,seg+1) of thepath ; + endfor ; + ) + -- (arcpoint (seg,tot-tim) of thepath) + endfor if cycle thepath : -- cycle fi + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +If you don't like the \type {hide} the next variant also works okay: + +\startbuffer +\startMPdefinitions + vardef mfun_arc_point(text tot)(text thepath) = + forever : + exitif tot < acc ; + seg := seg + 1 ; + tim := acc ; + acc := acc + subarclength (seg,seg+1) of thepath ; + endfor ; + (arcpoint (seg,tot-tim) of thepath) + enddef ; + + vardef arcpoints_c(expr thepath, cnt) = + save len, aln, seg, tot, tim, stp, acc ; + numeric len ; len := length thepath ; + numeric aln ; aln := arclength thepath ; + numeric seg ; seg := 0 ; + numeric tot ; tot := 0 ; + numeric tim ; tim := 0 ; + numeric stp ; stp := aln / cnt; + numeric acc ; acc := subarclength (0,1) of thepath ; + % + point 0 of thepath + for tot = stp step stp until aln : + -- mfun_arc_point(tot)(thepath) + endfor if cycle thepath : -- cycle fi + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +This got applied in three test agitators + +\startbuffer +\startMPdefinitions + vardef agitate_e_a(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + R := arcpoints_a(R, nbpoints) ; % original Mikael + R := for i=0 upto length R: + (point i of R) + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; + + vardef agitate_e_b(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + R := arcpoints_b(R, nbpoints) ; % merged Mikael + R := for i=0 upto length R: + (point i of R) + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; + + vardef agitate_e_c(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + R := arcpoints_c(R, nbpoints) ; % split Mikael + R := for i=0 upto length R: + (point i of R) + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +The new engine primitive shortens these agitators: + +\startbuffer +\startMPdefinitions + vardef agitate_e_d(expr thepath, S, n, fn, t, ft) = + save R, nbpoints, noiselevel ; + path R ; nbpoints := n ; noiselevel := t ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + noiselevel := noiselevel * ft ; + R := arcpointlist nbpoints of R; + R := for i=0 upto length R: + (point i of R) + randomized noiselevel + .. + endfor cycle ; + endfor ; + R + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +So are we done? Did we get rid of all bottlenecks? The answer is no! We still +loop over the list in order to randomize the points. For each point we start at +the beginning of the list. Let's first rewrite the agitator a little: + +\startbuffer +\startMPdefinitions + vardef agitate_f_a(expr pth, iterations, points, pointfactor, noise, noisefactor) = + save currentpath, currentpoints, currentnoise ; path currentpath ; + currentpath := pth ; + currentpoints := points ; + currentnoise := noise ; + for step = 1 upto iterations : + currentpath := arcpointlist currentpoints of currentpath ; + currentnoise := currentnoise * noisefactor ; + currentpoints := currentpoints * pointfactor ; + if currentnoise <> 0 : + currentpath := + for i = 0 upto length currentpath: + (point i of currentpath) randomized currentnoise .. + endfor + cycle ; + fi + endfor ; + currentpath + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +One of the \LUAMETAFUN\ extensions is a fast path iterator. In the next variant +the \type {inpath} macro sets up an iterator (regular loop) with the length as +final value. In the process the given path gets passed to \LUA\ where we can +access it as array. The \type {pointof} macro (again a \LUA\ call) injects a +pair. You will be surprised that even with passing the path to \LUA\ and calling +out to \LUA\ to inject the pair this is way faster than the built|-|in \type +{point of}. + +\startbuffer +\startMPdefinitions + vardef agitate_f_b(expr pth, iterations, points, pointfactor, noise, noisefactor) = + save currentpath, currentpoints, currentnoise ; path currentpath ; + currentpath := pth ; + currentpoints := points ; + currentnoise := noise ; + for step = 1 upto iterations : + currentnoise := currentnoise * noisefactor ; + currentpoints := currentpoints * pointfactor ; + currentpath := arcpointlist currentpoints of currentpath ; + if currentnoise <> 0 : + currentpath := + for i inpath currentpath : + (pointof i) randomized currentnoise .. + endfor + cycle ; + fi + endfor ; + currentpath + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +It was tempting to see if a more native solution pays of. One problem there is +that a path is not really suitable for that as we currently don't have a data +type that represents a point. Okay, actually we sort of have because we can use +the transform record that has six points but that is something I will look into +later (it just got added to the todo list). + +The \typ {i within pth} iterator is no conceptual beauty but does the job. Just +keep in mind that it is just means for this kind of applications: run over a path +point by point. The \type {i} has the current point number. Because we run over a +path following the links we only run forward. + +\startbuffer +\startMPdefinitions + vardef agitate_f_c(expr pth, iterations, points, pointfactor, noise, noisefactor) = + save currentpath, currentpoints, currentnoise ; path currentpath ; + currentpath := pth ; + currentpoints := points ; + currentnoise := noise ; + for step = 1 upto iterations : + currentnoise := currentnoise * noisefactor ; + currentpoints := currentpoints * pointfactor ; + currentpath := arcpointlist currentpoints of currentpath ; + if currentnoise <> 0 : + currentpath := + for i within currentpath : + pathpoint + randomized currentnoise + .. + endfor + cycle ; + fi + endfor ; + currentpath + enddef ; +\stopMPdefinitions +\stopbuffer + +\typebuffer[option=tex] \getbuffer + +Any primitive solution more complex than this, like first creating a fast access +data structure, of having a double linked list, or using some iterator larger +than a simple numeric is very likely to have no gain over the super fast \LUA\ +variant. + +\startMPdefinitions + vardef agitate_x(expr thepath, S, n, fn, t, ft) = + save R, nbpoints ; + path R ; nbpoints := n ; + R := thepath ; + for s=1 upto S : + nbpoints := nbpoints * fn ; + R := arcpointlist nbpoints of R ; + endfor ; + R + enddef ; +\stopMPdefinitions + +\startMPdefinitions + def TestMe (expr method, col, justtest) = + path P ; + + randomseed := 10; + % maxknotpool := 20000; % default is 1000 + defaultscale := .05; + + % NbCircles := 1 ; S := 1 ; nzero := 10 ; fn := 1.3 ; tzero := 5 ; ft := 0.8 ; + % NbCircles := 2 ; S := 10 ; nzero := 10 ; fn := 1.3 ; tzero := 5 ; ft := 0.8 ; + % NbCircles := 10 ; S := 5 ; nzero := 10 ; fn := 1.3 ; tzero := 5 ; ft := 0.8 ; + % NbCircles := 10 ; S := 10 ; nzero := 20 ; fn := 1.3 ; tzero := 5 ; ft := 0.8 ; + NbCircles := 15 ; S := 10 ; nzero := 10 ; fn := 1.3 ; tzero := 5 ; ft := 0.8 ; + + for c = NbCircles downto 1 : + P := fullcircle scaled (c*6.5) scaled 3 ; + nzero := floor(arclength(P)*0.5); + if method == 0 : P := agitate_x (P, S, nzero, fn, tzero, ft) ; + elseif method == 1 : P := agitate_a (P, S, nzero, fn, tzero, ft) ; + elseif method == 2 : P := agitate_b (P, S, nzero, fn, tzero, ft) ; + elseif method == 3 : P := agitate_c (P, S, nzero, fn, tzero, ft) ; + elseif method == 4 : P := agitate_d (P, S, nzero, fn, tzero, ft) ; + elseif method == 51 : P := agitate_e_a(P, S, nzero, fn, tzero, ft) ; + elseif method == 52 : P := agitate_e_b(P, S, nzero, fn, tzero, ft) ; + elseif method == 53 : P := agitate_e_c(P, S, nzero, fn, tzero, ft) ; + elseif method == 54 : P := agitate_e_d(P, S, nzero, fn, tzero, ft) ; + elseif method == 61 : P := agitate_f_a(P, S, nzero, fn, tzero, ft) ; + elseif method == 62 : P := agitate_f_b(P, S, nzero, fn, tzero, ft) ; + elseif method == 63 : P := agitate_f_c(P, S, nzero, fn, tzero, ft) ; + else : + fi ; + if justtest : + % do nothing + elseif method == 0 : + draw textext("no draw") ; + else : + eofill P withcolor transparent(1,4/NbCircles,col) ; + draw P withpen pencircle scaled 0.1 transparent(1,4/NbCircles,.90[black,col]) ; + % drawpoints P withpen pencircle scaled 0.2 withcolor white ; + % drawpointlabels P withcolor white; + fi ; + endfor ; + enddef ; +\stopMPdefinitions + +We show the average runtime for three runs. Here we don't render the paths, which +takes about one second, including conversion to \PDF. Of course measurements like +this can change a bit over time. To these times you need to add about a second +for the draw and fill operations as well as conversion to a \PDF\ stream with +transparencies. The improvement in runtime makes it possible to use agitators like +this at runtime especially because normally one will not use such (combinations +of) large paths. + +\starttabulate[|T|r|i2T|r|i2T|r|] + \BC agitate_a \NC 776.26 \BC agitate_e_a \NC 291.99 \BC agitate_f_a \NC 10.82 \NC \NR % 1 51 61 + \BC agitate_b \NC 276.43 \BC agitate_e_b \NC 76.06 \BC agitate_f_b \NC 2.55 \NC \NR % 2 52 62 + \BC agitate_c \NC 259.89 \BC agitate_e_c \NC 77.27 \BC agitate_f_c \NC 2.17 \NC \NR % 3 53 63 + \BC agitate_d \NC 260.41 \BC agitate_e_d \NC 18.67 \BC \NC \NC \NR % 4 54 +\stoptabulate + +The final version of the agitator is slightly different because it depends if we +start at zero or one but gives similar results and adapt the noise before or +after the loop. + +\typebuffer[final:definition][option=tex] + +We use a similar example as in the mentioned article but coded a bit differently: + +\typebuffer[final:graphic][option=tex] + +For Mikael and me, who both like \METAPOST, it was a nice distraction from +working months on extending math in \LUAMETATEX, but it also opens up the +possibilities to do more with rendering (math) functions and graphics, so in the +end we get paid back anyway. + +\stopchapter + +\stopcomponent + +% see agitate-002.tex + +% \startluacode +% local t = { +% 0, -- also initialization +% 1, 2, 3, 4, +% 51, 52, 53, 54, +% 61, 62, 63, +% } +% for i=1,#t do +% context.writestatus("TEST RUN",t[i]) +% context("\\testfeatureonce{3}{\\startMPcalculation TestMe (%i, blue, true) ; \\stopMPcalculation}",t[i]) +% end +% \stopluacode + +% \testfeatureonce{1}{\startMPpage TestMe ( 0, \MPcolor{darkblue} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe ( 1, \MPcolor{darkblue} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe ( 2, \MPcolor{darkblue} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe ( 3, \MPcolor{darkred} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe ( 4, \MPcolor{darkgreen} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe (51, \MPcolor{darkred} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe (52, \MPcolor{darkgreen} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe (53, \MPcolor{darkblue} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe (54, \MPcolor{darkblue} , false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe (61, \MPcolor{darkyellow}, false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe (62, \MPcolor{darkyellow}, false) ; \stopMPpage} +% \testfeatureonce{1}{\startMPpage TestMe (63, \MPcolor{darkyellow}, false) ; \stopMPpage} + +% \stoptext |