diff options
author | Hans Hagen <pragma@wxs.nl> | 2020-11-23 19:48:34 +0100 |
---|---|---|
committer | Context Git Mirror Bot <phg@phi-gamma.net> | 2020-11-23 19:48:34 +0100 |
commit | 18499e46a49b8ccf4346686d1cf626ada33935b8 (patch) | |
tree | bd0ae7b601b323e20954c10c07598637d9403e00 /tex/context/base/mkiv/mlib-cnt.lmt | |
parent | 4b089e589d39346a66a27d04f9857fe16e4b7b41 (diff) | |
download | context-18499e46a49b8ccf4346686d1cf626ada33935b8.tar.gz |
2020-11-23 18:39:00
Diffstat (limited to 'tex/context/base/mkiv/mlib-cnt.lmt')
-rw-r--r-- | tex/context/base/mkiv/mlib-cnt.lmt | 2022 |
1 files changed, 0 insertions, 2022 deletions
diff --git a/tex/context/base/mkiv/mlib-cnt.lmt b/tex/context/base/mkiv/mlib-cnt.lmt deleted file mode 100644 index 667384ed7..000000000 --- a/tex/context/base/mkiv/mlib-cnt.lmt +++ /dev/null @@ -1,2022 +0,0 @@ -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 |