From 40c9af48030927e3c47022e5b510f8dbb3675e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lera=20Elvo=C3=A9?= Date: Sat, 15 Feb 2025 13:37:39 +0300 Subject: [PATCH] better Vector3 coercion semantics --- data/scripts/types/vector3.lua | 95 +++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/data/scripts/types/vector3.lua b/data/scripts/types/vector3.lua index 5c08f53..1a511ef 100644 --- a/data/scripts/types/vector3.lua +++ b/data/scripts/types/vector3.lua @@ -27,6 +27,25 @@ local function is_weak_vector3(t) end end +---Coerces t to a Vector3. If t is already a Vector3, returns it without copying. +---@param t vectorlike +---@param do_err boolean? +---@param def Vector3? +---@return boolean error, Vector3 result +local function coerce(t, do_err, def) + do_err = do_err or true + if do_err and not is_weak_vector3(t) then + def = def or Vector3() + error("Vector3: can not coerce t into Vector3. Returning " .. tostring(def)) + return true, def + end + if t["_CLASS_"] == "Vector3" then + return false, t + end + + return false, Vector3(t) +end + ---Returns a Vector3 multiplied either component-wise (if b is a weak Vector3) or multiplies each component by b if b is a number. ---@param b number|vectorlike ---@return Vector3 @@ -167,12 +186,11 @@ end ---@param with vectorlike ---@return number function Vector3:dot(with) - if not is_weak_vector3(with) then - error("Vector3: with must be a Vector3-like table. Returning 0") - return 0 + local err, v2 = coerce(with) + if err then + return 0.0 end - local v2 = Vector3(with) return self.x * v2.x + self.y * v2.y + self.z * v2.z end @@ -180,11 +198,10 @@ end ---@param with vectorlike ---@return Vector3 function Vector3:cross(with) - if not is_weak_vector3(with) then - error("Vector3: with must be a Vector3-like table. Returning Vector3()") - return Vector3() + local err, v2 = coerce(with) + if err then + return v2 end - local v2 = Vector3(with) return Vector3 { self.y * v2.z - self.z * v2.y, self.z * v2.x - self.x * v2.z, @@ -197,17 +214,22 @@ end ---@param angle number ---@return Vector3 function Vector3:rotated(axis, angle) - if not is_weak_vector3(axis) then - error("Vector3: axis must be a Vector3-like table. Returning Vector3()") - return Vector3() + -- if not is_weak_vector3(axis) then + -- error("Vector3: axis must be a Vector3-like table. Returning Vector3()") + -- return Vector3() + -- end + local err, vaxis = coerce(axis) + if err then + return vaxis end - axis = Vector3(axis):normalized() +---@diagnostic disable-next-line: undefined-field + vaxis = vaxis:normalized() local cosa = math.cos(angle) local sina = math.sin(angle) -- __mul is only defined for the left operand (table), numbers don't get metatables. -- as such, the ordering of operations here is specific - local v = (self * cosa) + (axis * ((1 - cosa) * self:dot(axis))) + (axis:cross(self) * sina) + local v = (self * cosa) + (vaxis * ((1 - cosa) * self:dot(vaxis))) + (vaxis:cross(self) * sina) return Vector3(v) end @@ -222,12 +244,11 @@ end ---@param t number ---@return Vector3 function Vector3:lerp(b, t) - -- return self:copy() - if not is_weak_vector3(b) then - error("Vector3: b must be a Vector3-like table. Returning copy of self.") - return self:copy() + local err, w = coerce(b) + if err then + return w end - local w = Vector3(b) + return Vector3{ util.lerp(self.x, w.x, t), util.lerp(self.y, w.y, t), @@ -235,6 +256,44 @@ function Vector3:lerp(b, t) } end +function Vector3:direction_to(to) + local err, other = coerce(to) + if err then + return 0.0 + end + + return Vector3(other.x - self.x, other.y - self.y, other.z - self.z):normalized() +end + +---Returns the squared distance between this vector and to. +---@param to vectorlike +---@return number +function Vector3:distance_squared_to(to) + local err, other = coerce(to) + if err then + return 0.0 + end + + return (other - self):length_squared() +end + +---Returns the distance between this vector and to. +---@param to vectorlike +---@return number +function Vector3:distance_to(to) + local err, other = coerce(to) + if err then + return 0.0 + end + + return (other - self):length() +end + +---Returns a new vector with the Y discarded (0). +---@return Vector3 +function Vector3:horizontal() + return Vector3(self.x, 0.0, self.z) +end ---- CONSTANTS Vector3.UP = Vector3(0, 1, 0)