summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/metafun/metafun-lua.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/context/sources/general/manuals/metafun/metafun-lua.tex')
-rw-r--r--doc/context/sources/general/manuals/metafun/metafun-lua.tex1060
1 files changed, 1060 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/metafun/metafun-lua.tex b/doc/context/sources/general/manuals/metafun/metafun-lua.tex
new file mode 100644
index 000000000..da35cccde
--- /dev/null
+++ b/doc/context/sources/general/manuals/metafun/metafun-lua.tex
@@ -0,0 +1,1060 @@
+% language=uk
+%
+% copyright=pragma-ade readme=readme.pdf licence=cc-by-nc-sa
+
+% this is an extension of about-lua
+
+\startcomponent mfun-lua
+
+\environment metafun-environment
+
+\startchapter[title={Lua}]
+
+\index{\LUA}
+
+\startintro
+
+Already for some years I have been wondering how it would be if we could escape
+to \LUA\ inside \METAPOST, or in practice, use \MPLIB\ in \LUATEX. The idea is
+simple: embed \LUA\ code in a \METAPOST\ file that gets run as soon as it's seen.
+In case you wonder why \LUA\ code makes sense, imagine generating graphics using
+external data. The capabilities of \LUA\ to deal with that is more flexible and
+advanced than in \METAPOST. Of course we could generate a \METAPOST\ definition
+of a graphic from data but often it makes more sense to do the reverse. I finally
+found time and reason to look into this and in the following sections I will
+describe how it's done.
+
+\stopintro
+
+\startsection[title=Introduction]
+
+\stopsection
+
+\startsection[title=The basics]
+
+The approach is comparable to \LUATEX's \type {\directlua}. That primitive can be
+used to execute \LUA\ code and in combination with \type {tex.print} we can pipe
+back strings into the \TEX\ input stream. There a complication is that that we
+have to be able to operate under different so called catcode regimes: the meaning
+of characters can differ per regime. We also have to deal with line endings in
+special ways as they relate to paragraphs and such. In \METAPOST\ we don't have
+that complication so getting back input into the \METAPOST\ input, we can do so
+with simple strings. For that a mechanism similar to \type {scantokens} can be
+used. That way we can return anything (including nothing) as long as \METAPOST\
+can interpret it and as long as it fulfils the expectations.
+
+\starttyping
+numeric n ; n := scantokens("123.456") ;
+\stoptyping
+
+A script is run as follows:
+
+\starttyping
+numeric n ; n := runscript("return '123.456'") ;
+\stoptyping
+
+This primitive doesn't have the word \type {lua} in its name so in principle any
+wrapper around the library can use it as hook. In the case of \LUATEX\ the script
+language is of course \LUA. At the \METAPOST\ end we only expect a string. How
+that string is constructed is completely up to the \LUA\ script. In fact, the
+user is completely free to implement the runner any way she or he wants, like:
+
+\starttyping
+local function scriptrunner(code)
+ local f = loadstring(code)
+ if f then
+ return tostring(f())
+ else
+ return ""
+ end
+end
+\stoptyping
+
+This is hooked into an instance as follows:
+
+\starttyping
+local m = mplib.new {
+ ...
+ run_script = scriptrunner,
+ ...
+}
+\stoptyping
+
+Now, beware, this is not the \CONTEXT\ way. We provide print functions and other
+helpers, which we will explain in the next section.
+
+\stopsection
+
+\startsection[title=Helpers]
+
+After I got this feature up and running I played a bit with possible interfaces
+at the \CONTEXT\ (read: \METAFUN) end and ended up with a bit more advanced runner
+where no return value is used. The runner is wrapped in the \type {lua} macro.
+
+\startbuffer
+numeric n ; n := lua("mp.print(12.34567)") ;
+draw textext(n) xsized 4cm withcolor darkred ;
+\stopbuffer
+
+\typebuffer
+
+This renders as:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+In case you wonder how efficient calling \LUA\ is, don't worry: it's fast enough,
+especially if you consider suboptimal \LUA\ code and the fact that we switch
+between machineries.
+
+\startbuffer
+draw image (
+ lua("statistics.starttiming()") ;
+ for i=1 upto 10000 : draw
+ lua("mp.pair(math.random(-200,200),math.random(-50,50))") ;
+ endfor ;
+ setbounds currentpicture to fullsquare xyscaled (400,100) ;
+ lua("statistics.stoptiming()") ;
+) withcolor darkyellow withpen pencircle scaled 1 ;
+draw textext(lua("mp.print(statistics.elapsedtime())"))
+ ysized 50 withcolor darkred ;
+\stopbuffer
+
+\typebuffer
+
+Here the line:
+
+\starttyping
+draw lua("mp.pair(math.random(-200,200),math.random(-50,50))") ;
+\stoptyping
+
+effectively becomes (for instance):
+
+\starttyping
+draw scantokens "(25,40)" ;
+\stoptyping
+
+which in turn becomes:
+
+\starttyping
+draw scantokens (25,40) ;
+\stoptyping
+
+The same happens with this:
+
+\starttyping
+draw textext(lua("mp.print(statistics.elapsedtime())")) ...
+\stoptyping
+
+This becomes for instance:
+
+\starttyping
+draw textext(scantokens "1.23") ...
+\stoptyping
+
+and therefore:
+
+\starttyping
+draw textext(1.23) ...
+\stoptyping
+
+We can use \type {mp.print} here because the \type {textext} macro can deal with
+numbers. The next also works:
+
+\starttyping
+draw textext(lua("mp.quoted(statistics.elapsedtime())")) ...
+\stoptyping
+
+Now we get (in \METAPOST\ speak):
+
+\starttyping
+draw textext(scantokens (ditto & "1.23" & ditto) ...
+\stoptyping
+
+Here \type {ditto} represents the double quotes that mark a string. Of course,
+because we pass the strings directly to \type {scantokens}, there are no outer
+quotes at all, but this is how it can be simulated. In the end we have:
+
+\starttyping
+draw textext("1.23") ...
+\stoptyping
+
+What you use, \type {mp.print} or \type {mp.quoted} depends on what the expected
+code is: an assignment to a numeric can best be a number or an expression
+resulting in a number.
+
+This graphic becomes:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+The runtime on my current machine is some 0.25 seconds without and 0.12 seconds
+with caching. But to be honest, speed is not really a concern here as the amount
+of complex \METAPOST\ graphics can be neglected compared to extensive node list
+manipulation. With \LUAJITTEX\ generating the graphic takes 15\% less time.
+
+\startbuffer
+numeric n ; n := lua("mp.print(1) mp.print('+') mp.print(2)") ;
+draw textext(n) xsized 1cm withcolor darkred ;
+\stopbuffer
+
+The three print command accumulate their arguments:
+
+\typebuffer
+
+As expected we get:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+\startbuffer
+numeric n ; n := lua("mp.print(1,'+',2)") ;
+draw textext(n) xsized 1cm withcolor darkred ;
+\stopbuffer
+
+Equally valid is:
+
+\typebuffer
+
+This gives the same result:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+Of course all kind of action can happen between the prints. It is also legal to
+have nothing returned as could be seen in the 10.000 dot example: there the timer
+related code returns nothing so effectively we have \type {scantokens("")}. Another
+helper is \type {mp.quoted}, as in:
+
+\startbuffer
+draw
+ textext(lua("mp.quoted('@0.3f'," & decimal n & ")"))
+ withcolor darkred ;
+\stopbuffer
+
+\typebuffer
+
+This typesets \processMPbuffer. Watch the \type {@}. When no percent character is
+found in the format specifier, we assume that an \type {@} is used instead.
+
+\startbuffer
+\startluacode
+table.save("demo-data.lua",
+ {
+ { 1, 2 }, { 2, 4 }, { 3, 3 }, { 4, 2 },
+ { 5, 2 }, { 6, 3 }, { 7, 4 }, { 8, 1 },
+ }
+)
+\stopluacode
+\stopbuffer
+
+But, the real benefit of embedded \LUA\ is when we deal with data that is stored
+at the \LUA\ end. First we define a small dataset:
+
+\typebuffer
+
+\getbuffer
+
+There are several ways to deal with this table. I will show clumsy as well as
+better looking ways.
+
+\startbuffer
+lua("MP = { } MP.data = table.load('demo-data.lua')") ;
+numeric n ;
+lua("mp.print('n := ',\#MP.data)") ;
+for i=1 upto n :
+ drawdot
+ lua("mp.pair(MP.data[" & decimal i & "])") scaled cm
+ withpen pencircle scaled 2mm
+ withcolor darkred ;
+endfor ;
+\stopbuffer
+
+\typebuffer
+
+Here we load a \LUA\ table and assign the size to a \METAPOST\ numeric. Next we
+loop over the table entries and draw the coordinates.
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+We will stepwise improve this code. In the previous examples we omitted wrapper
+code but here we show it:
+
+\startbuffer
+\startluacode
+ MP.data = table.load('demo-data.lua')
+ function MP.n()
+ mp.print(#MP.data)
+ end
+ function MP.dot(i)
+ mp.pair(MP.data[i])
+ end
+\stopluacode
+
+\startMPcode
+ numeric n ; n := lua("MP.n()") ;
+ for i=1 upto n :
+ drawdot
+ lua("MP.dot(" & decimal i & ")") scaled cm
+ withpen pencircle scaled 2mm
+ withcolor darkred ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+So, we create a few helpers in the \type {MP} table. This table is predefined so
+normally you don't need to define it. You may however decide to wipe it clean.
+
+\startlinecorrection[blank]
+\getbuffer
+\stoplinecorrection
+
+You can decide to hide the data:
+
+\startbuffer
+\startluacode
+ local data = { }
+ function MP.load(name)
+ data = table.load(name)
+ end
+ function MP.n()
+ mp.print(#data)
+ end
+ function MP.dot(i)
+ mp.pair(data[i])
+ end
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+It is possible to use less \LUA, for instance in:
+
+\startbuffer
+\startluacode
+ local data = { }
+ function MP.loaded(name)
+ data = table.load(name)
+ mp.print(#data)
+ end
+ function MP.dot(i)
+ mp.pair(data[i])
+ end
+\stopluacode
+
+\startMPcode
+ for i=1 upto lua("MP.loaded('demo-data.lua')") :
+ drawdot
+ lua("MP.dot(",i,")") scaled cm
+ withpen pencircle scaled 4mm
+ withcolor darkred ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+Here we also omit the \type {decimal} because the \type {lua} macro is clever
+enough to recognize it as a number.
+
+\startlinecorrection[blank]
+\getbuffer
+\stoplinecorrection
+
+By using some \METAPOST\ magic we can even go a step further in readability:
+
+\startbuffer
+\startMPcode{doublefun}
+ lua.MP.load("demo-data.lua") ;
+
+ for i=1 upto lua.MP.n() :
+ drawdot
+ lua.MP.dot(i) scaled cm
+ withpen pencircle scaled 4mm
+ withcolor darkred ;
+ endfor ;
+
+ for i=1 upto MP.n() :
+ drawdot
+ MP.dot(i) scaled cm
+ withpen pencircle scaled 2mm
+ withcolor white ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+Here we demonstrate that it also works ok in \type {double} mode, which makes
+much sense when processing data from other sources. Watch how we omit the
+type {lua.} prefix: the \type {MP} macro will deal with that.
+
+\startlinecorrection[blank]
+\getbuffer
+\stoplinecorrection
+
+So in the end we can simplify the code that we started with to:
+
+\starttyping
+\startMPcode{doublefun}
+ for i=1 upto MP.loaded("demo-data.lua") :
+ drawdot
+ MP.dot(i) scaled cm
+ withpen pencircle scaled 2mm
+ withcolor darkred ;
+ endfor ;
+\stopMPcode
+\stoptyping
+
+\stopsection
+
+\startsection[title=Access to variables]
+
+The question with such mechanisms is always: how far should we go. Although
+\METAPOST\ is a macro language it has properties of procedural languages. It also
+has more introspective features at the user end. For instance, one can loop over
+the resulting picture and manipulate it. This means that we don't need full
+access to \METAPOST\ internals. However, it makes sense to provide access to
+basic variables: \type {numeric}, \type {string}, and \type {boolean}.
+
+\startbuffer
+draw textext(lua("mp.quoted('@0.15f',mp.get.numeric('pi')-math.pi)"))
+ ysized 1cm
+ withcolor darkred ;
+\stopbuffer
+
+\typebuffer
+
+In double mode you will get zero printed but in scaled mode we definitely get a
+difference:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+\startbuffer
+boolean b ; b := true ;
+draw textext(lua("mp.quoted(mp.get.boolean('b') and 'yes' or 'no')"))
+ ysized 1cm
+ withcolor darkred ;
+\stopbuffer
+
+In the next example we use \type {mp.quoted} to make sure that indeed we pass a
+string. The \type {textext} macro can deal with numbers but an unquoted \type
+{yes} or \type {no} is asking for problems.
+
+\typebuffer
+
+Especially when more text is involved it makes sense to predefine a helper in
+the \type {MP} namespace if only because \METAPOST\ (currently) doesn't like
+newlines in the middle of a string, so a \type {lua} call has to be on one line.
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+Here is an example where \LUA\ does something that would be close to impossible,
+especially if more complex text is involved.
+
+% \enabletrackers[metapost.lua]
+
+\startbuffer
+string s ; s := "ΤΕΧ" ; % "τεχ"
+draw textext(lua("mp.quoted(characters.lower(mp.get.string('s')))"))
+ ysized 1cm
+ withcolor darkred ;
+\stopbuffer
+
+\typebuffer
+
+As you can see here, the whole repertoire of helper functions can be used in
+a \METAFUN\ definition.
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+\stopsection
+
+\startsection[title=The library]
+
+In \CONTEXT\ we have a dedicated runner, but for the record we mention the
+low level constructor:
+
+\starttyping
+local m = mplib.new {
+ ...
+ script_runner = function(s) return loadstring(s)() end,
+ script_error = function(s) print(s) end,
+ ...,
+}
+\stoptyping
+
+An instance (in this case \type {m}) has a few extra methods. Instead you can use
+the helpers in the library.
+
+\starttabulate[|l|l|]
+\HL
+\NC \type {m:get_numeric(name)} \NC returns a numeric (double) \NC \NR
+\NC \type {m:get_boolean(name)} \NC returns a boolean (\type {true} or \type {false}) \NC \NR
+\NC \type {m:get_string (name)} \NC returns a string \NC \NR
+\HL
+\NC \type {mplib.get_numeric(m,name)} \NC returns a numeric (double) \NC \NR
+\NC \type {mplib.get_boolean(m,name)} \NC returns a boolean (\type {true} or \type {false}) \NC \NR
+\NC \type {mplib.get_string (m,name)} \NC returns a string \NC \NR
+\HL
+\stoptabulate
+
+In \CONTEXT\ the instances are hidden and wrapped in high level macros, so there
+you cannot use these commands.
+
+\stopsection
+
+\startsection[title=\CONTEXT\ helpers]
+
+The \type {mp} namespace provides the following helpers:
+
+\starttabulate[|l|l|]
+\HL
+\NC \type {print(...)} \NC returns one or more values \NC \NR
+\NC \type {pair(x,y)}
+ \type {pair(t)} \NC returns a proper pair \NC \NR
+\NC \type {triplet(x,y,z)}
+ \type {triplet(t)} \NC returns an \RGB\ color \NC \NR
+\NC \type {quadruple(w,x,y,z)}
+ \type {quadruple(t)} \NC returns an \CMYK\ color \NC \NR
+\NC \type {format(fmt,...)} \NC returns a formatted string \NC \NR
+\NC \type {quoted(fmt,...)}
+ \type {quoted(s)} \NC returns a (formatted) quoted string \NC \NR
+\NC \type {path(t[,connect][,close])} \NC returns a connected (closed) path \NC \NR
+\HL
+\stoptabulate
+
+The \type {mp.get} namespace provides the following helpers:
+
+\starttabulate[|l|l|]
+\HL
+\NC \type {numeric(name)} \NC gets a numeric from \METAPOST \NC \NR
+\NC \type {boolean(name)} \NC gets a boolean from \METAPOST \NC \NR
+\NC \type {string(name)} \NC gets a string from \METAPOST \NC \NR
+\HL
+\stoptabulate
+
+\stopsection
+
+\startsection[title=Paths]
+
+In the meantime we got several questions on the \CONTEXT\ mailing list about turning
+coordinates into paths. Now imagine that we have this dataset:
+
+\startbuffer[dataset]
+10 20 20 20 -- sample 1
+30 40 40 60
+50 10
+
+10 10 20 30 % sample 2
+30 50 40 50
+50 20
+
+10 20 20 10 # sample 3
+30 40 40 20
+50 10
+\stopbuffer
+
+\typebuffer[dataset]
+
+In this case I've put the data in a buffer so that it can be shown
+here as well as used in a demo. Watch how we can add comments. The
+following code converts this into a table with three subtables.
+
+\startbuffer
+\startluacode
+ MP.myset = mp.dataset(buffers.getcontent("dataset"))
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+We use the \type {MP} (user) namespace to store the table. Next we turn
+these subtables into paths:
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.print(mp.n(MP.myset))") :
+ draw
+ lua("mp.path(MP.myset[" & decimal i & "])")
+ xysized (HSize-.25ExHeight,10ExHeight)
+ withpen pencircle scaled .25ExHeight
+ withcolor basiccolors[i]/2 ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+This gives:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+Instead we can fill the path in which case we also need to close it. The
+\type {true} argument deals with that:
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.print(mp.n(MP.myset))") :
+ path p ; p :=
+ lua("mp.path(MP.myset[" & decimal i & "],true)")
+ xysized (HSize,10ExHeight) ;
+ fill p
+ withcolor basiccolors[i]/2
+ withtransparency (1,.5) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+We get:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.print(mp.n(MP.myset))") :
+ path p ; p :=
+ lua("mp.path(MP.myset[" & decimal i & "])")
+ xysized (HSize,10ExHeight) ;
+ p :=
+ (xpart llcorner boundingbox p,0) --
+ p --
+ (xpart lrcorner boundingbox p,0) --
+ cycle ;
+ fill p
+ withcolor basiccolors[i]/2
+ withtransparency (1,.25) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+The following makes more sense:
+
+\typebuffer
+
+So this gives:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+This (area) fill is so common that we have a helper for it:
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.size(MP.myset)") :
+ fill area
+ lua("mp.path(MP.myset[" & decimal i & "])")
+ xysized (HSize,5ExHeight)
+ withcolor basiccolors[i]/2
+ withtransparency (2,.25) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+So this gives:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+A variant call is the following: \footnote {Getting that to work properly in the
+library was non||trivial as the loop variable \type {i} is an abstract nameless
+variable at the \METAPOST\ end. When investigating this Luigi Scarso and I found out
+that the internals of \METAPOST\ are not really geared for interfacing this way
+but in the end it worked out well.}
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.size(MP.myset)") :
+ fill area
+ lua("mp.path(MP.myset[mp.get.numeric('i')])")
+ xysized (HSize,5ExHeight)
+ withcolor basiccolors[i]/2
+ withtransparency (2,.25) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+The result is the same:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+\startbuffer
+\startluacode
+ MP.mypath = function(i)
+ return mp.path(MP.myset[mp.get.numeric(i)])
+ end
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.size(MP.myset)") :
+ fill area
+ lua("MP.mypath('i')")
+ xysized (HSize,5ExHeight)
+ withcolor basiccolors[i]/2
+ withtransparency (2,.25) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+This snippet of \METAPOST\ code still looks kind of horrible so how can we make
+it look better? Here is an attempt, First we define a bit more \LUA:
+
+\startbuffer
+\startluacode
+local data = mp.dataset(buffers.getcontent("dataset"))
+
+MP.dataset = {
+ Line = function(n) mp.path(data[n]) end,
+ Size = function() mp.size(data) end,
+}
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+We can now make the \METAPOST\ look more natural. Of course this is possible
+because in \METAFUN\ the \type {lua} macro does some extra work.
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua.MP.dataset.Size() :
+ path p ; p :=
+ lua.MP.dataset.Line(i)
+ xysized (HSize-ExHeight,20ExHeight) ;
+ draw
+ p
+ withpen pencircle scaled .25ExHeight
+ withcolor basiccolors[i]/2 ;
+ drawpoints
+ p
+ withpen pencircle scaled ExHeight
+ withcolor basiccolors[i]/2 ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+As expected, we get the desired result:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+Once we start making things look nicer and more convenient, we quickly end up
+with helpers like the once in the next example. First we save some demo data
+in files:
+
+\startbuffer
+\startluacode
+ io.savedata("foo.tmp","10 20 20 20 30 40 40 60 50 10")
+ io.savedata("bar.tmp","10 10 20 30 30 50 40 50 50 20")
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+We load the data in datasets:
+
+\startbuffer
+\startMPcode
+ lua.mp.datasets.load("foo","foo.tmp") ;
+ lua.mp.datasets.load("bar","bar.tmp") ;
+ fill area
+ lua.mp.datasets.foo.Line()
+ xysized (HSize/2-EmWidth-.25ExHeight,10ExHeight)
+ withpen pencircle scaled .25ExHeight
+ withcolor darkyellow ;
+ fill area
+ lua.mp.datasets.bar.Line()
+ xysized (HSize/2-EmWidth-.25ExHeight,10ExHeight)
+ shifted (HSize/2+EmWidth,0)
+ withpen pencircle scaled .25ExHeight
+ withcolor darkred ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+Because the datasets are stores by name we can use them without worrying about
+them being forgotten:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+If no tag is given, the filename (without suffix) is used as tag, so the following is
+valid:
+
+\starttyping
+\startMPcode
+ lua.mp.datasets.load("foo.tmp") ;
+ lua.mp.datasets.load("bar.tmp") ;
+\stopMPcode
+\stoptyping
+
+The following methods are defined for a dataset:
+
+\starttabulate[|l|pl|]
+\HL
+\NC \type {method} \NC usage \NC \NR
+\HL
+\NC \type {Size} \NC the number of subsets in a dataset \NC \NR
+\NC \type {Line} \NC the joined pairs in a dataset making a non|-|closed path \NC \NR
+\NC \type {Data} \NC the table containing the data (in subsets, so there is always at least one subset) \NC \NR
+\HL
+\stoptabulate
+
+{\em In order avoid interference with suffix handling in \METAPOST\ the methods
+start with an uppercase character.}
+
+\stopsection
+
+\startsection[title=Passing variables]
+
+You can pass variables from \METAPOST\ to \CONTEXT. Originally that happened via
+a temporary file and so called \METAPOST\ specials. Nowadays it's done via \LUA.
+Here is an example:
+
+\startbuffer
+\startMPcalculation
+
+passvariable("version","1.0") ;
+passvariable("number",123) ;
+passvariable("string","whatever") ;
+passvariable("point",(1.5,2.8)) ;
+passvariable("triplet",(1/1,1/2,1/3)) ;
+passvariable("quad",(1.1,2.2,3.3,4.4)) ;
+passvariable("boolean",false) ;
+passvariable("path",fullcircle scaled 1cm) ;
+path p[] ; p[1] := fullcircle ; p[2] := fullsquare ;
+passarrayvariable("list",p,1,2,1) ; % first last step
+\stopMPcalculation
+\stopbuffer
+
+\typebuffer
+
+\getbuffer
+
+We can visualize the result with
+
+\startbuffer
+\startluacode
+context.tocontext(metapost.variables)
+\stopluacode
+\stopbuffer
+
+\typebuffer
+
+\getbuffer
+
+In \TEX\ you can access these variables as follows:
+
+\startbuffer
+\MPrunvar{version}
+\MPruntab{quad}{3}
+(\MPrunset{triplet}{,})
+
+$(x,y) = (\MPruntab{point}{1},\MPruntab{point}{2})$
+$(x,y) = (\MPrunset{point}{,})$
+\stopbuffer
+
+\typebuffer
+
+This becomes: % we need a hack as we cross pages and variables get replace then
+
+\startlines
+\getbuffer
+\stoplines
+
+Here we passed the code between \type {\startMPcalculation} and \type
+{\stopMPcalculation} which does not produce a graphic and therefore takes no
+space in the flow. Of course it also works with normal graphics.
+
+\startbuffer
+\startMPcode
+path p ; p := fullcircle xyscaled (10cm,2cm) ;
+path b ; b := boundingbox p ;
+startpassingvariable("mypath")
+ passvariable("points",p) ;
+ startpassingvariable("metadata")
+ passvariable("boundingbox",boundingbox p) ;
+ stoppassingvariable ;
+stoppassingvariable ;
+fill p withcolor .625red ;
+draw b withcolor .625yellow ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+\startlinecorrection[blank]
+ \getbuffer
+\stoplinecorrection
+
+This time we get:
+
+\ctxlua{context.tocontext(metapost.variables)}
+
+You need to be aware of the fact that a next graphic resets the previous
+variables. You can easily overcome that limitation by saving the variables (in
+\LUA). It helps that when a page is being shipped out (which can involve
+graphics) the variables are protected. You can push and pop variable sets with
+\type {\MPpushvariables} and \type {\MPpopvariables}. Because you can nest
+the \type {start}||\type{stop} pairs you can create quite complex indexed
+and hashed tables. If the results are not what you expect, you can enable a
+tracker to follow what gets passed:
+
+\starttyping
+\enabletrackers[metapost.variables]
+\stoptyping
+
+Serializing variables can be done with the \type {tostring} macro, for instance:
+
+\startbuffer
+\startMPcode
+message("doing circle",fullcircle);
+draw fullcircle ;
+\stopMPcode
+\stopbuffer
+
+In this case the \type {tostring} is redundant as the message already does the
+serialization.
+
+\stopsection
+
+\stopchapter
+
+% \startMPcode{doublefun}
+% numeric n ; n := 123.456 ;
+% lua("print('>>>>>>>>>>>> number',mp.get.number('n'))") ;
+% lua("print('>>>>>>>>>>>> number',mp.get.boolean('n'))") ;
+% lua("print('>>>>>>>>>>>> number',mp.get.string('n'))") ;
+% boolean b ; b := true ;
+% lua("print('>>>>>>>>>>>> boolean',mp.get.number('b'))") ;
+% lua("print('>>>>>>>>>>>> boolean',mp.get.boolean('b'))") ;
+% lua("print('>>>>>>>>>>>> boolean',mp.get.string('b'))") ;
+% string s ; s := "TEST" ;
+% lua("print('>>>>>>>>>>>> string',mp.get.number('s'))") ;
+% lua("print('>>>>>>>>>>>> string',mp.get.boolean('s'))") ;
+% lua("print('>>>>>>>>>>>> string',mp.get.string('s'))") ;
+% \stopMPcode
+
+% \usemodule[graph]
+%
+% \startluacode
+% local d = nil
+% function MP.set(data)
+% d = data
+% end
+% function MP.n()
+% mp.print(d and #d or 0)
+% end
+% function MP.get(i,j)
+% mp.print(d and d[i] and d[i][j] or 0)
+% end
+% \stopluacode
+%
+% \startluacode
+% MP.set {
+% { 1, 0.5, 2.5 },
+% { 2, 1.0, 3.5 },
+% }
+% \stopluacode
+%
+% \startMPpage[instance=graph,offset=2mm]
+%
+% draw begingraph(3cm,5cm);
+% numeric a[];
+% for j = 1 upto MP.n() :
+% path b;
+% augment.b(MP.get(j,1),MP.get(j,2));
+% augment.b(MP.get(j,1),MP.get(j,3));
+% setrange(0,0,3,4);
+% gdraw b;
+% endfor ;
+% endgraph ;
+% \stopMPpage
+
+% \starttext
+%
+% % \enabletrackers[metapost.variables]
+%
+% \startMPcode
+% numeric n[] ; for i=1 upto 10: n[i] := 1/i ; endfor ;
+% path p[] ; for i=1 upto 10: p[i] := fullcircle xyscaled (cm*i,cm/i) ; endfor ;
+% numeric r[][] ; for i=1 upto 4 : for j=1 upto 3 : r[i][j] := uniformdeviate(1) ; endfor ; endfor ;
+% pair u[][] ; for i=1 step 0.5 until 4 : for j=1 step 0.1 until 2 : u[i][j] := (i,j) ; endfor ; endfor ;
+%
+% passvariable("x",12345) ;
+% passarrayvariable("n-array",n,1,7,1) ;
+% passarrayvariable("p-array",p,1,7,1) ;
+% passvariable("p",(1,1) .. (2,2)) ;
+%
+% startpassingvariable("b")
+% for i=1 upto 4 :
+% startpassingvariable(i)
+% for j=1 upto 3 :
+% passvariable(j,r[i][j])
+% endfor
+% stoppassingvariable
+% endfor
+% stoppassingvariable ;
+%
+% startpassingvariable("a")
+% startpassingvariable("test 1")
+% passvariable(1,123)
+% passvariable(2,456)
+% stoppassingvariable ;
+% startpassingvariable("test 2")
+% passvariable(0,123)
+% passvariable(1,456)
+% passvariable(2,789)
+% passvariable(999,987)
+% stoppassingvariable ;
+% startpassingvariable("test 3")
+% passvariable("first",789)
+% passvariable("second",987)
+% stoppassingvariable
+% stoppassingvariable ;
+%
+% startpassingvariable("c")
+% for i=1 step 0.5 until 4 :
+% startpassingvariable(i)
+% for j=1 step 0.1 until 2 :
+% passvariable(j,u[i][j])
+% endfor
+% stoppassingvariable
+% endfor
+% stoppassingvariable ;
+%
+% draw fullcircle scaled 1cm ;
+% \stopMPcode
+%
+% \ctxluacode{inspect(metapost.variables)}
+%
+% \ctxcommand{mprunvar("x")}
+
+\stoptext
+
+