2018-05-20 10:52:16 +00:00
--- Flood-fill command for complex lakes etc.
-- @module worldeditadditions.floodfill
2018-05-20 11:23:09 +00:00
-------------------------------------------------------------------------------
--- A Queue implementation, taken & adapted from https://www.lua.org/pil/11.4.html
-- @submodule worldeditadditions.utils.queue
2018-05-20 10:52:16 +00:00
2018-05-20 11:23:09 +00:00
local Queue = { }
function Queue . new ( )
return { first = 0 , last = - 1 }
end
function Queue . enqueue ( queue , value )
local new_last = queue.last + 1
queue.last = new_last
queue [ new_last ] = value
end
2018-05-20 12:05:03 +00:00
function Queue . contains ( queue , value )
for i = queue.first , queue.last do
if queue [ i ] == value then
return true
end
end
return false
end
2018-05-20 11:23:09 +00:00
function Queue . is_empty ( queue )
return queue.first > queue.last
end
function Queue . dequeue ( queue )
local first = queue.first
if Queue.is_empty ( queue ) then
error ( " Error: The queue is empty! " )
end
local value = queue [ first ]
queue [ first ] = nil -- Help the garbage collector out
queue.first = first + 1
return value
end
-------------------------------------------------------------------------------
2018-05-20 12:05:03 +00:00
-- These really should be in a utilities file, but Lua is being stupid and preventing access to it (and minetest is also being stupid, as we can't modularise our code the way you ought to be able to - or at least the documentation on dofile() is so poor I've no idea at this point)
local function vector2string ( v )
return " ( " .. v.x .. " , " .. v.y .. " , " .. v.z .. " ) "
end
local clock = os.clock
function sleep ( n ) -- seconds
local t0 = clock ( )
while clock ( ) - t0 <= n do end
end
2018-05-20 11:23:09 +00:00
function worldedit . floodfill ( start_pos , radius , replace_node )
2018-05-20 10:52:16 +00:00
-- Calculate the area we want to modify
2018-05-20 11:23:09 +00:00
local pos1 = vector.add ( start_pos , { x = radius , y = 0 , z = radius } )
local pos2 = vector.subtract ( start_pos , { x = radius , y = radius , z = radius } )
2018-05-20 10:52:16 +00:00
pos1 , pos2 = worldedit.sort_pos ( pos1 , pos2 ) -- Just in case
2018-05-20 11:23:09 +00:00
minetest.log ( " action " , " radius: " .. radius )
2018-05-20 12:05:03 +00:00
minetest.log ( " action " , " pos1: " .. vector2string ( pos1 ) )
minetest.log ( " action " , " pos2: " .. vector2string ( pos2 ) )
2018-05-20 11:23:09 +00:00
2018-05-20 10:52:16 +00:00
-- Fetch the nodes in the specified area
2018-05-20 11:23:09 +00:00
local manip , area = worldedit.manip_helpers . init ( pos1 , pos2 )
2018-05-20 10:52:16 +00:00
local data = manip : get_data ( )
-- Setup for the floodfill operation itself
local start_pos_index = area : index ( start_pos.x , start_pos.y , start_pos.z ) ;
local search_id = data [ start_pos_index ]
local replace_id = minetest.get_content_id ( replace_node )
2018-05-20 11:23:09 +00:00
minetest.log ( " action " , " ids: " .. search_id .. " -> " .. replace_id )
2018-05-20 10:52:16 +00:00
local count = 0
local remaining_nodes = Queue.new ( )
Queue.enqueue ( remaining_nodes , start_pos_index )
-- Do the floodfill
while Queue.is_empty ( remaining_nodes ) == false do
local cur = Queue.dequeue ( remaining_nodes )
-- TODO: Check distance from start_pos
-- Replace this node
data [ cur ] = replace_id
count = count + 1
2018-05-20 12:05:03 +00:00
2018-05-20 10:52:16 +00:00
-- Check all the nearby nodes
-- We don't need to go upwards here, since we're filling in lake-style
2018-05-20 12:05:03 +00:00
local xplus = cur + 1 -- +X
if data [ xplus ] == search_id and not Queue.contains ( remaining_nodes , xplus ) then
-- minetest.log("action", "[floodfill] [+X] index " .. xplus .. " is a " .. data[xplus] .. ", searching for a " .. search_id)
Queue.enqueue ( remaining_nodes , xplus )
2018-05-20 10:52:16 +00:00
end
2018-05-20 12:05:03 +00:00
local xminus = cur - 1 -- -X
if data [ xminus ] == search_id and not Queue.contains ( remaining_nodes , xminus ) then
-- minetest.log("action", "[floodfill] [-X] index " .. xminus .. " is a " .. data[xminus] .. ", searching for a " .. search_id)
Queue.enqueue ( remaining_nodes , xminus )
2018-05-20 10:52:16 +00:00
end
2018-05-20 12:05:03 +00:00
local zplus = cur + area.zstride -- +Z
if data [ zplus ] == search_id and not Queue.contains ( remaining_nodes , zplus ) then
-- minetest.log("action", "[floodfill] [+Z] index " .. zplus .. " is a " .. data[zplus] .. ", searching for a " .. search_id)
Queue.enqueue ( remaining_nodes , zplus )
2018-05-20 10:52:16 +00:00
end
2018-05-20 12:05:03 +00:00
local zminus = cur - area.zstride -- -Z
if data [ zminus ] == search_id and not Queue.contains ( remaining_nodes , zminus ) then
-- minetest.log("action", "[floodfill] [-Z] index " .. zminus .. " is a " .. data[zminus] .. ", searching for a " .. search_id)
Queue.enqueue ( remaining_nodes , zminus )
2018-05-20 10:52:16 +00:00
end
2018-05-20 12:05:03 +00:00
local yminus = cur - area.ystride -- -Y
if data [ yminus ] == search_id and not Queue.contains ( remaining_nodes , yminus ) then
-- minetest.log("action", "[floodfill] [-Y] index " .. yminus .. " is a " .. data[yminus] .. ", searching for a " .. search_id)
Queue.enqueue ( remaining_nodes , yminus )
2018-05-20 10:52:16 +00:00
end
2018-05-20 12:05:03 +00:00
count = count + 1
2018-05-20 10:52:16 +00:00
end
-- Save the modified nodes back to disk & return
worldedit.manip_helpers . finish ( manip , data )
return count
end
return floodfill