diff --git a/worldeditadditions/lib/scale.lua b/worldeditadditions/lib/scale.lua index a35ccfb..bba8380 100644 --- a/worldeditadditions/lib/scale.lua +++ b/worldeditadditions/lib/scale.lua @@ -2,12 +2,17 @@ --- Scales the defined region by the given scale factor in the given anchors. -- Scale factor vectors containing both scale up and scale down operations are -- split into 2 different scale operations automatically. +-- Scale up operations are always performed before scale down operations to +-- preserve detail. If performance is important, you should split the scale +-- operations up manually! -- @param pos1 Vector Position 1 of the defined region, -- @param pos2 Vector Position 2 of the defined region. -- @param scale Vector The scale factor - as a vector - by which to scale (values between -1 and 1 are considered a scale down operation). --- @param anchor Vector The anchor to scale in - as a vector. e.g. { x = -1, y = 1, z = -1 } would mean scale in the negative x, positive y, and negative z directions. +-- @param anchor Vector The anchor to scale in - as a vector. e.g. { x = -1, y = 1, z = -1 } would mean scale in the negative x, positive y, and negative 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(pos1, pos2, scale, anchor) + pos1, pos2 = worldedit.sort_pos(pos1, pos2) + if scale.x == 0 or scale.y == 0 or scale.z == 0 then return false, "A component of the scale factoro was 0." end @@ -32,13 +37,20 @@ function worldeditadditions.scale(pos1, pos2, scale, anchor) stats_total.updated = stats.updated stats_total.operations = stats_total.operations + 1 stats_total.scale_down = stats.scale + pos1 = stats.pos1 + pos2 = stats.pos2 end if scale_down.x ~= 1 or scale_down.y ~= 1 or scale_down.z ~= 1 then success, stats = worldeditadditions.scale_down(pos1, pos2, scale_down, anchor) if not success then return success, stats end stats_total.updated = stats_total.updated + stats.updated stats_total.operations = stats_total.operations + 1 + pos1 = stats.pos1 + pos2 = stats.pos2 end + stats_total.pos1 = pos1 + stats_total.pos2 = pos2 + return true, stats_total end diff --git a/worldeditadditions/lib/scale_down.lua b/worldeditadditions/lib/scale_down.lua index 97d9168..10b79e3 100644 --- a/worldeditadditions/lib/scale_down.lua +++ b/worldeditadditions/lib/scale_down.lua @@ -24,8 +24,18 @@ function worldeditadditions.scale_down(pos1, pos2, scale, anchor) y = math.floor(1 / scale.y), z = math.floor(1 / scale.z) } - print("[DEBUG] scale_down", worldeditadditions.vector.tostring(scale_down)) local size = vector.subtract(pos2, pos1) + print("[DEBUG] scale_down", worldeditadditions.vector.tostring(scale_down), "size", size) + + if size.x < scale_down.x or size.y < scale_down.y or size.z < scale.z then + return false, "Error: Area isn't big enough to apply scale down by "..worldeditadditions.vector.tostring(scale).."." + end + + local size_small = { + x = math.floor(size.x / scale_down.x), + y = math.floor(size.y / scale_down.y), + z = math.floor(size.z / scale_down.z) + } local manip, area = worldedit.manip_helpers.init(pos1, pos2) local data = manip:get_data() @@ -34,7 +44,7 @@ function worldeditadditions.scale_down(pos1, pos2, scale, anchor) local node_id_air = minetest.get_content_id("air") - local stats = { updated = 0, scale = "scale_down" } + local stats = { updated = 0, scale = scale_down, pos1 = pos1, pos2 = vector.add(pos1, size_small) } -- Zero out the area we're scaling down into for i in area:iterp(pos1, pos2) do data_copy[i] = node_id_air @@ -48,8 +58,14 @@ function worldeditadditions.scale_down(pos1, pos2, scale, anchor) 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)) + -- local posi_copy = worldeditadditions.shallowcopy(posi_rel) + -- posi_copy = vector.floor(vector.divide(posi_rel/*, scale_down*/)) + + local posi_copy = { + x = math.floor(posi_rel.x / scale_down.x), + y = math.floor(posi_rel.y / scale_down.y), + z = math.floor(posi_rel.z / scale_down.z) + } if anchor.x < 0 then posi_copy.x = size.x - posi_copy.x end if anchor.y < 0 then posi_copy.y = size.y - posi_copy.y end @@ -73,5 +89,5 @@ function worldeditadditions.scale_down(pos1, pos2, scale, anchor) -- Save the modified nodes back to disk & return worldedit.manip_helpers.finish(manip, data_copy) - return true, changes + return true, stats end diff --git a/worldeditadditions/lib/scale_up.lua b/worldeditadditions/lib/scale_up.lua index 1e602ae..2f18ce2 100644 --- a/worldeditadditions/lib/scale_up.lua +++ b/worldeditadditions/lib/scale_up.lua @@ -34,7 +34,7 @@ function worldeditadditions.scale_up(pos1, pos2, scale, anchor) local size_big = vector.add(vector.subtract(pos2_big, pos1_big), 1) - print("scale_up: scaling "..wea.vector.tostring(pos1).." to "..wea.vector.tostring(pos2).." (volume "..worldedit.volume(pos1, pos2).."; size "..wea.vector.tostring(size)..") to "..wea.vector.tostring(pos1_big).." to "..wea.vector.tostring(pos2_big).." (volume "..worldedit.volume(pos1_big, pos2_big).."; size "..wea.vector.tostring(size_big)..")") + -- print("scale_up: scaling "..wea.vector.tostring(pos1).." to "..wea.vector.tostring(pos2).." (volume "..worldedit.volume(pos1, pos2).."; size "..wea.vector.tostring(size)..") to "..wea.vector.tostring(pos1_big).." to "..wea.vector.tostring(pos2_big).." (volume "..worldedit.volume(pos1_big, pos2_big).."; size "..wea.vector.tostring(size_big)..")") local manip_small, area_small = worldedit.manip_helpers.init(pos1, pos2) local manip_big, area_big = worldedit.manip_helpers.init(pos1_big, pos2_big) @@ -43,7 +43,7 @@ function worldeditadditions.scale_up(pos1, pos2, scale, anchor) local node_id_air = minetest.get_content_id("air") - local changes = { updated = 0, scale = "scale_up" } + local stats = { updated = 0, scale = scale, pos1 = pos1_big, pos2 = pos2_big } for z = pos2.z, pos1.z, -1 do for y = pos2.y, pos1.y, -1 do for x = pos2.x, pos1.x, -1 do @@ -55,11 +55,11 @@ function worldeditadditions.scale_up(pos1, pos2, scale, anchor) z = pos1_big.z + (posi_rel.z * scale.z) + (scale.z - 1) } - print( - "posi", wea.vector.tostring(vector.new(x, y, z)), - "posi_rel", wea.vector.tostring(posi_rel), - "kern_anchor", wea.vector.tostring(kern_anchor) - ) + -- print( + -- "posi", wea.vector.tostring(vector.new(x, y, z)), + -- "posi_rel", wea.vector.tostring(posi_rel), + -- "kern_anchor", wea.vector.tostring(kern_anchor) + -- ) local source_val = data_source[area_small:index(x, y, z)] @@ -67,7 +67,7 @@ function worldeditadditions.scale_up(pos1, pos2, scale, anchor) for ky = kern_anchor.y, kern_anchor.y - scale.y + 1, -1 do for kx = kern_anchor.x, kern_anchor.x - scale.x + 1, -1 do data_target[area_big:index(kx, ky, kz)] = source_val - changes.updated = changes.updated + 1 + stats.updated = stats.updated + 1 end end end @@ -79,5 +79,5 @@ function worldeditadditions.scale_up(pos1, pos2, scale, anchor) -- Save the region back to disk & return worldedit.manip_helpers.finish(manip_big, data_target) - return true, changes + return true, stats end diff --git a/worldeditadditions_commands/commands/scale.lua b/worldeditadditions_commands/commands/scale.lua index 4f17f70..d3e724e 100644 --- a/worldeditadditions_commands/commands/scale.lua +++ b/worldeditadditions_commands/commands/scale.lua @@ -1,3 +1,22 @@ +local function parse_scale_component(val) + local result = tonumber(val) + if result then return true, result end + if string.find(val, "/") then + local parts = worldeditadditions.split(val, "/", true) + local a = tonumber(parts[1]) + local b = tonumber(parts[2]) + if not b then return false, "Invalid number after the forward slash in scale component." end + if not a then return false, "Invalid number before the forward slash in scale component." end + return true, a / b + end + if string.find(val, "%%") then + local part = tonumber(string.sub(val, 1, -2)) + if not part then return false, "We thought a scale component was a percentage, but failed to parse percentage as number." end + return true, part / 100 + end + return false, "Failed to parse scale component (unrecognised format - we support things like '3', '0.5', '1/10', or '33%' without quotes)." +end + -- ███████ ██████ █████ ██ ███████ -- ██ ██ ██ ██ ██ ██ -- ███████ ██ ███████ ██ █████ @@ -22,8 +41,8 @@ worldedit.register_command("scale", { return false, "Error: Got 2 arguments, but the first doesn't look like the name of an axis." end local axis = parts[1] - local factor = tonumber(parts[2]) - if not factor then return false, "Error: Invalid scale factor." end + local success, factor = parse_scale_component(parts[2]) + if not success then return success, "Error: Invalid scale factor. Details: "..factor end if axis:sub(1, 1) == "-" then axis = axis:sub(2, 2) @@ -32,19 +51,21 @@ worldedit.register_command("scale", { scale[axis] = factor elseif #parts >= 3 then - local val = tonumber(parts[1]) - if not val then return false, "Error: x axis scale factor wasn't a number." end + local success, val = parse_scale_component(parts[1]) + if not success then return false, "Error: x axis scale factor wasn't a number. Details: "..val end scale.x = val - val = tonumber(parts[2]) - if not val then return false, "Error: y axis scale factor wasn't a number." end + + success, val = parse_scale_component(parts[2]) + if not success then return false, "Error: y axis scale factor wasn't a number. Details: "..val end scale.y = val - val = tonumber(parts[3]) - if not val then return false, "Error: z axis scale factor wasn't a number." end + + success, val = parse_scale_component(parts[3]) + if not success then return false, "Error: z axis scale factor wasn't a number. Details: "..val end scale.z = val else - local val = tonumber(parts[1]) - if not val then - return false, "Error: scale factor wasn't a number." + local success, val = parse_scale_component(parts[1]) + if not success then + return false, "Error: scale factor wasn't a number. Details: "..val end scale.x = val scale.y = val @@ -93,6 +114,10 @@ worldedit.register_command("scale", { ) if not success then return success, stats end + worldedit.pos1[name] = stats.pos1 + worldedit.pos2[name] = stats.pos2 + worldedit.marker_update(name) + local time_taken = worldeditadditions.get_ms_time() - start_time