1
2
3
4
5\startcomponent mfunlua
6
7\environment metafunenvironment
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 its 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 its 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
34
35
36
37\startsection[title=The basics]
38
39The approach is comparable to \LUATEXs \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 dont 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 doesnt 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 runscript = 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, dont worry: its 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(demodata.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 dont 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
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
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 scriptrunner = function(s) return loadstring(s)() end,
500 scripterror = 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[ll]
509\HL
510\NC \type {m:getnumeric(name)} \NC returns a numeric (double) \NC \NR
511\NC \type {m:getboolean(name)} \NC returns a boolean (\type {true} or \type {false}) \NC \NR
512\NC \type {m:getstring (name)} \NC returns a string \NC \NR
513\HL
514\NC \type {mplib.getnumeric(m,name)} \NC returns a numeric (double) \NC \NR
515\NC \type {mplib.getboolean(m,name)} \NC returns a boolean (\type {true} or \type {false}) \NC \NR
516\NC \type {mplib.getstring (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[ll]
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[ll]
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
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 Ive 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
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
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 (HSizeExHeight,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 (HSize2EmWidth-.25ExHeight,10ExHeight)
798 withpen pencircle scaled .25ExHeight
799 withcolor darkyellow ;
800 fill area
801 lua.mp.datasets("bar","line")
802 xysized (HSize2EmWidth-.25ExHeight,10ExHeight)
803 shifted (HSize2EmWidth,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[lpl]
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 nonclosed 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 its 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",(11,12,13)) ;
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) ;
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:
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
1012
1013
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. Lets 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 were 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 were 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 were 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
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738\stopsection
1739
1740\stopchapter
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852\stopcomponent
1853 |