if not modules then modules = { } end modules ['mlib-mpf'] = { version = 1.001, comment = "companion to mlib-ctx.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files", } -- moved from mlib-lua: local type, tostring, tonumber, select, loadstring = type, tostring, tonumber, select, loadstring local find, match, gsub, gmatch = string.find, string.match, string.gsub, string.gmatch local concat = table.concat local formatters = string.formatters local lpegmatch = lpeg.match local lpegpatterns = lpeg.patterns local P, S, Ct, Cs, Cc, C = lpeg.P, lpeg.S, lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.C local report_luarun = logs.reporter("metapost","lua") local report_script = logs.reporter("metapost","script") local report_message = logs.reporter("metapost") local trace_luarun = false trackers.register("metapost.lua",function(v) trace_luarun = v end) local be_tolerant = true directives.register("metapost.lua.tolerant", function(v) be_tolerant = v end) local set = mp.set local get = mp.get local aux = mp.aux local scan = mp.scan local inject = mp.inject do -- serializers local f_integer = formatters["%i"] local f_numeric = formatters["%F"] -- no %n as that can produce -e notation and that is not so nice for scaled butmaybe we -- should then switch between ... i.e. make a push/pop for the formatters here ... not now. local f_integer = formatters["%i"] local f_numeric = formatters["%F"] local f_pair = formatters["(%F,%F)"] local f_ctrl = formatters["(%F,%F) .. controls (%F,%F) and (%F,%F)"] local f_triplet = formatters["(%F,%F,%F)"] local f_quadruple = formatters["(%F,%F,%F,%F)"] local f_transform = formatters["totransform(%F,%F,%F,%F,%F,%F)"] local f_pen = formatters["(pencircle transformed totransform(%F,%F,%F,%F,%F,%F))"] local f_points = formatters["%p"] local f_pair_pt = formatters["(%p,%p)"] local f_ctrl_pt = formatters["(%p,%p) .. controls (%p,%p) and (%p,%p)"] local f_triplet_pt = formatters["(%p,%p,%p)"] local f_quadruple_pt = formatters["(%p,%p,%p,%p)"] local r = P('%') / "percent" + P('"') / "dquote" + P('\n') / "crlf" -- + P(' ') / "space" local a = Cc("&") local q = Cc('"') local p = Cs(q * (r * a)^-1 * (a * r * (P(-1) + a) + P(1))^0 * q) mp.cleaned = function(s) return lpegmatch(p,s) or s end -- management -- sometimes we gain (e.g. .5 sec on the sync test) local cache = table.makeweak() local runscripts = { } local runnames = { } local nofscripts = 0 function metapost.registerscript(name,f) nofscripts = nofscripts + 1 if f then runscripts[nofscripts] = f runnames[name] = nofscripts else runscripts[nofscripts] = name end return nofscripts end function metapost.scriptindex(name) return runnames[name] or 0 end -- The gbuffer sharing and such is not really needed now but make a dent when -- we have a high volume of simpel calls (loops) so we keep it around for a -- while. local nesting = 0 local runs = 0 local gbuffer = { } local buffer = gbuffer local n = 0 local function mpdirect1(a) n = n + 1 buffer[n] = a end local function mpdirect2(a,b) n = n + 1 buffer[n] = a n = n + 1 buffer[n] = b end local function mpdirect3(a,b,c) n = n + 1 buffer[n] = a n = n + 1 buffer[n] = b n = n + 1 buffer[n] = c end local function mpdirect4(a,b,c,d) n = n + 1 buffer[n] = a n = n + 1 buffer[n] = b n = n + 1 buffer[n] = c n = n + 1 buffer[n] = d end local function mpdirect5(a,b,c,d,e) n = n + 1 buffer[n] = a n = n + 1 buffer[n] = b n = n + 1 buffer[n] = c n = n + 1 buffer[n] = d n = n + 1 buffer[n] = e end local function mpflush(separator) buffer[1] = concat(buffer,separator or "",1,n) n = 1 end function metapost.getbuffer() local b = { } for i=1,n do b[i] = buffer end return b, n end function metapost.setbuffer(b, s) n = 0 for i=1,(s or #b) do local bi = b[i] if bi then n = n + 1 buffer[n] = tostring(bi) end end end function metapost.runscript(code) nesting = nesting + 1 runs = runs + 1 local index = type(code) == "number" local f local result if index then f = runscripts[code] if not f then report_luarun("%i: bad index: %s",nesting,code) elseif trace_luarun then report_luarun("%i: index: %i",nesting,code) end else if trace_luarun then report_luarun("%i: code: %s",nesting,code) end f = cache[code] if not f then f = loadstring("return " .. code) if f then cache[code] = f elseif be_tolerant then f = loadstring(code) if f then cache[code] = f end end end end -- returning nil is more efficient and a signal not to scan in mp if f then local lbuffer, ln if nesting == 1 then buffer = gbuffer n = 0 else lbuffer = buffer ln = n buffer = { } n = 0 end result = f() if result then local t = type(result) if t == "number" then result = f_numeric(result) elseif t == "table" then result = concat(result) -- no spaces here else result = tostring(result) end if trace_luarun then report_luarun("%i: %s result: %s",nesting,t,result) end elseif n == 0 then -- result = "" result = nil -- no scantokens done then if trace_luarun then report_luarun("%i: no buffered result",nesting) end elseif n == 1 then result = buffer[1] if trace_luarun then report_luarun("%i: 1 buffered result: %s",nesting,result) end else -- the space is why we sometimes have collectors if nesting == 1 then -- if we had no space we could pass result directly in lmtx result = concat(buffer," ",1,n) if n > 500 or #result > 10000 then gbuffer = { } -- newtable(20,0) lbuffer = gbuffer end else -- if we had no space we could pass result directly in lmtx result = concat(buffer," ") end if trace_luarun then report_luarun("%i: %i buffered results: %s",nesting,n,result) end end if nesting == 1 then n = 0 else buffer = lbuffer n = ln end else report_luarun("%i: no result, invalid code: %s",nesting,code) result = "" end nesting = nesting - 1 return result end function metapost.nofscriptruns() return runs end -- writers local function rawmpp(value) n = n + 1 local t = type(value) if t == "number" then buffer[n] = f_numeric(value) elseif t == "string" then buffer[n] = value elseif t == "table" then if #t == 6 then buffer[n] = "totransform(" .. concat(value,",") .. ")" else buffer[n] = "(" .. concat(value,",") .. ")" end else -- boolean or whatever buffer[n] = tostring(value) end end local function mpprint(first,second,...) if second == nil then if first ~= nil then rawmpp(first) end else for i=1,select("#",first,second,...) do local value = (select(i,first,second,...)) if value ~= nil then rawmpp(value) end end end end local function mpp(value) n = n + 1 local t = type(value) if t == "number" then buffer[n] = f_numeric(value) elseif t == "string" then buffer[n] = lpegmatch(p,value) elseif t == "table" then if #t > 4 then buffer[n] = "" else buffer[n] = "(" .. concat(value,",") .. ")" end else -- boolean or whatever buffer[n] = tostring(value) end end local function mpvprint(first,second,...) -- variable print if second == nil then if first ~= nil then mpp(first) end else for i=1,select("#",first,second,...) do local value = (select(i,first,second,...)) if value ~= nil then mpp(value) end end end end local function mpstring(value) n = n + 1 buffer[n] = lpegmatch(p,value) end local function mpboolean(b) n = n + 1 buffer[n] = b and "true" or "false" end local function mpnumeric(f) n = n + 1 if not f or f == 0 then buffer[n] = "0" else buffer[n] = f_numeric(f) end end local function mpinteger(i) n = n + 1 -- buffer[n] = i and f_integer(i) or "0" buffer[n] = i or "0" end local function mppoints(i) n = n + 1 if not i or i == 0 then buffer[n] = "0pt" else buffer[n] = f_points(i) end end local function mppair(x,y) n = n + 1 if type(x) == "table" then buffer[n] = f_pair(x[1],x[2]) else buffer[n] = f_pair(x,y) end end local function mppairpoints(x,y) n = n + 1 if type(x) == "table" then buffer[n] = f_pair_pt(x[1],x[2]) else buffer[n] = f_pair_pt(x,y) end end local function mptriplet(x,y,z) n = n + 1 if type(x) == "table" then buffer[n] = f_triplet(x[1],x[2],x[3]) else buffer[n] = f_triplet(x,y,z) end end local function mptripletpoints(x,y,z) n = n + 1 if type(x) == "table" then buffer[n] = f_triplet_pt(x[1],x[2],x[3]) else buffer[n] = f_triplet_pt(x,y,z) end end local function mpquadruple(w,x,y,z) n = n + 1 if type(w) == "table" then buffer[n] = f_quadruple(w[1],w[2],w[3],w[4]) else buffer[n] = f_quadruple(w,x,y,z) end end local function mpquadruplepoints(w,x,y,z) n = n + 1 if type(w) == "table" then buffer[n] = f_quadruple_pt(w[1],w[2],w[3],w[4]) else buffer[n] = f_quadruple_pt(w,x,y,z) end end local function mptransform(x,y,xx,xy,yx,yy) n = n + 1 if type(x) == "table" then buffer[n] = f_transform(x[1],x[2],x[3],x[4],x[5],x[6]) else buffer[n] = f_transform(x,y,xx,xy,yx,yy) end end local function mpcolor(c,m,y,k) n = n + 1 if type(c) == "table" then local l = #c if l == 4 then buffer[n] = f_quadruple(c[1],c[2],c[3],c[4]) elseif l == 3 then buffer[n] = f_triplet(c[1],c[2],c[3]) else buffer[n] = f_numeric(c[1]) end else if k then buffer[n] = f_quadruple(c,m,y,k) elseif y then buffer[n] = f_triplet(c,m,y) else buffer[n] = f_numeric(c) end end end -- we have three kind of connectors: -- -- .. ... -- (true) local function mp_path(f2,f6,t,connector,cycle) if type(t) == "table" then local tn = #t if tn == 1 then local t1 = t[1] n = n + 1 if t.pen then buffer[n] = f_pen(unpack(t1)) else buffer[n] = f2(t1[1],t1[2]) end elseif tn > 0 then if connector == true or connector == nil then connector = ".." elseif connector == false then connector = "--" end if cycle == nil then cycle = t.cycle if cycle == nil then cycle = true end end local six = connector == ".." -- otherwise we use whatever gets asked for local controls = connector -- whatever local a = t[1] local b = t[2] n = n + 1 buffer[n] = "(" n = n + 1 if six and #a == 6 and #b == 6 then buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4]) controls = ".." else buffer[n] = f2(a[1],a[2]) controls = connector end for i=2,tn-1 do a = b b = t[i+1] n = n + 1 buffer[n] = connector n = n + 1 if six and #a == 6 and #b == 6 then buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4]) controls = ".." else buffer[n] = f2(a[1],a[2]) controls = connector end end n = n + 1 buffer[n] = connector a = b b = t[1] n = n + 1 if cycle then if six and #a == 6 and #b == 6 then buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4]) controls = ".." else buffer[n] = f2(a[1],a[2]) controls = connector end n = n + 1 buffer[n] = connector n = n + 1 buffer[n] = "cycle" else buffer[n] = f2(a[1],a[2]) end n = n + 1 buffer[n] = ")" end end end local function mppath(...) mp_path(f_pair,f_ctrl,...) end local function mppathpoints(...) mp_path(f_pair_pt,f_ctrl_pt,...) end local function mpsize(t) n = n + 1 buffer[n] = type(t) == "table" and f_numeric(#t) or "0" end local replacer = lpeg.replacer("@","%%") local function mpfprint(fmt,...) n = n + 1 if not find(fmt,"%",1,true) then fmt = lpegmatch(replacer,fmt) end buffer[n] = formatters[fmt](...) end local function mpquoted(fmt,s,...) if s then n = n + 1 if not find(fmt,"%",1,true) then fmt = lpegmatch(replacer,fmt) end -- buffer[n] = '"' .. formatters[fmt](s,...) .. '"' buffer[n] = lpegmatch(p,formatters[fmt](s,...)) elseif fmt then n = n + 1 -- buffer[n] = '"' .. fmt .. '"' buffer[n] = lpegmatch(p,fmt) else -- something is wrong end end aux.direct = mpdirect1 aux.direct1 = mpdirect1 aux.direct2 = mpdirect2 aux.direct3 = mpdirect3 aux.direct4 = mpdirect4 aux.flush = mpflush aux.print = mpprint aux.vprint = mpvprint aux.boolean = mpboolean aux.string = mpstring aux.numeric = mpnumeric aux.number = mpnumeric aux.integer = mpinteger aux.points = mppoints aux.pair = mppair aux.pairpoints = mppairpoints aux.triplet = mptriplet aux.tripletpoints = mptripletpoints aux.quadruple = mpquadruple aux.quadruplepoints = mpquadruplepoints aux.path = mppath aux.pathpoints = mppathpoints aux.size = mpsize aux.fprint = mpfprint aux.quoted = mpquoted aux.transform = mptransform aux.color = mpcolor -- for the moment local function mpdraw(lines,list) -- n * 4 if list then local c = #lines for i=1,c do local ci = lines[i] local ni = #ci n = n + 1 buffer[n] = i < c and "d(" or "D(" for j=1,ni,2 do local l = j + 1 n = n + 1 buffer[n] = ci[j] n = n + 1 buffer[n] = "," n = n + 1 buffer[n] = ci[l] n = n + 1 buffer[n] = l < ni and ")--(" or ");" end end else local l = #lines local m = l - 4 for i=1,l,4 do n = n + 1 buffer[n] = i < m and "d(" or "D(" n = n + 1 buffer[n] = lines[i] n = n + 1 buffer[n] = "," n = n + 1 buffer[n] = lines[i+1] n = n + 1 buffer[n] = ")--(" n = n + 1 buffer[n] = lines[i+2] n = n + 1 buffer[n] = "," n = n + 1 buffer[n] = lines[i+3] n = n + 1 buffer[n] = ");" end end end local function mpfill(lines,list) if list then local c = #lines for i=1,c do local ci = lines[i] local ni = #ci n = n + 1 buffer[n] = i < c and "f(" or "F(" for j=1,ni,2 do local l = j + 1 n = n + 1 buffer[n] = ci[j] n = n + 1 buffer[n] = "," n = n + 1 buffer[n] = ci[l] n = n + 1 buffer[n] = l < ni and ")--(" or ")--C;" end end else local l = #lines local m = l - 4 for i=1,l,4 do n = n + 1 buffer[n] = i < m and "f(" or "F(" n = n + 1 buffer[n] = lines[i] n = n + 1 buffer[n] = "," n = n + 1 buffer[n] = lines[i+1] n = n + 1 buffer[n] = ")--(" n = n + 1 buffer[n] = lines[i+2] n = n + 1 buffer[n] = "," n = n + 1 buffer[n] = lines[i+3] n = n + 1 buffer[n] = ")--C;" end end end aux.draw = mpdraw aux.fill = mpfill for k, v in next, aux do mp[k] = v end -- mp.print = table.setmetatablecall(aux, function(t,...) -- mpprint(...) -- end) mp.print = table.setmetatablecall(aux, function(t,first,second,...) if second == nil then if first ~= nil then rawmpp(first) end else for i=1,select("#",first,second,...) do local value = (select(i,first,second,...)) if value ~= nil then rawmpp(value) end end end end) end do -- Another experimental feature: local mpnumeric = mp.numeric local scanstring = scan.string local scriptindex = metapost.scriptindex function mp.mf_script_index(name) local index = scriptindex(name) -- report_script("method %i, name %a, index %i",1,name,index) mpnumeric(index) end -- once bootstrapped ... (needs pushed mpx instances) metapost.registerscript("scriptindex",function() local name = scanstring() local index = scriptindex(name) -- report_script("method %i, name %a, index %i",2,name,index) mpnumeric(index) end) end -- the next will move to mlib-lmp.lua do local mpnamedcolor = attributes.colors.mpnamedcolor local mpprint = aux.print local scanstring = scan.string mp.mf_named_color = function(str) mpprint(mpnamedcolor(str)) end -- todo: we can inject but currently we always get a string back so then -- we need to deal with it upstream in the color module ... not now metapost.registerscript("namedcolor",function() mpprint(mpnamedcolor(scanstring())) end) end function mp.n(t) -- used ? return type(t) == "table" and #t or 0 end do -- experiment: names can change local mppath = aux.path local mpsize = aux.size local whitespace = lpegpatterns.whitespace local newline = lpegpatterns.newline local setsep = newline^2 local comment = (S("#%") + P("--")) * (1-newline)^0 * (whitespace - setsep)^0 local value = (1-whitespace)^1 / tonumber local entry = Ct( value * whitespace * value) local set = Ct((entry * (whitespace-setsep)^0 * comment^0)^1) local series = Ct((set * whitespace^0)^1) local pattern = whitespace^0 * series local datasets = { } mp.datasets = datasets function mp.dataset(str) return lpegmatch(pattern,str) end function datasets.load(tag,filename) if not filename then tag, filename = file.basename(tag), tag end local data = lpegmatch(pattern,io.loaddata(filename) or "") datasets[tag] = { data = data, line = function(n) mppath(data[n or 1]) end, size = function() mpsize(data) end, } end table.setmetatablecall(datasets,function(t,k,f,...) local d = datasets[k] local t = type(d) if t == "table" then d = d[f] if type(d) == "function" then d(...) else mpvprint(...) end elseif t == "function" then d(f,...) end end) end -- \startluacode -- local str = [[ -- 10 20 20 20 -- 30 40 40 60 -- 50 10 -- -- 10 10 20 30 -- 30 50 40 50 -- 50 20 -- the last one -- -- 10 20 % comment -- 20 10 -- 30 40 # comment -- 40 20 -- 50 10 -- ]] -- -- MP.myset = mp.dataset(str) -- -- inspect(MP.myset) -- \stopluacode -- -- \startMPpage -- color c[] ; c[1] := red ; c[2] := green ; c[3] := blue ; -- for i=1 upto lua("mp.print(mp.n(MP.myset))") : -- draw lua("mp.path(MP.myset[" & decimal i & "])") withcolor c[i] ; -- endfor ; -- \stopMPpage -- texts: do local mptriplet = mp.triplet local bpfactor = number.dimenfactors.bp local textexts = nil local mptriplet = mp.triplet local nbdimensions = nodes.boxes.dimensions function mp.mf_tt_initialize(tt) textexts = tt end function mp.mf_tt_dimensions(n) local box = textexts and textexts[n] if box then -- could be made faster with nuts but not critical mptriplet(box.width*bpfactor,box.height*bpfactor,box.depth*bpfactor) else mptriplet(0,0,0) end end function mp.mf_tb_dimensions(category,name) local w, h, d = nbdimensions(category,name) mptriplet(w*bpfactor,h*bpfactor,d*bpfactor) end function mp.report(a,b,c,...) if c then report_message("%s : %s",a,formatters[(gsub(b,"@","%%"))](c,...)) elseif b then report_message("%s : %s",a,b) elseif a then report_message("%s : %s","message",a) end end end do local mpprint = aux.print local modes = tex.modes local systemmodes = tex.systemmodes function mp.mode(s) mpprint(modes[s] and true or false) end function mp.systemmode(s) mpprint(systemmodes[s] and true or false) end mp.processingmode = mp.mode end -- for alan's nodes: do local mpprint = aux.print local mpquoted = aux.quoted function mp.isarray(str) mpprint(find(str,"%d") and true or false) end function mp.prefix(str) mpquoted(match(str,"^(.-)[%d%[]") or str) end -- function mp.dimension(str) -- local n = 0 -- for s in gmatch(str,"%[?%-?%d+%]?") do --todo: lpeg -- n = n + 1 -- end -- mpprint(n) -- end mp.dimension = lpeg.counter(P("[") * lpegpatterns.integer * P("]") + lpegpatterns.integer,mpprint) -- faster and okay as we don't have many variables but probably only -- basename makes sense and even then it's not called that often -- local hash = table.setmetatableindex(function(t,k) -- local v = find(k,"%d") and true or false -- t[k] = v -- return v -- end) -- -- function mp.isarray(str) -- mpprint(hash[str]) -- end -- -- local hash = table.setmetatableindex(function(t,k) -- local v = '"' .. (match(k,"^(.-)%d") or k) .. '"' -- t[k] = v -- return v -- end) -- -- function mp.prefix(str) -- mpprint(hash[str]) -- end end do local getmacro = tokens.getters.macro local setmacro = tokens.setters.macro local getdimen = tex.getdimen local getcount = tex.getcount local gettoks = tex.gettoks local setdimen = tex.setdimen local setcount = tex.setcount local settoks = tex.settoks local mpprint = mp.print local mpquoted = mp.quoted local bpfactor = number.dimenfactors.bp -- more helpers function mp.getmacro(k) mpquoted(getmacro(k)) end function mp.getdimen(k) mpprint (getdimen(k)*bpfactor) end function mp.getcount(k) mpprint (getcount(k)) end function mp.gettoks (k) mpquoted(gettoks (k)) end function mp.setmacro(k,v) setmacro(k,v) end function mp.setdimen(k,v) setdimen(k,v/bpfactor) end function mp.setcount(k,v) setcount(k,v) end function mp.settoks (k,v) settoks (k,v) end function mp.setglobalmacro(k,v) setmacro(k,v,"global") end function mp.setglobaldimen(k,v) setdimen("global",k,v/bpfactor) end function mp.setglobalcount(k,v) setcount("global",k,v) end function mp.setglobaltoks (k,v) settoks ("global",k,v) end -- def foo = lua.mp.foo ... enddef ; % loops due to foo in suffix mp._get_macro_ = mp.getmacro mp._get_dimen_ = mp.getdimen mp._get_count_ = mp.getcount mp._get_toks_ = mp.gettoks mp._set_macro_ = mp.setmacro mp._set_dimen_ = mp.setdimen mp._set_count_ = mp.setcount mp._set_toks_ = mp.settoks mp._set_global_macro_ = mp.setglobalmacro mp._set_global_dimen_ = mp.setglobaldimen mp._set_global_count_ = mp.setglobalcount mp._set_global_toks_ = mp.setglobaltoks end -- position fun do local mpprint = mp.print local mpfprint = mp.fprint local mpquoted = mp.quoted local jobpositions = job.positions local getwhd = jobpositions.whd local getxy = jobpositions.xy local getposition = jobpositions.position local getpage = jobpositions.page local getregion = jobpositions.region local getmacro = tokens.getters.macro function mp.positionpath(name) local w, h, d = getwhd(name) if w then mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",0,-d,w,-d,w,h,0,h) else mpprint("(origin--cycle)") end end function mp.positioncurve(name) local w, h, d = getwhd(name) if w then mpfprint("((%p,%p)..(%p,%p)..(%p,%p)..(%p,%p)..cycle)",0,-d,w,-d,w,h,0,h) else mpprint("(origin--cycle)") end end function mp.positionbox(name) local p, x, y, w, h, d = getposition(name) if p then mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",x,y-d,x+w,y-d,x+w,y+h,x,y+h) else mpprint("(%p,%p)",x,y) end end function mp.positionxy(name) local x, y = getxy(name) if x then mpfprint("(%p,%p)",x,y) else mpprint("origin") end end function mp.positionpage(name) mpfprint("%i",getpage(name) or 0) end function mp.positionregion(name) local r = getregion(name) if r then mpquoted(r) else mpquoted("unknown") end end function mp.positionwhd(name) local w, h, d = getwhd(name) if w then mpfprint("(%p,%p,%p)",w,h,d) else mpprint("(0,0,0)") end end function mp.positionpxy(name) local p, x, y = getposition(name) if p then mpfprint("(%p,%p,%p)",p,x,y) else mpprint("(0,0,0)") end end function mp.positionanchor() mpquoted(getmacro("MPanchorid")) end end do local mppair = mp.pair function mp.textextanchor(s) local x, y = match(s,"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg if x and y then x = tonumber(x) y = tonumber(y) end mppair(x or 0,y or 0) end end do local mpprint = mp.print local mpquoted = mp.quoted local getmacro = tokens.getters.macro function mp.texvar(name) mpprint(getmacro(metapost.namespace .. name)) end function mp.texstr(name) mpquoted(getmacro(metapost.namespace .. name)) end end do local mpprint = aux.print local mpvprint = aux.vprint local hashes = { } function mp.newhash(name) if name then hashes[name] = { } else for i=1,#hashes+1 do if not hashes[i] then hashes[i] = { } mpvprint(i) return end end end end function mp.disposehash(n) if tonumber(n) then hashes[n] = false else hashes[n] = nil end end function mp.inhash(n,key) local h = hashes[n] mpvprint(h and h[key] and true or false) end function mp.tohash(n,key,value) local h = hashes[n] if h then if value == nil then h[key] = true else h[key] = value end end end function mp.fromhash(n,key) local h = hashes[n] mpvprint(h and h[key] or false) end interfaces.implement { name = "MPfromhash", arguments = "2 strings", actions = function(name,key) local h = hashes[name] or hashes[tonumber(name)] if h then local v = h[key] or h[tonumber(key)] if v then context(v) end end end } end do -- a bit overkill: just a find(str,"mf_object=") can be enough -- -- todo : share with mlib-pps.lua metapost,isobject local mpboolean = aux.boolean local p1 = P("mf_object=") local p2 = lpegpatterns.eol * p1 local pattern = (1-p2)^0 * p2 + p1 function mp.isobject(str) mpboolean(pattern and str ~= "" and lpegmatch(pattern,str)) end end function mp.flatten(t) local tn = #t local t1 = t[1] local t2 = t[2] local t3 = t[3] local t4 = t[4] for i=1,tn-5,2 do local t5 = t[i+4] local t6 = t[i+5] if t1 == t3 and t3 == t5 and ((t2 <= t4 and t4 <= t6) or (t6 <= t4 and t4 <= t2)) then t[i+3] = t2 t4 = t2 t[i] = false t[i+1] = false elseif t2 == t4 and t4 == t6 and ((t1 <= t3 and t3 <= t5) or (t5 <= t3 and t3 <= t1)) then t[i+2] = t1 t3 = t1 t[i] = false t[i+1] = false end t1 = t3 t2 = t4 t3 = t5 t4 = t6 end -- remove duplicates local t1 = t[1] local t2 = t[2] for i=1,tn-2,2 do local t3 = t[i+2] local t4 = t[i+3] if t1 == t3 and t2 == t4 then t[i] = false t[i+1] = false end t1 = t3 t2 = t4 end -- move coordinates local m = 0 for i=1,tn,2 do if t[i] then m = m + 1 t[m] = t[i] m = m + 1 t[m] = t[i+1] end end -- prune the table (not gc'd) for i=tn,m+1,-1 do t[i] = nil end -- safeguard so that we have at least one segment if m == 2 then t[3] = t[1] t[4] = t[2] end end do -- if needed we can optimize the sub (cache last split) local mpnumeric = mp.numeric local mpquoted = mp.quoted local utflen = utf.len local utfsub = utf.sub function mp.utflen(s) mpnumeric(utflen(s)) end function mp.utfsub(s,f,t) mpquoted(utfsub(s,f,t)) end end