]> git.lizzy.rs Git - worldedit.git/commitdiff
Faster copying using vmanips
authorsfan5 <sfan5@live.de>
Sun, 8 Sep 2019 18:12:38 +0000 (20:12 +0200)
committersfan5 <sfan5@live.de>
Sun, 8 Sep 2019 20:14:07 +0000 (22:14 +0200)
worldedit/common.lua
worldedit/manipulations.lua

index be9a2c9c412673ba035c49ed2e8df7c0a05aa730..c62957f290953925ebea7e1dfafb5d4bef166c0a 100644 (file)
@@ -107,7 +107,9 @@ end
 
 function mh.finish(manip, data)
        -- Update map
-       manip:set_data(data)
+       if data ~= nil then
+               manip:set_data(data)
+       end
        manip:write_to_map()
        manip:update_map()
 end
index 76bb13bea8e7404e7e5fe2701fe03f95a7fc529f..406dc28faa4e1631ec468966e2963893d52f24c3 100644 (file)
@@ -76,9 +76,6 @@ function worldedit.replace(pos1, pos2, search_node, replace_node, inverse)
 \r
        local count = 0\r
 \r
-       --- TODO: This could be shortened by checking `inverse` in the loop,\r
-       -- but that would have a speed penalty.  Is the penalty big enough\r
-       -- to matter?\r
        if not inverse then\r
                for i in area:iterp(pos1, pos2) do\r
                        if data[i] == search_id then\r
@@ -135,70 +132,28 @@ end
 function worldedit.copy(pos1, pos2, axis, amount)\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
 \r
-       worldedit.keep_loaded(pos1, pos2)\r
-\r
-       local get_node, get_meta, set_node = minetest.get_node,\r
-                       minetest.get_meta, minetest.set_node\r
-       -- Copy things backwards when negative to avoid corruption.\r
-       -- FIXME: Lots of code duplication here.\r
-       if amount < 0 then\r
-               local pos = {}\r
-               pos.x = pos1.x\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 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
-                                       set_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
-                               pos.y = pos.y + 1\r
-                       end\r
-                       pos.x = pos.x + 1\r
-               end\r
-       else\r
-               local pos = {}\r
-               pos.x = pos2.x\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
-                                       local value = pos[axis] -- Store current position\r
-                                       pos[axis] = value + amount -- Move along axis\r
-                                       set_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
-                               pos.y = pos.y - 1\r
-                       end\r
-                       pos.x = pos.x - 1\r
-               end\r
+       local dim = vector.add(vector.subtract(pos2, pos1), 1)\r
+       if amount > 0 and amount < dim[axis] then\r
+               -- Source and destination region are overlapping and moving needs to\r
+               -- happen in reverse.\r
+               -- FIXME: I can't be bothered, so just defer to the legacy code for now.\r
+               return worldedit.legacy_copy(pos1, pos2, axis, amount)\r
        end\r
-       return worldedit.volume(pos1, pos2)\r
+\r
+       local off = {x=0, y=0, z=0}\r
+       off[axis] = amount\r
+       return worldedit.copy2(pos1, pos2, off)\r
 end\r
 \r
---- Copies a region by offset vector `off`.\r
--- @param pos1\r
--- @param pos2\r
--- @param off\r
--- @return The number of nodes copied.\r
-function worldedit.copy2(pos1, pos2, off)\r
+-- This function is not offical part of the API and may be removed at any time.\r
+function worldedit.legacy_copy(pos1, pos2, axis, amount)\r
        local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
 \r
        worldedit.keep_loaded(pos1, pos2)\r
 \r
        local get_node, get_meta, set_node = minetest.get_node,\r
                        minetest.get_meta, minetest.set_node\r
+       -- Copy things backwards\r
        local pos = {}\r
        pos.x = pos2.x\r
        while pos.x >= pos1.x do\r
@@ -208,9 +163,11 @@ function worldedit.copy2(pos1, pos2, off)
                        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
-                               local newpos = vector.add(pos, off) -- Calculate new position\r
-                               set_node(newpos, node) -- Copy node to new position\r
-                               get_meta(newpos):from_table(meta) -- Set metadata of new node\r
+                               local value = pos[axis] -- Store current position\r
+                               pos[axis] = value + amount -- Move along axis\r
+                               set_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
                        pos.y = pos.y - 1\r
@@ -220,6 +177,81 @@ function worldedit.copy2(pos1, pos2, off)
        return worldedit.volume(pos1, pos2)\r
 end\r
 \r
+--- Copies a region by offset vector `off`.\r
+-- @param pos1\r
+-- @param pos2\r
+-- @param off\r
+-- @return The number of nodes copied.\r
+function worldedit.copy2(pos1, pos2, off)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       local src_manip, src_area = mh.init(pos1, pos2)\r
+       local src_stride = {x=1, y=src_area.ystride, z=src_area.zstride}\r
+       local src_offset = vector.subtract(pos1, src_area.MinEdge)\r
+\r
+       local dpos1 = vector.add(pos1, off)\r
+       local dpos2 = vector.add(pos2, off)\r
+       local dim = vector.add(vector.subtract(pos2, pos1), 1)\r
+\r
+       local dst_manip, dst_area = mh.init(dpos1, dpos2)\r
+       local dst_stride = {x=1, y=dst_area.ystride, z=dst_area.zstride}\r
+       local dst_offset = vector.subtract(dpos1, dst_area.MinEdge)\r
+\r
+       local function do_copy(src_data, dst_data)\r
+               for z = 0, dim.z-1 do\r
+                       local src_index_z = (src_offset.z + z) * src_stride.z + 1 -- +1 for 1-based indexing\r
+                       local dst_index_z = (dst_offset.z + z) * dst_stride.z + 1\r
+                       for y = 0, dim.y-1 do\r
+                               local src_index_y = src_index_z + (src_offset.y + y) * src_stride.y\r
+                               local dst_index_y = dst_index_z + (dst_offset.y + y) * dst_stride.y\r
+                               -- Copy entire row at once\r
+                               local src_index_x = src_index_y + src_offset.x\r
+                               local dst_index_x = dst_index_y + dst_offset.x\r
+                               for x = 0, dim.x-1 do\r
+                                       dst_data[dst_index_x + x] = src_data[src_index_x + x]\r
+                               end\r
+                       end\r
+               end\r
+       end\r
+\r
+       -- Copy node data\r
+       local src_data = src_manip:get_data()\r
+       local dst_data = dst_manip:get_data()\r
+       do_copy(src_data, dst_data)\r
+       dst_manip:set_data(dst_data)\r
+\r
+       -- Copy param1\r
+       src_manip:get_light_data(src_data)\r
+       dst_manip:get_light_data(dst_data)\r
+       do_copy(src_data, dst_data)\r
+       dst_manip:set_light_data(dst_data)\r
+\r
+       -- Copy param2\r
+       src_manip:get_param2_data(src_data)\r
+       dst_manip:get_param2_data(dst_data)\r
+       do_copy(src_data, dst_data)\r
+       dst_manip:set_param2_data(dst_data)\r
+\r
+       mh.finish(dst_manip)\r
+       src_data = nil\r
+       dst_data = nil\r
+\r
+       -- Copy metadata\r
+       local get_meta = minetest.get_meta\r
+       for z = 0, dim.z-1 do\r
+               for y = 0, dim.y-1 do\r
+                       for x = 0, dim.x-1 do\r
+                               local pos = {x=pos1.x+x, y=pos1.y+y, z=pos1.z+z}\r
+                               local meta = get_meta(pos):to_table()\r
+                               pos = vector.add(pos, off)\r
+                               get_meta(pos):from_table(meta)\r
+                       end\r
+               end\r
+       end\r
+\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
 --- Moves a region along `axis` by `amount` nodes.\r
 -- @return The number of nodes moved.\r
 function worldedit.move(pos1, pos2, axis, amount)\r