Add //count

This commit is contained in:
Starbeamrainbowlabs 2020-05-12 00:38:42 +01:00
parent f0dc2f5a3c
commit f3a8c1dd64
No known key found for this signature in database
GPG Key ID: 1BE5172E637709C2
8 changed files with 144 additions and 25 deletions

@ -6,15 +6,19 @@
-- @author Starbeamrainbowlabs -- @author Starbeamrainbowlabs
worldeditadditions = {} worldeditadditions = {}
worldeditadditions.modpath = minetest.get_modpath("worldeditadditions")
dofile(worldeditadditions.modpath.."/utils/strings.lua")
dofile(worldeditadditions.modpath.."/utils/numbers.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/utils.lua") dofile(worldeditadditions.modpath.."/utils.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/floodfill.lua") dofile(worldeditadditions.modpath.."/lib/floodfill.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/overlay.lua") dofile(worldeditadditions.modpath.."/lib/overlay.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/ellipsoid.lua") dofile(worldeditadditions.modpath.."/lib/ellipsoid.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/torus.lua") dofile(worldeditadditions.modpath.."/lib/torus.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/maze2d.lua") dofile(worldeditadditions.modpath.."/lib/walls.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/maze3d.lua") dofile(worldeditadditions.modpath.."/lib/maze2d.lua")
dofile(worldeditadditions.modpath.."/lib/maze3d.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/walls.lua") dofile(worldeditadditions.modpath.."/lib/count.lua")
dofile(minetest.get_modpath("worldeditadditions") .. "/lib/bonemeal.lua") dofile(worldeditadditions.modpath.."/lib/bonemeal.lua")

@ -0,0 +1,48 @@
--- Counts the nodes in a given area.
-- @module worldeditadditions.count
-- ██████ ██████ ██ ██ ███ ██ ████████
-- ██ ██ ██ ██ ██ ████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██████ ██ ████ ██
function worldeditadditions.count(pos1, pos2)
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
-- pos2 will always have the highest co-ordinates now
-- Fetch the nodes in the specified area
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
local data = manip:get_data()
-- z y x is the preferred loop order (because CPU cache I'd guess, since then we're iterating linearly through the data array)
local counts = {}
for z = pos2.z, pos1.z, -1 do
for y = pos2.y, pos1.y, -1 do
for x = pos2.x, pos1.x, -1 do
local next = data[area:index(x, y, z)]
if not counts[next] then
counts[next] = 0
end
counts[next] = counts[next] + 1
end
end
end
local total = worldedit.volume(pos1, pos2)
local results = {}
for node_name, count in pairs(counts) do
table.insert(results, {
count,
tostring(worldeditadditions.round((count / total) * 100, 2)).."%",
minetest.get_name_from_content_id(node_name)
})
end
table.sort(results, function(a, b) return a[1] < b[1] end)
-- Save the modified nodes back to disk & return
-- No need to save - this function doesn't actually change anything
-- worldedit.manip_helpers.finish(manip, data)
return true, results, total
end

@ -1,7 +1,5 @@
local we_c = worldeditadditions_commands
-- From http://lua-users.org/wiki/SimpleRound -- From http://lua-users.org/wiki/SimpleRound
function we_c.round(num, numDecimalPlaces) function worldeditadditions.round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0) local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult return math.floor(num * mult + 0.5) / mult
end end

@ -1,9 +1,6 @@
-- Licence: GPLv2 (MPL-2.0 is compatible, so we can use this here) -- Licence: GPLv2 (MPL-2.0 is compatible, so we can use this here)
-- Source: https://stackoverflow.com/a/43582076/1460422 -- Source: https://stackoverflow.com/a/43582076/1460422
local we_c = worldeditadditions_commands
-- gsplit: iterate over substrings in a string separated by a pattern -- gsplit: iterate over substrings in a string separated by a pattern
-- --
-- Parameters: -- Parameters:
@ -18,7 +15,7 @@ local we_c = worldeditadditions_commands
-- for substr in gsplit(text, pattern, plain) do -- for substr in gsplit(text, pattern, plain) do
-- doSomething(substr) -- doSomething(substr)
-- end -- end
function we_c.gsplit(text, pattern, plain) function worldeditadditions.gsplit(text, pattern, plain)
local splitStart, length = 1, #text local splitStart, length = 1, #text
return function () return function ()
if splitStart then if splitStart then
@ -54,10 +51,49 @@ end
-- string, not a Lua pattern -- string, not a Lua pattern
-- --
-- Returns: table (a sequence table containing the substrings) -- Returns: table (a sequence table containing the substrings)
function we_c.split(text, pattern, plain) function worldeditadditions.split(text, pattern, plain)
local ret = {} local ret = {}
for match in we_c.gsplit(text, pattern, plain) do for match in worldeditadditions.gsplit(text, pattern, plain) do
table.insert(ret, match) table.insert(ret, match)
end end
return ret return ret
end end
--- Pads str to length len with char from right
-- @source https://snipplr.com/view/13092/strlpad--pad-string-to-the-left
function worldeditadditions.str_padend(str, len, char)
if char == nil then char = ' ' end
return str .. string.rep(char, len - #str)
end
--- Pads str to length len with char from left
-- Adapted from the above
function worldeditadditions.str_padstart(str, len, char)
if char == nil then char = ' ' end
return string.rep(char, len - #str) .. str
end
function worldeditadditions.make_ascii_table(data)
local extra_padding = 2
local result = {}
local max_lengths = {}
for y = 1, #data, 1 do
for x = 1, #data[y], 1 do
if not max_lengths[x] then
max_lengths[x] = 0
end
max_lengths[x] = math.max(max_lengths[x], #tostring(data[y][x]) + extra_padding)
end
end
for _key, row in ipairs(data) do
local row_result = {}
for i = 1, #row, 1 do
row_result[#row_result + 1] = worldeditadditions.str_padend(tostring(row[i]), max_lengths[i], " ")
end
result[#result+1] = table.concat(row_result, "")
end
-- TODO: Add multi-column support here
return table.concat(result, "\n")
end

@ -15,7 +15,7 @@ worldedit.register_command("bonemeal", {
params_text = "1" params_text = "1"
end end
local parts = we_c.split(params_text, "%s+", false) local parts = worldeditadditions.split(params_text, "%s+", false)
local strength = 1 local strength = 1
local chance = 1 local chance = 1
@ -52,7 +52,7 @@ worldedit.register_command("bonemeal", {
-- nodes_bonemealed is an error message here because success == false -- nodes_bonemealed is an error message here because success == false
return success, nodes_bonemealed return success, nodes_bonemealed
end end
local percentage = we_c.round((nodes_bonemealed / candidates)*100, 2) local percentage = worldeditadditions.round((nodes_bonemealed / candidates)*100, 2)
local time_taken = os.clock() - start_time local time_taken = os.clock() - start_time
minetest.log("action", name .. " used //bonemeal at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos2[name])..", bonemealing " .. nodes_bonemealed.." nodes (out of "..candidates.." nodes) at strength "..strength.." in "..time_taken.."s") minetest.log("action", name .. " used //bonemeal at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos2[name])..", bonemealing " .. nodes_bonemealed.." nodes (out of "..candidates.." nodes) at strength "..strength.." in "..time_taken.."s")

@ -0,0 +1,35 @@
local we_c = worldeditadditions_commands
-- ██████ ██████ ██ ██ ███ ██ ████████
-- ██ ██ ██ ██ ██ ████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██████ ██ ████ ██
worldedit.register_command("count", {
params = "",
description = "Counts all the nodes in the defined region.",
privs = { worldedit = true },
require_pos = 2,
parse = function(params_text)
return true
end,
nodes_needed = function(name)
-- We don't actually modify anything, but without returning a
-- number here safe_region doesn't work
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
end,
func = function(name)
local start_time = os.clock()
local success, counts, total = worldeditadditions.count(worldedit.pos1[name], worldedit.pos2[name], target_node)
local result = worldeditadditions.make_ascii_table(counts).."\n"..
string.rep("=", 6 + #tostring(total) + 6).."\n"..
"Total "..total.." nodes\n"
local time_taken = os.clock() - start_time
minetest.log("action", name.." used //count at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos1[name])..", counting "..total.." nodes in "..time_taken.."s")
return true, result
end
})

@ -11,7 +11,7 @@ local function parse_params_maze(params_text, is_3d)
return false, "No arguments specified" return false, "No arguments specified"
end end
local parts = we_c.split(params_text, "%s+", false) local parts = worldeditadditions.split(params_text, "%s+", false)
local replace_node = parts[1] local replace_node = parts[1]
local seed = os.time() local seed = os.time()

@ -10,9 +10,6 @@ local we_c = worldeditadditions_commands
we_c.modpath = minetest.get_modpath("worldeditadditions_commands") we_c.modpath = minetest.get_modpath("worldeditadditions_commands")
dofile(we_c.modpath.."/utils/strings.lua")
dofile(we_c.modpath.."/utils/numbers.lua")
dofile(we_c.modpath.."/multi.lua") dofile(we_c.modpath.."/multi.lua")
-- We no longer need our own implementation of safe_region thanks to @sfan5's -- We no longer need our own implementation of safe_region thanks to @sfan5's
@ -24,9 +21,10 @@ dofile(we_c.modpath.."/commands/floodfill.lua")
dofile(we_c.modpath.."/commands/overlay.lua") dofile(we_c.modpath.."/commands/overlay.lua")
dofile(we_c.modpath.."/commands/ellipsoid.lua") dofile(we_c.modpath.."/commands/ellipsoid.lua")
dofile(we_c.modpath.."/commands/torus.lua") dofile(we_c.modpath.."/commands/torus.lua")
dofile(we_c.modpath.."/commands/walls.lua")
dofile(we_c.modpath.."/commands/maze.lua") dofile(we_c.modpath.."/commands/maze.lua")
dofile(we_c.modpath.."/commands/walls.lua") dofile(we_c.modpath.."/commands/count.lua")
-- Don't registry the //bonemeal command if the bonemeal mod isn't present -- Don't registry the //bonemeal command if the bonemeal mod isn't present
if minetest.get_modpath("bonemeal") then if minetest.get_modpath("bonemeal") then