Validate arguments in the vector API

This doesn't produce the best error messages (should "self" be argument
0 or 1?), but is better than throwing errors in vector's internals.
This commit is contained in:
Jonathan Coates 2024-04-01 22:21:18 +01:00
parent 0f623c2cca
commit 0c1ab780bb
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
2 changed files with 121 additions and 16 deletions

View File

@ -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,

View File

@ -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)