diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/vector.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/vector.lua index 71f4be94f..9501f53c1 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/vector.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/vector.lua @@ -13,6 +13,11 @@ -- @module vector -- @since 1.31 +local getmetatable = getmetatable +local expect = dofile("rom/modules/main/cc/expect.lua").expect + +local vmetatable + --- A 3-dimensional vector, with `x`, `y`, and `z` values. -- -- This is suitable for representing both position and directional vectors. @@ -27,6 +32,9 @@ local vector = { -- @usage v1:add(v2) -- @usage v1 + v2 add = function(self, o) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + if getmetatable(o) ~= vmetatable then expect(2, o, "vector") end + return vector.new( self.x + o.x, self.y + o.y, @@ -42,6 +50,9 @@ local vector = { -- @usage v1:sub(v2) -- @usage v1 - v2 sub = function(self, o) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + if getmetatable(o) ~= vmetatable then expect(2, o, "vector") end + return vector.new( self.x - o.x, self.y - o.y, @@ -52,30 +63,36 @@ local vector = { --- Multiplies a vector by a scalar value. -- -- @tparam Vector self The vector to multiply. - -- @tparam number m The scalar value to multiply with. + -- @tparam number factor The scalar value to multiply with. -- @treturn Vector A vector with value `(x * m, y * m, z * m)`. - -- @usage v:mul(3) - -- @usage v * 3 - mul = function(self, m) + -- @usage vector.new(1, 2, 3):mul(3) + -- @usage vector.new(1, 2, 3) * 3 + mul = function(self, factor) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + expect(2, factor, "number") + return vector.new( - self.x * m, - self.y * m, - self.z * m + self.x * factor, + self.y * factor, + self.z * factor ) end, --- Divides a vector by a scalar value. -- -- @tparam Vector self The vector to divide. - -- @tparam number m The scalar value to divide by. + -- @tparam number factor The scalar value to divide by. -- @treturn Vector A vector with value `(x / m, y / m, z / m)`. - -- @usage v:div(3) - -- @usage v / 3 - div = function(self, m) + -- @usage vector.new(1, 2, 3):div(3) + -- @usage vector.new(1, 2, 3) / 3 + div = function(self, factor) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + expect(2, factor, "number") + return vector.new( - self.x / m, - self.y / m, - self.z / m + self.x / factor, + self.y / factor, + self.z / factor ) end, @@ -83,8 +100,9 @@ local vector = { -- -- @tparam Vector self The vector to negate. -- @treturn Vector The negated vector. - -- @usage -v + -- @usage -vector.new(1, 2, 3) unm = function(self) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end return vector.new( -self.x, -self.y, @@ -99,6 +117,9 @@ local vector = { -- @treturn Vector The dot product of `self` and `o`. -- @usage v1:dot(v2) dot = function(self, o) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + if getmetatable(o) ~= vmetatable then expect(2, o, "vector") end + return self.x * o.x + self.y * o.y + self.z * o.z end, @@ -109,6 +130,9 @@ local vector = { -- @treturn Vector The cross product of `self` and `o`. -- @usage v1:cross(v2) cross = function(self, o) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + if getmetatable(o) ~= vmetatable then expect(2, o, "vector") end + return vector.new( self.y * o.z - self.z * o.y, self.z * o.x - self.x * o.z, @@ -120,6 +144,7 @@ local vector = { -- @tparam Vector self This vector. -- @treturn number The length of this vector. length = function(self) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) end, @@ -141,6 +166,9 @@ local vector = { -- nearest 0.5. -- @treturn Vector The rounded vector. round = function(self, tolerance) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + expect(2, tolerance, "number", "nil") + tolerance = tolerance or 1.0 return vector.new( math.floor((self.x + tolerance * 0.5) / tolerance) * tolerance, @@ -156,6 +184,8 @@ local vector = { -- @usage v:tostring() -- @usage tostring(v) tostring = function(self) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + return self.x .. "," .. self.y .. "," .. self.z end, @@ -165,11 +195,15 @@ local vector = { -- @tparam Vector other The second vector to compare to. -- @treturn boolean Whether or not the vectors are equal. equals = function(self, other) + if getmetatable(self) ~= vmetatable then expect(1, self, "vector") end + if getmetatable(other) ~= vmetatable then expect(2, other, "vector") end + return self.x == other.x and self.y == other.y and self.z == other.z end, } -local vmetatable = { +vmetatable = { + __name = "vector", __index = vector, __add = vector.add, __sub = vector.sub, diff --git a/projects/core/src/test/resources/test-rom/spec/apis/vector_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/vector_spec.lua new file mode 100644 index 000000000..663081b2e --- /dev/null +++ b/projects/core/src/test/resources/test-rom/spec/apis/vector_spec.lua @@ -0,0 +1,71 @@ +-- SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +describe("The vector library", function() + local vec = vector.new(1, 2, 3) + + describe("vector.add", function() + it("validates arguments", function() + expect.error(vec.add, nil, vec):eq("bad argument #1 (vector expected, got nil)") + expect.error(vec.add, vec, nil):eq("bad argument #2 (vector expected, got nil)") + end) + + it("returns the correct value", function() + expect(vector.new(1, 2, 3) + vector.new(6, 4, 2)):eq(vector.new(7, 6, 5)) + end) + end) + + describe("vector.sub", function() + it("validates arguments", function() + expect.error(vec.sub, nil, vec):eq("bad argument #1 (vector expected, got nil)") + expect.error(vec.sub, vec, nil):eq("bad argument #2 (vector expected, got nil)") + end) + + it("returns the correct value", function() + expect(vector.new(6, 4, 2) - vector.new(1, 2, 3)):eq(vector.new(5, 2, -1)) + end) + end) + + describe("vector.mul", function() + it("validates arguments", function() + expect.error(vec.mul, nil, vec):eq("bad argument #1 (vector expected, got nil)") + expect.error(vec.mul, vec, nil):eq("bad argument #2 (number expected, got nil)") + end) + + it("returns the correct value", function() + expect(vector.new(1, 2, 3) * 2):eq(vector.new(2, 4, 6)) + end) + end) + + describe("vector.div", function() + it("validates arguments", function() + expect.error(vec.div, nil, vec):eq("bad argument #1 (vector expected, got nil)") + expect.error(vec.div, vec, nil):eq("bad argument #2 (number expected, got nil)") + end) + + it("returns the correct value", function() + expect(vector.new(1, 2, 3) / 2):eq(vector.new(0.5, 1, 1.5)) + end) + end) + + describe("vector.unm", function() + it("validates arguments", function() + expect.error(vec.unm, nil):eq("bad argument #1 (vector expected, got nil)") + end) + + it("returns the correct value", function() + expect(-vector.new(2, 3, 6)):eq(vector.new(-2, -3, -6)) + end) + end) + + describe("vector.length", function() + it("validates arguments", function() + expect.error(vec.length, nil):eq("bad argument #1 (vector expected, got nil)") + end) + + it("returns the correct value", function() + expect(vector.new(2, 3, 6):length()):eq(7) + end) + end) +end)