Add untested efficient flood fill algorithm

http://www.adammil.net/blog/v126_A_More_Efficient_Flood_Fill.html
This commit is contained in:
HybridDog 2019-12-01 11:00:53 +01:00
parent 8d78f3cb9b
commit dc368f7a7e
2 changed files with 132 additions and 11 deletions

116
adammil_flood_fill.lua Normal file

@ -0,0 +1,116 @@
-- http://www.adammil.net/blog/v126_A_More_Efficient_Flood_Fill.html
local can_go
local marked_places
local function calc_2d_index(x, y)
return (y + 32768) * 65536 + x + 32768
end
local function mark(x, y)
marked_places[calc_2d_index(x, y)] = true
end
local _fill
local function fill(x, y)
if can_go(x, y) then
_fill(x, y)
end
end
local corefill
function _fill(x, y)
while true do
local ox = x
local oy = y
while can_go(x, y-1) do
y = y-1
end
while can_go(x-1, y) do
x = x-1
end
if x == ox
and y == oy then
break
end
end
corefill(x, y)
end
function corefill(x, y)
local lastcnt = 0
repeat
local cnt = 0
local sx = x
if lastcnt ~= 0
and not can_go(y, x) then
-- go right to find the x start
repeat
lastcnt = lastcnt-1
if lastcnt == 0 then
return
end
x = x+1
until can_go(x, y)
sx = x
else
-- go left if possible, and mark and _fill above
while can_go(x-1, y) do
x = x-1
mark(x, y)
if can_go(x, y-1) then
_fill(x, y-1)
end
cnt = cnt+1
lastcnt = lastcnt+1
end
end
-- go right if possible, and mark
while can_go(sx, y) do
mark(sx, y)
cnt = cnt+1
sx = sx+1
end
if cnt < lastcnt then
local e = x + lastcnt
sx = sx+1
while sx < e do
if can_go(sx, y) then
corefill(sx, y)
end
sx = sx+1
end
elseif cnt > lastcnt then
local ux = x + lastcnt + 1
while ux < sx do
if can_go(ux, y-1) then
_fill(ux, y-1)
end
ux = ux+1
end
end
lastcnt = cnt
y = y+1
until lastcnt == 0
end
local function apply_fill(go_test, x0, y0, allow_revisit)
if allow_revisit then
can_go = go_test
else
local visited = {}
can_go = function(x, y)
local vi = calc_2d_index(x, y)
if visited[vi] then
return false
end
visited[vi] = true
return go_test(x, y)
end
end
marked_places = {}
fill(x0, y0)
return marked_places
end
return apply_fill

@ -1,4 +1,4 @@
local load_time_start = minetest.get_us_time() local path = minetest.get_modpath"vector_extras"
local funcs = {} local funcs = {}
@ -399,6 +399,21 @@ function funcs.from_number(i)
return {x=i, y=i, z=i} return {x=i, y=i, z=i}
end end
local adammil_fill = dofile(path .. "/adammil_flood_fill.lua")
function funcs.search_2d(go_test, x0, y0, allow_revisit, give_map)
marked_places = adammil_fill(go_test, x0, y0, allow_revisit)
if give_map then
return marked_places
end
local l = {}
for vi in pairs(marked_places) do
local x = (vi % 65536) - 32768
local y = (math.floor(x / 65536) % 65536) - 32768
l[#l+1] = {x, y}
end
return l
end
local explosion_tables = {} local explosion_tables = {}
function funcs.explosion_table(r) function funcs.explosion_table(r)
local table = explosion_tables[r] local table = explosion_tables[r]
@ -961,7 +976,6 @@ end
vector_extras_functions = funcs vector_extras_functions = funcs
local path = minetest.get_modpath"vector_extras"
dofile(path .. "/legacy.lua") dofile(path .. "/legacy.lua")
--dofile(minetest.get_modpath("vector_extras").."/vector_meta.lua") --dofile(minetest.get_modpath("vector_extras").."/vector_meta.lua")
@ -976,12 +990,3 @@ for name,func in pairs(funcs) do
vector[name] = func vector[name] = func
end end
end end
local time = (minetest.get_us_time() - load_time_start) / 1000000
local msg = "[vector_extras] loaded after ca. " .. time .. " seconds."
if time > 0.01 then
print(msg)
else
minetest.log("info", msg)
end