mlib-lmp.lmt /size: 18 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['mlib-lmp'] = {
2    version   = 1.001,
3    comment   = "companion to mlib-ctx.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files",
7}
8
9-- path relates stuff ... todo: use a stack (or numeric index to list)
10
11local type, tonumber, tostring = type, tonumber, tostring
12local find, match = string.find, string.match
13local insert, remove, sort = table.insert, table.remove, table.sort
14
15local aux            = mp.aux
16local mpnumeric      = aux.numeric
17local mppair         = aux.pair
18
19local registerdirect = metapost.registerdirect
20local registerscript = metapost.registerscript
21
22local scan           = mp.scan
23local skip           = mp.skip
24local get            = mp.get
25local inject         = mp.inject
26
27local scannumber     = scan.number
28local scanstring     = scan.string
29local scaninteger    = scan.integer
30local scannumeric    = scan.numeric
31local scanwhatever   = scan.whatever
32local scanpath       = scan.path
33local scanproperty   = scan.property
34
35local gethashentry   = get.hashentry
36
37local bpfactor       = number.dimenfactors.bp
38
39local injectwhatever = inject.whatever
40local injectboolean  = inject.boolean
41local injectnumeric  = inject.numeric
42local injectstring   = inject.string
43local injectpair     = inject.pair
44local injectpath     = inject.path
45
46local injectwhd      = inject.whd -- scaled
47local injectxy       = inject.xy
48local injectpt       = inject.pt
49
50local report         = logs.reporter("metapost", "log")
51local report_message = logs.reporter("metapost")
52
53local codes          = metapost.codes
54local types          = metapost.types
55local procodes       = mplib.propertycodes
56
57local implement      = interfaces.implement
58
59do
60
61    local function s(a,b)
62        local aa = a[1]
63        local bb = b[1]
64        if aa == bb then
65            aa = a[2]
66            bb = b[2]
67        end
68        return aa < bb
69    end
70
71    registerscript("sortedpath", function()
72        local p = scanpath()
73        for i=1,#p do
74            local pi = p[i]
75            p[i] = { pi[1], pi[2] }
76        end
77        sort(p,s)
78        injectpath(p)
79    end)
80
81    registerscript("uniquepath", function()
82        local p = scanpath()
83        local u = { }
84        local n = 0
85        local xx = nil
86        local yy = nil
87        sort(p,s)
88        for i=1,#p do
89            local pi = p[i]
90            local x = pi[1]
91            local y = pi[2]
92            if x ~= xx or y ~= yy then
93                n = n + 1
94                u[n] = { x, y }
95                xx = x
96                yy = y
97            end
98        end
99        injectpath(u)
100    end)
101
102end
103
104do
105
106    local p = nil
107    local n = 0
108
109    registerscript("pathreset", function()
110        p = nil
111        n = 0
112    end)
113
114    registerdirect("pathlengthof", function()
115        p = scanpath()
116        n = p and #p or 1
117        return n
118    end)
119
120    registerdirect("pathpointof", function()
121        local i = scaninteger()
122        if i > 0 and i <= n then
123            local pi = p[i]
124            injectpair(pi[1],pi[2])
125        end
126    end)
127
128    registerdirect("pathleftof", function()
129        local i = scaninteger()
130        if i > 0 and i <= n then
131            local pi = p[i]
132            injectpair(pi[5],pi[6])
133        end
134    end)
135
136    registerdirect("pathrightof", function()
137        local i = scaninteger()
138        if i > 0 and i <= n then
139            local pn
140            if i == 1 then
141                pn = p[2] or p[1]
142            else
143                pn = p[i+1] or p[1]
144            end
145            injectpair(pn[3],pn[4])
146        end
147    end)
148
149end
150
151registerscript("showproperty", function()
152    local k, s, p, d = scanproperty()
153    if k then
154        report("name %a, property %a, command %a, detail %a",s,procodes[p] or "-",codes[k] or "-",types[d] or "-")
155    end
156end)
157
158registerscript("showhashentry", function()
159    local s = scanstring()
160    if s then
161        local k, p, d = gethashentry(s)
162        if k then
163            report("name %a, property %a, command %a, detail %a",s,procodes[p] or "-",codes[k] or "-",types[d] or "-")
164        end
165    end
166end)
167
168-- local getmacro      = tokens.getters.macro
169-- local mpgnamespace  = getmacro("??graphicvariable")
170
171-- registerscript("mpv_numeric",   function() injectnumeric  (getmacro(mpgnamespace .. getmacro("currentmpcategory") .. ":" .. scanmpstring())) end)
172-- registerscript("mpv_dimension", function() return getmacro(mpgnamespace .. getmacro("currentmpcategory") .. ":" .. scanmpstring()) end)
173-- registerscript("mpv_string",    function() injectstring   (getmacro(mpgnamespace .. getmacro("currentmpcategory") .. ":" .. scanmpstring())) end)
174
175-- registerscript("mpvar", function() return getmacro(mpgnamespace .. getmacro("currentmpcategory") .. ":" .. scanmpstring(), true) end) -- Isn't it already edef'd?
176-- registerscript("mpvar", function() return getmacro(metapost.namespace .. scanmpstring(), true) end) -- Isn't it already edef'd?
177
178do
179
180    local expandtex       = mp.expandtex
181
182    local tokenvalues     = tokens.values
183    local dimension_value = tokenvalues.dimension
184    local integer_value   = tokenvalues.integer
185    local boolean_value   = tokenvalues.boolean
186    local string_value    = tokenvalues.string
187    local unknown_value   = tokenvalues.none
188
189    registerdirect("mpvard", function()
190        if not expandtex(dimension_value,"mpcategoryparameter",true,scanstring()) then
191            injectnumeric(0)
192        end
193    end)
194
195    registerdirect("mpvarn", function()
196        if not expandtex(integer_value,"mpcategoryparameter",true,scanstring()) then
197            injectnumeric(0)
198        end
199    end)
200
201    registerdirect("mpvars", function()
202        if not expandtex(string_value,"mpcategoryparameter",true,scanstring()) then
203            injectstring("")
204        end
205    end)
206
207    registerdirect("mpvarb", function()
208        if not expandtex(boolean_value,"mpcategoryparameter",true,scanstring()) then
209            injectboolean(false)
210        end
211    end)
212
213    registerdirect("mpvar", function()
214        if not expandtex(unknown_value,"mpcategoryparameter",true,scanstring()) then
215            injectnumeric(0)
216        end
217    end)
218
219    -- older:
220
221    registerscript("texvar", function()
222        if not expandtex(unknown_value,"mpcategoryparameter",true,scanstring()) then
223            injectnumeric(0)
224        end
225    end)
226
227    registerscript("texstr", function()
228        if not expandtex(string_value,"mpcategoryparameter",true,scanstring()) then
229            injectstring("")
230        end
231    end)
232
233end
234
235do
236
237    registerscript("textextanchor", function()
238        local x, y = match(scanstring(),"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg
239        if x and y then
240            x = tonumber(x)
241            y = tonumber(y)
242        end
243        injectpair(x or 0,y or 0)
244    end)
245
246end
247
248do
249
250    local mpnamedcolor = attributes.colors.mpnamedcolor
251    local mpprint      = mp.aux.print
252
253    mp.mf_named_color = function(str)
254        mpprint(mpnamedcolor(str))
255    end
256
257    -- todo: we can inject but currently we always get a string back so then
258    -- we need to deal with it upstream in the color module ... not now
259
260    registerscript("namedcolor",function() mpprint(mpnamedcolor(scanstring())) end)
261
262end
263
264do
265
266    local hashes = table.setmetatableindex("table")
267
268    -- There is no need to 'new' a hash in which case one can use any reasonable
269    -- tag. The registry aproach is mkiv compatible.
270
271    local registery = { }
272
273 -- registerdirect("lmt_hash_new", function()
274 --  -- local name  = scanstring()
275 --     local name  = scanwhatever()
276 --     hashes[name] = { }
277 -- end)
278
279    registerdirect("lmt_hash_new", function()
280        for i=1,#registry + 1 do
281            if not registry[i] then
282                registry[i] = { }
283                injectwhatever(i)
284            end
285        end
286    end)
287
288    registerdirect("lmt_hash_dispose", function()
289     -- local name  = scanstring()
290        local name  = scanwhatever()
291        hashes[name] = nil
292        -- when new'd
293        if registry[name] then
294            registry[name] = false
295        end
296    end)
297
298    registerdirect("lmt_hash_in", function()
299     -- local name  = scanstring()
300        local name  = scanwhatever()
301     -- local key   = scanstring()
302        local key   = scanwhatever()
303        local hash  = hashes[name]
304        injectwhatever(hash and hash[key] and true or false)
305    end)
306
307    registerdirect("lmt_hash_to", function()
308     -- local name  = scanstring()
309        local name  = scanwhatever()
310     -- local key   = scanstring()
311        local key   = scanwhatever()
312        local value = scanwhatever()
313        local hash  = hashes[name]
314        if hash then
315            hash[key] = value
316        end
317    end)
318
319    registerdirect("lmt_hash_from", function()
320     -- local name  = scanstring()
321        local name  = scanwhatever()
322     -- local key  = scanstring()
323        local key  = scanwhatever()
324        local hash = hashes[name]
325        injectwhatever(hash and hash[key] or false)
326    end)
327
328    interfaces.implement {
329        name      = "MPfromhash",
330        arguments = "2 strings",
331        actions   = function(name,key)
332            local hash = hashes[name] or hashes[tonumber(name)] or hashes[tostring(name)]
333            if hash then
334                local v = hash[key]
335                if v then
336                    context(v)
337                end
338            end
339        end
340    }
341
342end
343
344do
345
346    local bpfactor     = number.dimenfactors.bp
347    local nbdimensions = nodes.boxes.dimensions
348
349    registerdirect("boxdimensions", function()
350        local category = scanstring()
351        local index    = scanwhatever()
352        injectwhd(nbdimensions(category,index))
353    end)
354
355end
356
357do
358
359    local skiptoken      = skip.token
360
361    local comma_code     = codes.comma
362
363    local getmacro       = tokens.getters.macro
364    local setmacro       = tokens.setters.macro
365
366    local getdimen       = tex.getdimen
367    local getcount       = tex.getcount
368    local gettoks        = tex.gettoks
369    local setdimen       = tex.setdimen
370    local setcount       = tex.setcount
371    local settoks        = tex.settoks
372
373    -- more helpers
374
375    registerdirect("getmacro", function() return getmacro(scanstring()) end)
376    registerdirect("getcount", function() return getcount(scanwhatever()) end)
377    registerdirect("gettoks",  function() return gettoks (scanwhatever()) end)
378    registerdirect("getdimen", function() return getdimen(scanwhatever()) * bpfactor end)
379
380    registerscript("setmacro", function() setmacro(scanstring(),scanstring()) end)
381    registerscript("setdimen", function() setdimen(scanwhatever(),scannumeric()/bpfactor) end)
382    registerscript("setcount", function() setcount(scanwhatever(),scannumeric()) end)
383    registerscript("settoks",  function() settoks (scanwhatever(),scanstring()) end)
384
385    registerscript("setglobalmacro", function() setmacro(scanstring(),scanstring(),"global") end)
386    registerscript("setglobaldimen", function() setdimen("global",scanwhatever(),scannumeric()/bpfactor) end)
387    registerscript("setglobalcount", function() setcount("global",scanwhatever(),scaninteger()) end)
388    registerscript("setglobaltoks",  function() settoks ("global",scanwhatever(),scanstring()) end)
389
390    local utfnum = utf.byte
391    local utflen = utf.len
392    local utfsub = utf.sub
393
394    registerdirect("utfnum", function() return utfnum(scanstring()) end)
395    registerdirect("utflen", function() return utflen(scanstring()) end)
396
397    registerdirect("utfsub", function() -- we have an optional third argument so we explicitly scan a text argument
398        return utfsub(scanstring(),skiptoken(comma_code) and scannumeric(),skiptoken(comma_code) and scannumeric())
399    end)
400
401    local setlogging = metapost.setlogging
402
403    registerscript("message", function()
404        setlogging(false)
405        local str = scanstring()
406        setlogging(true)
407        report_message("message : %s",str)
408    end)
409
410end
411
412-- position fun
413
414do
415
416    local getcount       = tex.getcount
417
418    local mpprint        = mp.print
419    local mpfprint       = mp.fprint
420
421    local mpscaninteger  = mp.scan.integer
422    local mpscannumber   = mp.scan.number
423
424    local jobpositions   = job.positions
425    local getwhd         = jobpositions.whd
426    local getxy          = jobpositions.xy
427    local getx           = jobpositions.x
428    local gety           = jobpositions.y
429    local getposition    = jobpositions.position
430    local getpage        = jobpositions.page
431    local getparagraph   = jobpositions.paragraph
432    local getregion      = jobpositions.region
433    local getcolumn      = jobpositions.column
434    local getmacro       = tokens.getters.macro
435
436    local columnofpos    = jobpositions.columnofpos
437    local getcolumndata  = jobpositions.getcolumndata
438 -- local overlapping    = jobpositions.overlapping
439 -- local onsamepage     = jobpositions.onsamepage
440 -- local columnofpos    = jobpositions.columnofpos
441
442    -- why not inject path directly
443
444    registerscript("positionpath", function()
445        local w, h, d = getwhd(scanstring())
446        if w then
447            mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",0,-d,w,-d,w,h,0,h)
448        else
449            mpprint("(origin--cycle)")
450        end
451    end)
452
453    registerscript("positioncurve", function()
454        local w, h, d = getwhd(scanstring())
455        if w then
456            mpfprint("((%p,%p)..(%p,%p)..(%p,%p)..(%p,%p)..cycle)",0,-d,w,-d,w,h,0,h)
457        else
458            mpprint("(origin--cycle)")
459        end
460    end)
461
462    registerscript("positionbox", function()
463        local p, x, y, w, h, d = getposition(scanstring())
464        if p then
465            mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",x,y-d,x+w,y-d,x+w,y+h,x,y+h)
466        else
467            mpprint("(%p,%p)--cycle",x or 0,y or 0)
468        end
469    end)
470
471    registerscript("positioncolumnbox", function()
472        local column = mpscaninteger()
473        local data   = getcolumndata(getcount("realpageno"),column)
474        if data then
475            local x, y, w, h, d = data.x, data.y, data.w, data.h, data.d
476            mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",x,y-d,x+w,y-d,x+w,y+h,x,y+h)
477        else
478            mpprint("(0,0)--cycle")
479        end
480    end)
481
482    registerscript("overlaycolumnbox", function()
483        local column = mpscaninteger()
484        local data   = getcolumndata(getcount("realpageno"),column)
485        if data then
486            local w, hd = data.w, data.h + data.d
487            mpfprint("((0,0)--(%p,0)--(%p,%p)--(0,%p)--cycle)",w,w,hd,hd)
488        else
489            mpprint("(0,0)--cycle")
490        end
491    end)
492
493    registerdirect("positionpage",      function() return getpage     (scanstring()) or 0 end)
494    registerdirect("positioncolumn",    function() return getcolumn   (scanstring()) or 0 end)
495    registerdirect("positionparagraph", function() return getparagraph(scanstring()) or 0 end)
496    registerdirect("positionregion",    function() return getregion   (scanstring()) or "unknown" end)
497    registerdirect("positionanchor",    function() return getmacro    ("MPanchorid") end)
498    registerdirect("positionwhd",       function() injectwhd(getwhd(scanstring())) end)
499    registerdirect("positionxy",        function() injectxy (getxy (scanstring())) end)
500    registerdirect("positionx",         function() injectpt (getx  (scanstring())) end)
501    registerdirect("positiony",         function() injectpt (gety  (scanstring())) end)
502
503    registerdirect("positioncolumnatx", function()
504        local realpage  = mpscaninteger()
505        local xposition = mpscannumber()
506        return columnofpos(realpage,xposition)
507    end)
508
509end
510
511do
512
513    local modes       = tex.modes
514    local systemmodes = tex.systemmodes
515
516    registerdirect("mode",       function() injectboolean(modes      [scanstring()] and true or false) end)
517    registerdirect("systemmode", function() injectboolean(systemmodes[scanstring()] and true or false) end)
518
519    -- for compatibility reasons we keep this (metafun manual):
520
521    local modes       = tex.modes
522    local systemmodes = tex.systemmodes
523
524    function mp.mode(s)
525        injectboolean(modes[s] and true or false)
526    end
527
528    function mp.systemmode(s)
529        injectboolean(systemmodes[s] and true or false)
530    end
531
532    mp.processingmode = mp.mode
533
534end
535
536-- for alan's nodes:
537
538do
539
540    local lpegmatch, lpegpatterns, P = lpeg.match, lpeg.patterns, lpeg.P
541
542    -- todo: scansuffix / why no return boolean (first one)
543
544    registerdirect("isarray", function()
545         injectboolean(find(scanstring(),"%d") and true or false)
546    end)
547
548    registerdirect("prefix", function()
549        local str = scanstring()
550        return match(str,"^(.-)[%d%[]") or str
551    end)
552
553    local dimension = lpeg.counter(P("[") * lpegpatterns.integer * P("]") + lpegpatterns.integer)
554
555    registerdirect("dimension", function() return dimension(scanstring()) end)
556
557    -- todo : share with mlib-pps.lua metapost,isobject
558
559    -- registerdirect("isobject", function()
560    --     injectboolean(find(scanstring(),"mf_object="))
561    -- end
562
563    local p1      = P("mf_object=")
564    local p2      = lpegpatterns.eol * p1
565    local pattern = (1-p2)^0 * p2 + p1
566
567    registerdirect("isobject", function()
568        local str = scanstring()
569        injectboolean(pattern and str ~= "" and lpegmatch(pattern,str))
570    end)
571
572end
573
574-- key/values (moved here, old mechanism)
575
576do
577
578    local stack, top = { }, nil
579
580    local function setvariable(k,v)
581        if top then
582            top[k] = v
583        else
584            metapost.variables[k] = v
585        end
586    end
587
588    local function pushvariable(k)
589        local t = { }
590        if top then
591            insert(stack,top)
592            top[k] = t
593        else
594            metapost.variables[k] = t
595        end
596        top = t
597    end
598
599    local function popvariable()
600        top = remove(stack)
601    end
602
603    registerscript("passvariable", function() setvariable (scanstring(), scanwhatever()) end)
604    registerscript("pushvariable", function() pushvariable(scanstring()) end)
605    registerscript("popvariable",  function() popvariable () end)
606
607    local stack = { }
608
609    local function pushvariables()
610        insert(stack,metapost.variables)
611        metapost.variables = { }
612    end
613
614    local function popvariables()
615        metapost.variables = remove(stack) or metapost.variables
616    end
617
618    metapost.setvariable   = setvariable
619    metapost.pushvariable  = pushvariable
620    metapost.popvariable   = popvariable
621    metapost.pushvariables = pushvariables
622    metapost.popvariables  = popvariables
623
624    implement {
625        name      = "mppushvariables",
626        actions   = pushvariables,
627    }
628
629    implement {
630        name      = "mppopvariables",
631        actions   = popvariables,
632    }
633
634end
635
636do
637
638    local repeatable = utilities.randomizer.repeatable
639
640    registerdirect("repeatablerandom", function()
641        return repeatable(scanstring())
642    end)
643
644end
645