summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/mlib-cnt.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/mlib-cnt.lmt')
-rw-r--r--tex/context/base/mkiv/mlib-cnt.lmt2022
1 files changed, 2022 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/mlib-cnt.lmt b/tex/context/base/mkiv/mlib-cnt.lmt
new file mode 100644
index 000000000..667384ed7
--- /dev/null
+++ b/tex/context/base/mkiv/mlib-cnt.lmt
@@ -0,0 +1,2022 @@
+if not modules then modules = { } end modules ['mlib-cnt'] = {
+ version = 1.001,
+ optimize = true,
+ 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",
+}
+
+-- The only useful reference that I could find about this topic is in wikipedia:
+--
+-- https://en.wikipedia.org/wiki/Marching_squares
+--
+-- I derived the edge code from:
+--
+-- https://physiology.arizona.edu/people/secomb/contours
+--
+-- and also here:
+--
+-- https://github.com/secomb/GreensV4
+--
+-- which has the banner:
+--
+-- TWS, November 1989. Converted to C September 2007. Revised February 2009.
+--
+-- and has a liberal licence. I optimized the code so that it runs a bit faster in Lua and
+-- there's probably more room for optimization (I tried several variants). For instance I
+-- don't use that many tables because access is costly. We don't have a compiler that does
+-- some optimizing (even then the c code can probably be made more efficient).
+--
+-- We often have the same node so it's cheaper to reuse the wsp tables and reconstruct
+-- the point in the path then to alias the original point. We can be more clever:
+-- straighten, but it's more work so maybe I will do that later; for now I only added
+-- a test for equality. There are some experiments in this file too and not all might
+-- work. It's a playground for me.
+--
+-- The code is meant for metafun so it is not general purpose. The bitmap variant is
+-- relatively fast and bitmaps compress well. When all is settled I might make a couple
+-- of helpers in C for this purpose but not now.
+--
+-- I removed optimization code (more aggressive flattening and such because it didn't really
+-- pay off now that we use combined paths with just line segments. I also moved some other
+-- code to a experimental copy. So we now only have the bare helpers needed here.
+
+-- Todo: look into zero case (lavel 1) for shapes ... omiting the background calculation
+-- speeds up quite a bit.
+
+local next, type, tostring = next, type, tostring
+local round, abs, min, max, floor = math.round, math.abs, math.min, math.max, math.floor
+local concat, move = table.concat, table.move
+
+local bor = bit32.bor -- it's really time to ditch support for luajit
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local elapsedtime = statistics.elapsedtime
+
+local formatters = string.formatters
+local setmetatableindex = table.setmetatableindex
+local sortedkeys = table.sortedkeys
+local sequenced = table.sequenced
+
+local metapost = metapost or { }
+local mp = mp or { }
+
+local getparameterset = metapost.getparameterset
+
+local mpflush = mp.flush
+local mpcolor = mp.color
+local mpstring = mp.string
+local mpdraw = mp.draw
+local mpfill = mp.fill
+local mpflatten = mp.flatten
+
+local report = logs.reporter("mfun contour")
+
+local version = 0.007
+
+-- we iterate using integers so that we get a better behaviour at zero
+
+local f_function_n = formatters [ [[
+ local math = math
+ local round = math.round
+ %s
+ return function(data,nx,ny,nxmin,nxmax,xstep,nymin,nymax,ystep)
+ local sx = nxmin
+ for mx=1,nx do
+ local dx = data[mx]
+ local x = sx * xstep
+ local sy = nymin
+ for my=1,ny do
+ local y = sy * ystep
+ dx[my] = %s
+ sy = sy + 1
+ end
+ sx = sx + 1
+ end
+ return 0
+ end
+]] ]
+
+local f_function_y = formatters [ [[
+ local math = math
+ local round = math.round
+ local nan = NaN
+ local inf = math.huge
+ %s
+ return function(data,nx,ny,nxmin,nxmax,xstep,nymin,nymax,ystep,dnan,dinf,report)
+ local sx = nxmin
+ local er = 0
+ for mx=nxmin,nxmax do
+ local dx = data[mx]
+ local x = sx * xstep
+ local sy = nymin
+ for my=nymin,nymax do
+ local y = sy * ystep
+ local n = %s
+ if n == nan then
+ er = er + 1
+ if er < 10 then
+ report("nan at (%s,%s)",x,y)
+ end
+ n = dnan
+ elseif n == inf then
+ er = er + 1
+ if er < 10 then
+ report("inf at (%s,%s)",x,y)
+ end
+ n = dinf
+ end
+ dx[my] = n
+ sy = sy + 1
+ end
+ sx = sx + 1
+ end
+ return er
+ end
+]] ]
+
+local f_color = formatters [ [[
+ local math = math
+ local min = math.min
+ local max = math.max
+ local n = %s
+ local minz = %s
+ local maxz = %s
+
+ local color_value = 0
+ local color_step = mp.lmt_color_functions.step
+ local color_shade = mp.lmt_color_functions.shade
+
+ local function step(...)
+ return color_step(color_value,n,...)
+ end
+ local function shade(...)
+ return color_shade(color_value,n,...)
+ end
+ local function lin(l)
+ return l/n
+ end
+ %s
+ return function(l)
+ color_value = l
+ return %s
+ end
+]] ]
+
+local inbetween = attributes and attributes.colors.helpers.inbetween
+
+mp.lmt_color_functions = {
+ step = function(l,n,r,g,b,s)
+ if not s then
+ s = 1
+ end
+ local f = l / n
+ local r = s * 1.5 - 4 * abs(f-r)
+ local g = s * 1.5 - 4 * abs(f-g)
+ local b = s * 1.5 - 4 * abs(f-b)
+ return min(max(r,0),1), min(max(g,0),1), min(max(b,0),1)
+ end,
+ shade = function(l,n,one,two)
+ local f = l / n
+ local r = inbetween(one,two,1,f)
+ local g = inbetween(one,two,2,f)
+ local b = inbetween(one,two,3,f)
+ return min(max(r,0),1), min(max(g,0),1), min(max(b,0),1)
+ end,
+}
+
+local f_box = formatters [ [[draw rawtexbox("contour",%s) xysized (%s,%s) ;]] ]
+
+local n_box = 0
+
+-- todo: remove old one, so we need to store old hashes
+
+local nofcontours = 0
+
+-- We don't want cosmetics like axis and labels to trigger a calculation,
+-- especially a slow one.
+
+local hashfields = {
+ "xmin", "xmax", "xstep", "ymin", "ymax", "ystep",
+ "levels", "colors", "level", "preamble", "function", "functions", "color", "values",
+ "background", "foreground", "linewidth", "backgroundcolor", "linecolor",
+}
+
+local function prepare(p)
+ local h = { }
+ for i=1,#hashfields do
+ local f = hashfields[i]
+ h[f] = p[f]
+ end
+ local hash = md5.HEX(sequenced(h))
+ local name = formatters["%s-m-c-%03i.lua"](tex.jobname,nofcontours)
+ return name, hash
+end
+
+local function getcache(p)
+ local cache = p.cache
+ if cache then
+ local name, hash = prepare(p)
+ local data = table.load(name)
+ if data and data.hash == hash and data.version == version then
+ p.result = data
+ return true
+ else
+ return false, hash, name
+ end
+ end
+ return false, nil, nil
+end
+
+local function setcache(p)
+ local result = p.result
+ local name = result.name
+ if name then
+ if result.bitmap then
+ result.bitmap = nil
+ else
+ result.data = nil
+ end
+ result.color = nil
+ result.action = nil
+ result.cached = nil
+ io.savedata(name, table.fastserialize(result))
+ else
+ os.remove((prepare(p)))
+ end
+end
+
+function mp.lmt_contours_start()
+
+ starttiming("lmt_contours")
+
+ nofcontours = nofcontours + 1
+
+ local p = getparameterset()
+
+ local xmin = p.xmin
+ local xmax = p.xmax
+ local ymin = p.ymin
+ local ymax = p.ymax
+ local xstep = p.xstep
+ local ystep = p.ystep
+ local levels = p.levels
+ local colors = p.colors
+ local nx = 0
+ local ny = 0
+ local means = setmetatableindex("number")
+ local values = setmetatableindex("number")
+ local data = setmetatableindex("table")
+ local minmean = false
+ local maxmean = false
+
+ local cached, hash, name = getcache(p)
+
+ local function setcolors(preamble,levels,minz,maxz,color,nofvalues)
+ if colors then
+ local function f(k)
+ return #colors[1] == 1 and 0 or { 0, 0, 0 }
+ end
+ setmetatableindex(colors, function(t,k)
+ local v = f(k)
+ t[k] = v
+ return v
+ end)
+ local c = { }
+ local n = 1
+ for i=1,nofvalues do
+ c[i] = colors[n]
+ n = n + 1
+ end
+ return c, f
+ else
+ local tcolor = f_color(levels,minz,maxz,preamble,color)
+ local colors = { }
+ local fcolor = load(tcolor)
+ if type(fcolor) ~= "function" then
+ report("error in color function, case %i: %s",1,color)
+ fcolor = false
+ else
+ fcolor = fcolor()
+ if type(fcolor) ~= "function" then
+ report("error in color function, case %i: %s",2,color)
+ fcolor = false
+ end
+ end
+ if not fcolor then
+ local color_step = mp.lmt_color_functions.step
+ fcolor = function(l)
+ return color_step(l,levels,0.25,0.50,0.75)
+ end
+ end
+ for i=1,nofvalues do
+ colors[i] = { fcolor(i) }
+ end
+ if attributes.colors.model == "cmyk" then
+ for i=1,#colors do
+ local c = colors[i]
+ colors[i] = { 1 - c[1], 1 - c[2], 1 - c[3], 0 }
+ end
+ end
+ return colors, fcolor
+ end
+ end
+
+ if cached then
+ local result = p.result
+ local colors, color = setcolors(
+ p.preamble,
+ p.levels,
+ result.minz,
+ result.maxz,
+ p.color,
+ result.nofvalues
+ )
+ result.color = color
+ result.colors = colors
+ result.cached = true
+ return
+ end
+
+ local functioncode = p["function"]
+ local functionrange = p.range
+ local functionlist = functionrange and p.functions
+ local preamble = p.preamble
+
+ if xstep == 0 then xstep = (xmax - xmin)/100 end
+ if ystep == 0 then ystep = (ymax - ymin)/100 end
+
+ local nxmin = round(xmin/xstep)
+ local nxmax = round(xmax/xstep)
+ local nymin = round(ymin/ystep)
+ local nymax = round(ymax/ystep)
+ local nx = nxmax - nxmin + 1
+ local ny = nymax - nymin + 1
+
+ local function executed(data,code)
+ local fcode = p.check and f_function_y or f_function_n
+ fcode = fcode(preamble,code)
+ fcode = load(fcode)
+ if type(fcode) == "function" then
+ fcode = fcode()
+ end
+ if type(fcode) == "function" then
+ local er = fcode(
+ data, nx, ny,
+ nxmin, nxmax, xstep,
+ nymin, nymax, ystep,
+ p.defaultnan, p.defaultinf, report
+ )
+ if er > 0 then
+ report("%i errors in: %s",er,code)
+ end
+ return true
+ else
+ return false -- fatal error
+ end
+ end
+
+ -- for i=1,nx do
+ -- data[i] = lua.newtable(ny,0)
+ -- end
+
+ if functionlist then
+
+ local datalist = { }
+ local minr = functionrange[1]
+ local maxr = functionrange[2] or minr
+ local size = #functionlist
+
+ for l=1,size do
+
+ local func = setmetatableindex("table")
+ if not executed(func,functionlist[l]) then
+ report("error in executing function %i from functionlist",l)
+ return
+ end
+
+ local bit = l -- + 1
+
+ if l == 1 then
+ for i=1,nx do
+ local di = data[i]
+ local fi = func[i]
+ for j=1,ny do
+ local f = fi[j]
+ if f >= minr and f <= maxr then
+ di[j] = bit
+ else
+ di[j] = 0
+ end
+ end
+ end
+ else
+ for i=1,nx do
+ local di = data[i]
+ local fi = func[i]
+ for j=1,ny do
+ local f = fi[j]
+ if f >= minr and f <= maxr then
+ di[j] = bor(di[j],bit)
+ end
+ end
+ end
+ end
+
+ end
+
+ -- we can simplify the value mess below
+
+ elseif functioncode then
+
+ if not executed(data,functioncode) then
+ report("error in executing function")
+ return -- fatal error
+ end
+
+ else
+
+ report("no valid function(s)")
+ return -- fatal error
+
+ end
+
+ minz = data[1][1]
+ maxz = minz
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ local v = d[j]
+ if v < minz then
+ minz = v
+ elseif v > maxz then
+ maxz = v
+ end
+ end
+ end
+
+ if functionlist then
+
+ for i=minz,maxz do
+ values[i] = i
+ end
+
+ levels = maxz
+
+ minmean = minz
+ maxmean = maxz
+
+ else
+
+ local snap = (maxz - minz + 1) / levels
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ local dj = d[j]
+ local v = round((dj - minz) / snap)
+ values[v] = values[v] + 1
+ means [v] = means [v] + dj
+ d[j] = v
+ end
+ end
+
+ local list = sortedkeys(values)
+ local count = values
+ local remap = { }
+
+ values = { }
+
+ for i=1,#list do
+ local v = list[i]
+ local m = means[v] / count[v]
+ remap [v] = i
+ values[i] = m
+ if not minmean then
+ minmean = m
+ maxmean = m
+ elseif m < minmean then
+ minmean = m
+ elseif m > maxmean then
+ maxmean = m
+ end
+ end
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ d[j] = remap[d[j]]
+ end
+ end
+
+ end
+
+ -- due to rounding we have values + 1 so we can have a floor, ceil, round
+ -- option as well as levels -1
+
+ local nofvalues = #values
+
+ local colors = setcolors(
+ p.preamble,levels,minz,maxz,p.color,nofvalues
+ )
+
+ p.result = {
+ version = version,
+ values = values,
+ nofvalues = nofvalues,
+ minz = minz,
+ maxz = maxz,
+ minmean = minmean,
+ maxmean = maxmean,
+ data = data,
+ color = color,
+ nx = nx,
+ ny = ny,
+ colors = colors,
+ name = name,
+ hash = hash,
+ islist = functionlist and true or false,
+ }
+
+ report("index %i, nx %i, ny %i, levels %i", nofcontours, nx, ny, nofvalues)
+end
+
+function mp.lmt_contours_stop()
+ local p = getparameterset()
+ local e = stoptiming("lmt_contours")
+ setcache(p)
+ p.result = nil
+ local f = p["function"]
+ local l = p.functions
+ report("index %i, %0.3f seconds for: %s",
+ nofcontours, e, "[ " .. concat(l or { f } ," ] [ ") .. " ]"
+ )
+end
+
+function mp.lmt_contours_bitmap_set()
+ local p = getparameterset()
+ local result = p.result
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local rawdata = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local colors = result.colors
+ local depth = #colors[1] -- == 3 and "rgb" or "gray"
+
+ -- i need to figure out this offset of + 1
+
+ local bitmap = graphics.bitmaps.new(
+ nx, ny,
+ (depth == 3 and "rgb") or (depth == 4 and "cmyk") or "gray",
+ 1, false, true
+ )
+
+ local palette = bitmap.index or { } -- has to start at 0
+ local data = bitmap.data
+ local p = 0
+
+ if depth == 3 or depth == 4 then
+ for i=1,nofvalues do
+ local c = colors[i]
+ local r = round((c[1] or 0) * 255)
+ local g = round((c[2] or 0) * 255)
+ local b = round((c[3] or 0) * 255)
+ local k = depth == 4 and round((c[4] or 0) * 255) or nil
+ palette[p] = {
+ (r > 255 and 255) or (r < 0 and 0) or r,
+ (g > 255 and 255) or (g < 0 and 0) or g,
+ (b > 255 and 255) or (b < 0 and 0) or b,
+ k
+ }
+ p = p + 1
+ end
+ else
+ for i=1,nofvalues do
+ local s = colors[i][1]
+ local s = round((s or 0) * 255)
+ palette[p] = (
+ (s > 255 and 255) or (s < 0 and 0) or s
+ )
+ p = p + 1
+ end
+ end
+
+ -- As (1,1) is the left top corner so we need to flip of we start in
+ -- the left bottom (we cannot loop reverse because we want a properly
+ -- indexed table.
+
+ local k = 0
+ for y=ny,1,-1 do
+ k = k + 1
+ local d = data[k]
+ for x=1,nx do
+ d[x] = rawdata[x][y] - 1
+ end
+ end
+
+ result.bitmap = bitmap
+end
+
+function mp.lmt_contours_bitmap_get()
+ local p = getparameterset()
+ local result = p.result
+ local bitmap = result.bitmap
+ local box = nodes.hpack(graphics.bitmaps.flush(bitmap))
+ n_box = n_box + 1
+ nodes.boxes.savenode("contour",tostring(n_box),box)
+ return f_box(n_box,bitmap.xsize,bitmap.ysize)
+end
+
+function mp.lmt_contours_cleanup()
+ nodes.boxes.reset("contour")
+ n_box = 0
+end
+
+function mp.lmt_contours_edge_set()
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+
+ local xmin = p.xmin
+ local xmax = p.xmax
+ local ymin = p.ymin
+ local ymax = p.ymax
+ local xstep = p.xstep
+ local ystep = p.ystep
+
+ local wsp = { }
+ local edges = { }
+
+ for value=1,nofvalues do
+
+ local iwsp = 0
+ local di = data[1]
+ local dc
+ local edge = { }
+ local e = 0
+ -- the next loop is fast
+ for i=1,nx do
+ local di1 = data[i+1]
+ local dij = di[1]
+ local d = dij - value
+ local dij1
+ for j=1,ny do
+ if j < ny then
+ dij1 = di[j+1]
+ dc = dij1 - value
+ if (d >= 0 and dc < 0) or (d < 0 and dc >= 0) then
+ iwsp = iwsp + 1
+ local y = (d * (j+1) - dc * j) / (d - dc)
+ if i == 1 then
+ wsp[iwsp] = { i, y, 0, (i + (j-1)*nx) }
+ elseif i == nx then
+ wsp[iwsp] = { i, y, (i - 1 + (j-1)*nx), 0 }
+ else
+ local jx = (i + (j-1)*nx)
+ wsp[iwsp] = { i, y, jx - 1, jx }
+ end
+ end
+ end
+ if i < nx then
+ local dc = di1[j] - value
+ if (d >= 0 and dc < 0) or (d < 0 and dc >= 0) then
+ iwsp = iwsp + 1
+ local x = (d * (i+1) - dc * i) / (d - dc)
+ if j == 1 then
+ wsp[iwsp] = { x, j, 0, (i + (j-1)*nx) }
+ elseif j == ny then
+ wsp[iwsp] = { x, j, (i + (j-2)*nx), 0 }
+ else
+ local jx = i + (j-1)*nx
+ wsp[iwsp] = { x, j, jx - nx, jx }
+ end
+ end
+ end
+ dij = dij1
+ d = dc
+ end
+ di = di1
+ end
+ -- the next loop takes time
+ for i=1,iwsp do
+ local wspi = wsp[i]
+ for isq=3,4 do
+ local nsq = wspi[isq]
+ if nsq ~= 0 then
+ local px = wspi[1]
+ local py = wspi[2]
+ local p = { px, py }
+ local pn = 2
+ wspi[isq] = 0
+ while true do
+ for ii=1,iwsp do
+ local w = wsp[ii]
+ local n1 = w[3]
+ local n2 = w[4]
+ if n1 == nsq then
+ local x = w[1]
+ local y = w[2]
+ if x ~= px or y ~= py then
+ pn = pn + 1
+ p[pn] = x
+ pn = pn + 1
+ p[pn] = y
+ px = x
+ py = y
+ end
+ nsq = n2
+ w[3] = 0
+ w[4] = 0
+ if nsq == 0 then
+ if pn == 1 then
+ pn = pn + 1
+ p[pn] = w
+ end
+ goto flush
+ end
+ elseif n2 == nsq then
+ local x = w[1]
+ local y = w[2]
+ if x ~= px or y ~= py then
+ pn = pn + 1
+ p[pn] = x
+ pn = pn + 1
+ p[pn] = y
+ px = x
+ py = y
+ end
+ nsq = n1
+ w[3] = 0
+ w[4] = 0
+ if nsq == 0 then
+ goto flush
+ end
+ end
+ end
+ end
+ ::flush::
+ e = e + 1
+ edge[e] = p
+ if mpflatten then
+ mpflatten(p)
+ end
+ end
+ end
+ end
+
+
+ edges[value] = edge
+
+ end
+
+ result.edges = edges
+
+end
+
+function mp.lmt_contours_shade_set(edgetoo)
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local color = result.color
+
+ local edges = setmetatableindex("table")
+ local shades = setmetatableindex("table")
+
+ local sqtype = setmetatableindex("table")
+
+ local xspoly = { 0, 0, 0, 0, 0, 0 }
+ local yspoly = { 0, 0, 0, 0, 0, 0 }
+ local xrpoly = { }
+ local yrpoly = { }
+
+ local xrpoly = { } -- lua.newtable(2000,0)
+ local yrpoly = { } -- lua.newtable(2000,0)
+
+ -- for i=1,2000 do
+ -- xrpoly[i] = 0
+ -- yrpoly[i] = 0
+ -- end
+
+ -- Unlike a c compiler lua will not optimize loops to run in parallel so we expand
+ -- some of the loops and make sure we don't calculate when not needed. Not that nice
+ -- but not that bad either. Maybe I should just write this from scratch.
+
+-- local i = 0
+-- local j = 0
+
+ -- Analyze each rectangle separately. Overwrite lower colors
+
+ -- Unrolling the loops and copying code and using constants is faster and doesn't
+ -- produce much more code in the end, also because we then can leave out the not
+ -- seen branches. One can argue about the foundit2* blobs but by stepwise optimizing
+ -- this is the result.
+
+ shades[1] = { { 0, 0, nx - 1, 0, nx - 1, ny - 1, 0, ny - 1 } }
+ edges [1] = { { } }
+
+ -- this is way too slow ... i must have messed up some loop .. what is this with value 1
+
+ for value=1,nofvalues do
+-- for value=2,nofvalues do
+
+ local edge = { }
+ local nofe = 0
+ local shade = { }
+ local nofs = 0
+
+ for i=1,nx-1 do
+ local s = sqtype[i]
+ for j=1,ny-1 do
+ s[j] = 0
+ end
+ end
+
+ local nrp = 0
+
+ local function addedge(a,b,c,d)
+ nofe = nofe + 1 edge[nofe] = a
+ nofe = nofe + 1 edge[nofe] = b
+ nofe = nofe + 1 edge[nofe] = c
+ nofe = nofe + 1 edge[nofe] = d
+ end
+ while true do
+ -- search for a square of type 0 with >= 1 corner above contour level
+ local i
+ local j
+ local d0 = data[1]
+ local d1 = data[2]
+ for ii=1,nx do
+ local s = sqtype[ii]
+ for jj=1,ny do
+ if s[jj] == 0 then
+ if d0[jj] > value then i = ii j = jj goto foundit end
+ if d1[jj] > value then i = ii j = jj goto foundit end
+ local j1 = jj + 1
+ if d1[j1] > value then i = ii j = jj goto foundit end
+ if d0[j1] > value then i = ii j = jj goto foundit end
+ end
+ end
+ d0 = d1
+ d1 = data[ii+1]
+ end
+ break
+ ::foundit::
+ -- initialize r-polygon (nrp seems to be 1 or 2)
+ nrp = nrp + 1
+
+ local first = true
+ local nrpoly = 0
+ local nspoly = 0
+ local nrpm = -nrp
+ -- this is the main loop
+ while true do
+ -- search for a square of type -nrp
+ if first then
+ first = false
+ if sqtype[i][j] == 0 then -- true anyway
+ goto foundit1
+ end
+ end
+ for ii=1,nx do
+ local s = sqtype[ii]
+ for jj=1,ny do
+ if s[jj] == nrpm then
+ i = ii
+ j = jj
+ goto foundit1
+ end
+ end
+ end
+ break
+ ::foundit1::
+ while true do
+
+ -- search current then neighboring squares for square type 0, with a corner in common with current square above contour level
+
+ -- top/bottom ... a bit cheating here
+
+ local i_l, i_c, i_r -- i left current right
+ local j_b, j_c, j_t -- j bottom current top
+
+ local i_n = i + 1 -- i next (right)
+ local j_n = j + 1 -- j next (top)
+
+ local i_p = i - 1 -- i previous (bottom)
+ local j_p = j - 1 -- j previous (right)
+
+ local d_c = data[i]
+ local d_r = data[i_n]
+
+ local sq
+
+ i_c = i ; j_c = j ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_c] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit21 end
+ if d_c[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit22 end
+ if d_r[j_c] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit23 end
+ if d_r[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit24 end
+ end end
+
+ i_c = i_n ; j_c = j ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_r[j_c] > value then i_l = i ; i_r = i_n + 1 ; j_b = j_p ; j_t = j_n ; d_c = d_r ; d_r = data[i_r] ; goto foundit21 end
+ if d_r[j_n] > value then i_l = i ; i_r = i_n + 1 ; j_b = j_p ; j_t = j_n ; d_c = d_r ; d_r = data[i_r] ; goto foundit22 end
+ end end
+
+ i_c = i ; j_c = j_n ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j ; j_t = j_n + 1 ; goto foundit21 end
+ if d_r[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j ; j_t = j_n + 1 ; goto foundit23 end
+ end end
+
+ i_c = i_p ; j_c = j ; if i_c > 0 and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_c] > value then i_l = i_p - 1 ; i_r = i ; j_b = j_p ; j_t = j_n ; d_r = d_c ; d_c = data[i_p] ; goto foundit23 end
+ if d_c[j_n] > value then i_l = i_p - 1 ; i_r = i ; j_b = j_p ; j_t = j_n ; d_r = d_c ; d_c = data[i_p] ; goto foundit24 end
+ end end
+
+ i_c = i ; j_c = j_p ; if i < nx and j_c > 0 then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j] > value then i_l = i_p ; i_r = i_n ; j_b = j_p - 1 ; j_t = j ; goto foundit22 end
+ if d_r[j] > value then i_l = i_p ; i_r = i_n ; j_b = j_p - 1 ; j_t = j ; goto foundit24 end
+ end end
+
+ -- not found
+
+ sqtype[i][j] = nrp
+
+ break
+
+ -- define s-polygon for found square (i_c,j_c) - may have up to 6 sides
+
+ ::foundit21:: -- 1 2 3 4
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_l ; yspoly[1] = j_b
+ xspoly[2] = i_c ; yspoly[2] = j_b
+ if d_r[j_c] > value then -- dd2
+ xspoly[3] = i_c ; yspoly[3] = j_c
+ if d_r[j_t] > value then -- dd3
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ if d_c[j_t] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ end
+ elseif d_c[j_t] > value then -- dd4
+ xspoly[4] = i_c ; yspoly[4] = j_c ;
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ else
+ xspoly[4] = i_l ; yspoly[4] = j_c ; nspoly = 4
+ if edgetoo then addedge(i_c, j_c, i_l, j_c) end
+ end
+ elseif d_r[j_t] > value then -- dd3
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_c
+ if d_c[j_t] > value then -- dd4
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_c ;
+ xspoly[6] = i_l ; yspoly[6] = j_c ; nspoly = 6
+ end
+ elseif d_c[j_t] > value then -- dd4
+ if edgetoo then addedge(i_c, j_b, i_c, j_c) end
+ xspoly[3] = i_c ; yspoly[3] = j_c ;
+ xspoly[4] = i_l ; yspoly[4] = j_c ; nspoly = 4
+ else
+ if edgetoo then addedge(i_c, j_b, i_l, j_c) end
+ xspoly[3] = i_l ; yspoly[3] = j_c ; nspoly = 3
+ end
+ goto done
+
+ ::foundit22:: -- 4 1 2 3
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_l ; yspoly[1] = j_c
+ xspoly[2] = i_l ; yspoly[2] = j_b
+ if d_c[j_c] > value then -- dd2
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ if d_r[j_c] > value then -- dd3
+ xspoly[4] = i_c ; yspoly[4] = j_c
+ if d_r[j_t] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_c ; yspoly[5] = j_c ; nspoly = 5 -- suspicious, the same
+ end
+ elseif d_r[j_t] > value then -- dd4
+ xspoly[4] = i_c ; yspoly[4] = j_b ;
+ xspoly[5] = i_c ; yspoly[5] = j_c ; nspoly = 5
+ else
+ if edgetoo then addedge(i_c, j_b, i_c, j_c) end
+ xspoly[4] = i_c ; yspoly[4] = j_c ; nspoly = 4
+ end
+ elseif d_r[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b
+ xspoly[5] = i_c ; yspoly[5] = j_c
+ if d_r[j_t] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_c ; yspoly[6] = j_c ; nspoly = 6
+ end
+ elseif d_r[j_t] > value then -- dd4
+ if edgetoo then addedge(i_l, j_b, i_c, j_b) end
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_c ; nspoly = 4
+ else
+ if edgetoo then addedge(i_l, j_b, i_c, j_c) end
+ xspoly[3] = i_c ; yspoly[3] = j_c ; nspoly = 3
+ end
+ goto done
+
+ ::foundit23:: -- 2 3 4 1
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_c ; yspoly[1] = j_b
+ xspoly[2] = i_c ; yspoly[2] = j_c
+ if d_r[j_t] > value then -- dd2
+ xspoly[3] = i_l ; yspoly[3] = j_c
+ if d_c[j_t] > value then -- dd3
+ xspoly[4] = i_l ; yspoly[4] = j_b
+ if d_c[j_c] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_b ; nspoly = 5
+ end
+ elseif d_c[j_c] > value then -- dd4
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ xspoly[5] = i_l ; yspoly[5] = j_b ; nspoly = 5
+ else
+ if edgetoo then addedge(i_l, j_c, i_l, j_b) end
+ xspoly[4] = i_l ; yspoly[4] = j_b ; nspoly = 4
+ end
+ elseif d_c[j_t] > value then -- dd3
+ xspoly[3] = i_c ; yspoly[3] = j_c
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ xspoly[5] = i_l ; yspoly[5] = j_b
+ if d_c[j_c] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_l ; yspoly[6] = j_b ; nspoly = 6
+ end
+ elseif d_c[j_c] > value then -- dd4
+ if edgetoo then addedge(i_c, j_c, i_l, j_c) end
+ xspoly[3] = i_l ; yspoly[3] = j_c ;
+ xspoly[4] = i_l ; yspoly[4] = j_b ; nspoly = 4
+ else
+ if edgetoo then addedge(i_c, j_c, i_l, j_b) end
+ xspoly[3] = i_l ; yspoly[3] = j_b ; nspoly = 3
+ end
+ goto done
+
+ ::foundit24:: -- 3 4 1 2
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_c ; yspoly[1] = j_c
+ xspoly[2] = i_l ; yspoly[2] = j_c
+ if d_c[j_t] > value then -- dd2
+ if d_c[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b
+ if d_r[j_c] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_c ; yspoly[5] = j_b ; nspoly = 5
+ end
+ else
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ if d_r[j_c] > value then -- dd4
+
+ local xv34 = (dd3*i_c-dd4*i_l)/(dd3 - dd4) -- probably i_l
+ print("4.4 : xv34",xv34,i_c,i_l)
+
+ -- if edgetoo then addedge(i_l, j_b, xv34, j_b) end
+ xspoly[4] = xv34 ; yspoly[4] = j_b ;
+ xspoly[5] = i_c ; yspoly[5] = j_b ; nspoly = 5
+ else
+ if edgetoo then addedge(i_l, j_b, i_c, j_b) end
+ xspoly[4] = i_c ; yspoly[4] = j_b ; nspoly = 4
+ end
+ end
+ elseif d_c[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_l ; yspoly[4] = j_b
+ xspoly[5] = i_c ; yspoly[5] = j_b
+ if d_r[j_c] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_c ; yspoly[6] = j_b ; nspoly = 6
+ end
+ elseif d_r[j_c] > value then -- dd4
+ if edgetoo then addedge(i_l, j_c, i_l, j_b) end
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b ; nspoly = 4
+ else
+ if edgetoo then addedge(i_l, j_c, i_c, j_b) end
+ xspoly[3] = i_c ; yspoly[3] = j_b ; nspoly = 3
+ end
+ -- goto done
+
+ ::done::
+ -- combine s-polygon with existing r-polygon, eliminating redundant segments
+
+ if nrpoly == 0 then
+ -- initiate r-polygon
+ for i=1,nspoly do
+ xrpoly[i] = xspoly[i]
+ yrpoly[i] = yspoly[i]
+ end
+ nrpoly = nspoly
+ else
+ -- search r-polygon and s-polygon for one side that matches
+ --
+ -- this is a bottleneck ... we keep this variant here but next go for a faster
+ -- alternative
+ --
+ -- local ispoly, irpoly
+ -- for r=nrpoly,1,-1 do
+ -- local r1
+ -- for s=1,nspoly do
+ -- local s1 = s % nspoly + 1
+ -- if xrpoly[r] == xspoly[s1] and yrpoly[r] == yspoly[s1] then
+ -- if not r1 then
+ -- r1 = r % nrpoly + 1
+ -- end
+ -- if xrpoly[r1] == xspoly[s] and yrpoly[r1] == yspoly[s] then
+ -- ispoly = s
+ -- irpoly = r
+ -- goto foundit3
+ -- end
+ -- end
+ -- end
+ -- end
+ --
+ -- local ispoly, irpoly
+ -- local xr1 = xrpoly[1]
+ -- local yr1 = yrpoly[1]
+ -- for r0=nrpoly,1,-1 do
+ -- for s0=1,nspoly do
+ -- if xr1 == xspoly[s0] and yr1 == yspoly[s0] then
+ -- if s0 == nspoly then
+ -- if xr0 == xspoly[1] and yr0 == yspoly[1] then
+ -- ispoly = s0
+ -- irpoly = r0
+ -- goto foundit3
+ -- end
+ -- else
+ -- local s1 = s0 + 1
+ -- if xr0 == xspoly[s1] and yr0 == yspoly[s1] then
+ -- ispoly = s0
+ -- irpoly = r0
+ -- goto foundit3
+ -- end
+ -- end
+ -- end
+ -- end
+ -- xr1 = xrpoly[r0]
+ -- yr1 = yrpoly[r0]
+ -- end
+ --
+ -- but ...
+ --
+ local minx = xspoly[1]
+ local miny = yspoly[1]
+ local maxx = xspoly[1]
+ local maxy = yspoly[1]
+ for i=1,nspoly do
+ local y = yspoly[i]
+ if y < miny then
+ miny = y
+ elseif y > maxy then
+ maxy = y
+ end
+ local x = xspoly[i]
+ if x < minx then
+ minx = y
+ elseif x > maxx then
+ maxx = x
+ end
+ end
+ -- we can delay accessing y ...
+ local ispoly, irpoly
+ local xr1 = xrpoly[1]
+ local yr1 = yrpoly[1]
+ for r0=nrpoly,1,-1 do
+ if xr1 >= minx and xr1 <= maxx and yr1 >= miny and yr1 <= maxy then
+ local xr0 = xrpoly[r0]
+ local yr0 = yrpoly[r0]
+ for s0=1,nspoly do
+ if xr1 == xspoly[s0] and yr1 == yspoly[s0] then
+ if s0 == nspoly then
+ if xr0 == xspoly[1] and yr0 == yspoly[1] then
+ ispoly = s0
+ irpoly = r0
+ goto foundit3
+ end
+ else
+ local s1 = s0 + 1
+ if xr0 == xspoly[s1] and yr0 == yspoly[s1] then
+ ispoly = s0
+ irpoly = r0
+ goto foundit3
+ end
+ end
+ end
+ end
+ xr1 = xr0
+ yr1 = yr0
+ else
+ xr1 = xrpoly[r0]
+ yr1 = yrpoly[r0]
+ end
+ end
+ --
+ goto nomatch3
+ ::foundit3::
+ local match1 = 0
+ local rpoly1 = irpoly + nrpoly
+ local spoly1 = ispoly - 1
+ for i=2,nspoly-1 do
+ -- search for further matches nearby
+ local ir = (rpoly1 - i) % nrpoly + 1
+ local is = (spoly1 + i) % nspoly + 1
+ if xrpoly[ir] == xspoly[is] and yrpoly[ir] == yspoly[is] then
+ match1 = match1 + 1
+ else
+ break -- goto nomatch1
+ end
+ end
+ ::nomatch1::
+ local match2 = 0
+ local rpoly2 = irpoly - 1
+ local spoly2 = ispoly + nspoly
+ for i=2,nspoly-1 do
+ -- search other way for further matches nearby
+ local ir = (rpoly2 + i) % nrpoly + 1
+ local is = (spoly2 - i) % nspoly + 1
+ if xrpoly[ir] == xspoly[is] and yrpoly[ir] == yspoly[is] then
+ match2 = match2 + 1
+ else
+ break -- goto nomatch2
+ end
+ end
+ ::nomatch2::
+ -- local dnrpoly = nspoly - 2 - 2*match1 - 2*match2
+ local dnrpoly = nspoly - 2*(match1 + match2 + 1)
+ local ispolystart = (ispoly + match1) % nspoly + 1 -- first node of s-polygon to include
+ local irpolyend = (rpoly1 - match1 - 1) % nrpoly + 1 -- last node of s-polygon to include
+ if dnrpoly ~= 0 then
+ local irpolystart = (irpoly + match2) % nrpoly + 1 -- first node of s-polygon to include
+ if irpolystart > irpolyend then
+ -- local ispolyend = (spoly1 - match2 + nspoly)%nspoly + 1 -- last node of s-polygon to include
+ if dnrpoly > 0 then
+ -- expand the arrays xrpoly and yrpoly
+ for i=nrpoly,irpolystart,-1 do
+ local k = i + dnrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ else -- if dnrpoly < 0 then
+ -- contract the arrays xrpoly and yrpoly
+ for i=irpolystart,nrpoly do
+ local k = i + dnrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ end
+ end
+ nrpoly = nrpoly + dnrpoly
+ end
+ if nrpoly < irpolyend then
+ for i=irpolyend,nrpoly+1,-1 do
+ -- otherwise these values get lost!
+ local k = i - nrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ end
+ local n = nspoly - 2 - match1 - match2
+ if n == 1 then
+ local irpoly1 = irpolyend % nrpoly + 1
+ local ispoly1 = ispolystart % nspoly + 1
+ xrpoly[irpoly1] = xspoly[ispoly1]
+ yrpoly[irpoly1] = yspoly[ispoly1]
+ elseif n > 0 then
+ -- often 2
+ for i=1,n do
+ local ii = i - 1
+ local ir = (irpolyend + ii) % nrpoly + 1
+ local is = (ispolystart + ii) % nspoly + 1
+ xrpoly[ir] = xspoly[is]
+ yrpoly[ir] = yspoly[is]
+ end
+ end
+ ::nomatch3::
+ end
+ end
+ end
+
+ if nrpoly > 0 then
+ local t = { }
+ local n = 0
+ for i=1,nrpoly do
+ n = n + 1 t[n] = xrpoly[i]
+ n = n + 1 t[n] = yrpoly[i]
+ end
+ if mpflatten then
+ mpflatten(t) -- maybe integrate
+ end
+ nofs = nofs + 1
+ shade[nofs] = t
+ -- print(value,nrpoly,#t,#t-nrpoly*2)
+ end
+
+ end
+
+ edges [value+1] = edge
+ shades[value+1] = shade
+-- edges [value] = edge
+-- shades[value] = shade
+ end
+
+ result.shades = shades
+ result.shapes = edges
+
+end
+
+-- accessors
+
+function mp.lmt_contours_nx (i) return getparameterset().result.nx end
+function mp.lmt_contours_ny (i) return getparameterset().result.ny end
+
+function mp.lmt_contours_nofvalues() return getparameterset().result.nofvalues end
+function mp.lmt_contours_value (i) return getparameterset().result.values[i] end
+
+function mp.lmt_contours_minz (i) return getparameterset().result.minz end
+function mp.lmt_contours_maxz (i) return getparameterset().result.maxz end
+
+function mp.lmt_contours_minmean (i) return getparameterset().result.minmean end
+function mp.lmt_contours_maxmean (i) return getparameterset().result.maxmean end
+
+function mp.lmt_contours_xrange () local p = getparameterset() mpstring(formatters["x = [%.3N,%.3N] ;"](p.xmin,p.xmax)) end
+function mp.lmt_contours_yrange () local p = getparameterset() mpstring(formatters["y = [%.3N,%.3N] ;"](p.ymin,p.ymax)) end
+
+function mp.lmt_contours_format()
+ local p = getparameterset()
+ return mpstring(p.result.islist and "@i" or p.zformat or p.format)
+end
+
+function mp.lmt_contours_function()
+ local p = getparameterset()
+ return mpstring(p.result.islist and concat(p["functions"], ", ") or p["function"])
+end
+
+function mp.lmt_contours_range()
+ local p = getparameterset()
+ local r = p.result.islist and p.range
+ if not r or #r == 0 then
+ return mpstring("")
+ elseif #r == 1 then
+ return mpstring(r[1])
+ else
+ return mpstring(formatters["z = [%s,%s]"](r[1],r[2]))
+ end
+end
+
+function mp.lmt_contours_edge_paths(value)
+ mpdraw(getparameterset().result.edges[value],true)
+ mpflush()
+end
+
+function mp.lmt_contours_shape_paths(value)
+ mpdraw(getparameterset().result.shapes[value],false)
+ mpflush()
+end
+
+function mp.lmt_contours_shade_paths(value)
+ mpfill(getparameterset().result.shades[value],true)
+ mpflush()
+end
+
+function mp.lmt_contours_color(value)
+ local p = getparameterset()
+ local color = p.result.colors[value]
+ if color then
+ mpcolor(color)
+ end
+end
+
+-- The next code is based on the wikipedia page. It was a bit tedius job to define the
+-- coordinates but hupefully I made no errors. I rendered all shapes independently and
+-- tripple checked bit one never knows ...
+
+-- maybe some day write from scatch, like this (axis are swapped):
+
+local d = 1/2
+
+local paths = {
+ { 0, d, d, 0 },
+ { 1, d, d, 0 },
+ { 0, d, 1, d },
+ { 1, d, d, 1 },
+ { 0, d, d, 1, d, 0, 1, d }, -- saddle
+ { d, 0, d, 1 },
+ { 0, d, d, 1 },
+ { 0, d, d, 1 },
+ { d, 0, d, 1 },
+ { 0, d, d, 0, 1, d, d, 1 }, -- saddle
+ { 1, d, d, 1 },
+ { 0, d, 1, d },
+ { d, 0, 1, d },
+ { d, 0, 0, d },
+}
+
+local function whatever(data,nx,ny,threshold)
+ local edges = { }
+ local e = 0
+ local d0 = data[1]
+ for j=1,ny-1 do
+ local d1 = data[j+1]
+ local k = j + 1
+ for i=1,nx-1 do
+ local v = 0
+ local l = i + 1
+ local c1 = d0[i]
+ if c1 < threshold then
+ v = v + 8
+ end
+ local c2 = d0[l]
+ if c2 < threshold then
+ v = v + 4
+ end
+ local c3 = d1[l]
+ if c3 < threshold then
+ v = v + 2
+ end
+ local c4 = d1[i]
+ if c4 < threshold then
+ v = v + 1
+ end
+ if v > 0 and v < 15 then
+ if v == 5 or v == 10 then
+ local a = (c1 + c2 + c3 + c4) / 4
+ if a < threshold then
+ v = v == 5 and 10 or 5
+ end
+ local p = paths[v]
+ e = e + 1 edges[e] = k - p[2]
+ e = e + 1 edges[e] = i + p[1]
+ e = e + 1 edges[e] = k - p[4]
+ e = e + 1 edges[e] = i + p[3]
+ e = e + 1 edges[e] = k - p[6]
+ e = e + 1 edges[e] = i + p[5]
+ e = e + 1 edges[e] = k - p[8]
+ e = e + 1 edges[e] = i + p[7]
+ else
+ local p = paths[v]
+ e = e + 1 edges[e] = k - p[2]
+ e = e + 1 edges[e] = i + p[1]
+ e = e + 1 edges[e] = k - p[4]
+ e = e + 1 edges[e] = i + p[3]
+ end
+ end
+ end
+ d0 = d1
+ end
+ return edges
+end
+
+-- todo: just fetch when needed, no need to cache
+
+function mp.lmt_contours_edge_set_by_cell()
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local lines = { }
+ result.lines = lines
+ for value=1,nofvalues do
+ lines[value] = whatever(data,ny,nx,value)
+ end
+end
+
+function mp.lmt_contours_edge_get_cell(value)
+ mpdraw(getparameterset().result.lines[value])
+ mpflush()
+end
+
+local singles = {
+ { d, 0, 0, 0, 0, d }, -- 1 0001
+ { d, 0, 0, d }, -- 2 0002
+ { 1, d, 1, 0, d, 0 }, -- 3 0010
+ { 1, d, 1, 0, 0, 0, 0, d }, -- 4 0011
+ { 1, d, 1, 0, d, 0, 0, d }, -- 5 0012
+ { 1, d, d, 0 }, -- 6 0020
+ { 1, d, d, 0, 0, 0, 0, d }, -- 7 0021
+ { 1, d, 0, d }, -- 8 0022
+ { d, 1, 1, 1, 1, d }, -- 9 0100
+ false, -- 10 0101
+ false, -- 11 0102
+ { d, 1, 1, 1, 1, 0, d, 0 }, -- 12 0110
+ { d, 1, 1, 1, 1, 0, 0, 0, 0, d }, -- 13 0111
+ { d, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 14 0112
+ { d, 1, 1, 1, 1, d, d, 0 }, -- 15 0120
+ { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, -- 16 0121
+ { d, 1, 1, 1, 1, d, 0, d }, -- 17 0122
+ { d, 1, 1, d }, -- 18 0200
+ false, -- 19 0201
+ false, -- 20 0202
+ { d, 1, 1, d, 1, 0, d, 0 }, -- 21 0210
+ { d, 1, 1, d, 1, 0, 0, 0, 0, d }, -- 22 0211
+ false, -- 23 0212
+ { d, 1, d, 0 }, -- 24 0220
+ { d, 1, d, 0, 0, 0, 0, d }, -- 25 0221
+ { d, 1, 0, d }, -- 26 0222
+ { 0, 1, d, 1, 0, d }, -- 27 1000
+ { 0, 1, d, 1, d, 0, 0, 0 }, -- 28 1001
+ { 0, 1, d, 1, d, 0, 0, d }, -- 29 1002
+ false, -- 30 1010
+ { 0, 1, d, 1, 1, d, 1, 0, 0, 0 }, -- 31 1011
+ { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, -- 32 1012
+ false, -- 33 1020
+ { 0, 1, d, 1, 1, d, d, 0, 0, 0 }, -- 34 1021
+ { 0, 1, d, 1, 1, d, 0, d }, -- 35 1022
+ { 0, 1, 1, 1, 1, d, 0, d }, -- 36 1100
+ { 0, 1, 1, 1, 1, d, d, 0, 0, 0 }, -- 37 1101
+ { 0, 1, 1, 1, 1, d, d, 0, 0, d }, -- 38 1102
+ { 0, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 39 1110
+ { 0, 1, 1, 1, 1, 0, 0, 0 }, -- 40 1111
+ { 0, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 41 1112
+ { 0, 1, 1, 1, 1, d, d, 0, 0, d }, -- 42 1120
+ { 0, 1, 1, 1, 1, d, d, 0, 0, 0 }, -- 43 1121
+ { 0, 1, 1, 1, 1, d, 0, d }, -- 44 1122
+ { 0, 1, d, 1, 1, d, 0, d }, -- 45 1200
+ { 0, 1, d, 1, 1, d, d, 0, 0, 0 }, -- 46 1201
+ false, -- 47 1202
+ { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, -- 48 1210
+ { 0, 1, d, 1, 1, d, 1, 0, 0, 0 }, -- 49 1211
+ false, -- 50 1212
+ { 0, 1, d, 1, d, 0, 0, d }, -- 51 1220
+ { 0, 1, d, 1, d, 0, 0, 0 }, -- 52 1221
+ { 0, 1, d, 1, 0, d }, -- 53 1222
+ { d, 1, 0, d }, -- 54 2000
+ { d, 1, d, 0, 0, 0, 0, d }, -- 55 2001
+ { d, 1, d, 0 }, -- 56 2002
+ false, -- 57 2010
+ { d, 1, 1, d, 1, 0, 0, 0, 0, d }, -- 58 2011
+ { d, 1, 1, d, 1, 0, d, 0 }, -- 59 2012
+ false, -- 60 2020
+ false, -- 61 2021
+ { d, 1, 1, d }, -- 62 2022
+ { d, 1, 1, 1, 1, d, 0, d }, -- 63 2100
+ { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, -- 64 2101
+ { d, 1, 1, 1, 1, d, d, 0 }, -- 65 2102
+ { d, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 66 2110
+ { d, 1, 1, 1, 1, 0, 0, 0, 0, d }, -- 67 2111
+ { d, 1, 1, 1, 1, 0, d, 0 }, -- 68 2112
+ false, -- 69 2120
+ false, -- 70 2121
+ { d, 1, 1, 1, 1, d }, -- 71 2122
+ { 1, d, 0, d }, -- 72 2200
+ { 1, d, d, 0, 0, 0, 0, d }, -- 73 2201
+ { 1, d, d, 0 }, -- 74 2202
+ { 1, d, 1, 0, d, 0, 0, d }, -- 75 2210
+ { 1, d, 1, 0, 0, 0, 0, d }, -- 76 2211
+ { 1, d, 1, 0, d, 0 }, -- 77 2212
+ { d, 0, 0, d }, -- 78 2220
+ { 0, d, 0, 0, d, 0 }, -- 79 2221
+}
+
+local sadles = {
+ false, false, false, false, false, false, false, false, false,
+ { { d, 1, 1, 1, 1, d }, { d, 0, 0, 0, 0, d }, { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, false, false, false }, -- 10 0101
+ { { d, 1, 1, 1, 1, d }, { d, 0, 0, d }, { d, 1, 1, 1, 1, d, d, 0, 0, d }, false, false, false }, -- 11 0102
+ false, false, false, false, false, false, false,
+ { { d, 1, 1, d }, { d, 0, 0, 0, 0, d }, { d, 1, 1, d, d, 0, 0, 0, 0, d }, false, false, false }, -- 19 0201
+ { { d, 1, 1, d }, { d, 0, 0, d }, { d, 1, 1, d, d, 0, 0, d }, false, { d, 1, 0, d }, { 1, d, d, 0 } }, -- 20 0202
+ false, false,
+ { false, false, { d, 1, 1, d, 1, 0, d, 0, 0, d }, false, { d, 1, 0,d, }, { 1, d, 1, 0,d, 0 } }, -- 23 0212
+ false, false, false, false, false, false,
+ { { 0, 1, d, 1, 0, d }, { 1, d, 1, 0, d, 0 }, { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, false, false, false }, -- 30 1010
+ false, false,
+ { { 1, 0, d, 0, 0, d, }, { 1, d, d, 0 }, { 0, 1, d, 1, 1, d, d, 0, 0, d }, false, false, false }, -- 33 1020
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ { false, false, { 0,1, d, 1, 1, d, d, 0, 0, d }, false, { 0,1, d, 1, 0, d }, {1, d, d, 0 } }, -- 47 1202
+ false, false,
+ { false, false, { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, false, { 0, 1, d, 1, 0, d }, { 1, d, 1, 0, d, 0 } }, -- 50 1212
+ false, false, false, false, false, false,
+ { { d, 1, 0, d }, { 1, d, 1, 0, 0, d }, { d, 1, 1, d, 1, 0, d, 0, 0, d }, false, false, false }, -- 57 2010
+ false, false,
+ { { d, 1, 0,d }, { 1, d, d, 0 }, { d, 1, 1, d, d, 0, 0, d }, false, { d, 1, 1, d }, { d, 0, 0, d } }, -- 60 2020
+ { false, false, { d, 1, 1, d, d, 0, 0, 0, 0, d }, false, { d, 1, 1, d }, { d, 0, 0, 0, 0, d } }, -- 61 2021
+ false, false, false, false, false, false, false,
+ { false, false, { d, 1, 1, 1, 1, d, d, 0, 0, d }, false, { d, 1, 1, 1, 1, d }, { d, 0,0,d } }, -- 69 2120
+ { false, false, { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, false, { d, 1, 1, 1, 1, d }, { d, 0, 0, 0, 0, d } }, -- 70 2121
+}
+
+local function whatever(data,nx,ny,threshold,background)
+
+ if background then
+
+ local llx = 1/2
+ local lly = llx
+ local urx = ny + llx
+ local ury = nx + lly
+
+ return { { llx, lly, urx, 0, urx, ury, 0, ury } }
+
+ else
+
+ local bands = { }
+ local b = 0
+
+ local function band(s,n,x,y) -- simple. no closure so fast
+ if n == 6 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ }
+ elseif n == 8 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7],
+ }
+ elseif n == 10 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7], x - s[10], y + s[ 9],
+ }
+ elseif n == 4 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3],
+ }
+ else -- 12
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7], x - s[10], y + s[ 9], x - s[12], y + s[11],
+ }
+ end
+ end
+
+ local pp = { }
+
+ local d0 = data[1]
+ for j=1,ny-1 do
+ local d1 = data[j+1]
+ local k = j + 1
+ local p = false
+ for i=1,nx-1 do
+ local v = 0
+ local l = i + 1
+ local c1 = d0[i]
+ if c1 == threshold then
+ v = v + 27
+ elseif c1 > threshold then
+ v = v + 54
+ end
+ local c2 = d0[l]
+ if c2 == threshold then
+ v = v + 9
+ elseif c2 > threshold then
+ v = v + 18
+ end
+ local c3 = d1[l]
+ if c3 == threshold then
+ v = v + 3
+ elseif c3 > threshold then
+ v = v + 6
+ end
+ local c4 = d1[i]
+ if c4 == threshold then
+ v = v + 1
+ elseif c4 > threshold then
+ v = v + 2
+ end
+ if v > 0 and v < 80 then
+ if v == 40 then
+ -- a little optimization: full areas appended horizontally
+ if p then
+ p[4] = l -- i + 1
+ p[6] = l -- i + 1
+ else
+ -- x-0 y+1 x-1 y+1 x-1 y+0 x-0 y+0
+ p = { j, i, j, l, k, l, k, i }
+ b = b + 1 ; bands[b] = p
+ end
+ else
+ local s = singles[v]
+ if s then
+ b = b + 1 ; bands[b] = band(s,#s,k,i)
+ else
+ local s = sadles[v]
+ if s then
+ local m = (c1 + c2 + c3 + c4) / 4
+ if m < threshold then
+ local s1 = s[1] if s1 then b = b + 1 ; bands[b] = band(s1,#s1,i,j) end
+ local s2 = s[2] if s2 then b = b + 1 ; bands[b] = band(s2,#s2,i,j) end
+ elseif m == threshold then
+ local s3 = s[3] if s3 then b = b + 1 ; bands[b] = band(s3,#s3,i,j) end
+ local s4 = s[4] if s4 then b = b + 1 ; bands[b] = band(s4,#s4,i,j) end
+ else
+ local s5 = s[5] if s5 then b = b + 1 ; bands[b] = band(s5,#s5,i,j) end
+ local s6 = s[6] if s6 then b = b + 1 ; bands[b] = band(s6,#s6,i,j) end
+ end
+ end
+ end
+ p = false
+ end
+ else
+ p = false
+ end
+ end
+ d0 = d1
+ end
+ return bands
+ end
+end
+
+function mp.lmt_contours_edge_set_by_band(value)
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local bands = { }
+ result.bands = bands
+ for value=1,nofvalues do
+ bands[value] = whatever(data,ny,nx,value,value == 1)
+ end
+end
+
+function mp.lmt_contours_edge_get_band(value)
+ mpfill(getparameterset().result.bands[value],true)
+ mpflush()
+end
+
+-- Because we share some code surface plots also end up here. When working on the
+-- contour macros by concidence I ran into a 3D plot in
+--
+-- https://staff.science.uva.nl/a.j.p.heck/Courses/mptut.pdf
+--
+-- The code is pure MetaPost and works quite well. With a bit of optimization
+-- performance is also ok, but in the end a Lua solution is twice as fast and also
+-- permits some more tweaking at no cost. So, below is an adaptation of an example
+-- in the mentioned link. It's one of these cases where access to pseudo arrays
+-- is slowing down MP.
+
+local sqrt, sin, cos = math.sqrt, math.sin, math.cos
+
+local f_fill_rgb = formatters["F (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N) ;"]
+local f_draw_rgb = formatters["D (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor %.3F ;"]
+local f_mesh_rgb = formatters["U (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N) ;"]
+local f_fill_cmy = formatters["F (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N,0) ;"]
+local f_draw_cmy = formatters["D (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor %.3F ;"]
+local f_mesh_cmy = formatters["U (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N,0) ;"]
+
+local f_function_n = formatters [ [[
+ local math = math
+ local round = math.round
+ %s
+ return function(x,y)
+ return %s
+ end
+]] ]
+
+local f_function_y = formatters [ [[
+ local math = math
+ local round = math.round
+ local nan = NaN
+ local inf = math.huge
+ local er = 0
+ %s
+ return function(x,y,dnan,dinf,report)
+ local n = %s
+ if n == nan then
+ er = er + 1
+ if er < 10 then
+ report("nan at (%s,%s)",x,y)
+ end
+ n = dnan
+ elseif n == inf then
+ er = er + 1
+ if er < 10 then
+ report("inf at (%s,%s)",x,y)
+ end
+ n = dinf
+ end
+ dx[my] = n
+ sy = sy + 1
+ end
+ return n, er
+end
+]] ]
+
+local f_color = formatters [ [[
+ local math = math
+ return function(f)
+ return %s
+ end
+]] ]
+
+function mp.lmt_surface_do(specification)
+ --
+ -- The projection and color brightness calculation have been inlined. We also store
+ -- differently.
+ --
+ -- todo: ignore weird paths
+ --
+ -- The prototype is now converted to use lmt parameter sets.
+ --
+ local p = getparameterset("surface")
+ --
+ local preamble = p.preamble or ""
+ local code = p.code or "return x + y"
+ local colorcode = p.color or "return f, f, f"
+ local linecolor = p.linecolor or 1
+ local xmin = p.xmin or -1
+ local xmax = p.xmax or 1
+ local ymin = p.ymin or -1
+ local ymax = p.ymax or 1
+ local xstep = p.xstep or .1
+ local ystep = p.ystep or .1
+ local bf = p.brightness or 100
+ local clip = p.clip or false
+ local lines = p.lines
+ local ha = p.snap or 0.01
+ local hb = 2 * ha
+ --
+ if lines == nil then lines = true end
+ --
+ if xstep == 0 then xstep = (xmax - xmin)/100 end
+ if ystep == 0 then ystep = (ymax - ymin)/100 end
+
+ local nxmin = round(xmin/xstep)
+ local nxmax = round(xmax/xstep)
+ local nymin = round(ymin/ystep)
+ local nymax = round(ymax/ystep)
+ local nx = nxmax - nxmin + 1
+ local ny = nymax - nymin + 1
+ --
+ local xvector = p.xvector or { -0.7, -0.7 }
+ local yvector = p.yvector or { 1, 0 }
+ local zvector = p.zvector or { 0, 1 }
+ local light = p.light or { 3, 3, 10 }
+ --
+ local xrx, xry = xvector[1], xvector[2]
+ local yrx, yry = yvector[1], yvector[2]
+ local zrx, zry = zvector[1], zvector[2]
+ local xp, yp, zp = light[1], light[2], light[3]
+ --
+ local data = setmetatableindex("table")
+ local dx = (xmax - xmin) / nx
+ local dy = (ymax - ymin) / ny
+ local xt = xmin
+ --
+ local minf, maxf
+ --
+ -- similar as contours but no data loop here
+ --
+ local fcode = load((p.check and f_function_y or f_function_n)(preamble,code))
+ local func = type(fcode) == "function" and fcode()
+ if type(func) ~= "function" then
+ return false -- fatal error
+ end
+ --
+ local ccode = load(f_color(colorcode))
+ local color = type(ccode) == "function" and ccode()
+ if type(color) ~= "function" then
+ return false -- fatal error
+ end
+ --
+ for i=0,nx do
+ local yt = ymin
+ for j=0,ny do
+ local zt = func(xt,yt)
+ -- projection from 3D to 2D coordinates
+ local x = xt * xrx + yt * yrx + zt * zrx
+ local y = xt * xry + yt * yry + zt * zry
+ local z = zt
+ -- numerical derivatives by central differences
+ local dfx = (func(xt+ha,yt) - func(xt-ha,yt)) / hb
+ local dfy = (func(xt,yt+ha) - func(xt,yt-ha)) / hb
+ -- compute brightness factor at a point
+ local ztp = zt - zp
+ local ytp = yt - yp
+ local xtp = xt - xp
+ local ztp = zt - zp
+ local ytp = yt - yp
+ local xtp = xt - xp
+ local ca = -ztp + dfy*ytp + dfx*xtp
+ local cb = sqrt(1+dfx*dfx+dfy*dfy)
+ local cc = sqrt(ztp*ztp + ytp*ytp + xtp*xtp)
+ local fac = bf*ca/(cb*cc*cc*cc)
+ -- addition: check range
+ if not minf then
+ minf = fac
+ maxf = fac
+ elseif fac < minf then
+ minf = fac
+ elseif fac > maxf then
+ maxf = fac
+ end
+ --
+ data[i][j] = { x, y, fac }
+ --
+ yt = yt + dy
+ end
+ xt = xt + dx
+ end
+ local result = { }
+ local r = 0
+ local range = maxf - minf
+ local cl = linecolor or 1
+ local enforce = attributes.colors.model == "cmyk"
+ for i=0,nx-1 do
+ for j=0,ny-1 do
+ -- points
+ local z1 = data[i] [j]
+ local z2 = data[i] [j+1]
+ local z3 = data[i+1][j+1]
+ local z4 = data[i+1][j]
+ -- color
+ local cf = z1[3]
+ if clip then
+ -- best clip here if needed
+ if cf < 0 then
+ cf = 0
+ elseif cf > 1 then
+ cf = 1
+ end
+ else
+ -- or remap when we want to
+ cf = (z1[3] - minf) / range
+ end
+ local z11 = z1[1]
+ local z12 = z1[2]
+ local z21 = z2[1]
+ local z22 = z2[2]
+ local z31 = z3[1]
+ local z32 = z3[2]
+ local z41 = z4[1]
+ local z42 = z4[2]
+ -- if lines then
+ -- -- fill first and draw then, previous shapes can be covered
+ -- else
+ -- -- fill and draw in one go to prevent artifacts
+ -- end
+ local cr, cg, cb = color(cf)
+ if not cr then cr = 0 end
+ if not cg then cg = 0 end
+ if not cb then cb = 0 end
+ if enforce then
+ cr, cg, cb = 1 - cr, 1 - cg, 1 - cb
+ r = r + 1
+ if lines then
+ result[r] = f_fill_cmy(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ r = r + 1
+ result[r] = f_draw_cmy(z11,z12,z21,z22,z31,z32,z41,z42,cl)
+ else
+ result[r] = f_mesh_cmy(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ end
+ else
+ r = r + 1
+ if lines then
+ result[r] = f_fill_rgb(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ r = r + 1
+ result[r] = f_draw_rgb(z11,z12,z21,z22,z31,z32,z41,z42,cl)
+ else
+ result[r] = f_mesh_rgb(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ end
+ end
+ end
+ end
+ mp.direct(concat(result))
+end