]> git.lizzy.rs Git - worldedit.git/blobdiff - worldedit/manipulations.lua
Update `stack2` API documentation.
[worldedit.git] / worldedit / manipulations.lua
index ad64df6118fe4e606c0954bd552be73a65983740..fb8506fd56ffbaa27f481a747206e05c11170d9c 100644 (file)
@@ -1,4 +1,5 @@
 worldedit = worldedit or {}\r
+local minetest = minetest --local copy of global\r
 \r
 --modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions\r
 worldedit.sort_pos = function(pos1, pos2)\r
@@ -23,62 +24,296 @@ worldedit.volume = function(pos1, pos2)
 end\r
 \r
 --sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled\r
-worldedit.set = function(pos1, pos2, nodename)\r
+worldedit.set = function(pos1, pos2, nodenames)\r
+    if type(nodenames) == 'string' then\r
+        nodenames = {nodenames}\r
+    end\r
+\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
 \r
-       local node = {name=nodename}\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               env:add_node(pos, node)\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
+       --set up voxel manipulator\r
+       local manip = minetest.get_voxel_manip()\r
+       local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
+       local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
+\r
+       --fill emerged area with ignore\r
+       local nodes = {}\r
+       local ignore = minetest.get_content_id("ignore")\r
+       for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
+               nodes[i] = ignore\r
+       end\r
+\r
+       --fill selected area with node\r
+       local node_ids = {}\r
+    for i,v in ipairs(nodenames) do\r
+        node_ids[i] = minetest.get_content_id(nodenames[i])\r
+    end\r
+       if #node_ids == 1 then --only one type of node\r
+               local id = node_ids[1]\r
+               for i in area:iterp(pos1, pos2) do\r
+                       nodes[i] = node_ids[id]\r
+               end\r
+       else --fill randomly with all types of specified nodes\r
+               local id_count, rand = #node_ids, math.random\r
+               for i in area:iterp(pos1, pos2) do\r
+                       nodes[i] = node_ids[rand(id_count)]\r
                end\r
-               pos.x = pos.x + 1\r
        end\r
+\r
+       --update map nodes\r
+       manip:set_data(nodes)\r
+       manip:write_to_map()\r
+       manip:update_map()\r
+\r
        return worldedit.volume(pos1, pos2)\r
 end\r
 \r
 --replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced\r
 worldedit.replace = function(pos1, pos2, searchnode, replacenode)\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
 \r
-       if minetest.registered_nodes[searchnode] == nil then\r
-               searchnode = "default:" .. searchnode\r
+       --set up voxel manipulator\r
+       local manip = minetest.get_voxel_manip()\r
+       local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
+       local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
+\r
+       local nodes = manip:get_data()\r
+       local searchnode_id = minetest.get_content_id(searchnode)\r
+       local replacenode_id = minetest.get_content_id(replacenode)\r
+       local count = 0\r
+       for i in area:iterp(pos1, pos2) do --replace searchnode with replacenode\r
+               if nodes[i] == searchnode_id then\r
+                       nodes[i] = replacenode_id\r
+                       count = count + 1\r
+               end\r
        end\r
 \r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       local node = {name=replacenode}\r
+       --update map nodes\r
+       manip:set_data(nodes)\r
+       manip:write_to_map()\r
+       manip:update_map()\r
+\r
+       return count\r
+end\r
+\r
+--replaces all nodes other than `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced\r
+worldedit.replaceinverse = function(pos1, pos2, searchnode, replacenode)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       --set up voxel manipulator\r
+       local manip = minetest.get_voxel_manip()\r
+       local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
+       local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
+\r
+       local nodes = manip:get_data()\r
+       local searchnode_id = minetest.get_content_id(searchnode)\r
+       local replacenode_id = minetest.get_content_id(replacenode)\r
        local count = 0\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               if env:get_node(pos).name == searchnode then\r
-                                       env:add_node(pos, node)\r
-                                       count = count + 1\r
-                               end\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
+       for i in area:iterp(pos1, pos2) do --replace anything that is not searchnode with replacenode\r
+               if nodes[i] ~= searchnode_id then\r
+                       nodes[i] = replacenode_id\r
+                       count = count + 1\r
                end\r
-               pos.x = pos.x + 1\r
        end\r
+\r
+       --update map nodes\r
+       manip:set_data(nodes)\r
+       manip:write_to_map()\r
+       manip:update_map()\r
+\r
        return count\r
 end\r
 \r
+--copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes copied\r
+worldedit.copy = function(pos1, pos2, axis, amount) --wip: replace the old version below\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       if amount == 0 then\r
+               return\r
+       end\r
+\r
+       local other1, other2\r
+       if axis == "x" then\r
+               other1, other2 = "y", "z"\r
+       elseif axis == "y" then\r
+               other1, other2 = "x", "z"\r
+       else --axis == "z"\r
+               other1, other2 = "x", "y"\r
+       end\r
+\r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       --prepare slice along axis\r
+       local extent = {\r
+               [axis] = 1,\r
+               [other1]=pos2[other1] - pos1[other1] + 1,\r
+               [other2]=pos2[other2] - pos1[other2] + 1,\r
+       }\r
+       local nodes = {}\r
+       local schematic = {size=extent, data=nodes}\r
+\r
+       local currentpos = {x=pos1.x, y=pos1.y, z=pos1.z}\r
+       local stride = {x=1, y=extent.x, z=extent.x * extent.y}\r
+       local get_node = minetest.get_node\r
+       for index1 = 1, extent[axis] do --go through each slice\r
+               --copy slice into schematic\r
+               local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed\r
+               for index2 = 1, extent[other1] do\r
+                       local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]\r
+                       for index3 = 1, extent[other2] do\r
+                               local i = newindex2 + (index3 + offset[other2]) * stride[other2]\r
+                               local node = get_node(pos)\r
+                               node.param1 = 255 --node will always appear\r
+                               nodes[i] = node\r
+                       end\r
+               end\r
+\r
+               --copy schematic to target\r
+               currentpos[axis] = currentpos[axis] + amount\r
+               place_schematic(currentpos, schematic)\r
+\r
+               --wip: copy meta\r
+\r
+               currentpos[axis] = currentpos[axis] + 1\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+worldedit.copy2 = function(pos1, pos2, direction, volume)\r
+       -- the overlap shouldn't matter as long as we\r
+       -- 1) start at the furthest separated corner\r
+       -- 2) complete an edge before moving inward, either edge works\r
+       -- 3) complete a face before moving inward, similarly\r
+       --\r
+       -- to do this I\r
+       -- 1) find the furthest destination in the direction, of each axis\r
+       -- 2) call those the furthest separated corner\r
+       -- 3) make sure to iterate inward from there\r
+       -- 4) nested loop to make sure complete edge, complete face, then complete cube.\r
+\r
+       local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
+       local somemeta = get_meta(pos1) -- hax lol\r
+       local to_table = somemeta.to_table\r
+       local from_table = somemeta.from_table\r
+       somemeta = nil\r
+\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       local sx, sy, sz -- direction sign\r
+       local ix, iy, iz -- initial destination\r
+       local ex, ey, ez -- final destination\r
+       local originalx, originaly, originalz -- source\r
+       -- vim -> :'<,'>s/\<\([ioes]\?\)x\>/\1y/g\r
+       if direction.x > 0 then\r
+               originalx = pos2.x\r
+               ix = originalx + direction.x\r
+               ex = pos1.x + direction.x\r
+               sx = -1\r
+       elseif direction.x < 0 then\r
+               originalx = pos1.x\r
+               ix = originalx + direction.x\r
+               ex = pos2.x + direction.x\r
+               sx = 1\r
+       else\r
+               originalx = pos1.x\r
+               ix = originalx -- whatever\r
+               ex = pos2.x\r
+               sx = 1\r
+       end\r
+\r
+       if direction.y > 0 then\r
+               originaly = pos2.y\r
+               iy = originaly + direction.y\r
+               ey = pos1.y + direction.y\r
+               sy = -1\r
+       elseif direction.y < 0 then\r
+               originaly = pos1.y\r
+               iy = originaly + direction.y\r
+               ey = pos2.y + direction.y\r
+               sy = 1\r
+       else\r
+               originaly = pos1.y\r
+               iy = originaly -- whatever\r
+               ey = pos2.y\r
+               sy = 1\r
+       end\r
+\r
+       if direction.z > 0 then\r
+               originalz = pos2.z\r
+               iz = originalz + direction.z\r
+               ez = pos1.z + direction.z\r
+               sz = -1\r
+       elseif direction.z < 0 then\r
+               originalz = pos1.z\r
+               iz = originalz + direction.z\r
+               ez = pos2.z + direction.z\r
+               sz = 1\r
+       else\r
+               originalz = pos1.z\r
+               iz = originalz -- whatever\r
+               ez = pos2.z\r
+               sz = 1\r
+       end\r
+       -- print('copy',originalx,ix,ex,sx,originaly,iy,ey,sy,originalz,iz,ez,sz)\r
+\r
+       local ox,oy,oz\r
+\r
+       ox = originalx\r
+       for x = ix, ex, sx do\r
+               oy = originaly\r
+               for y = iy, ey, sy do\r
+                       oz = originalz\r
+                       for z = iz, ez, sz do\r
+                               -- reusing pos1/pos2 as source/dest here\r
+                               pos1.x, pos1.y, pos1.z = ox, oy, oz\r
+                               pos2.x, pos2.y, pos2.z = x, y, z\r
+                               local node = get_node(pos1)\r
+                               local meta = to_table(get_meta(pos1)) --get meta of current node\r
+                               add_node(pos2,node)\r
+                               from_table(get_meta(pos2),meta)\r
+                               oz = oz + sz\r
+                       end\r
+                       oy = oy + sy\r
+               end\r
+               ox = ox + sx\r
+       end\r
+end\r
+\r
+--duplicates the region defined by positions `pos1` and `pos2` `amount` times with offset vector `direction`, returning the number of nodes stacked\r
+worldedit.stack2 = function(pos1, pos2, direction, amount, finished)\r
+       local i = 0\r
+       local translated = {x=0,y=0,z=0}\r
+       local function nextone()\r
+               if i <= amount then\r
+                       i = i + 1\r
+                       translated.x = translated.x + direction.x\r
+                       translated.y = translated.y + direction.y\r
+                       translated.z = translated.z + direction.z\r
+                       worldedit.copy2(pos1, pos2, translated, volume)\r
+                       minetest.after(0, nextone)\r
+               else\r
+                       if finished then\r
+                               finished()\r
+                       end\r
+               end\r
+       end\r
+       nextone()\r
+       return worldedit.volume(pos1, pos2) * amount\r
+end\r
+\r
 --copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes copied\r
 worldedit.copy = function(pos1, pos2, axis, amount)\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
 \r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
        if amount < 0 then\r
                local pos = {x=pos1.x, y=0, z=0}\r
                while pos.x <= pos2.x do\r
@@ -86,12 +321,12 @@ worldedit.copy = function(pos1, pos2, axis, amount)
                        while pos.y <= pos2.y do\r
                                pos.z = pos1.z\r
                                while pos.z <= pos2.z do\r
-                                       local node = env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get meta of current node\r
+                                       local node = get_node(pos) --obtain current node\r
+                                       local meta = get_meta(pos):to_table() --get meta of current node\r
                                        local value = pos[axis] --store current position\r
                                        pos[axis] = value + amount --move along axis\r
-                                       env:add_node(pos, node) --copy node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       add_node(pos, node) --copy node to new position\r
+                                       get_meta(pos):from_table(meta) --set metadata of new node\r
                                        pos[axis] = value --restore old position\r
                                        pos.z = pos.z + 1\r
                                end\r
@@ -106,12 +341,12 @@ worldedit.copy = function(pos1, pos2, axis, amount)
                        while pos.y >= pos1.y do\r
                                pos.z = pos2.z\r
                                while pos.z >= pos1.z do\r
-                                       local node = minetest.env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get meta of current node\r
+                                       local node = get_node(pos) --obtain current node\r
+                                       local meta = get_meta(pos):to_table() --get meta of current node\r
                                        local value = pos[axis] --store current position\r
                                        pos[axis] = value + amount --move along axis\r
-                                       minetest.env:add_node(pos, node) --copy node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       add_node(pos, node) --copy node to new position\r
+                                       get_meta(pos):from_table(meta) --set metadata of new node\r
                                        pos[axis] = value --restore old position\r
                                        pos.z = pos.z - 1\r
                                end\r
@@ -126,8 +361,13 @@ end
 --moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes moved\r
 worldedit.move = function(pos1, pos2, axis, amount)\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
 \r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       --wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method)\r
+       local get_node, get_meta, add_node, remove_node = minetest.get_node, minetest.get_meta, minetest.add_node, minetest.remove_node\r
        if amount < 0 then\r
                local pos = {x=pos1.x, y=0, z=0}\r
                while pos.x <= pos2.x do\r
@@ -135,13 +375,13 @@ worldedit.move = function(pos1, pos2, axis, amount)
                        while pos.y <= pos2.y do\r
                                pos.z = pos1.z\r
                                while pos.z <= pos2.z do\r
-                                       local node = env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get metadata of current node\r
-                                       env:remove_node(pos)\r
+                                       local node = get_node(pos) --obtain current node\r
+                                       local meta = get_meta(pos):to_table() --get metadata of current node\r
+                                       remove_node(pos)\r
                                        local value = pos[axis] --store current position\r
                                        pos[axis] = value + amount --move along axis\r
-                                       env:add_node(pos, node) --move node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       add_node(pos, node) --move node to new position\r
+                                       get_meta(pos):from_table(meta) --set metadata of new node\r
                                        pos[axis] = value --restore old position\r
                                        pos.z = pos.z + 1\r
                                end\r
@@ -156,13 +396,13 @@ worldedit.move = function(pos1, pos2, axis, amount)
                        while pos.y >= pos1.y do\r
                                pos.z = pos2.z\r
                                while pos.z >= pos1.z do\r
-                                       local node = env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get metadata of current node\r
-                                       env:remove_node(pos)\r
+                                       local node = get_node(pos) --obtain current node\r
+                                       local meta = get_meta(pos):to_table() --get metadata of current node\r
+                                       remove_node(pos)\r
                                        local value = pos[axis] --store current position\r
                                        pos[axis] = value + amount --move along axis\r
-                                       env:add_node(pos, node) --move node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       add_node(pos, node) --move node to new position\r
+                                       get_meta(pos):from_table(meta) --set metadata of new node\r
                                        pos[axis] = value --restore old position\r
                                        pos.z = pos.z - 1\r
                                end\r
@@ -188,33 +428,123 @@ worldedit.stack = function(pos1, pos2, axis, count)
                amount = amount + length\r
                copy(pos1, pos2, axis, amount)\r
        end\r
-       return worldedit.volume(pos1, pos2)\r
+       return worldedit.volume(pos1, pos2) * count\r
+end\r
+\r
+--stretches the region defined by positions `pos1` and `pos2` by an factor of positive integers `stretchx`, `stretchy`. and `stretchz` along the X, Y, and Z axes, respectively, with `pos1` as the origin, returning the number of nodes scaled, the new scaled position 1, and the new scaled position 2\r
+worldedit.stretch = function(pos1, pos2, stretchx, stretchy, stretchz) --wip: test this\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       --prepare schematic of large node\r
+       local get_node, get_meta, place_schematic = minetest.get_node, minetest.get_meta, minetest.place_schematic\r
+       local placeholder_node = {name="", param1=255, param2=0}\r
+       local nodes = {}\r
+       for i = 1, stretchx * stretchy * stretchz do\r
+               nodes[i] = placeholder_node\r
+       end\r
+       local schematic = {size={x=stretchx, y=stretchy, z=stretchz}, data=nodes}\r
+\r
+       local sizex, sizey, sizez = stretchx - 1, stretchy - 1, stretchz - 1\r
+\r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       local new_pos2 = {\r
+               x=pos1.x + (pos2.x - pos1.x) * stretchx + sizex,\r
+               y=pos1.y + (pos2.y - pos1.y) * stretchy + sizey,\r
+               z=pos1.z + (pos2.z - pos1.z) * stretchz + sizez,\r
+       }\r
+       manip:read_from_map(pos1, new_pos2)\r
+\r
+       local pos = {x=pos2.x, y=0, z=0}\r
+       local bigpos = {x=0, y=0, z=0}\r
+       while pos.x >= pos1.x do\r
+               pos.y = pos2.y\r
+               while pos.y >= pos1.y do\r
+                       pos.z = pos2.z\r
+                       while pos.z >= pos1.z do\r
+                               local node = get_node(pos) --obtain current node\r
+                               local meta = get_meta(pos):to_table() --get meta of current node\r
+\r
+                               --calculate far corner of the big node\r
+                               local posx = pos1.x + (pos.x - pos1.x) * stretchx\r
+                               local posy = pos1.y + (pos.y - pos1.y) * stretchy\r
+                               local posz = pos1.z + (pos.z - pos1.z) * stretchz\r
+\r
+                               --create large node\r
+                               placeholder_node.name = node.name\r
+                               placeholder_node.param2 = node.param2\r
+                               bigpos.x, bigpos.y, bigpos.z = posx, posy, posz\r
+                               place_schematic(bigpos, schematic)\r
+\r
+                               --fill in large node meta\r
+                               if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then --node has meta fields\r
+                                       for x = 0, sizex do\r
+                                               for y = 0, sizey do\r
+                                                       for z = 0, sizez do\r
+                                                               bigpos.x, bigpos.y, bigpos.z = posx + x, posy + y, posz + z\r
+                                                               get_meta(bigpos):from_table(meta) --set metadata of new node\r
+                                                       end\r
+                                               end\r
+                                       end\r
+                               end\r
+                               pos.z = pos.z - 1\r
+                       end\r
+                       pos.y = pos.y - 1\r
+               end\r
+               pos.x = pos.x - 1\r
+       end\r
+       return worldedit.volume(pos1, pos2) * stretchx * stretchy * stretchz, pos1, new_pos2\r
 end\r
 \r
---transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed\r
+--transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new transposed position 1, and the new transposed position 2\r
 worldedit.transpose = function(pos1, pos2, axis1, axis2)\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
 \r
+       local compare\r
+       local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]\r
+\r
+       if extent1 > extent2 then\r
+               compare = function(extent1, extent2)\r
+                       return extent1 > extent2\r
+               end\r
+       else\r
+               compare = function(extent1, extent2)\r
+                       return extent1 < extent2\r
+               end\r
+       end\r
+\r
+       --calculate the new position 2 after transposition\r
+       local new_pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
+       new_pos2[axis1] = pos1[axis1] + extent2\r
+       new_pos2[axis2] = pos1[axis2] + extent1\r
+\r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       local upperbound = {x=pos2.x, y=pos2.y, z=pos2.z}\r
+       if upperbound[axis1] < new_pos2[axis1] then upperbound[axis1] = new_pos2[axis1] end\r
+       if upperbound[axis2] < new_pos2[axis2] then upperbound[axis2] = new_pos2[axis2] end\r
+       manip:read_from_map(pos1, upperbound)\r
+\r
        local pos = {x=pos1.x, y=0, z=0}\r
-       local env = minetest.env\r
+       local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
        while pos.x <= pos2.x do\r
                pos.y = pos1.y\r
                while pos.y <= pos2.y do\r
                        pos.z = pos1.z\r
                        while pos.z <= pos2.z do\r
                                local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]\r
-                               if extent1 < extent2 then\r
-                                       local node1 = env:get_node(pos)\r
-                                       local meta1 = env:get_meta(pos):to_table()\r
-                                       local value1, value2 = pos[axis1], pos[axis2]\r
-                                       pos[axis1], pos[axis2] = value1 + extent2, value2 + extent1\r
-                                       local node2 = env:get_node(pos)\r
-                                       local meta2 = env:get_meta(pos):to_table()\r
-                                       env:add_node(pos, node1)\r
-                                       env:get_meta(pos):from_table(meta1)\r
-                                       pos[axis1], pos[axis2] = value1, value2\r
-                                       env:add_node(pos, node2)\r
-                                       env:get_meta(pos):from_table(meta2)\r
+                               if compare(extent1, extent2) then --transpose only if below the diagonal\r
+                                       local node1 = get_node(pos)\r
+                                       local meta1 = get_meta(pos):to_table()\r
+                                       local value1, value2 = pos[axis1], pos[axis2] --save position values\r
+                                       pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 --swap axis extents\r
+                                       local node2 = get_node(pos)\r
+                                       local meta2 = get_meta(pos):to_table()\r
+                                       add_node(pos, node1)\r
+                                       get_meta(pos):from_table(meta1)\r
+                                       pos[axis1], pos[axis2] = value1, value2 --restore position values\r
+                                       add_node(pos, node2)\r
+                                       get_meta(pos):from_table(meta2)\r
                                end\r
                                pos.z = pos.z + 1\r
                        end\r
@@ -222,33 +552,38 @@ worldedit.transpose = function(pos1, pos2, axis1, axis2)
                end\r
                pos.x = pos.x + 1\r
        end\r
-       return worldedit.volume(pos1, pos2)\r
+       return worldedit.volume(pos1, pos2), pos1, new_pos2\r
 end\r
 \r
 --flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z"), returning the number of nodes flipped\r
 worldedit.flip = function(pos1, pos2, axis)\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
 \r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       --wip: flip the region slice by slice along the flip axis using schematic method\r
        local pos = {x=pos1.x, y=0, z=0}\r
        local start = pos1[axis] + pos2[axis]\r
        pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)\r
-       local env = minetest.env\r
+       local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
        while pos.x <= pos2.x do\r
                pos.y = pos1.y\r
                while pos.y <= pos2.y do\r
                        pos.z = pos1.z\r
                        while pos.z <= pos2.z do\r
-                               local node1 = env:get_node(pos)\r
-                               local meta1 = env:get_meta(pos):to_table()\r
+                               local node1 = get_node(pos)\r
+                               local meta1 = get_meta(pos):to_table()\r
                                local value = pos[axis]\r
                                pos[axis] = start - value\r
-                               local node2 = env:get_node(pos)\r
-                               local meta2 = env:get_meta(pos):to_table()\r
-                               env:add_node(pos, node1)\r
-                               env:get_meta(pos):from_table(meta1)\r
+                               local node2 = get_node(pos)\r
+                               local meta2 = get_meta(pos):to_table()\r
+                               add_node(pos, node1)\r
+                               get_meta(pos):from_table(meta1)\r
                                pos[axis] = value\r
-                               env:add_node(pos, node2)\r
-                               env:get_meta(pos):from_table(meta2)\r
+                               add_node(pos, node2)\r
+                               get_meta(pos):from_table(meta2)\r
                                pos.z = pos.z + 1\r
                        end\r
                        pos.y = pos.y + 1\r
@@ -272,36 +607,121 @@ worldedit.rotate = function(pos1, pos2, axis, angle)
        end\r
        angle = angle % 360\r
 \r
+       local count\r
        if angle == 90 then\r
-               worldedit.transpose(pos1, pos2, axis1, axis2)\r
-               worldedit.flip(pos1, pos2, axis2)\r
+               worldedit.flip(pos1, pos2, axis1)\r
+               count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)\r
        elseif angle == 180 then\r
                worldedit.flip(pos1, pos2, axis1)\r
-               worldedit.flip(pos1, pos2, axis2)\r
+               count = worldedit.flip(pos1, pos2, axis2)\r
        elseif angle == 270 then\r
-               worldedit.transpose(pos1, pos2, axis1, axis2)\r
-               worldedit.flip(pos1, pos2, axis1)\r
+               worldedit.flip(pos1, pos2, axis2)\r
+               count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)\r
        end\r
-       return worldedit.volume(pos1, pos2)\r
+       return count, pos1, pos2\r
 end\r
 \r
---digs a region defined by positions `pos1` and `pos2`, returning the number of nodes dug\r
-worldedit.dig = function(pos1, pos2)\r
+--rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis, returning the number of nodes oriented\r
+worldedit.orient = function(pos1, pos2, angle) --wip: support 6D facedir rotation along arbitrary axis\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
+       local registered_nodes = minetest.registered_nodes\r
+\r
+       local wallmounted = {\r
+               [90]={[0]=0, [1]=1, [2]=5, [3]=4, [4]=2, [5]=3},\r
+               [180]={[0]=0, [1]=1, [2]=3, [3]=2, [4]=5, [5]=4},\r
+               [270]={[0]=0, [1]=1, [2]=4, [3]=5, [4]=3, [5]=2}\r
+       }\r
+       local facedir = {\r
+               [90]={[0]=1, [1]=2, [2]=3, [3]=0},\r
+               [180]={[0]=2, [1]=3, [2]=0, [3]=1},\r
+               [270]={[0]=3, [1]=0, [2]=1, [3]=2}\r
+       }\r
+\r
+       angle = angle % 360\r
+       if angle == 0 then\r
+               return 0\r
+       end\r
+       local wallmounted_substitution = wallmounted[angle]\r
+       local facedir_substitution = facedir[angle]\r
 \r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       local count = 0\r
+       local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
        local pos = {x=pos1.x, y=0, z=0}\r
        while pos.x <= pos2.x do\r
                pos.y = pos1.y\r
                while pos.y <= pos2.y do\r
                        pos.z = pos1.z\r
                        while pos.z <= pos2.z do\r
-                               env:dig_node(pos)\r
+                               local node = get_node(pos)\r
+                               local def = registered_nodes[node.name]\r
+                               if def then\r
+                                       if def.paramtype2 == "wallmounted" then\r
+                                               node.param2 = wallmounted_substitution[node.param2]\r
+                                               local meta = get_meta(pos):to_table()\r
+                                               add_node(pos, node)\r
+                                               get_meta(pos):from_table(meta)\r
+                                               count = count + 1\r
+                                       elseif def.paramtype2 == "facedir" then\r
+                                               node.param2 = facedir_substitution[node.param2]\r
+                                               local meta = get_meta(pos):to_table()\r
+                                               add_node(pos, node)\r
+                                               get_meta(pos):from_table(meta)\r
+                                               count = count + 1\r
+                                       end\r
+                               end\r
                                pos.z = pos.z + 1\r
                        end\r
                        pos.y = pos.y + 1\r
                end\r
                pos.x = pos.x + 1\r
        end\r
-       return worldedit.volume(pos1, pos2)\r
-end
\ No newline at end of file
+       return count\r
+end\r
+\r
+--fixes the lighting in a region defined by positions `pos1` and `pos2`, returning the number of nodes updated\r
+worldedit.fixlight = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       local nodes = minetest.find_nodes_in_area(pos1, pos2, "air")\r
+       local dig_node = minetest.dig_node\r
+       for _, pos in ipairs(nodes) do\r
+               dig_node(pos)\r
+       end\r
+       return #nodes\r
+end\r
+\r
+--clears all objects in a region defined by the positions `pos1` and `pos2`, returning the number of objects cleared\r
+worldedit.clearobjects = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       --make area stay loaded\r
+       local manip = minetest.get_voxel_manip()\r
+       manip:read_from_map(pos1, pos2)\r
+\r
+       local pos1x, pos1y, pos1z = pos1.x, pos1.y, pos1.z\r
+       local pos2x, pos2y, pos2z = pos2.x + 1, pos2.y + 1, pos2.z + 1\r
+       local center = {x=(pos1x + pos2x) / 2, y=(pos1y + pos2y) / 2, z=(pos1z + pos2z) / 2} --center of region\r
+       local radius = ((center.x - pos1x + 0.5) + (center.y - pos1y + 0.5) + (center.z - pos1z + 0.5)) ^ 0.5 --bounding sphere radius\r
+       local count = 0\r
+       for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do --all objects in bounding sphere\r
+               local entity = obj:get_luaentity()\r
+               if not (entity and entity.name:find("^worldedit:")) then --avoid WorldEdit entities\r
+                       local pos = obj:getpos()\r
+                       if pos.x >= pos1x and pos.x <= pos2x\r
+                       and pos.y >= pos1y and pos.y <= pos2y\r
+                       and pos.z >= pos1z and pos.z <= pos2z then --inside region\r
+                               obj:remove()\r
+                               count = count + 1\r
+                       end\r
+               end\r
+       end\r
+       return count\r
+end\r