diff --git a/.tests/Vector3/expand_region.test.lua b/.tests/Vector3/expand_region.test.lua new file mode 100644 index 0000000..2847464 --- /dev/null +++ b/.tests/Vector3/expand_region.test.lua @@ -0,0 +1,48 @@ +local Vector3 = require("worldeditadditions.utils.vector3") + +describe("Vector3.expand_region", function() + it("should work with positive vectors", function() + local a = Vector3.new(16, 64, 16) + local b = Vector3.new(1, 4, 6) + local target = Vector3.new(99, 99, 99) + + local result_a, result_b = target:expand_region(a, b) + assert.are.same(Vector3.new(1, 4, 6), result_a) + assert.are.same(Vector3.new(99, 99, 99), result_b) + end) + it("should work with mixed components", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + local target = Vector3.new(-99, -99, -99) + + local result_a, result_b = target:expand_region(a, b) + assert.are.same(Vector3.new(-99, -99, -99), result_a) + assert.are.same(Vector3.new(16, 4, 60), result_b) + end) + it("should work with negative vectors", function() + local a = Vector3.new(-9, -16, -25) + local b = Vector3.new(-3, -6, -2) + local target = Vector3.new(-99, -99, -99) + + local result_a, result_b = target:expand_region(a, b) + assert.are.same(Vector3.new(-99, -99, -99), result_a) + assert.are.same(Vector3.new(-3, -6, -2), result_b) + end) + it("should return new Vector3 instances", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + local target = Vector3.new(99, 99, 99) + + local result_a, result_b = target:expand_region(a, b) + assert.are.same(Vector3.new(1, 1, 16), result_a) + assert.are.same(Vector3.new(99, 99, 99), result_b) + + result_a.y = 999 + result_b.y = 999 + + assert.are.same(Vector3.new(16, 1, 16), a) + assert.are.same(Vector3.new(1, 4, 60), b) + assert.are.same(Vector3.new(1, 999, 16), result_a) + assert.are.same(Vector3.new(99, 999, 99), result_b) + end) +end) diff --git a/.tests/Vector3/is_contained.test.lua b/.tests/Vector3/is_contained.test.lua new file mode 100644 index 0000000..9c245ee --- /dev/null +++ b/.tests/Vector3/is_contained.test.lua @@ -0,0 +1,44 @@ +local Vector3 = require("worldeditadditions.utils.vector3") + +describe("Vector3.is_contained", function() + it("should return true when inside", function() + local a = Vector3.new(3, 4, 5) + local b = Vector3.new(30, 40, 50) + local target = Vector3.new(6, 6, 6) + + assert.are.same( + true, + target:is_contained(a, b) + ) + end) + it("should return false when outside x", function() + local a = Vector3.new(3, 4, 5) + local b = Vector3.new(30, 40, 50) + local target = Vector3.new(60, 6, 6) + + assert.are.same( + false, + target:is_contained(a, b) + ) + end) + it("should return false when outside y", function() + local a = Vector3.new(3, 4, 5) + local b = Vector3.new(30, 40, 50) + local target = Vector3.new(6, 60, 6) + + assert.are.same( + false, + target:is_contained(a, b) + ) + end) + it("should return false when outside z", function() + local a = Vector3.new(3, 4, 5) + local b = Vector3.new(30, 40, 50) + local target = Vector3.new(6, 6, 60) + + assert.are.same( + false, + target:is_contained(a, b) + ) + end) +end) diff --git a/.tests/Vector3/max.test.lua b/.tests/Vector3/max.test.lua new file mode 100644 index 0000000..f8d3d2a --- /dev/null +++ b/.tests/Vector3/max.test.lua @@ -0,0 +1,44 @@ +local Vector3 = require("worldeditadditions.utils.vector3") + +describe("Vector3.max", function() + it("should work with positive vectors", function() + local a = Vector3.new(16, 64, 16) + local b = Vector3.new(1, 4, 6) + + assert.are.same( + Vector3.new(16, 64, 16), + Vector3.max(a, b) + ) + end) + it("should work with mixed components", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + + assert.are.same( + Vector3.new(16, 4, 60), + Vector3.max(a, b) + ) + end) + it("should work with negative vectors", function() + local a = Vector3.new(-9, -16, -25) + local b = Vector3.new(-3, -6, -2) + + assert.are.same( + Vector3.new(-3, -6, -2), + Vector3.max(a, b) + ) + end) + it("should return new Vector3 instances", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + + local result = Vector3.max(a, b) + assert.are.same(Vector3.new(16, 4, 60), result) + + result.y = 999 + + assert.are.same(Vector3.new(16, 1, 16), a) + assert.are.same(Vector3.new(1, 4, 60), b) + assert.are.same(Vector3.new(16, 999, 60), result) + end) +end) diff --git a/.tests/Vector3/mean.test.lua b/.tests/Vector3/mean.test.lua new file mode 100644 index 0000000..0604d68 --- /dev/null +++ b/.tests/Vector3/mean.test.lua @@ -0,0 +1,47 @@ +local Vector3 = require("worldeditadditions.utils.vector3") + +describe("Vector3.mean", function() + it("should work with a positive vector", function() + local a = Vector3.new(2, 2, 2) + local b = Vector3.new(4, 4, 4) + assert.are.same( + Vector3.new(3, 3, 3), + a:mean(b) + ) + end) + it("should work with a positive vector the other way around", function() + local a = Vector3.new(2, 2, 2) + local b = Vector3.new(4, 4, 4) + assert.are.same( + Vector3.new(3, 3, 3), + b:mean(a) + ) + end) + it("should mean another positive vector", function() + local a = Vector3.new(6, 6, 6) + local b = Vector3.new(10, 10, 10) + assert.are.same( + Vector3.new(8, 8, 8), + a:mean(b) + ) + end) + it("should mean a negative vector", function() + local a = Vector3.new(-2, -2, -2) + local b = Vector3.new(0, 0, 0) + assert.are.same( + Vector3.new(-1, -1, -1), + a:mean(b) + ) + end) + it("should return a new Vector3 instance", function() + local a = Vector3.new(6, 6, 6) + local b = Vector3.new(10, 10, 10) + assert.are.same( + Vector3.new(8, 8, 8), + a:mean(b) + ) + + assert.are.same(Vector3.new(6, 6, 6), a) + assert.are.same(Vector3.new(10, 10, 10), b) + end) +end) diff --git a/.tests/Vector3/min.test.lua b/.tests/Vector3/min.test.lua new file mode 100644 index 0000000..332d74d --- /dev/null +++ b/.tests/Vector3/min.test.lua @@ -0,0 +1,44 @@ +local Vector3 = require("worldeditadditions.utils.vector3") + +describe("Vector3.min", function() + it("should work with positive vectors", function() + local a = Vector3.new(16, 64, 16) + local b = Vector3.new(1, 4, 6) + + assert.are.same( + Vector3.new(1, 4, 6), + Vector3.min(a, b) + ) + end) + it("should work with mixed components", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + + assert.are.same( + Vector3.new(1, 1, 16), + Vector3.min(a, b) + ) + end) + it("should work with negative vectors", function() + local a = Vector3.new(-9, -16, -25) + local b = Vector3.new(-3, -6, -2) + + assert.are.same( + Vector3.new(-9, -16, -25), + Vector3.min(a, b) + ) + end) + it("should return new Vector3 instances", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + + local result = Vector3.min(a, b) + assert.are.same(Vector3.new(1, 1, 16), result) + + result.y = 999 + + assert.are.same(Vector3.new(16, 1, 16), a) + assert.are.same(Vector3.new(1, 4, 60), b) + assert.are.same(Vector3.new(1, 999, 16), result) + end) +end) diff --git a/.tests/Vector3/sort_pos.test.lua b/.tests/Vector3/sort_pos.test.lua new file mode 100644 index 0000000..df8a89f --- /dev/null +++ b/.tests/Vector3/sort_pos.test.lua @@ -0,0 +1,44 @@ +local Vector3 = require("worldeditadditions.utils.vector3") + +describe("Vector3.sort", function() + it("should work with positive vectors", function() + local a = Vector3.new(16, 64, 16) + local b = Vector3.new(1, 4, 6) + + local result_a, result_b = Vector3.sort(a, b) + assert.are.same(Vector3.new(1, 4, 6), result_a) + assert.are.same(Vector3.new(16, 64, 16), result_b) + end) + it("should work with mixed components", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + + local result_a, result_b = Vector3.sort(a, b) + assert.are.same(Vector3.new(1, 1, 16), result_a) + assert.are.same(Vector3.new(16, 4, 60), result_b) + end) + it("should work with negative vectors", function() + local a = Vector3.new(-9, -16, -25) + local b = Vector3.new(-3, -6, -2) + + local result_a, result_b = Vector3.sort(a, b) + assert.are.same(Vector3.new(-9, -16, -25), result_a) + assert.are.same(Vector3.new(-3, -6, -2), result_b) + end) + it("should return new Vector3 instances", function() + local a = Vector3.new(16, 1, 16) + local b = Vector3.new(1, 4, 60) + + local result_a, result_b = Vector3.sort(a, b) + assert.are.same(Vector3.new(1, 1, 16), result_a) + assert.are.same(Vector3.new(16, 4, 60), result_b) + + result_a.y = 999 + result_b.y = 999 + + assert.are.same(Vector3.new(16, 1, 16), a) + assert.are.same(Vector3.new(1, 4, 60), b) + assert.are.same(Vector3.new(1, 999, 16), result_a) + assert.are.same(Vector3.new(16, 999, 60), result_b) + end) +end) diff --git a/worldeditadditions/utils/vector3.lua b/worldeditadditions/utils/vector3.lua index 2598c07..cfa2440 100644 --- a/worldeditadditions/utils/vector3.lua +++ b/worldeditadditions/utils/vector3.lua @@ -253,6 +253,102 @@ function Vector3.abs(a) return Vector3.new(math.abs(a.x), math.abs(a.y), math.abs(a.z)) end +--- Sorts the components of the given vectors. +-- pos1 will contain the minimum values, and pos2 the maximum values. +-- Returns 2 new vectors. +-- Note that the vectors provided do not *have* to be instances of Vector3. +-- It is only required that they have the keys x, y, and z. +-- Vector3 instances are always returned. +-- This enables convenient ingesting of positions from outside. +-- @param pos1 Vector3 The first vector to operate on. +-- @param pos2 Vector3 The second vector to operate on. +-- @returns Vector3,Vector3 The 2 sorted vectors. +function Vector3.sort(pos1, pos2) + local pos1_new = Vector3.clone(pos1) -- This way we can accept non-Vector3 instances + local pos2_new = Vector3.clone(pos2) -- This way we can accept non-Vector3 instances + if pos1_new.x > pos2_new.x then + pos1_new.x, pos2_new.x = pos2_new.x, pos1_new.x + end + if pos1_new.y > pos2_new.y then + pos1_new.y, pos2_new.y = pos2_new.y, pos1_new.y + end + if pos1_new.z > pos2_new.z then + pos1_new.z, pos2_new.z = pos2_new.z, pos1_new.z + end + return pos1_new, pos2_new +end + +--- Determines if this vector is contained within the region defined by the given vectors. +-- @param a Vector3 The target vector to check. +-- @param pos1 Vector3 pos1 of the defined region. +-- @param pos2 Vector3 pos2 of the defined region. +-- @return boolean Whether the given target is contained within the defined worldedit region. +function Vector3.is_contained(target, pos1, pos2) + local pos1, pos2 = Vector3.sort(pos1, pos2) + + return pos1.x <= target.x + and pos1.y <= target.y + and pos1.z <= target.z + and pos2.x >= target.x + and pos2.y >= target.y + and pos2.z >= target.z +end + + +--- Expands the defined region to include the given point. +-- @param target Vector3 The target vector to include. +-- @param pos1 Vector3 pos1 of the defined region. +-- @param pos2 Vector3 pos2 of the defined region. +-- @returns Vector3,Vector3 2 vectors that represent the expand_region. +function Vector3.expand_region(target, pos1, pos2) + local pos1, pos2 = Vector3.sort(pos1, pos2) + + if target.x < pos1.x then pos1.x = target.x end + if target.y < pos1.y then pos1.y = target.y end + if target.z < pos1.z then pos1.z = target.z end + + if target.x > pos2.x then pos2.x = target.x end + if target.y > pos2.y then pos2.y = target.y end + if target.z > pos2.z then pos2.z = target.z end + + return pos1, pos2 +end + +--- Returns the mean (average) of 2 positions. +-- In other words, returns the centre of 2 points. +-- @param pos1 Vector3 pos1 of the defined region. +-- @param pos2 Vector3 pos2 of the defined region. +-- @param target Vector3 Centre coordinates. +function Vector3.mean(pos1, pos2) + return (pos1 + pos2) / 2 +end + + +--- Returns a vector of the min components of 2 vectors. +-- @param pos1 Vector3 The first vector to operate on. +-- @param pos2 Vector3 The second vector to operate on. +-- @return Vector3 The minimum values from the input vectors +function Vector3.min(pos1, pos2) + return Vector3.new( + math.min(pos1.x, pos2.x), + math.min(pos1.y, pos2.y), + math.min(pos1.z, pos2.z) + ) +end + +--- Returns a vector of the max values of 2 vectors. +-- @param pos1 Vector3 The first vector to operate on. +-- @param pos2 Vector3 The second vector to operate on. +-- @return Vector3 The maximum values from the input vectors. +function Vector3.max(pos1, pos2) + return Vector3.new( + math.max(pos1.x, pos2.x), + math.max(pos1.y, pos2.y), + math.max(pos1.z, pos2.z) + ) +end + + -- ██████ ██████ ███████ ██████ █████ ████████ ██████ ██████ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██