From c22b5565114941ce5d62f64916fe713eb1141880 Mon Sep 17 00:00:00 2001
From: Cy <whatever>
Date: Mon, 30 Jun 2014 16:14:14 -0700
Subject: Improved stacking

This stack / copy uses a direction vector, so it's not limited to only
along the X/Y/Z axis, and can go diagonally. This enables things like
building staircases.
---
 worldedit/manipulations.lua | 123 ++++++++++++++++++++++++++++++++++++++++++++
 worldedit_commands/init.lua |  46 +++++++++++++++++
 2 files changed, 169 insertions(+)

diff --git a/worldedit/manipulations.lua b/worldedit/manipulations.lua
index 40915ed..0a37e16 100644
--- a/worldedit/manipulations.lua
+++ b/worldedit/manipulations.lua
@@ -172,6 +172,129 @@ worldedit.copy = function(pos1, pos2, axis, amount) --wip: replace the old versi
 	return worldedit.volume(pos1, pos2)
 end
 
+worldedit.copy2 = function(pos1, pos2, direction, volume)
+    -- the overlap shouldn't matter as long as we
+    -- 1) start at the furthest separated corner
+    -- 2) complete an edge before moving inward, either edge works
+    -- 3) complete a face before moving inward, similarly
+    --
+    -- to do this I
+    -- 1) find the furthest destination in the direction, of each axis
+    -- 2) call those the furthest separated corner
+    -- 3) make sure to iterate inward from there
+    -- 4) nested loop to make sure complete edge, complete face, then complete cube.
+    
+	local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
+    local somemeta = get_meta(pos1) -- hax lol
+    local to_table = somemeta.to_table
+    local from_table = somemeta.from_table
+    somemeta = nil
+
+	local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
+	local manip = minetest.get_voxel_manip()
+	manip:read_from_map(pos1, pos2)
+
+    local sx,sy,sz -- direction sign
+    local ix,iy,iz -- initial destination
+    local ex,ey,ez -- final destination
+    local originalx,originaly,originalz -- source 
+    -- vim -> :'<,'>s/\<\([ioes]\?\)x\>/\1y/g
+    if direction.x > 0 then 
+        originalx = pos2.x
+        ix = originalx + direction.x
+        ex = pos1.x + direction.x
+        sx = -1
+    elseif direction.x < 0 then 
+        originalx = pos1.x
+        ix = originalx + direction.x
+        ex = pos2.x + direction.x
+        sx = 1
+    else 
+        originalx = pos1.x
+        ix = originalx -- whatever
+        ex = pos2.x
+        sx = 1
+    end
+
+    if direction.y > 0 then 
+        originaly = pos2.y
+        iy = originaly + direction.y
+        ey = pos1.y + direction.y
+        sy = -1
+    elseif direction.y < 0 then 
+        originaly = pos1.y
+        iy = originaly + direction.y
+        ey = pos2.y + direction.y
+        sy = 1
+    else 
+        originaly = pos1.y
+        iy = originaly -- whatever
+        ey = pos2.y
+        sy = 1
+    end
+
+    if direction.z > 0 then 
+        originalz = pos2.z
+        iz = originalz + direction.z
+        ez = pos1.z + direction.z
+        sz = -1
+    elseif direction.z < 0 then 
+        originalz = pos1.z
+        iz = originalz + direction.z
+        ez = pos2.z + direction.z
+        sz = 1
+    else 
+        originalz = pos1.z
+        iz = originalz -- whatever
+        ez = pos2.z
+        sz = 1
+    end
+    -- print('copy',originalx,ix,ex,sx,originaly,iy,ey,sy,originalz,iz,ez,sz)
+
+    local ox,oy,oz
+
+    ox = originalx
+    for x = ix,ex,sx do
+        oy = originaly
+        for y = iy,ey,sy do
+            oz = originalz
+            for z = iz,ez,sz do
+                -- reusing pos1/pos2 as source/dest here
+                pos1.x = ox; pos1.y = oy; pos1.z = oz
+                pos2.x = x; pos2.y = y; pos2.z = z
+                local node = get_node(pos1)
+				local meta = to_table(get_meta(pos1)) --get meta of current node
+                add_node(pos2,node)
+                from_table(get_meta(pos2),meta)
+                oz = oz + sz
+            end
+            oy = oy + sy
+        end
+        ox = ox + sx
+    end
+end            
+
+worldedit.stack2 = function(pos1, pos2, direction, amount, finished) 
+    local i = 0
+    local translated = {x=0,y=0,z=0}
+    local function nextone()
+        if i <= amount then
+            i = i + 1
+            translated.x = translated.x + direction.x
+            translated.y = translated.y + direction.y
+            translated.z = translated.z + direction.z
+            worldedit.copy2(pos1,pos2,translated,volume)
+            minetest.after(0,nextone)
+        else
+            if finished then
+                finished()
+            end
+        end
+    end
+    nextone()
+    return nil
+end
+
 --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
 worldedit.copy = function(pos1, pos2, axis, amount)
 	local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
diff --git a/worldedit_commands/init.lua b/worldedit_commands/init.lua
index 04f2f4c..e7b0f74 100644
--- a/worldedit_commands/init.lua
+++ b/worldedit_commands/init.lua
@@ -619,6 +619,52 @@ minetest.register_chatcommand("/stack", {
 	end),
 })
 
+minetest.register_chatcommand("/stack2", {
+	params = "<count> <x>/<y>/<z>",
+	description = "Stack the current WorldEdit region <count> times translating each time by x, y and z in the respective directions.",
+	privs = {worldedit=true},
+	func = function(name, param)
+		local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+        if pos1 == nil or pos2 == nil then
+            worldedit.player_notify(name, "Select a position first!")
+            return
+        end
+        local repetitions, incs = param:match("([0-9]+)%s*(.+)")
+        repetitions = repetitions and tonumber(repetitions)
+        if repetitions == nil then
+			worldedit.player_notify(name, "invalid count: " .. param)
+            return 
+        end
+
+        local x,y,z = incs:match("(.+)/(.+)/(.+)")
+        if x == nil then
+            worldedit.player_notify(name, "invalid increments: " .. param)
+            return
+        end
+        x = tonumber(x)
+        y = tonumber(y)
+        z = tonumber(z)
+        if x == nil or y == nil or z == nil then
+            worldedit.player_notify(name, "increments must be numbers: " .. param)
+            return
+        end
+
+        local count = worldedit.volume(pos1,pos2) * repetitions
+
+        return safe_region(function()
+   		  worldedit.stack2(pos1, pos2, {x=x,y=y,z=z}, repetitions,
+          function()
+		    worldedit.player_notify(name, count .. " nodes stacked")
+        end)
+
+        end,
+        function() 
+            return count
+    	end)(name,param) -- more hax
+	end
+})
+
+
 minetest.register_chatcommand("/stretch", {
 	params = "<stretchx> <stretchy> <stretchz>",
 	description = "Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin",
-- 
cgit v1.2.3