metafun-lua.tex /size: 48 Kb    last modification: 2023-12-21 09:43
1% language=us runpath=texruns:manuals/metafun
2
3% this is an extension of about-lua
4
5\startcomponent mfun-lua
6
7\environment metafun-environment
8
9\startchapter[title={Lua}]
10
11\index{\LUA}
12
13\startintro
14
15Already for some years I have been wondering how it would be if we could escape
16to \LUA\ inside \METAPOST, or in practice, use \MPLIB\ in \LUATEX. The idea is
17simple: embed \LUA\ code in a \METAPOST\ file that gets run as soon as it's seen.
18In case you wonder why \LUA\ code makes sense, imagine generating graphics using
19external data. The capabilities of \LUA\ to deal with that is more flexible and
20advanced than in \METAPOST. Of course we could generate a \METAPOST\ definition
21of a graphic from data but often it makes more sense to do the reverse. I finally
22found time and reason to look into this and in the following sections I will
23describe how it's done.
24
25\blank
26
27{\bi The \LUA\ interface in \MKIV\ is way more limited than in \LMTX\ and new
28features will only show up in \LMTX, simply because the \METAPOST\ library in
29\LUAMETATEX\ is more powerful.}
30
31\stopintro
32
33% \startsection[title=Introduction]
34%
35% \stopsection
36
37\startsection[title=The basics]
38
39The approach is comparable to \LUATEX's \type {\directlua}. That primitive can be
40used to execute \LUA\ code and in combination with \type {tex.print} we can pipe
41back strings into the \TEX\ input stream. There a complication is that that we
42have to be able to operate under different so called catcode regimes: the meaning
43of characters can differ per regime. We also have to deal with line endings in
44special ways as they relate to paragraphs and such. In \METAPOST\ we don't have
45that complication so getting back input into the \METAPOST\ input, we can do so
46with simple strings. For that a mechanism similar to \type {scantokens} can be
47used. That way we can return anything (including nothing) as long as \METAPOST\
48can interpret it and as long as it fulfils the expectations.
49
50\starttyping
51numeric n ; n := scantokens("123.456") ;
52\stoptyping
53
54A script is run as follows:
55
56\starttyping
57numeric n ; n := runscript("return '123.456'") ;
58\stoptyping
59
60This primitive doesn't have the word \type {lua} in its name so in principle any
61wrapper around the library can use it as hook. In the case of \LUATEX\ the script
62language is of course \LUA. At the \METAPOST\ end we only expect a string. How
63that string is constructed is completely up to the \LUA\ script. In fact, the
64user is completely free to implement the runner any way she or he wants, like:
65
66\starttyping
67local function scriptrunner(code)
68    local f = loadstring(code)
69    if f then
70        return tostring(f())
71    else
72        return ""
73    end
74end
75\stoptyping
76
77This is hooked into an instance as follows:
78
79\starttyping
80local m = mplib.new {
81    ...
82    run_script = scriptrunner,
83    ...
84}
85\stoptyping
86
87Now, beware, this is not the \CONTEXT\ way. We provide print functions and other
88helpers, which we will explain in the next section.
89
90\stopsection
91
92\startsection[title=Helpers]
93
94After I got this feature up and running I played a bit with possible interfaces
95at the \CONTEXT\ (read: \METAFUN) end and ended up with a bit more advanced runner
96where no return value is used. The runner is wrapped in the \type {lua} macro.
97
98\startbuffer
99numeric n ; n := lua("mp.print(12.34567)") ;
100draw textext(n) xsized 4cm withcolor darkred ;
101\stopbuffer
102
103\typebuffer
104
105This renders as:
106
107\startlinecorrection[blank]
108\processMPbuffer
109\stoplinecorrection
110
111In case you wonder how efficient calling \LUA\ is, don't worry: it's fast enough,
112especially if you consider suboptimal \LUA\ code and the fact that we switch
113between machineries.
114
115\startbuffer
116draw image (
117    lua("statistics.starttiming()") ;
118    for i=1 upto 10000 : draw
119        lua("mp.pair(math.random(-200,200),math.random(-50,50))") ;
120    endfor ;
121    setbounds currentpicture to fullsquare xyscaled (400,100) ;
122    lua("statistics.stoptiming()") ;
123) withcolor darkyellow withpen pencircle scaled 1 ;
124draw textext(lua("mp.print(statistics.elapsedtime())"))
125    ysized 50 withcolor darkred ;
126\stopbuffer
127
128\typebuffer
129
130Here the line:
131
132\starttyping
133draw lua("mp.pair(math.random(-200,200),math.random(-50,50))") ;
134\stoptyping
135
136effectively becomes (for instance):
137
138\starttyping
139draw scantokens "(25,40)" ;
140\stoptyping
141
142which in turn becomes:
143
144\starttyping
145draw scantokens (25,40) ;
146\stoptyping
147
148The same happens with this:
149
150\starttyping
151draw textext(lua("mp.print(statistics.elapsedtime())")) ...
152\stoptyping
153
154This becomes for instance:
155
156\starttyping
157draw textext(scantokens "1.23") ...
158\stoptyping
159
160and therefore:
161
162\starttyping
163draw textext(1.23) ...
164\stoptyping
165
166We can use \type {mp.print} here because the \type {textext} macro can deal with
167numbers. The next also works:
168
169\starttyping
170draw textext(lua("mp.quoted(statistics.elapsedtime())")) ...
171\stoptyping
172
173Now we get (in \METAPOST\ speak):
174
175\starttyping
176draw textext(scantokens (ditto & "1.23" & ditto) ...
177\stoptyping
178
179Here \type {ditto} represents the double quotes that mark a string. Of course,
180because we pass the strings directly to \type {scantokens}, there are no outer
181quotes at all, but this is how it can be simulated. In the end we have:
182
183\starttyping
184draw textext("1.23") ...
185\stoptyping
186
187What you use, \type {mp.print} or \type {mp.quoted} depends on what the expected
188code is: an assignment to a numeric can best be a number or an expression
189resulting in a number.
190
191This graphic becomes:
192
193\startlinecorrection[blank]
194\processMPbuffer
195\stoplinecorrection
196
197The runtime on my current machine is some 0.25 seconds without and 0.12 seconds
198with caching. But to be honest, speed is not really a concern here as the amount
199of complex \METAPOST\ graphics can be neglected compared to extensive node list
200manipulation. With \LUAJITTEX\ generating the graphic takes 15\% less time.
201
202\startbuffer
203numeric n ; n := lua("mp.print(1) mp.print('+') mp.print(2)") ;
204draw textext(n) xsized 1cm withcolor darkred ;
205\stopbuffer
206
207The three print command accumulate their arguments:
208
209\typebuffer
210
211As expected we get:
212
213\startlinecorrection[blank]
214\processMPbuffer
215\stoplinecorrection
216
217\startbuffer
218numeric n ; n := lua("mp.print(1,'+',2)") ;
219draw textext(n) xsized 1cm withcolor darkred ;
220\stopbuffer
221
222Equally valid is:
223
224\typebuffer
225
226This gives the same result:
227
228\startlinecorrection[blank]
229\processMPbuffer
230\stoplinecorrection
231
232Of course all kind of action can happen between the prints. It is also legal to
233have nothing returned as could be seen in the 10.000 dot example: there the timer
234related code returns nothing so effectively we have \type {scantokens("")}. Another
235helper is \type {mp.quoted}, as in:
236
237\startbuffer
238draw
239    textext(lua("mp.quoted('@0.3f'," & decimal 1.234 & ")"))
240    withcolor darkred ;
241\stopbuffer
242
243\typebuffer
244
245This typesets \processMPbuffer. Watch the \type {@}. When no percent character is
246found in the format specifier, we assume that an \type {@} is used instead.
247
248\startbuffer
249\startluacode
250table.save("demo-data.lua",
251    {
252        { 1, 2 }, { 2, 4 }, { 3, 3 }, { 4, 2 },
253        { 5, 2 }, { 6, 3 }, { 7, 4 }, { 8, 1 },
254    }
255)
256\stopluacode
257\stopbuffer
258
259But, the real benefit of embedded \LUA\ is when we deal with data that is stored
260at the \LUA\ end. First we define a small dataset:
261
262\typebuffer
263
264\getbuffer
265
266There are several ways to deal with this table. I will show clumsy as well as
267better looking ways.
268
269\startbuffer
270lua("MP.data = table.load('demo-data.lua')") ;
271numeric n ;
272lua("mp.print('n := ',\#MP.data)") ;
273for i=1 upto n :
274    drawdot
275        lua("mp.pair(MP.data[" & decimal i & "])") scaled cm
276        withpen pencircle scaled 2mm
277        withcolor darkred ;
278endfor ;
279\stopbuffer
280
281\typebuffer
282
283Here we load a \LUA\ table and assign the size to a \METAPOST\ numeric. Next we
284loop over the table entries and draw the coordinates.
285
286\startlinecorrection[blank]
287\processMPbuffer
288\stoplinecorrection
289
290We will stepwise improve this code. In the previous examples we omitted wrapper
291code but here we show it:
292
293\startbuffer
294\startluacode
295    MP.data = table.load('demo-data.lua')
296    function MP.n()
297        mp.print(#MP.data)
298    end
299    function MP.dot(i)
300        mp.pair(MP.data[i])
301    end
302\stopluacode
303
304\startMPcode
305    numeric n ; n := lua("MP.n()") ;
306    for i=1 upto n :
307        drawdot
308            lua("MP.dot(" & decimal i & ")") scaled cm
309            withpen pencircle scaled 2mm
310            withcolor darkred ;
311    endfor ;
312\stopMPcode
313\stopbuffer
314
315\typebuffer
316
317So, we create a few helpers in the \type {MP} table. This table is predefined so
318normally you don't need to define it. You may however decide to wipe it clean.
319
320\startlinecorrection[blank]
321\getbuffer
322\stoplinecorrection
323
324You can decide to hide the data:
325
326\startbuffer
327\startluacode
328    local data = { }
329    function MP.load(name)
330        data = table.load(name)
331    end
332    function MP.n()
333        mp.print(#data)
334    end
335    function MP.dot(i)
336        mp.pair(data[i])
337    end
338\stopluacode
339\stopbuffer
340
341\typebuffer \getbuffer
342
343It is possible to use less \LUA, for instance in:
344
345\startbuffer
346\startluacode
347    local data = { }
348    function MP.loaded(name)
349        data = table.load(name)
350        mp.print(#data)
351    end
352    function MP.dot(i)
353        mp.pair(data[i])
354    end
355\stopluacode
356
357\startMPcode
358    for i=1 upto lua("MP.loaded('demo-data.lua')") :
359        drawdot
360            lua("MP.dot(",i,")") scaled cm
361            withpen pencircle scaled 4mm
362            withcolor darkred ;
363    endfor ;
364\stopMPcode
365\stopbuffer
366
367\typebuffer
368
369Here we also omit the \type {decimal} because the \type {lua} macro is clever
370enough to recognize it as a number.
371
372\startlinecorrection[blank]
373\getbuffer
374\stoplinecorrection
375
376By using some \METAPOST\ magic we can even go a step further in readability:
377
378\startbuffer
379\startMPcode{doublefun}
380    lua.MP.load("demo-data.lua") ;
381
382    for i=1 upto lua.MP.n() :
383        drawdot
384            lua.MP.dot(i) scaled cm
385            withpen pencircle scaled 4mm
386            withcolor darkred ;
387    endfor ;
388
389    for i=1 upto MP.n() :
390        drawdot
391            MP.dot(i) scaled cm
392            withpen pencircle scaled 2mm
393            withcolor white ;
394    endfor ;
395\stopMPcode
396\stopbuffer
397
398\typebuffer
399
400Here we demonstrate that it also works ok in \type {double} mode, which makes
401much sense when processing data from other sources. Watch how we omit the
402type {lua.} prefix: the \type {MP} macro will deal with that.
403
404\startlinecorrection[blank]
405\getbuffer
406\stoplinecorrection
407
408So in the end we can simplify the code that we started with to:
409
410\starttyping
411\startMPcode{doublefun}
412    for i=1 upto MP.loaded("demo-data.lua") :
413        drawdot
414            MP.dot(i) scaled cm
415            withpen pencircle scaled 2mm
416            withcolor darkred ;
417    endfor ;
418\stopMPcode
419\stoptyping
420
421\stopsection
422
423% \startsection[title=Access to variables]
424%
425% The question with such mechanisms is always: how far should we go. Although
426% \METAPOST\ is a macro language it has properties of procedural languages. It also
427% has more introspective features at the user end. For instance, one can loop over
428% the resulting picture and manipulate it. This means that we don't need full
429% access to \METAPOST\ internals. However, it makes sense to provide access to
430% basic variables: \type {numeric}, \type {string}, and \type {boolean}.
431%
432% \startbuffer
433% draw textext(lua("mp.quoted('@0.15f',mp.get.numeric('pi')-math.pi)"))
434%     ysized 1cm
435%     withcolor darkred ;
436% \stopbuffer
437%
438% \typebuffer
439%
440% In double mode you will get zero printed but in scaled mode we definitely get a
441% difference:
442%
443% \startlinecorrection[blank]
444% \processMPbuffer
445% \stoplinecorrection
446%
447% \startbuffer
448% boolean b ; b := true ;
449% draw textext(lua("mp.quoted(mp.get.boolean('b') and 'yes' or 'no')"))
450%     ysized 1cm
451%     withcolor darkred ;
452% \stopbuffer
453%
454% In the next example we use \type {mp.quoted} to make sure that indeed we pass a
455% string. The \type {textext} macro can deal with numbers but an unquoted \type
456% {yes} or \type {no} is asking for problems.
457%
458% \typebuffer
459%
460% Especially when more text is involved it makes sense to predefine a helper in
461% the \type {MP} namespace if only because \METAPOST\ (currently) doesn't like
462% newlines in the middle of a string, so a \type {lua} call has to be on one line.
463%
464% \startlinecorrection[blank]
465% \processMPbuffer
466% \stoplinecorrection
467%
468% Here is an example where \LUA\ does something that would be close to impossible,
469% especially if more complex text is involved.
470%
471% % \enabletrackers[metapost.lua]
472%
473% \startbuffer
474% string s ; s := "ΤΕΧ" ; % "τεχ"
475% draw textext(lua("mp.quoted(characters.lower(mp.get.string('s')))"))
476%     ysized 1cm
477%     withcolor darkred ;
478% \stopbuffer
479%
480% \typebuffer
481%
482% As you can see here, the whole repertoire of helper functions can be used in
483% a \METAFUN\ definition.
484%
485% \startlinecorrection[blank]
486% \processMPbuffer
487% \stoplinecorrection
488%
489% \stopsection
490
491\startsection[title=The library]
492
493In \CONTEXT\ we have a dedicated runner, but for the record we mention the
494low level constructor:
495
496\starttyping
497local m = mplib.new {
498    ...
499    script_runner = function(s) return loadstring(s)() end,
500    script_error  = function(s) print(s) end,
501    ...,
502}
503\stoptyping
504
505An instance (in this case \type {m}) has a few extra methods. Instead you can use
506the helpers in the library.
507
508\starttabulate[|l|l|]
509\HL
510\NC \type {m:get_numeric(name)}       \NC returns a numeric (double) \NC \NR
511\NC \type {m:get_boolean(name)}       \NC returns a boolean (\type {true} or \type {false}) \NC \NR
512\NC \type {m:get_string (name)}       \NC returns a string \NC \NR
513\HL
514\NC \type {mplib.get_numeric(m,name)} \NC returns a numeric (double) \NC \NR
515\NC \type {mplib.get_boolean(m,name)} \NC returns a boolean (\type {true} or \type {false}) \NC \NR
516\NC \type {mplib.get_string (m,name)} \NC returns a string \NC \NR
517\HL
518\stoptabulate
519
520In \CONTEXT\ the instances are hidden and wrapped in high level macros, so there
521you cannot use these commands.
522
523\stopsection
524
525\startsection[title=\CONTEXT\ helpers]
526
527The \type {mp} namespace provides the following helpers:
528
529\starttabulate[|l|l|]
530\HL
531\NC \type {print(...)}                      \NC returns one or more values \NC \NR
532\NC \type {fprint(fmt,...)}                 \NC returns a formatted result \NC \NR
533\NC \type {boolean(b)}                      \NC returns \type {true} or \type {false} \NC \NR
534\NC \type {numeric(f)}                      \NC returns a floating point number \NC \NR
535\NC \type {integer(i)}                      \NC returns a whole number \NC \NR
536\NC \type {points(i)}                       \NC returns a floating point with unit \type {pt} \NC \NR
537\NC \type {pair(x,y)|(t)}                   \NC returns a proper pair \NC \NR
538\NC \type {pairpoints(x,y)|(t)}             \NC returns a proper pair with unit \type {pt} \NC \NR
539\NC \type {triplet(x,y,z)|(t)}              \NC returns a \RGB\ color \NC \NR
540\NC \type {tripletpoints(x,y,z)|(t)}        \NC returns a \RGB\ color but with unit \type {pt} \NC \NR
541\NC \type {quadruple(w,x,y,z)|(t)}          \NC returns a \CMYK\ color \NC \NR
542\NC \type {quadruplepoints(w,x,y,z)|(t)}    \NC returns a \CMYK\ color but with unit \type {pt} \NC \NR
543\NC \type {format(fmt,...)}                 \NC returns a formatted string \NC \NR
544\NC \type {quoted(fmt,...)}
545    \type {quoted(s)}                       \NC returns a (formatted) quoted string  \NC \NR
546\NC \type {path(t[,connect][,close])}       \NC returns a connected (closed) path \NC \NR
547\NC \type {pathpoints(t[,connect][,close])} \NC returns a connected (closed) path with units \type {pt} \NC \NR
548\HL
549\stoptabulate
550
551The \type {mp.get} namespace provides the following helpers:
552
553\starttabulate[|l|l|]
554\HL
555\NC \type {numeric(name)}      \NC gets a numeric from \METAPOST \NC \NR
556\NC \type {boolean(name)}      \NC gets a boolean from \METAPOST \NC \NR
557\NC \type {string(name)}       \NC gets a string from \METAPOST \NC \NR
558\HL
559\stoptabulate
560
561\stopsection
562
563\startsection[title=Paths]
564
565In the meantime we got several questions on the \CONTEXT\ mailing list about turning
566coordinates into paths. Now imagine that we have this dataset:
567
568\startbuffer[dataset]
56910 20 20 20 -- sample 1
57030 40 40 60
57150 10
572
57310 10 20 30 % sample 2
57430 50 40 50
57550 20
576
57710 20 20 10 # sample 3
57830 40 40 20
57950 10
580\stopbuffer
581
582\typebuffer[dataset]
583
584In this case I've put the data in a buffer so that it can be shown
585here as well as used in a demo. Watch how we can add comments. The
586following code converts this into a table with three subtables.
587
588\startbuffer
589\startluacode
590  MP.myset = mp.dataset(buffers.getcontent("dataset"))
591\stopluacode
592\stopbuffer
593
594\typebuffer \getbuffer
595
596We use the \type {MP} (user) namespace to store the table. Next we turn
597these subtables into paths:
598
599\startbuffer
600\startMPcode
601  for i=1 upto lua("mp.print(mp.n(MP.myset))") :
602    draw
603      lua("mp.path(MP.myset[" & decimal i & "])")
604      xysized (HSize-.25ExHeight,10ExHeight)
605      withpen pencircle scaled .25ExHeight
606      withcolor basiccolors[i]/2 ;
607  endfor ;
608\stopMPcode
609\stopbuffer
610
611\typebuffer
612
613This gives:
614
615\startlinecorrection[blank] \getbuffer \stoplinecorrection
616
617Instead we can fill the path in which case we also need to close it. The
618\type {true} argument deals with that:
619
620\startbuffer
621\startMPcode
622  for i=1 upto lua("mp.print(mp.n(MP.myset))") :
623    path p ; p :=
624      lua("mp.path(MP.myset[" & decimal i & "],true)")
625      xysized (HSize,10ExHeight) ;
626    fill p
627      withcolor basiccolors[i]/2
628      withtransparency (1,.5) ;
629  endfor ;
630\stopMPcode
631\stopbuffer
632
633\typebuffer
634
635We get:
636
637\startlinecorrection[blank] \getbuffer \stoplinecorrection
638
639\startbuffer
640\startMPcode
641  for i=1 upto lua("mp.print(mp.n(MP.myset))") :
642    path p ; p :=
643      lua("mp.path(MP.myset[" & decimal i & "])")
644      xysized (HSize,10ExHeight) ;
645    p :=
646      (xpart llcorner boundingbox p,0) --
647      p --
648      (xpart lrcorner boundingbox p,0) --
649      cycle ;
650    fill p
651      withcolor basiccolors[i]/2
652      withtransparency (1,.25) ;
653  endfor ;
654\stopMPcode
655\stopbuffer
656
657The following makes more sense:
658
659\typebuffer
660
661So this gives:
662
663\startlinecorrection[blank] \getbuffer \stoplinecorrection
664
665This (area) fill is so common that we have a helper for it:
666
667\startbuffer
668\startMPcode
669  for i=1 upto lua("mp.size(MP.myset)") :
670    fill area
671      lua("mp.path(MP.myset[" & decimal i & "])")
672      xysized (HSize,5ExHeight)
673      withcolor basiccolors[i]/2
674      withtransparency (2,.25) ;
675  endfor ;
676\stopMPcode
677\stopbuffer
678
679\typebuffer
680
681So this gives:
682
683\startlinecorrection[blank] \getbuffer \stoplinecorrection
684
685% A variant call is the following: \footnote {Getting that to work properly in the
686% library was non||trivial as the loop variable \type {i} is an abstract nameless
687% variable at the \METAPOST\ end. When investigating this Luigi Scarso and I found out
688% that the internals of \METAPOST\ are not really geared for interfacing this way
689% but in the end it worked out well.}
690%
691% \startbuffer
692% \startMPcode
693%   for i=1 upto lua("mp.size(MP.myset)") :
694%     fill area
695%       lua("mp.path(MP.myset[mp.get.numeric('i')])")
696%       xysized (HSize,5ExHeight)
697%       withcolor basiccolors[i]/2
698%       withtransparency (2,.25) ;
699%   endfor ;
700% \stopMPcode
701% \stopbuffer
702%
703% \typebuffer
704%
705% The result is the same:
706%
707% \startlinecorrection[blank] \getbuffer \stoplinecorrection
708%
709% \startbuffer
710% \startluacode
711%   MP.mypath = function(i)
712%     return mp.path(MP.myset[mp.get.numeric(i)])
713%   end
714% \stopluacode
715% \stopbuffer
716%
717% \typebuffer \getbuffer
718%
719% \startbuffer
720% \startMPcode
721%   for i=1 upto lua("mp.size(MP.myset)") :
722%     fill area
723%       lua("MP.mypath('i')")
724%       xysized (HSize,5ExHeight)
725%       withcolor basiccolors[i]/2
726%       withtransparency (2,.25) ;
727%   endfor ;
728% \stopMPcode
729% \stopbuffer
730%
731% \typebuffer
732
733This snippet of \METAPOST\ code still looks kind of horrible so how can we make
734it look better? Here is an attempt, First we define a bit more \LUA:
735
736\startbuffer
737\startluacode
738local data = mp.dataset(buffers.getcontent("dataset"))
739
740MP.dataset = {
741  Line = function(n) mp.path(data[n]) end,
742  Size = function()  mp.size(data)    end,
743}
744\stopluacode
745\stopbuffer
746
747\typebuffer \getbuffer
748
749We can now make the \METAPOST\ look more natural. Of course this is possible
750because in \METAFUN\ the \type {lua} macro does some extra work.
751
752\startbuffer
753\startMPcode
754  for i=1 upto lua.MP.dataset.Size() :
755    path p ; p :=
756      lua.MP.dataset.Line(i)
757      xysized (HSize-ExHeight,20ExHeight) ;
758    draw
759      p
760      withpen pencircle scaled .25ExHeight
761      withcolor basiccolors[i]/2 ;
762    drawpoints
763      p
764      withpen pencircle scaled ExHeight
765      withcolor basiccolors[i]/2 ;
766  endfor ;
767\stopMPcode
768\stopbuffer
769
770\typebuffer
771
772As expected, we get the desired result:
773
774\startlinecorrection[blank] \getbuffer \stoplinecorrection
775
776Once we start making things look nicer and more convenient, we quickly end up
777with helpers like the once in the next example. First we save some demo data
778in files:
779
780\startbuffer
781\startluacode
782  io.savedata("foo.tmp","10 20 20 20 30 40 40 60 50 10")
783  io.savedata("bar.tmp","10 10 20 30 30 50 40 50 50 20")
784\stopluacode
785\stopbuffer
786
787\typebuffer \getbuffer
788
789We load the data in datasets:
790
791\startbuffer
792\startMPcode
793  lua.mp.datasets("load","foo","foo.tmp") ;
794  lua.mp.datasets("load","bar","bar.tmp") ;
795  fill area
796    lua.mp.datasets("foo","line")
797    xysized (HSize/2-EmWidth-.25ExHeight,10ExHeight)
798    withpen pencircle scaled .25ExHeight
799    withcolor darkyellow ;
800  fill area
801    lua.mp.datasets("bar","line")
802    xysized (HSize/2-EmWidth-.25ExHeight,10ExHeight)
803    shifted (HSize/2+EmWidth,0)
804    withpen pencircle scaled .25ExHeight
805    withcolor darkred ;
806\stopMPcode
807\stopbuffer
808
809\typebuffer
810
811Because the datasets are stored by name we can use them without worrying about
812them being forgotten:
813
814\startlinecorrection[blank] \getbuffer \stoplinecorrection
815
816If no tag is given, the filename (without suffix) is used as tag, so the following is
817valid:
818
819\starttyping
820\startMPcode
821  lua.mp.datasets("load","foo.tmp") ;
822  lua.mp.datasets("load","bar.tmp") ;
823\stopMPcode
824\stoptyping
825
826The following methods are defined for a dataset:
827
828\starttabulate[|l|pl|]
829\HL
830\NC \type {method} \NC usage \NC \NR
831\HL
832\NC \type {size}   \NC the number of subsets in a dataset \NC \NR
833\NC \type {line}   \NC the joined pairs in a dataset making a non|-|closed path \NC \NR
834\NC \type {data}   \NC the table containing the data (in subsets, so there is always at least one subset) \NC \NR
835\HL
836\stoptabulate
837
838{\em In order avoid interference with suffix handling in \METAPOST\ the methods
839start with an uppercase character.}
840
841\stopsection
842
843\startsection[title=Passing variables]
844
845You can pass variables from \METAPOST\ to \CONTEXT. Originally that happened via
846a temporary file and so called \METAPOST\ specials. Nowadays it's done via \LUA.
847Here is an example:
848
849\startbuffer
850\startMPcalculation
851
852passvariable("version","1.0") ;
853passvariable("number",123) ;
854passvariable("string","whatever") ;
855passvariable("point",(1.5,2.8)) ;
856passvariable("triplet",(1/1,1/2,1/3)) ;
857passvariable("quad",(1.1,2.2,3.3,4.4)) ;
858passvariable("boolean",false) ;
859passvariable("path",fullcircle scaled 1cm) ;
860save p ; path p[] ; p[1] := fullcircle ; p[2] := fullsquare ;
861passarrayvariable("list",p,1,2,1) ; % first last step
862\stopMPcalculation
863\stopbuffer
864
865\typebuffer
866
867\getbuffer
868
869We can visualize the result with
870
871\startbuffer
872\startluacode
873context.tocontext(metapost.variables)
874\stopluacode
875\stopbuffer
876
877\typebuffer
878
879\getbuffer
880
881In \TEX\ you can access these variables as follows:
882
883\startbuffer
884\MPrunvar{version}
885\MPruntab{quad}{3}
886(\MPrunset{triplet}{,})
887
888$(x,y) = (\MPruntab{point}{1},\MPruntab{point}{2})$
889$(x,y) = (\MPrunset{point}{,})$
890\stopbuffer
891
892\typebuffer
893
894This becomes: % we need a hack as we cross pages and variables get replace then
895
896\startlines
897\getbuffer
898\stoplines
899
900Here we passed the code between \type {\startMPcalculation} and \type
901{\stopMPcalculation} which does not produce a graphic and therefore takes no
902space in the flow. Of course it also works with normal graphics.
903
904\startbuffer
905\startMPcode
906path p ; p := fullcircle xyscaled (10cm,2cm) ;
907path b ; b := boundingbox p ;
908startpassingvariable("mypath")
909    passvariable("points",p) ;
910    startpassingvariable("metadata")
911        passvariable("boundingbox",boundingbox p) ;
912    stoppassingvariable ;
913stoppassingvariable ;
914fill p withcolor .625red ;
915draw b withcolor .625yellow ;
916\stopMPcode
917\stopbuffer
918
919\typebuffer
920
921\startlinecorrection[blank]
922    \getbuffer
923\stoplinecorrection
924
925This time we get:
926
927\ctxlua{context.tocontext(metapost.variables)}
928
929You need to be aware of the fact that a next graphic resets the previous
930variables. You can easily overcome that limitation by saving the variables (in
931\LUA). It helps that when a page is being shipped out (which can involve
932graphics) the variables are protected. You can push and pop variable sets with
933\type {\MPpushvariables} and \type {\MPpopvariables}. Because you can nest
934the \type {start}||\type{stop} pairs you can create quite complex indexed
935and hashed tables. If the results are not what you expect, you can enable a
936tracker to follow what gets passed:
937
938\starttyping
939\enabletrackers[metapost.variables]
940\stoptyping
941
942Serializing variables can be done with the \type {tostring} macro, for instance:
943
944\startbuffer
945\startMPcode
946message("doing circle",fullcircle);
947draw fullcircle ;
948\stopMPcode
949\stopbuffer
950
951In this case the \type {tostring} is redundant as the message already does the
952serialization.
953
954\stopsection
955
956\startsection[title={Interference}]
957
958In this section we will discuss a potential conflict with other mechanisms,
959especially primitives and macros. A simple example of using the interface is:
960
961\startbuffer
962\startluacode
963function MP.AnExample(str)
964    mp.aux.quoted(string.reverse(str))
965end
966\stopluacode
967
968\startMPcode
969draw textext(lua.MP.AnExample("Hi there!"))
970    rotated 45
971    ysized 2cm ;
972\stopMPcode
973\stopbuffer
974
975\typebuffer
976
977\startlinecorrection
978\getbuffer
979\stoplinecorrection
980
981The \type {mp} namespace is reserved for functionality provided by \CONTEXT\
982itself so you should not polute it with your own code. Instead use the \type {MP}
983namespace and mix in some uppercase characters.
984
985Here you see a subnamespace \type {aux} which is where officially the helpers are
986organized but they are also accessible directly (as shown in previous sections).
987The reason for the \type {aux} namespace is, apart from propection aginst
988redefinition, also that we have \type {get} and \type {set} namespaces and there
989might be more in the future. At the \LUA\ end you can best use these namespaces
990because they are less likely to be accidentally overwritten by user code.
991
992As mentioned, there can still be conflicts. For instance the following will not
993work:
994
995\startbuffer
996\startluacode
997function MP.reverse(str)
998    mp.aux.quoted(string.reverse(str))
999end
1000\stopluacode
1001
1002\startMPcode
1003draw textext(lua.MP.reverse("Hi there!"))
1004    rotated -45
1005    ysized 2cm ;
1006\stopMPcode
1007\stopbuffer
1008
1009\typebuffer
1010
1011% \startlinecorrection
1012% \getbuffer
1013% \stoplinecorrection
1014
1015The reason is that \type {reverse} gets expanded as part of parsing the macro
1016name and this command expects an expression. A way out of this is the following:
1017
1018\startbuffer
1019\startluacode
1020function MP.reverse(str)
1021    mp.aux.quoted(string.reverse(str))
1022end
1023\stopluacode
1024
1025\startMPcode
1026draw textext(lua.MP("reverse","Hi there!"))
1027    rotated -45
1028    ysized 2cm ;
1029\stopMPcode
1030\stopbuffer
1031
1032\typebuffer
1033
1034\startlinecorrection
1035\getbuffer
1036\stoplinecorrection
1037
1038You can add litle bit of protection for your own code by using a prefix in the
1039name, like:
1040
1041\startbuffer
1042\startluacode
1043MP["mynamespace.reverse"] = function(str)
1044    mp.aux.quoted(string.reverse(str))
1045end
1046\stopluacode
1047
1048\startMPcode
1049draw textext(lua.MP("mynamespace.reverse","Hi there!"))
1050    rotated -90
1051    ysized 2cm ;
1052\stopMPcode
1053\stopbuffer
1054
1055\typebuffer
1056
1057\startlinecorrection
1058\getbuffer
1059\stoplinecorrection
1060
1061\stopsection
1062
1063\startsection[title=Predefined properties]
1064
1065As mentioned, the \type {mp} namespace is reserved for commands that come
1066with the \CONTEXT|-|\METAFUN\ combination. For instance, a lot of layout
1067related calls are there::
1068
1069\startcolumns[n=3]
1070\starttyping
1071BackSpace
1072BaseLineSkip
1073BodyFontSize
1074BottomDistance
1075BottomHeight
1076BottomSpace
1077CurrentColumn
1078CurrentHeight
1079CurrentWidth
1080CutSpace
1081EmWidth
1082ExHeight
1083FooterDistance
1084FooterHeight
1085HeaderDistance
1086HeaderHeight
1087InnerEdgeDistance
1088InnerEdgeWidth
1089InnerMarginDistance
1090InnerMarginWidth
1091LastPageNumber
1092LayoutColumnDistance
1093LayoutColumns
1094LayoutColumnWidth
1095LeftEdgeDistance
1096LeftEdgeWidth
1097LeftMarginDistance
1098LeftMarginWidth
1099LineHeight
1100MakeupHeight
1101MakeupWidth
1102NOfColumns
1103NOfPages
1104NOfPages
1105NOfSubPages
1106OuterEdgeDistance
1107OuterEdgeWidth
1108OuterMarginDistance
1109OuterMarginWidth
1110PageDepth
1111PageFraction
1112PageNumber
1113PageNumber
1114PageOffset
1115PaperBleed
1116PaperHeight
1117PaperWidth
1118PrintPaperHeight
1119PrintPaperWidth
1120RealPageNumber
1121RealPageNumber
1122RightEdgeDistance
1123RightEdgeWidth
1124RightMarginDistance
1125RightMarginWidth
1126SpineWidth
1127StrutDepth
1128StrutHeight
1129SubPageNumber
1130TextHeight
1131TextWidth
1132TopDistance
1133TopHeight
1134TopSkip
1135TopSpace
1136\stoptyping
1137\stopcolumns
1138
1139There all return dimensions, contrary to the next few that return a boolean:
1140
1141\startcolumns[n=3]
1142\starttyping
1143OnRightPage
1144OnOddPage
1145InPageBody
1146\stoptyping
1147\stopcolumns
1148
1149There are also calls related to backgrounds:
1150
1151\startcolumns[n=3]
1152\starttyping
1153OverlayWidth
1154OverlayHeight
1155OverlayDepth
1156OverlayLineWidth
1157OverlayOffset
1158\stoptyping
1159\stopcolumns
1160
1161And one related to color:
1162
1163\startcolumns[n=3]
1164\starttyping
1165NamedColor
1166\stoptyping
1167\stopcolumns
1168
1169In most cases such \type {lua.mp.command()} calls have a \METAPOST\ macro with
1170the same name defined.
1171
1172\stopsection
1173
1174\startsection[title=Accessing paths]
1175
1176A path in \METAPOST\ is internally a linked list of knots and each knot has a
1177coordinate and two control points. Access to specific points of very large path
1178can be somewhat slow. First of all, the lookup start at the beginning and when
1179you use fractions (say halfway between point 4 and 5) the engine has to find the
1180spot. For this (and other reasons not mentioned here) we have a way to access
1181paths different, using \LUA\ behind the scenes.
1182
1183\startbuffer
1184path p ; p := fullcircle xysized (4cm,2cm) ;
1185for i inpath p:
1186    drawdot  leftof i withpen pencircle scaled 2mm withcolor darkred ;
1187    drawdot pointof i withpen pencircle scaled 3mm withcolor darkgreen ;
1188    drawdot rightof i withpen pencircle scaled 2mm withcolor darkblue ;
1189endfor ;
1190draw for i inpath p:
1191    pointof i .. controls (leftof i) and (rightof i) ..
1192endfor cycle withpen pencircle scaled .5mm withcolor white ;
1193
1194p := p shifted (5cm,0) ;
1195draw for i inpath p:
1196    pointof i --
1197endfor cycle withpen pencircle scaled .5mm withcolor .5white ;
1198for i inpath p:
1199    drawdot pointof i withpen pencircle scaled 3mm withcolor darkgreen ;
1200endfor ;
1201\stopbuffer
1202
1203\typebuffer
1204
1205Here we access the main coordinate and the two control points. The last draw is of course
1206just mimicking drawing the path.
1207
1208\startlinecorrection[blank]
1209\processMPbuffer
1210\stoplinecorrection
1211
1212\stopsection
1213
1214\startsection[title=Acessing \TEX]
1215
1216In \MKIV\ and \LMTX\ it is possible to access \TEX\ registers and macros from the
1217\METAPOST\ end. Let's first define and set some:
1218
1219\startbuffer
1220\newdimen\MyMetaDimen  \MyMetaDimen = 2mm
1221\newcount\MyMetaCount  \MyMetaCount = 10
1222\newtoks \MyMetaToks   \MyMetaToks  = {\bfd \TeX}
1223     \def\MyMetaMacro                 {not done}
1224\stopbuffer
1225
1226\typebuffer \getbuffer
1227
1228\startbuffer
1229\startMPcode
1230    for i=1 upto getcount("MyMetaCount") :
1231        draw fullcircle scaled (i * getdimen("MyMetaDimen")) ;
1232    endfor ;
1233    draw textext(gettoks("MyMetaToks")) xsized 15mm withcolor darkred ;
1234    setglobaldimen("MyMetaDimen", bbwidth(currentpicture)) ;
1235    setglobalmacro("MyMetaMacro", "done") ;
1236\stopMPcode
1237\stopbuffer
1238
1239\typebuffer
1240
1241\startlinecorrection[blank]
1242    \getbuffer
1243\stoplinecorrection
1244
1245We can now look at the two updated globals where \type {\MyMetaMacro: \the\MyMetaDimen}
1246typesets: {\tttf \MyMetaMacro: \the\MyMetaDimen}. As demonstrated you can best define your
1247own registers but in principle you can also access system ones, like \type {\scratchdimen}
1248and friends.
1249
1250\stopsection
1251
1252\startsection[title=Abstraction]
1253
1254We will now stepwise implement some simple helpers for accessing data in files.
1255The examples are kind of useless but demonstrate how interfaces evolved. The
1256basic command to communicate with \LUA\ is \type {runscript}. In this example
1257we will load a (huge) file and run over the lines.
1258
1259\starttyping
1260\startMPcode{doublefun}
1261  save q ; string q ; q := "'\\" & ditto & "'" ;
1262  runscript (
1263    "GlobalData = string.splitlines(io.loaddata('foo.tmp')) return ''"
1264  ) ;
1265  numeric l ; l = runscript (
1266    "return string.format('\letterpercent q',\letterhash GlobalData)"
1267  );
1268  for i=1 step 1 until l :
1269    l := length ( runscript (
1270      "return string.format('\letterpercent q',GlobalData[" & decimal i & "])"
1271    ) ) ;
1272  endfor ;
1273  draw textext(decimal l);
1274\stopMPcode
1275\stoptyping
1276
1277The \type {runscript} primitive takes a string and should return a string (in
1278\LUAMETATEX\ you can also return nothing). This low level solution will serve as
1279our benchmark: it takes 2.04 seconds on the rather large (64MB) test file with
128010.000 lines.
1281
1282The code looks somewhat clumsy. This is because in \METAPOST\ escaping is not
1283built in so one has to append a double quote character using \type {char 34} and
1284the \type {ditto} string is defined as such. This mess is why in \CONTEXT\ we
1285have an interface:
1286
1287\starttyping
1288\startMPcode{doublefun}
1289  lua("GlobalData = string.splitlines(io.loaddata('foo.tmp'))") ;
1290  numeric l ;
1291  for i=1 step 1 until lua("mp.print(\#GlobalData)") :
1292    l := length(lua("mp.quoted(GlobalData[" & decimal i & "])")) ;
1293  endfor ;
1294  draw textext(decimal l);
1295\stopMPcode
1296\stoptyping
1297
1298As expected we pay a price for the additional overhead, so this time we need 2.28
1299seconds to process the file. The return value of a run is a string that is fed
1300into \type {scantokens}. Here \type {print} function prints the number as string
1301and that gets scanned back to a number. The \type {quoted} function returns a
1302string in a string so when we're back in \METAPOST\ that gets scanned as string.
1303
1304When code is used more frequently, we can make a small library, like this:
1305
1306\starttyping
1307\startluacode
1308  local MyData = { }
1309  function mp.LoadMyData(filename)
1310    MyData = string.splitlines(io.loaddata(filename))
1311  end
1312  local mpprint  = mp.print
1313  local mpquoted = mp.quoted
1314  function mp.MyDataSize()
1315    mpprint(#MyData)
1316  end
1317  function mp.MyDataString(i)
1318    mpquoted(MyData[i] or "")
1319  end
1320\stopluacode
1321\stoptyping
1322
1323It is not that hard to imagine a more advanced mechanisms where data from multiple
1324files can be handled at the same time. This code is used as:
1325
1326\starttyping
1327\startMPcode{doublefun}
1328  lua.mp.LoadMyData("foo.tmp") ;
1329  numeric l ;
1330  for i=1 step 1 until lua.mp.MyDataSize() :
1331    l := length(lua.mp.MyDataString(i)) ;
1332  endfor ;
1333  draw textext(decimal l);
1334\stopMPcode
1335\stoptyping
1336
1337The \type {mp} namespace at the \LUA\ end is a subnamespace at the \METAPOST\
1338end. This solution needs 2.20 seconds so we're still slower than the first one,
1339but in \LUAMETATEX\ with \LMTX we can do better. First the \LUA\ code:
1340
1341\starttyping
1342\startluacode
1343  local injectnumeric = mp.inject.numeric
1344  local injectstring  = mp.inject.string
1345  local MyData = { }
1346  function mp.LoadMyData(filename)
1347    MyData = string.splitlines(io.loaddata(filename))
1348  end
1349  function mp.MyDataSize()
1350    injectnumeric(#MyData)
1351  end
1352  function mp.MyDataString(i)
1353    injectstring(MyData[i] or "")
1354  end
1355\stopluacode
1356\stoptyping
1357
1358This time we use injectors. The mentioned \type {print} helpers serialize data so
1359numbers, pairs, colors etc are converted to a string that represents them that is
1360fed back to \METAPOST\ after the snippet is run. Multiple prints are collected
1361into one string. An injecter follows a more direct route: it pushes back a proper
1362\METAPOST\ data type.
1363
1364\starttyping
1365\startMPcode{doublefun}
1366  lua.mp.LoadMyData("foo.tmp") ;
1367  numeric l ;
1368  for i=1 step 1 until lua.mp.MyDataSize() :
1369    l := length(lua.mp.MyDataString(i)) ;
1370  endfor ;
1371  draw textext(decimal l);
1372\stopMPcode
1373\stoptyping
1374
1375This usage brings us down to 1.14 seconds, so we're still not good. The next
1376variant is performing similar: 1.05 seconds.
1377
1378\starttyping
1379\startMPcode{doublefun}
1380  runscript("mp.LoadMyData('foo.tmp')") ;
1381  numeric l ;
1382  for i=1 step 1 until runscript("mp.MyDataSize()") :
1383    l := length(runscript("mp.MyDataString(" & decimal i & ")")) ;
1384  endfor ;
1385  draw textext(decimal l);
1386\stopMPcode
1387\stoptyping
1388
1389We will now delegate scanning to the \LUA\ end.
1390
1391\starttyping
1392\startluacode
1393  local injectnumeric = mp.inject.numeric
1394  local injectstring  = mp.inject.string
1395  local scannumeric   = mp.scan.numeric
1396  local scanstring    = mp.scan.string
1397  local MyData = { }
1398  function mp.LoadMyData()
1399    MyData = string.splitlines(io.loaddata(scanstring()))
1400  end
1401  function mp.MyDataSize()
1402    injectnumeric(#MyData)
1403  end
1404  function mp.MyDataString()
1405    injectstring(MyData[scannumeric()] or "")
1406  end
1407\stopluacode
1408\stoptyping
1409
1410This time we are faster than the clumsy code we started with: 0.87 seconds.
1411
1412\starttyping
1413\startMPcode{doublefun}
1414  runscript("mp.LoadMyData()") "foo.tmp" ;
1415  numeric l ;
1416  for i=1 step 1 until runscript("mp.MyDataSize()") :
1417    l := length(runscript("mp.MyDataString()") i) ;
1418  endfor ;
1419  draw textext(decimal l);
1420\stopMPcode
1421\stoptyping
1422
1423In \LMTX\ we can add some more abstraction. Performance is about the same and
1424sometimes a bit faster but that depends on extreme usage: you need thousands of
1425call to notice.
1426
1427\starttyping
1428\startluacode
1429  local injectnumeric = mp.inject.numeric
1430  local injectstring  = mp.inject.string
1431  local scannumeric   = mp.scan.numeric
1432  local scanstring    = mp.scan.string
1433  local MyData = { }
1434  metapost.registerscript("LoadMyData", function()
1435    MyData = string.splitlines(io.loaddata(scanstring()))
1436  end)
1437  metapost.registerscript("MyDataSize", function()
1438    injectnumeric(#MyData)
1439  end)
1440  metapost.registerscript("MyDataString", function()
1441    injectstring(MyData[scannumeric()] or "")
1442  end)
1443\stopluacode
1444\stoptyping
1445
1446We have the same scripts but we register them. At the \METAPOST\ end we resolve
1447the registered scripts and then call \type {runscript} with the (abstract) numeric
1448value:
1449
1450\starttyping
1451\startMPcode{doublefun}
1452  newscriptindex my_script_LoadMyData   ;
1453  newscriptindex my_script_MyDataSize   ;
1454  newscriptindex my_script_MyDataString ;
1455
1456  my_script_LoadMyData   := scriptindex "LoadMyData"   ;
1457  my_script_MyDataSize   := scriptindex "MyDataSize"   ;
1458  my_script_MyDataString := scriptindex "MyDataString" ;
1459
1460  runscript my_script_LoadMyData "foo.tmp" ;
1461  numeric l ;
1462  for i=1 step 1 until runscript my_script_MyDataSize :
1463    l := length(my_script_MyDataString i) ;
1464  endfor ;
1465  draw textext(decimal l);
1466\stopMPcode
1467\stoptyping
1468
1469This is of course nicer:
1470
1471\starttyping
1472\startMPcode{doublefun}
1473  def LoadMyData  (expr s) = runscript my_script_LoadMyData   s enddef ;
1474  def MyDataSize           = runscript my_script_MyDataSize     enddef ;
1475  def MyDataString(expr i) = runscript my_script_MyDataString i enddef ;
1476
1477  LoadMyData("foo.tmp") ;
1478  numeric l ;
1479  for i=1 step 1 until MyDataSize :
1480    l := length(MyDataString(i)) ;
1481  endfor ;
1482  draw textext(decimal l);
1483\stopMPcode
1484\stoptyping
1485
1486So, to sumarize, there are many ways to look at this: verbose direct ones
1487but also nicely abstract ones.
1488
1489\stopsection
1490
1491% The plugins (like those dealing with text) also use calls in the \type {mp}
1492% namespace but they have sort of protected names, starting with \type {mf_}. These
1493% are visible but not meant to be used by users. Not only can their name change,
1494% their functionality can as well.
1495%
1496% The following are actually private as they have related macros but also have a
1497% public alias:
1498%
1499% \startlines
1500% lua.mp.pathlength(name)
1501% lua.mp.pathpoint(i)
1502% lua.mp.pathleft(i)
1503% lua.mp.pathright(i)
1504% lua.mp.pathreset()
1505% \stoplines
1506%
1507% They are meant for special high|-|performance path access, for example:
1508%
1509% \startlinecorrection
1510% \startMPcode
1511%     save p, q, r;
1512%     path p ; p := for i=1 upto 1000 :
1513%         (i,0) -- (i,4 + uniformdeviate 4) --
1514%     endfor cycle ;
1515%     fill p xysized (TextWidth,20mm) withcolor red ;
1516%
1517%     path q ; q := for i=1 upto lua.mp.pathlength("p") :
1518%         if (i mod 4) == 0 : lua.mp.pathpoint(i) -- fi
1519%     endfor cycle ;
1520%     fill q xysized (TextWidth,2cm) shifted (0,-45mm) withcolor green ;
1521%
1522%     path r ; r := for i inpath p :
1523%         if not odd (i) : pointof i -- fi
1524%     endfor cycle ;
1525%     fill r xysized (TextWidth,2cm) shifted (0,-70mm) withcolor blue ;
1526% \stopMPcode
1527% \stoplinecorrection
1528%
1529% Because a lookup of a point in \METAPOST\ is a linear lookup over a linked list
1530% for very large paths the gain is significant when using \LUA\ because there the
1531% points are stored in an indexed table. The left and right points can be used
1532% vebose like
1533%
1534% \starttyping
1535% lua.mp.pathpoint(i) controls lua.mp.pathleft(i) and lua.mp.pathright(i)
1536% \stoptyping
1537%
1538% or more terse:
1539%
1540% \starttyping
1541% pointof i controls leftof i and rightof i
1542% \stoptyping
1543%
1544% Beware: this kind of trickery is {\em only} needed when you have very large paths
1545% that are to be manipulated and the. Otherwise it's overkill.
1546%
1547% \stopsection
1548%
1549% \startsection[title={Interfacing to \TEX}]
1550%
1551% The next bunch of calls is for accessing \TEX\ registers. You can set their
1552% values and get them as well.
1553%
1554% \starttyping
1555% lua.mp.getmacro(k)
1556% lua.mp.getdimen(k)
1557% lua.mp.getcount(k)
1558% lua.mp.gettoks (k)
1559%
1560% lua.mp.setmacro(k,v)
1561% lua.mp.setdimen(k,v)
1562% lua.mp.setcount(k,v)
1563% lua.mp.settoks (k,v)
1564% \stoptyping
1565%
1566% When you mess around with variables and run into issues it might help to
1567% report values on the console. The \type {report} function does this:
1568%
1569% \starttyping
1570% lua.mp.report(a,b)
1571% \stoptyping
1572%
1573% \startlinecorrection
1574% \startMPcode
1575% lua.mp.report("status","a circle") ;
1576% fill fullcircle xyscaled (2cm,1cm) withcolor red ;
1577% lua.mp.report("status","its boundingbox [@N,@N,@N,@N]",
1578%     xpart llcorner currentpicture, ypart llcorner currentpicture,
1579%     xpart urcorner currentpicture, ypart urcorner currentpicture
1580% ) ;
1581% draw boundingbox currentpicture withcolor blue ;
1582% report("status","the size: @Nbp x @Nbp",
1583%     bbwidth(currentpicture), bbheight(currentpicture)
1584% ) ;
1585% message("done") ;
1586% \stopMPcode
1587% \stoplinecorrection
1588%
1589% The console shows:
1590%
1591% \starttyping
1592% metapost > status : a circle
1593% metapost > status : its boundingbox [-28.34645,-14.17323,28.34645,14.17323]
1594% metapost > status : the size: 57.1929bp x 28.84647bp
1595% metapost > message : done
1596% \stoptyping
1597%
1598% There are two more getters. These can be used to access the specific graphic
1599% related variables set at the \TEX\ end.
1600%
1601% \starttyping
1602% mp.texvar(name)
1603% mp.texstr(name)
1604% \stoptyping
1605%
1606% If you have adaptive styles you might want to test for modes.
1607%
1608% \starttyping
1609% if lua.mp.mode("screen") : % or processingmode
1610%     % some action only needed for screen documents
1611% fi ;
1612% if lua.mp.systemmode("first") :
1613%     % some action for the first run
1614% fi ;
1615% \stoptyping
1616%
1617% For convenience these are wrapped into macros:
1618%
1619% \starttyping
1620% if texmode("screen") :
1621%     % some action only needed for screen documents
1622% fi ;
1623% if systemmode("first") :
1624%     % some action for the first run
1625% fi ;
1626% \stoptyping
1627%
1628% When you implement your own helpers you can fall back on some auxiliary functions
1629% in the \type {mp} namespace. Actually these are collected in \type {mp.aux} and
1630% thereby protected from being overwritten by mistake. Here they are:
1631%
1632% % mp.flush()
1633% % mp.size(t)
1634%
1635% \stopsection
1636
1637% \startsection[title={Interfacing to \METAPOST}]
1638%
1639% There is also experimental access to some of the \METAPOST\ internals. In order
1640% to deal with (large) paths a few more iterator related helpers are provided too.
1641%
1642% % mp.set (numeric string path boolean)
1643%
1644% \starttyping
1645% n = mp.getnumeric(name)
1646% s = mp.getstring(name)
1647% b = mp.getboolean(name)
1648% p = mp.getpath(name)
1649% \stoptyping
1650%
1651% Although it might look like \METAPOST\ supports arrays the reality is that it
1652% doesn't really. There is a concept of suffixes but internally these are just a
1653% way to compose macros. The following test is one that is used in one of the
1654% \METAFUN\ modules written by Alan Braslau.
1655%
1656% \startlinecorrection
1657% \startMPcode
1658% path p, q[] ;
1659%
1660% if lua.mp.isarray(str q[1]) :
1661%     fill fullcircle scaled 1cm withcolor red ;
1662%     draw textext(lua.mp.prefix(str q[1])) withcolor white;
1663% else :
1664%     fill fullsquare scaled 1cm withcolor blue ;
1665% fi ;
1666%
1667% currentpicture := currentpicture shifted (-2cm,0) ;
1668%
1669% if lua.mp.isarray(str p) :
1670%     fill fullcircle scaled 1cm withcolor red ;
1671% else :
1672%     fill fullsquare scaled 1cm withcolor blue ;
1673% fi ;
1674% \stopMPcode
1675% \stoplinecorrection
1676%
1677% Another helper relates to extensions that \METAFUN\ adds to \METAPOST. When you
1678% iterate over a picture you can recognize these as objects. The next code shows
1679% the \LUA\ call as well as the more convenient macro call. So, \type {textext}
1680% clearly is a foreign object.
1681%
1682% \startlinecorrection
1683% \startMPcode
1684% picture p ; p := image (
1685%     fill fullcircle scaled 1cm withcolor red ;
1686%     draw textext("ok") withcolor white ;
1687% ) ;
1688%
1689% for i within p :
1690%     if not picture i :
1691%         draw i ysized 4cm ;
1692%     elseif isobject i :
1693%         draw i xsized 3cm ;
1694%     else :
1695%         draw i ysized 4cm ;
1696%     fi ;
1697% endfor ;
1698%
1699% currentpicture := currentpicture shifted (-6cm,0) ;
1700%
1701% for i within p :
1702%     if isobject(i) :
1703%         draw i xsized 5cm ;
1704%     else :
1705%         draw i ysized 3cm withcolor blue;
1706%     fi ;
1707% endfor ;
1708% \stopMPcode
1709% \stoplinecorrection
1710
1711% not yet, still experimental:
1712%
1713% mp.dataset(str)
1714% mp.n(t)
1715
1716% not for users:
1717%
1718% mp.defaultcolormodel()
1719
1720% rather specialized:
1721%
1722% mp.positionpath(name)
1723% mp.positioncurve(name)
1724% mp.positionbox(name)
1725% mp.positionxy(name)
1726% mp.positionpage(name)
1727% mp.positionregion(name)
1728% mp.positionwhd(name)
1729% mp.positionpxy(name)
1730% mp.positionanchor()
1731
1732
1733% mp.cleaned
1734% mp.format(fmt,str)
1735% mp.formatted(fmt,...)
1736% mp.graphformat(fmt,num)
1737
1738\stopsection
1739
1740\stopchapter
1741
1742% \startMPcode{doublefun}
1743%     numeric n ; n := 123.456 ;
1744%     lua("print('>>>>>>>>>>>> number',mp.get.number('n'))") ;
1745%     lua("print('>>>>>>>>>>>> number',mp.get.boolean('n'))") ;
1746%     lua("print('>>>>>>>>>>>> number',mp.get.string('n'))") ;
1747%     boolean b ; b := true ;
1748%     lua("print('>>>>>>>>>>>> boolean',mp.get.number('b'))") ;
1749%     lua("print('>>>>>>>>>>>> boolean',mp.get.boolean('b'))") ;
1750%     lua("print('>>>>>>>>>>>> boolean',mp.get.string('b'))") ;
1751%     string s ; s := "TEST" ;
1752%     lua("print('>>>>>>>>>>>> string',mp.get.number('s'))") ;
1753%     lua("print('>>>>>>>>>>>> string',mp.get.boolean('s'))") ;
1754%     lua("print('>>>>>>>>>>>> string',mp.get.string('s'))") ;
1755% \stopMPcode
1756
1757% \usemodule[graph]
1758%
1759% \startluacode
1760%     local d = nil
1761%     function MP.set(data)
1762%         d = data
1763%     end
1764%     function MP.n()
1765%         mp.print(d and #d or 0)
1766%     end
1767%     function MP.get(i,j)
1768%         mp.print(d and d[i] and d[i][j] or 0)
1769%     end
1770% \stopluacode
1771%
1772% \startluacode
1773%     MP.set {
1774%         { 1, 0.5, 2.5 },
1775%         { 2, 1.0, 3.5 },
1776%     }
1777% \stopluacode
1778%
1779% \startMPpage[instance=graph,offset=2mm]
1780%
1781% draw begingraph(3cm,5cm);
1782%     numeric a[];
1783%     for j = 1 upto MP.n() :
1784%         path b;
1785%         augment.b(MP.get(j,1),MP.get(j,2));
1786%         augment.b(MP.get(j,1),MP.get(j,3));
1787%         setrange(0,0,3,4);
1788%         gdraw b;
1789%         endfor ;
1790% endgraph ;
1791% \stopMPpage
1792
1793% \starttext
1794%
1795% % \enabletrackers[metapost.variables]
1796%
1797% \startMPcode
1798%     numeric n[]   ; for i=1 upto 10: n[i] := 1/i ; endfor ;
1799%     path    p[]   ; for i=1 upto 10: p[i] := fullcircle xyscaled (cm*i,cm/i) ; endfor ;
1800%     numeric r[][] ; for i=1 upto 4 : for j=1 upto 3 : r[i][j] := uniformdeviate(1) ; endfor ; endfor ;
1801%     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 ;
1802%
1803%     passvariable("x",12345) ;
1804%     passarrayvariable("n-array",n,1,7,1) ;
1805%     passarrayvariable("p-array",p,1,7,1) ;
1806%     passvariable("p",(1,1) .. (2,2)) ;
1807%
1808%     startpassingvariable("b")
1809%         for i=1 upto 4 :
1810%             startpassingvariable(i)
1811%                 for j=1 upto 3 :
1812%                     passvariable(j,r[i][j])
1813%                 endfor
1814%             stoppassingvariable
1815%         endfor
1816%     stoppassingvariable ;
1817%
1818%     startpassingvariable("a")
1819%         startpassingvariable("test 1")
1820%             passvariable(1,123)
1821%             passvariable(2,456)
1822%         stoppassingvariable ;
1823%         startpassingvariable("test 2")
1824%             passvariable(0,123)
1825%             passvariable(1,456)
1826%             passvariable(2,789)
1827%             passvariable(999,987)
1828%         stoppassingvariable ;
1829%         startpassingvariable("test 3")
1830%             passvariable("first",789)
1831%             passvariable("second",987)
1832%         stoppassingvariable
1833%     stoppassingvariable ;
1834%
1835%     startpassingvariable("c")
1836%         for i=1 step 0.5 until 4 :
1837%             startpassingvariable(i)
1838%                 for j=1 step 0.1 until 2 :
1839%                     passvariable(j,u[i][j])
1840%                 endfor
1841%             stoppassingvariable
1842%         endfor
1843%     stoppassingvariable ;
1844%
1845%     draw fullcircle scaled 1cm ;
1846% \stopMPcode
1847%
1848% \ctxluacode{inspect(metapost.variables)}
1849%
1850% \ctxcommand{mprunvar("x")}
1851
1852\stopcomponent
1853