From f3745dc6ba2f8959a1ef390ecc25e63d1defdc8a Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Sun, 10 Jun 2018 13:48:11 +0100 Subject: [PATCH] Fix a few small bugs --- README.md | 17 ++++++ worldeditadditions/ellipsoid.lua | 2 - worldeditadditions/init.lua | 1 + worldeditadditions/torus.lua | 78 ++++++++++++++++++++++++ worldeditadditions_commands/init.lua | 91 +++++++++++++++++++++++++++- 5 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 worldeditadditions/torus.lua diff --git a/README.md b/README.md index 299bc06..a238e2f 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,23 @@ Creates a hollow ellipsoid at position 1 with the radius `(rx, ry, rz)`. Works t //hollowellipsoid 21 11 41 stone ``` +### `//torus ` +Creates a solid torus at position 1 with the specified major and minor radii. The major radius is the distance from the centre of the torus to the centre of the circle bit, and the minor radius is the radius of the circle bit. + +``` +//torus 10 5 15 ice +//torus 3 5 10 dirt +//torus 20 10 40 air +``` + +### `//hollowtorus ` +Creates a hollow torus at position 1 with the radius `(rx, ry, rz)`. Works the same way as `//torus` does. + +``` +//hollowtorus 10 5 15 glass +//hollowtorus 21 11 41 stone +``` + ## Contributing Contributions are welcome! Please state in your pull request(s) that you release your contribution under the _Mozilla Public License 2.0_. diff --git a/worldeditadditions/ellipsoid.lua b/worldeditadditions/ellipsoid.lua index 83820b6..3c86fe6 100644 --- a/worldeditadditions/ellipsoid.lua +++ b/worldeditadditions/ellipsoid.lua @@ -18,8 +18,6 @@ function worldedit.ellipsoid(position, radius, target_node, hollow) local node_id_air = minetest.get_content_id("air") local stride_z, stride_y = area.zstride, area.ystride - -- TODO: This won't work, because we need to vary the calculation we use this in based on what part of the ellipsoid we're working on / in, not compare to a static value - local radius_distance_sq = worldeditadditions.vector.lengthsquared(radius) local count = 0 -- The number of nodes replaced diff --git a/worldeditadditions/init.lua b/worldeditadditions/init.lua index 035ee02..249e68d 100644 --- a/worldeditadditions/init.lua +++ b/worldeditadditions/init.lua @@ -11,3 +11,4 @@ dofile(minetest.get_modpath("worldeditadditions") .. "/utils.lua") dofile(minetest.get_modpath("worldeditadditions") .. "/floodfill.lua") dofile(minetest.get_modpath("worldeditadditions") .. "/overlay.lua") dofile(minetest.get_modpath("worldeditadditions") .. "/ellipsoid.lua") +dofile(minetest.get_modpath("worldeditadditions") .. "/torus.lua") diff --git a/worldeditadditions/torus.lua b/worldeditadditions/torus.lua new file mode 100644 index 0000000..6567ae7 --- /dev/null +++ b/worldeditadditions/torus.lua @@ -0,0 +1,78 @@ +--- Overlap command. Places a specified node on top of +-- @module worldeditadditions.overlay + +function worldedit.torus(position, major_radius, minor_radius, target_node, hollow) + -- position = { x, y, z } + local total_radius = major_radius + minor_radius + local inner_minor_radius = minor_radius - 1.75 + local major_radius_sq = major_radius*major_radius + local minor_radius_sq = minor_radius*minor_radius + local inner_minor_radius_sq = inner_minor_radius*inner_minor_radius + + -- Fetch the nodes in the specified area + -- OPTIMIZE: We should be able to calculate a more efficient box-area here + local manip, area = worldedit.manip_helpers.init_radius(position, total_radius) + local data = manip:get_data() + + local node_id = minetest.get_content_id(target_node) + local node_id_air = minetest.get_content_id("air") + + local stride_z, stride_y = area.zstride, area.ystride + + local count = 0 -- The number of nodes replaced + + local idx_z_base = area:index(position.x - total_radius, position.y - total_radius, position.z - total_radius) -- initial z offset + for z = -total_radius, total_radius do + local z_sq = z*z + + local idx_y_base = idx_z_base + for y = -total_radius, total_radius do + local y_sq = y*y + + local i = idx_y_base + for x = -total_radius, total_radius do + local x_sq = x*x + + -- (x^2+y^2+z^2-(a^2+b^2))^2-4 a b (b^2-z^2) + -- Where: + -- (x, y, z) is the point + -- a is the major radius (centre to centre of circle) + -- b is the minor radius (radius of circle + local comp_a = (x_sq+y_sq+z_sq - (major_radius_sq+minor_radius_sq)) + local test_value = comp_a*comp_a - 4*major_radius*minor_radius*(minor_radius_sq-z_sq) + + -- If we're inside the torus, then fill it in + if test_value <= 1 then + local place_ok = not hollow; + + if not place_ok then + -- It must be hollow! Do some additional calculations. + local inner_comp_a = (x_sq+y_sq+z_sq - (major_radius_sq+inner_minor_radius_sq)) + local inner_test_value = inner_comp_a*inner_comp_a - 4*major_radius*inner_minor_radius*(inner_minor_radius_sq-z_sq) + + -- It's only ok to place it if it's outside our inner torus + place_ok = inner_test_value >= 0 + end + + if place_ok then + data[i] = node_id + count = count + 1 + end + end + + + i = i + 1 + end + idx_y_base = idx_y_base + stride_y + + end + idx_z_base = idx_z_base + stride_z + + end + + + -- Save the modified nodes back to disk & return + worldedit.manip_helpers.finish(manip, data) + + return count +end diff --git a/worldeditadditions_commands/init.lua b/worldeditadditions_commands/init.lua index ac8adbd..e46f6ba 100644 --- a/worldeditadditions_commands/init.lua +++ b/worldeditadditions_commands/init.lua @@ -158,7 +158,7 @@ minetest.register_chatcommand("/ellipsoid", { -- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit? minetest.register_chatcommand("/hollowellipsoid", { params = " ", - description = "Creates a 3D hollow ellipsoid with a radius of (rx, ry, rz) at pos1, filled with .", + description = "Creates a 3D hollow ellipsoid with a radius of (rx, ry, rz) at pos1, made out of .", privs = { worldedit = true }, func = safe_region(function(name, params_text) local target_node, radius = parse_params_ellipsoid(params_text) @@ -181,10 +181,97 @@ minetest.register_chatcommand("/hollowellipsoid", { end, function(name, params_text) local target_node, radius = parse_params_ellipsoid(params_text) if not target_node or not radius then - worldedit.player_notify(name, "Error: Invalid input '" .. params_text .. "'. Try '/help /ellipsoid' to learn how to use this command.") + worldedit.player_notify(name, "Error: Invalid input '" .. params_text .. "'. Try '/help /hollowellipsoid' to learn how to use this command.") return 0 end return math.ceil(4/3 * math.pi * radius.x * radius.y * radius.z) end) }) + +-- ████████ ██████ ██████ ██ ██ ███████ +-- ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██████ ██ ██ ███████ +-- ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██████ ██ ██ ██████ ███████ +local function parse_params_torus(params_text) + local found, _, major_radius, minor_radius, replace_node = params_text:find("([0-9]+)%s+([0-9]+)%s+([a-z:_\\-]+)") + + if found == nil then + return nil, nil + end + + major_radius = tonumber(major_radius) + minor_radius = tonumber(minor_radius) + + replace_node = worldedit.normalize_nodename(replace_node) + + return replace_node, major_radius, minor_radius +end + +minetest.register_chatcommand("/torus", { + params = " ", + description = "Creates a 3D torus with a major radius of and a minor radius of at pos1, filled with .", + privs = { worldedit = true }, + func = safe_region(function(name, params_text) + local target_node, major_radius, minor_radius = parse_params_torus(params_text) + + if not target_node then + worldedit.player_notify(name, "Error: Invalid node name.") + return false + end + if not major_radius or not minor_radius then + worldedit.player_notify(name, "Error: Invalid radius(es).") + return false + end + + local start_time = os.clock() + local replaced = worldedit.torus(worldedit.pos1[name], major_radius, minor_radius, target_node, false) + local time_taken = os.clock() - start_time + + worldedit.player_notify(name, replaced .. " nodes replaced in " .. time_taken .. "s") + minetest.log("action", name .. " used //torus at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. replaced .. " nodes in " .. time_taken .. "s") + end, function(name, params_text) + local target_node, major_radius, minor_radius = parse_params_torus(params_text) + if not target_node or not major_radius or not minor_radius then + worldedit.player_notify(name, "Error: Invalid input '" .. params_text .. "'. Try '/help /torus' to learn how to use this command.") + return 0 + end + + return math.ceil(2 * math.pi*math.pi * major_radius * minor_radius*minor_radius) + end) +}) + +-- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit? +minetest.register_chatcommand("/hollowtorus", { + params = " ", + description = "Creates a 3D hollow torus with a major radius of and a minor radius of at pos1, made out of .", + privs = { worldedit = true }, + func = safe_region(function(name, params_text) + local target_node, major_radius, minor_radius = parse_params_torus(params_text) + + if not target_node then + worldedit.player_notify(name, "Error: Invalid node name.") + return false + end + if not major_radius or not minor_radius then + worldedit.player_notify(name, "Error: Invalid radius(es).") + return false + end + + local start_time = os.clock() + local replaced = worldedit.torus(worldedit.pos1[name], major_radius, minor_radius, target_node, true) + local time_taken = os.clock() - start_time + + worldedit.player_notify(name, replaced .. " nodes replaced in " .. time_taken .. "s") + minetest.log("action", name .. " used //hollowtorus at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. replaced .. " nodes in " .. time_taken .. "s") + end, function(name, params_text) + local target_node, major_radius, minor_radius = parse_params_torus(params_text) + if not target_node or not major_radius or not minor_radius then + worldedit.player_notify(name, "Error: Invalid input '" .. params_text .. "'. Try '/help /hollowtorus' to learn how to use this command.") + return 0 + end + + return math.ceil(2 * math.pi*math.pi * major_radius * minor_radius*minor_radius) + end) +})