From 5845da7ca9439fbfb923dce13c04c7fb745fb93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wi=C5=9Bniewski?= Date: Sat, 22 Mar 2025 19:33:53 +0100 Subject: [PATCH] Add `/rotate` command (#1225) * Revert "Remove rotate command" This reverts commit 3a84e03d158e5cc038ae3e936384c348431a9ac4. * Fix stair rotation * Add rotate fn to Blueprint * Apply suggestions from code review Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> * Replace cos sin with switch * Add angle parameter to /rotate * Update src/blueprint.zig * Apply review suggestions * Fix formatting issues * Update src/rotation.zig --------- Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> --- src/blueprint.zig | 28 +++++++++++++++++++++++++ src/rotation.zig | 4 ++-- src/server/command/_list.zig | 1 + src/server/command/worldedit/rotate.zig | 28 +++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/server/command/worldedit/rotate.zig diff --git a/src/blueprint.zig b/src/blueprint.zig index 25daff04..28bcd745 100644 --- a/src/blueprint.zig +++ b/src/blueprint.zig @@ -10,6 +10,8 @@ const Array3D = main.utils.Array3D; const Block = main.blocks.Block; const NeverFailingAllocator = main.heap.NeverFailingAllocator; const User = main.server.User; +const ServerChunk = main.chunk.ServerChunk; +const Degrees = main.rotation.Degrees; const GameIdToBlueprintIdMapType = std.AutoHashMap(u16, u16); const BlockIdSizeType = u32; @@ -36,6 +38,32 @@ pub const Blueprint = struct { pub fn clone(self: *Blueprint, allocator: NeverFailingAllocator) Blueprint { return .{.blocks = self.blocks.clone(allocator)}; } + pub fn rotateZ(self: Blueprint, allocator: NeverFailingAllocator, angle: Degrees) Blueprint { + var new = Blueprint{ + .blocks = switch(angle) { + .@"0", .@"180" => .init(allocator, self.blocks.width, self.blocks.depth, self.blocks.height), + .@"90", .@"270" => .init(allocator, self.blocks.depth, self.blocks.width, self.blocks.height), + }, + }; + + for(0..self.blocks.width) |xOld| { + for(0..self.blocks.depth) |yOld| { + const xNew, const yNew = switch(angle) { + .@"0" => .{xOld, yOld}, + .@"90" => .{new.blocks.width - yOld - 1, xOld}, + .@"180" => .{new.blocks.width - xOld - 1, new.blocks.depth - yOld - 1}, + .@"270" => .{yOld, new.blocks.depth - xOld - 1}, + }; + + for(0..self.blocks.height) |z| { + const block = self.blocks.get(xOld, yOld, z); + new.blocks.set(xNew, yNew, z, block.rotateZ(angle)); + } + } + } + return new; + } + const CaptureResult = union(enum) { success: Blueprint, failure: struct {pos: Vec3i, message: []const u8}, diff --git a/src/rotation.zig b/src/rotation.zig index e1958cec..acafccc2 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -806,7 +806,7 @@ pub const RotationModes = struct { comptime var rotationTable: [4][256]u8 = undefined; comptime for(0..4) |a| { for(0..256) |old| { - var new: u8 = 0; + var new: u8 = 0b11_11_11_11; for(0..2) |i| for(0..2) |j| for(0..2) |k| { const sin: f32 = @sin((std.math.pi/2.0)*@as(f32, @floatFromInt(a))); @@ -819,7 +819,7 @@ pub const RotationModes = struct { const rY = @intFromBool(x*sin + y*cos > 0); if(hasSubBlock(@intCast(old), @intCast(i), @intCast(j), @intCast(k))) { - new |= subBlockMask(rX, rY, @intCast(k)); + new &= ~subBlockMask(rX, rY, @intCast(k)); } }; rotationTable[a][old] = new; diff --git a/src/server/command/_list.zig b/src/server/command/_list.zig index afee7f90..9b5646ca 100644 --- a/src/server/command/_list.zig +++ b/src/server/command/_list.zig @@ -12,3 +12,4 @@ pub const deselect = @import("worldedit/deselect.zig"); pub const copy = @import("worldedit/copy.zig"); pub const paste = @import("worldedit/paste.zig"); pub const blueprint = @import("worldedit/blueprint.zig"); +pub const rotate = @import("worldedit/rotate.zig"); diff --git a/src/server/command/worldedit/rotate.zig b/src/server/command/worldedit/rotate.zig new file mode 100644 index 00000000..f508b57a --- /dev/null +++ b/src/server/command/worldedit/rotate.zig @@ -0,0 +1,28 @@ +const std = @import("std"); + +const main = @import("root"); +const User = main.server.User; +const Degrees = main.rotation.Degrees; + +pub const description = "rotate clipboard content around Z axis counterclockwise."; +pub const usage = + \\/rotate + \\/rotate <0/90/180/270> +; + +pub fn execute(args: []const u8, source: *User) void { + var angle: Degrees = .@"90"; + if(args.len != 0) { + angle = std.meta.stringToEnum(Degrees, args) orelse { + source.sendMessage("#ff0000Error: Invalid angle '{s}'. Use 0, 90, 180 or 270.", .{args}); + return; + }; + } + if(source.worldEditData.clipboard == null) { + source.sendMessage("#ff0000Error: No clipboard content to rotate.", .{}); + return; + } + const current = source.worldEditData.clipboard.?; + defer current.deinit(main.globalAllocator); + source.worldEditData.clipboard = current.rotateZ(main.globalAllocator, angle); +}