From db5d25d1deeff630d6efea2932c40ddec2deb173 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Tue, 28 Dec 2021 18:31:46 +0000 Subject: [PATCH] //convolve: update matrix & kernel to use Vector3 A lot of the maths remains in the old style, but at least it doesn't take a zero-indexed table --- worldeditadditions/lib/conv/conv.lua | 18 +++--- worldeditadditions/lib/conv/convolve.lua | 55 +++++++++++-------- worldeditadditions/lib/erode/snowballs.lua | 3 +- .../commands/convolve.lua | 36 +++++++----- 4 files changed, 68 insertions(+), 44 deletions(-) diff --git a/worldeditadditions/lib/conv/conv.lua b/worldeditadditions/lib/conv/conv.lua index 0330f95..b374fea 100644 --- a/worldeditadditions/lib/conv/conv.lua +++ b/worldeditadditions/lib/conv/conv.lua @@ -1,3 +1,5 @@ +local Vector3 = worldeditadditions.Vector3 + worldeditadditions.conv = {} dofile(worldeditadditions.modpath.."/lib/conv/kernels.lua") @@ -41,14 +43,16 @@ end function worldeditadditions.convolve(pos1, pos2, kernel, kernel_size) pos1, pos2 = worldedit.sort_pos(pos1, pos2) - local border_size = {} - border_size[0] = (kernel_size[0]-1) / 2 -- height - border_size[1] = (kernel_size[1]-1) / 2 -- width + local border_size = Vector3.new( + (kernel_size.x-1) / 2, -- x = height + 0, + (kernel_size.z-1) / 2 -- z = width + ) - pos1.z = pos1.z - border_size[0] - pos2.z = pos2.z + border_size[0] - pos1.x = pos1.x - border_size[1] - pos2.x = pos2.x + border_size[1] + pos1.z = pos1.z - border_size.x + pos2.z = pos2.z + border_size.x + pos1.x = pos1.x - border_size.z + pos2.x = pos2.x + border_size.z local manip, area = worldedit.manip_helpers.init(pos1, pos2) local data = manip:get_data() diff --git a/worldeditadditions/lib/conv/convolve.lua b/worldeditadditions/lib/conv/convolve.lua index bcf3e4b..50b86d0 100644 --- a/worldeditadditions/lib/conv/convolve.lua +++ b/worldeditadditions/lib/conv/convolve.lua @@ -1,31 +1,39 @@ - +local wea = worldeditadditions +local Vector3 = wea.Vector3 --[[ Convolves over a given 2D heightmap with a given matrix. Note that this *mutates* the given heightmap. Note also that the dimensions of the matrix must *only* be odd. -@param {number[]} heightmap The 2D heightmap to convolve over. -@param {[number,number]} heightmap_size The size of the heightmap as [ height, width ] -@param {number[]} matrix The matrix to convolve with. -@param {[number, number]} matrix_size The size of the convolution matrix as [ height, width ] +@param {number[]} heightmap The 2D heightmap to convolve over. +@param Vector3 heightmap_size The size of the heightmap as an X/Z Vector3 instance. +@param {number[]} matrix The matrix to convolve with. +@param Vector3 matrix_size The size of the convolution matrix as an X/Z Vector3 instance. ]]-- function worldeditadditions.conv.convolve(heightmap, heightmap_size, matrix, matrix_size) - if matrix_size[0] % 2 ~= 1 or matrix_size[1] % 2 ~= 1 then + if matrix_size.x % 2 ~= 1 or matrix_size.z % 2 ~= 1 then return false, "Error: The matrix size must contain only odd numbers (even number detected)" end - local border_size = {} - border_size[0] = (matrix_size[0]-1) / 2 -- height - border_size[1] = (matrix_size[1]-1) / 2 -- width - -- print("[convolve] matrix_size", matrix_size[0], matrix_size[1]) - -- print("[convolve] border_size", border_size[0], border_size[1]) + -- We need to reference a *copy* of the heightmap when convolving + -- This is because we need the original values when we perform a + -- convolution on a given pixel + local heightmap_copy = wea.table.shallowcopy(heightmap) + + local border_size = Vector3.new( + (matrix_size.x-1) / 2, -- x = height + 0, + (matrix_size.z-1) / 2 -- z = width + ) + -- print("[convolve] matrix_size", matrix_size.x, matrix_size.z) + -- print("[convolve] border_size", border_size.x, border_size.z) -- print("[convolve] heightmap_size: ", heightmap_size.z, heightmap_size.x) -- - -- print("[convolve] z: from", (heightmap_size.z-border_size[0]) - 1, "to", border_size[0], "step", -1) - -- print("[convolve] x: from", (heightmap_size.x-border_size[1]) - 1, "to", border_size[1], "step", -1) + -- print("[convolve] z: from", (heightmap_size.z-border_size.x) - 1, "to", border_size.x, "step", -1) + -- print("[convolve] x: from", (heightmap_size.x-border_size.z) - 1, "to", border_size.z, "step", -1) -- Convolve over only the bit that allows us to use the full convolution matrix - for z = (heightmap_size.z-border_size[0]) - 1, border_size[0], -1 do - for x = (heightmap_size.x-border_size[1]) - 1, border_size[1], -1 do + for z = (heightmap_size.z-border_size.x) - 1, border_size.x, -1 do + for x = (heightmap_size.x-border_size.z) - 1, border_size.z, -1 do local total = 0 @@ -33,21 +41,22 @@ function worldeditadditions.conv.convolve(heightmap, heightmap_size, matrix, mat -- print("[convolve/internal] z", z, "x", x, "hi", hi) -- No continue statement in Lua :-/ - if heightmap[hi] ~= -1 then - for mz = matrix_size[0]-1, 0, -1 do - for mx = matrix_size[1]-1, 0, -1 do - local mi = (mz * matrix_size[1]) + mx - local cz = z + (mz - border_size[0]) - local cx = x + (mx - border_size[1]) + if heightmap_copy[hi] ~= -1 then + for mz = matrix_size.x-1, 0, -1 do + for mx = matrix_size.z-1, 0, -1 do + local mi = (mz * matrix_size.z) + mx + local cz = z + (mz - border_size.x) + local cx = x + (mx - border_size.z) local i = (cz * heightmap_size.x) + cx -- A value of -1 = nothing in this column (so we should ignore it) - if heightmap[i] ~= -1 then - total = total + (matrix[mi] * heightmap[i]) + if heightmap_copy[i] ~= -1 then + total = total + (matrix[mi] * heightmap_copy[i]) end end end + -- Rounding hack - ref https://stackoverflow.com/a/18313481/1460422 -- heightmap[hi] = math.floor(total + 0.5) heightmap[hi] = math.ceil(total) diff --git a/worldeditadditions/lib/erode/snowballs.lua b/worldeditadditions/lib/erode/snowballs.lua index 5ffc403..29853a5 100644 --- a/worldeditadditions/lib/erode/snowballs.lua +++ b/worldeditadditions/lib/erode/snowballs.lua @@ -1,3 +1,4 @@ +local Vector3 = worldeditadditions.Vector3 -- Test command: //multi //fp set1 1313 6 5540 //fp set2 1338 17 5521 //erode snowballs @@ -132,7 +133,7 @@ function worldeditadditions.erode.snowballs(heightmap_initial, heightmap, height if not params.noconv then local success, matrix = worldeditadditions.get_conv_kernel("gaussian", 3, 3) if not success then return success, matrix end - local matrix_size = {} matrix_size[0] = 3 matrix_size[1] = 3 + local matrix_size = Vector3.new(3, 0, 3) worldeditadditions.conv.convolve( heightmap, heightmap_size, matrix, diff --git a/worldeditadditions_commands/commands/convolve.lua b/worldeditadditions_commands/commands/convolve.lua index 1ad1171..878e7d8 100644 --- a/worldeditadditions_commands/commands/convolve.lua +++ b/worldeditadditions_commands/commands/convolve.lua @@ -1,3 +1,6 @@ +local wea = worldeditadditions +local Vector3 = wea.Vector3 + -- ██████ ██████ ███ ██ ██ ██ ██████ ██ ██ ██ ███████ -- ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █████ @@ -11,8 +14,8 @@ worldedit.register_command("convolve", { parse = function(params_text) if not params_text then params_text = "" end - -- local parts = worldeditadditions.split(params_text, "%s+", false) - local parts = worldeditadditions.split_shell(params_text) + -- local parts = wea.split(params_text, "%s+", false) + local parts = wea.split_shell(params_text) local kernel_name = "gaussian" local width = 5 @@ -23,7 +26,7 @@ worldedit.register_command("convolve", { kernel_name = parts[1] end if #parts >= 2 then - local parts_dimension = worldeditadditions.split(parts[2], ",%s*", false) + local parts_dimension = wea.split(parts[2], ",%s*", false) width = tonumber(parts_dimension[1]) if not width then return false, "Error: Invalid width (it must be a positive odd integer)." @@ -50,26 +53,33 @@ worldedit.register_command("convolve", { return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) end, func = function(name, kernel_name, kernel_width, kernel_height, sigma) - local start_time = worldeditadditions.get_ms_time() + local start_time = wea.get_ms_time() - local success, kernel = worldeditadditions.get_conv_kernel(kernel_name, kernel_width, kernel_height, sigma) + local success, kernel = wea.get_conv_kernel(kernel_name, kernel_width, kernel_height, sigma) if not success then return success, kernel end - local kernel_size = {} - kernel_size[0] = kernel_height - kernel_size[1] = kernel_width + local kernel_size = Vector3.new( + kernel_height, + 0, + kernel_width + ) + + local pos1, pos2 = Vector3.sort( + worldedit.pos1[name], + worldedit.pos2[name] + ) local stats - success, stats = worldeditadditions.convolve( - worldedit.pos1[name], worldedit.pos2[name], + success, stats = wea.convolve( + pos1, pos2, kernel, kernel_size ) if not success then return success, stats end - local time_taken = worldeditadditions.get_ms_time() - start_time + local time_taken = wea.get_ms_time() - start_time - minetest.log("action", name.." used //convolve at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos2[name])..", adding "..stats.added.." nodes and removing "..stats.removed.." nodes in "..time_taken.."s") - return true, "Added "..stats.added.." and removed "..stats.removed.." nodes in " .. worldeditadditions.format.human_time(time_taken) + minetest.log("action", name.." used //convolve at "..pos1.." - "..pos2..", adding "..stats.added.." nodes and removing "..stats.removed.." nodes in "..time_taken.."s") + return true, "Added "..stats.added.." and removed "..stats.removed.." nodes in " .. wea.format.human_time(time_taken) end })