From 2a268d9ff843693f5292f1f0db9254fd601aeec7 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Wed, 17 Feb 2021 23:46:03 +0000 Subject: [PATCH] Implement scale_up Next up: testing! --- worldeditadditions/lib/scale_up.lua | 125 ++++++++++++++-------------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/worldeditadditions/lib/scale_up.lua b/worldeditadditions/lib/scale_up.lua index 10b69cb..c3a8dfc 100644 --- a/worldeditadditions/lib/scale_up.lua +++ b/worldeditadditions/lib/scale_up.lua @@ -11,65 +11,68 @@ -- @param direction Vector The direction to scale in - as a vector. e.g. { x = -1, y = 1, z = -1 } would mean scale in the negative x, positive y, and nevative z directions. -- @return boolean, string|table Whether the operation was successful or not. If not, then an error messagea as a string is also passed. If it was, then a statistics object is returned instead. function worldeditadditions.scale_up(pos1, pos2, scale, direction) - return false, "Error: Scaling up isn't implemented yet." - -- pos1, pos2 = worldedit.sort_pos(pos1, pos2) - -- if scale.x > 1 or scale.y > 1 or scale.z > 1 or scale.x < -1 or scale.y < -1 or scale.z < -1 then - -- return false, "Error: Scale factor vectors may not mix values -1 < factor < 1 and (1 < factor or factor < -1) - in other words, you can't scale both up and down at the same time (try worldeditadditions.scale, which automatically applies such scale factor vectors as 2 successive operations)" - -- end - -- if direction.x == 0 or direction.y == 0 or direction.z == 0 then - -- return false, "Error: One of the components of the direction vector was 0 (direction components should either be greater than or less than 0 to indicate the direction to scale in.)" - -- end - -- - -- local scale_down = { - -- x = math.floor(1 / scale.x), - -- y = math.floor(1 / scale.y), - -- z = math.floor(1 / scale.z) - -- } - -- local size = vector.subtract(pos2, pos1) - -- - -- local data = manip:get_data() - -- local data_copy = worldeditadditions.shallowcopy(data) - -- - -- local node_id_air = minetest.get_content_id("air") - -- - -- - -- local count = 0 -- The number of nodes replaced - -- - -- -- Zero out the area we're scaling down into - -- for i in area:iterp(pos1, pos2) do - -- data_copy[i] = node_id_air - -- end - -- - -- local changes = { replaced = 0 } - -- for z = pos2.z, pos1.z, -1 do - -- for y = pos2.y, pos1.y, -1 do - -- for x = pos2.x, pos1.x, -1 do - -- local posi_rel = vector.subtract({ x = x, y = y, z = z }, pos1) - -- - -- local posi_copy = worldeditadditions.shallowcopy(posi_rel) - -- posi_copy = vector.floor(vector.divide(scale_down)) - -- - -- if direction.x < 0 then posi_copy.x = size.x - posi_copy.x end - -- if direction.y < 0 then posi_copy.y = size.y - posi_copy.y end - -- if direction.z < 0 then posi_copy.z = size.z - posi_copy.z end - -- - -- local posi_copy = vector.add(pos1, posi_copy) - -- - -- local i_source = area:index(x, y, z) - -- local i_target = area:index(posi_copy.x, posi_copy.y, posi_copy.z) - -- - -- -- Technically we could save some operations here by not setting - -- -- the target multiple times per copy, but the calculations - -- -- above are probably a lot more taxing - -- -- TODO Be more intelligent about deciding what node to replace with here - -- data_copy[i_target] = data[i_source] - -- end - -- end - -- end - -- - -- - -- -- Save the modified nodes back to disk & return - -- worldedit.manip_helpers.finish(manip, data_copy) - -- - -- return true, changes + pos1, pos2 = worldedit.sort_pos(pos1, pos2) + if (scale.x < 1 and scale.x > -1) and (scale.y < 1 and scale.y > -1) and (scale.z < 1 and scale.z > -1) then + return false, "Error: Scale factor vectors may not mix values -1 < factor < 1 and (1 < factor or factor < -1) - in other words, you can't scale both up and down at the same time (try worldeditadditions.scale, which automatically applies such scale factor vectors as 2 successive operations)" + end + if direction.x == 0 or direction.y == 0 or direction.z == 0 then + return false, "Error: One of the components of the direction vector was 0 (direction components should either be greater than or less than 0 to indicate the direction to scale in.)" + end + + local size = vector.subtract(pos2, pos1) + + local pos1_big = vector.new(pos1) + local pos2_big = vector(pos2) + + if direction.x < 1 then pos1_big.x = pos1_big.x - size.x + else pos2_big.x = pos2_big.x + size.x end + if direction.y < 1 then pos1_big.y = pos1_big.y - size.y + else pos2_big.y = pos2_big.y + size.y end + if direction.z < 1 then pos1_big.z = pos1_big.z - size.z + else pos2_big.z = pos2_big.z + size.z end + + + + local manip_small, area_small = worldedit.manip_helpers.init(pos1, pos2) + local manip_big, area_big = worldedit.manip_helpers.init(pos1_big, pos2_big) + local data_source = manip_small:get_data() + local data_target = manip_big:get_data() + + local node_id_air = minetest.get_content_id("air") + + local kern_volume = math.abs(scale.x) * math.abs(scale.y) * math.abs(scale.z) + + local changes = { updated = 0, scale = "scale_up" } + for z = pos2.z, pos1.z, -1 do + for y = pos2.y, pos1.y, -1 do + for x = pos2.x, pos1.x, -1 do + local posi_rel = vector.subtract({ x = x, y = y, z = z }, pos1) + + local kern_anchor = vector.add( + pos1_big, + vector.cross( + vector.add(posi_rel, 1), + scale + ) + ) + + local source_val = data_source[area_small:index(x, y, z)] + + for kz = kern_anchor.z, kern_anchor.z - scale.z, -1 do + for ky = kern_anchor.y, kern_anchor.y - scale.y, -1 do + for kx = kern_anchor.x, kern_anchor.x - scale.x, -1 do + data_target[area_big:index(kx, ky, kz)] = source_val + end + end + end + + changes.updated = changes.updated + kern_volume + end + end + end + + -- Save the region back to disk & return + worldedit.manip_helpers.finish(manip_big, data_target) + + return true, changes end