From bb8456b71119ca6303b9e9706829a84dc7f81ab3 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Wed, 29 Oct 2014 22:47:08 -0400 Subject: Cleanup and fixup Non-stylistic changes: * Add LuaDoc/LDoc support. * Fix `clear_objects` area size calculation. * Fix `clear_objects` removing player objects. * Fix shadowing of marker entity name with player name. * Make visualization functions use `swap_node`. * Make hidden nodes unwalkable. * Prevent `hide` from hiding air. * Make deprecated functions log to deprecated stream when called. * Fixed `replaceinverse` not using normalized node names. * Added .gitignore. * Bump version to 1.1. Stylistic changes: * Change `x = function` to `function x`. * Change comment format. * Make missing VoxelManip error less obnoxious. * Move `sort_pos` into `common.lua`, which is a required module. * Remove local copies of `minetest`. * Remove `worldedit = worldedit or {}` from modules. * Replace replaceinverse with an inverse argument to `replace`. * Added `error()`s on on invalid axes. * Change `wip` to `TODO`. * Rename `clearobjects` to `clear_objects`. * Remove `hollow_{sphere,dome,cylinder}` and replace them with a hollow parameter to each function. * Add helpers to reduce code duplication. * Renamed `Chat Commands.md` to `ChatCommands.md`. --- worldedit/primitives.lua | 503 ++++++++++++++--------------------------------- 1 file changed, 153 insertions(+), 350 deletions(-) (limited to 'worldedit/primitives.lua') diff --git a/worldedit/primitives.lua b/worldedit/primitives.lua index 1baa29e..6d3b026 100644 --- a/worldedit/primitives.lua +++ b/worldedit/primitives.lua @@ -1,470 +1,273 @@ -worldedit = worldedit or {} -local minetest = minetest --local copy of global - ---adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added -worldedit.hollow_sphere = function(pos, radius, nodename) - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius} - local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore - end +--- Functions for creating primitive shapes. +-- @module worldedit.primitives - --fill selected area with node - local node_id = minetest.get_content_id(nodename) - local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1) - local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z - local zstride, ystride = area.zstride, area.ystride - local count = 0 - for z = -radius, radius do - local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed - for y = -radius, radius do - local newy = newz + (y + offsety) * ystride - for x = -radius, radius do - local squared = x * x + y * y + z * z - if squared >= min_radius and squared <= max_radius then --position is on surface of sphere - local i = newy + (x + offsetx) - nodes[i] = node_id - count = count + 1 - end - end - end - end +local mh = worldedit.manip_helpers - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() - return count -end +--- Adds a sphere of `node_name` centered at `pos`. +-- @param pos Position to center sphere at. +-- @param radius Sphere radius. +-- @param node_name Name of node to make shere of. +-- @param hollow Whether the sphere should be hollow. +-- @return The number of nodes added. +function worldedit.sphere(pos, radius, node_name, hollow) + local manip, area = mh.init_radius(pos, radius) ---adds a sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added -worldedit.sphere = function(pos, radius, nodename) - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius} - local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore - end + local data = mh.get_empty_data(area) - --fill selected area with node - local node_id = minetest.get_content_id(nodename) - local max_radius = radius * (radius + 1) - local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z - local zstride, ystride = area.zstride, area.ystride + -- Fill selected area with node + local node_id = minetest.get_content_id(node_name) + local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1) + local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z + local stride_z, stride_y = area.zstride, area.ystride local count = 0 for z = -radius, radius do - local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed + -- Offset contributed by z plus 1 to make it 1-indexed + local new_z = (z + offset_z) * stride_z + 1 for y = -radius, radius do - local newy = newz + (y + offsety) * ystride + local new_y = new_z + (y + offset_y) * stride_y for x = -radius, radius do - if x * x + y * y + z * z <= max_radius then --position is inside sphere - local i = newy + (x + offsetx) - nodes[i] = node_id + local squared = x * x + y * y + z * z + if squared <= max_radius and (not hollow or squared >= min_radius) then + -- Position is on surface of sphere + local i = new_y + (x + offset_x) + data[i] = node_id count = count + 1 end end end end - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() + mh.finish(manip, data) return count end ---adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added -worldedit.hollow_dome = function(pos, radius, nodename) - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius} - local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore - end - local miny, maxy = 0, radius +--- Adds a dome. +-- @param pos Position to center dome at. +-- @param radius Dome radius. Negative for concave domes. +-- @param node_name Name of node to make dome of. +-- @param hollow Whether the dome should be hollow. +-- @return The number of nodes added. +-- TODO: Add axis option. +function worldedit.dome(pos, radius, node_name, hollow) + local min_y, max_y = 0, radius if radius < 0 then radius = -radius - miny, maxy = -radius, 0 + min_y, max_y = -radius, 0 end - --fill selected area with node - local node_id = minetest.get_content_id(nodename) + local manip, area = mh.init_axis_radius(pos, "y", radius) + local data = mh.get_empty_data(area) + + -- Add dome + local node_id = minetest.get_content_id(node_name) local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1) - local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z - local zstride, ystride = area.zstride, area.ystride + local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z + local stride_z, stride_y = area.zstride, area.ystride local count = 0 for z = -radius, radius do - local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed - for y = miny, maxy do - local newy = newz + (y + offsety) * ystride + local new_z = (z + offset_z) * stride_z + 1 --offset contributed by z plus 1 to make it 1-indexed + for y = min_y, max_y do + local new_y = new_z + (y + offset_y) * stride_y for x = -radius, radius do local squared = x * x + y * y + z * z - if squared >= min_radius and squared <= max_radius then --position is on surface of sphere - local i = newy + (x + offsetx) - nodes[i] = node_id - count = count + 1 - end - end - end - end - - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() - - return count -end - ---adds a dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added -worldedit.dome = function(pos, radius, nodename) - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius} - local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore - end - - local miny, maxy = 0, radius - if radius < 0 then - radius = -radius - miny, maxy = -radius, 0 - end - - --fill selected area with node - local node_id = minetest.get_content_id(nodename) - local max_radius = radius * (radius + 1) - local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z - local zstride, ystride = area.zstride, area.ystride - local count = 0 - for z = -radius, radius do - local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed - for y = miny, maxy do - local newy = newz + (y + offsety) * ystride - for x = -radius, radius do - if x * x + y * y + z * z <= max_radius then --position is inside sphere - local i = newy + (x + offsetx) - nodes[i] = node_id + if squared <= max_radius and (not hollow or squared >= min_radius) then + -- Position is in dome + local i = new_y + (x + offset_x) + data[i] = node_id count = count + 1 end end end end - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() + mh.finish(manip, data) return count end ---adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added -worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename) - local other1, other2 - if axis == "x" then - other1, other2 = "y", "z" - elseif axis == "y" then - other1, other2 = "x", "z" - else --axis == "z" - other1, other2 = "x", "y" - end - - --handle negative lengths - local currentpos = {x=pos.x, y=pos.y, z=pos.z} +--- Adds a cylinder. +-- @param pos Position to center base of cylinder at. +-- @param axis Axis ("x", "y", or "z") +-- @param length Cylinder length. +-- @param radius Cylinder radius. +-- @param node_name Name of node to make cylinder of. +-- @param hollow Whether the cylinder should be hollow. +-- @return The number of nodes added. +function worldedit.cylinder(pos, axis, length, radius, node_name, hollow) + local other1, other2 = worldedit.get_axis_others(axis) + + -- Handle negative lengths + local current_pos = {x=pos.x, y=pos.y, z=pos.z} if length < 0 then length = -length - currentpos[axis] = currentpos[axis] - length + current_pos[axis] = current_pos[axis] - length end - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local pos1 = { - [axis]=currentpos[axis], - [other1]=currentpos[other1] - radius, - [other2]=currentpos[other2] - radius - } - local pos2 = { - [axis]=currentpos[axis] + length - 1, - [other1]=currentpos[other1] + radius, - [other2]=currentpos[other2] + radius - } - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore - end + -- Set up voxel manipulator + local manip, area = mh.init_axis_radius_length(current_pos, axis, radius, length) + local data = mh.get_empty_data(area) - --fill selected area with node - local node_id = minetest.get_content_id(nodename) + -- Add cylinder + local node_id = minetest.get_content_id(node_name) local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1) local stride = {x=1, y=area.ystride, z=area.zstride} - local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z} + local offset = { + x = current_pos.x - area.MinEdge.x, + y = current_pos.y - area.MinEdge.y, + z = current_pos.z - area.MinEdge.z, + } local min_slice, max_slice = offset[axis], offset[axis] + length - 1 local count = 0 for index2 = -radius, radius do - local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed + -- Offset contributed by other axis 1 plus 1 to make it 1-indexed + local new_index2 = (index2 + offset[other1]) * stride[other1] + 1 for index3 = -radius, radius do - local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2] + local new_index3 = new_index2 + (index3 + offset[other2]) * stride[other2] local squared = index2 * index2 + index3 * index3 - if squared >= min_radius and squared <= max_radius then --position is on surface of cylinder - for index1 = min_slice, max_slice do --add column along axis - local i = newindex3 + index1 * stride[axis] - nodes[i] = node_id + if squared <= max_radius and (not hollow or squared >= min_radius) then + -- Position is in cylinder + -- Add column along axis + for index1 = min_slice, max_slice do + local vi = new_index3 + index1 * stride[axis] + data[vi] = node_id end count = count + length end end end - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() + mh.finish(manip, data) return count end ---adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added -worldedit.cylinder = function(pos, axis, length, radius, nodename) - local other1, other2 - if axis == "x" then - other1, other2 = "y", "z" - elseif axis == "y" then - other1, other2 = "x", "z" - else --axis == "z" - other1, other2 = "x", "y" - end - - --handle negative lengths - local currentpos = {x=pos.x, y=pos.y, z=pos.z} - if length < 0 then - length = -length - currentpos[axis] = currentpos[axis] - length - end - - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local pos1 = { - [axis]=currentpos[axis], - [other1]=currentpos[other1] - radius, - [other2]=currentpos[other2] - radius - } - local pos2 = { - [axis]=currentpos[axis] + length - 1, - [other1]=currentpos[other1] + radius, - [other2]=currentpos[other2] + radius - } - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore - end - - --fill selected area with node - local node_id = minetest.get_content_id(nodename) - local max_radius = radius * (radius + 1) - local stride = {x=1, y=area.ystride, z=area.zstride} - local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z} - local min_slice, max_slice = offset[axis], offset[axis] + length - 1 - local count = 0 - for index2 = -radius, radius do - local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed - for index3 = -radius, radius do - local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2] - if index2 * index2 + index3 * index3 <= max_radius then --position is within cylinder - for index1 = min_slice, max_slice do --add column along axis - local i = newindex3 + index1 * stride[axis] - nodes[i] = node_id - end - count = count + length - end - end - end - - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() - return count -end +--- Adds a pyramid. +-- @param pos Position to center base of pyramid at. +-- @param axis Axis ("x", "y", or "z") +-- @param height Pyramid height. +-- @param node_name Name of node to make pyramid of. +-- @return The number of nodes added. +function worldedit.pyramid(pos, axis, height, node_name) + local other1, other2 = worldedit.get_axis_others(axis) ---adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added -worldedit.pyramid = function(pos, axis, height, nodename) - local other1, other2 - if axis == "x" then - other1, other2 = "y", "z" - elseif axis == "y" then - other1, other2 = "x", "z" - else --axis == "z" - other1, other2 = "x", "y" - end - - local pos1 = {x=pos.x - height, y=pos.y - height, z=pos.z - height} - local pos2 = {x=pos.x + height, y=pos.y + height, z=pos.z + height} + -- Set up voxel manipulator + local manip, area = mh.init_axis_radius(pos, axis, + height >= 0 and height or -height) + local data = mh.get_empty_data() - --handle inverted pyramids - local startaxis, endaxis, step + -- Handle inverted pyramids + local start_axis, end_axis, step if height > 0 then height = height - 1 step = 1 - pos1[axis] = pos[axis] --upper half of box else height = height + 1 step = -1 - pos2[axis] = pos[axis] --lower half of box - end - - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore end - --fill selected area with node - local node_id = minetest.get_content_id(nodename) + -- Add pyramid + local node_id = minetest.get_content_id(node_name) local stride = {x=1, y=area.ystride, z=area.zstride} - local offset = {x=pos.x - emerged_pos1.x, y=pos.y - emerged_pos1.y, z=pos.z - emerged_pos1.z} + local offset = { + x = pos.x - area.MinEdge.x, + y = pos.y - area.MinEdge.y, + z = pos.z - area.MinEdge.z, + } local size = height * step local count = 0 - for index1 = 0, height, step do --go through each level of the pyramid - local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed + -- For each level of the pyramid + for index1 = 0, height, step do + -- Offset contributed by axis plus 1 to make it 1-indexed + local new_index1 = (index1 + offset[axis]) * stride[axis] + 1 for index2 = -size, size do - local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1] + local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1] for index3 = -size, size do - local i = newindex2 + (index3 + offset[other2]) * stride[other2] - nodes[i] = node_id + local i = new_index2 + (index3 + offset[other2]) * stride[other2] + data[i] = node_id end end count = count + (size * 2 + 1) ^ 2 size = size - 1 end - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() + mh.finish(manip, data) return count end ---adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added -worldedit.spiral = function(pos, length, height, spacer, nodename) +--- Adds a spiral. +-- @param pos Position to center spiral at. +-- @param length Spral length. +-- @param height Spiral height. +-- @param spacer Space between walls. +-- @param node_name Name of node to make spiral of. +-- @return Number of nodes added. +-- TODO: Add axis option. +function worldedit.spiral(pos, length, height, spacer, node_name) local extent = math.ceil(length / 2) - local pos1 = {x=pos.x - extent, y=pos.y, z=pos.z - extent} - local pos2 = {x=pos.x + extent, y=pos.y + height, z=pos.z + extent} - - --set up voxel manipulator - local manip = minetest.get_voxel_manip() - local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) - local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) - - --fill emerged area with ignore - local nodes = {} - local ignore = minetest.get_content_id("ignore") - for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do - nodes[i] = ignore - end - --set up variables - local node_id = minetest.get_content_id(nodename) + local manip, area = mh.init_axis_radius_length(pos, "y", extent, height) + local data = mh.get_empty_data(area) + + -- Set up variables + local node_id = minetest.get_content_id(node_name) local stride = {x=1, y=area.ystride, z=area.zstride} - local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z - local i = offsetz * stride.z + offsety * stride.y + offsetx + 1 + local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z + local i = offset_z * stride.z + offset_y * stride.y + offset_x + 1 - --add first column + -- Add first column local count = height local column = i for y = 1, height do - nodes[column] = node_id + data[column] = node_id column = column + stride.y end - --add spiral segments - local strideaxis, strideother = stride.x, stride.z + -- Add spiral segments + local stride_axis, stride_other = stride.x, stride.z local sign = -1 local segment_length = 0 spacer = spacer + 1 - for segment = 1, math.floor(length / spacer) * 2 do --go through each segment except the last - if segment % 2 == 1 then --change sign and length every other turn starting with the first + -- Go through each segment except the last + for segment = 1, math.floor(length / spacer) * 2 do + -- Change sign and length every other turn starting with the first + if segment % 2 == 1 then sign = -sign segment_length = segment_length + spacer end - for index = 1, segment_length do --fill segment - i = i + strideaxis * sign --move along the direction of the segment + -- Fill segment + for index = 1, segment_length do + -- Move along the direction of the segment + i = i + stride_axis * sign local column = i - for y = 1, height do --add column - nodes[column] = node_id + -- Add column + for y = 1, height do + data[column] = node_id column = column + stride.y end end count = count + segment_length * height - strideaxis, strideother = strideother, strideaxis --swap axes + stride_axis, stride_other = stride_other, stride_axis -- Swap axes end - --add shorter final segment + -- Add shorter final segment sign = -sign for index = 1, segment_length do - i = i + strideaxis * sign + i = i + stride_axis * sign local column = i - for y = 1, height do --add column - nodes[column] = node_id + -- Add column + for y = 1, height do + data[column] = node_id column = column + stride.y end end count = count + segment_length * height - --update map nodes - manip:set_data(nodes) - manip:write_to_map() - manip:update_map() + mh.finish(manip, data) return count -end \ No newline at end of file +end -- cgit v1.2.3