---@alias Vec3Like { x: number, y: number, z: number } ---@alias Vec3 Vec3Like ---@class Vector3 ---@field x number ---@field y number ---@field z number ---@operator add: Vector3 ---@operator sub: Vector3 local Vector3 = {} Vector3.__index = Vector3 ---@param x number ---@param y number ---@param z number ---@return Vector3 function Vector3.new(x, y, z) return setmetatable({ x = x or 0, y = y or 0, z = z or 0 }, Vector3) end ---@return Vector3 function Vector3:copy() return Vector3.new(self.x, self.y, self.z) end ---@return string function Vector3:__tostring() return string.format("Vector3(%.3f, %.3f, %.3f)", self.x, self.y, self.z) end ---@param v Vector3 | Vec3Like ---@return Vector3 function Vector3:add(v) return Vector3.new(self.x + v.x, self.y + v.y, self.z + v.z) end ---@param v Vector3 | Vec3Like ---@return Vector3 function Vector3:sub(v) return Vector3.new(self.x - v.x, self.y - v.y, self.z - v.z) end ---@param scalar number ---@return Vector3 function Vector3:mul(scalar) return Vector3.new(self.x * scalar, self.y * scalar, self.z * scalar) end ---@param scalar number ---@return Vector3 function Vector3:div(scalar) return Vector3.new(self.x / scalar, self.y / scalar, self.z / scalar) end ---@param self Vector3 | Vec3Like ---@param v Vector3 | Vec3Like ---@return Vector3 function Vector3.__add(self, v) return Vector3.new(self.x + v.x, self.y + v.y, self.z + v.z) end ---@param self Vector3 | Vec3Like ---@param v Vector3 | Vec3Like ---@return Vector3 function Vector3.__sub(self, v) return Vector3.new(self.x - v.x, self.y - v.y, self.z - v.z) end ---@return number function Vector3:length() return math.sqrt(self.x^2 + self.y^2 + self.z^2) end ---@return number function Vector3:lengthSq() return self.x^2 + self.y^2 + self.z^2 end ---@return Vector3 function Vector3:normalize() local len = self:length() if len == 0 then return Vector3.new(0, 0, 0) end return Vector3.new(self.x / len, self.y / len, self.z / len) end ---@param v Vector3 | Vec3Like ---@return number function Vector3:dot(v) return self.x * v.x + self.y * v.y + self.z * v.z end ---@param v Vector3 | Vec3Like ---@return Vector3 function Vector3:cross(v) return Vector3.new( self.y * v.z - self.z * v.y, self.z * v.x - self.x * v.z, self.x * v.y - self.y * v.x ) end ---@param v Vector3 | Vec3Like ---@return number function Vector3:distanceTo(v) return (self:sub(v)):length() end ---@param v Vector3 | Vec3Like ---@return number function Vector3:distanceToSq(v) return (self:sub(v)):lengthSq() end ---@param v Vector3 | Vec3Like ---@param epsilon number ---@return boolean function Vector3:equals(v, epsilon) epsilon = epsilon or 1e-5 return math.abs(self.x - v.x) < epsilon and math.abs(self.y - v.y) < epsilon and math.abs(self.z - v.z) < epsilon end ---@param func function ---@return Vector3 function Vector3:map(func) return Vector3.new(func(self.x), func(self.y), func(self.z)) end ---@return table function Vector3:toTable() return { x = self.x, y = self.y, z = self.z } end ---@param tbl Vec3Like | Vector3 ---@return Vector3 function Vector3.fromTable(tbl) return Vector3.new(tbl.x, tbl.y, tbl.z) end ---@param tbl table ---@return Vector3 function Vector3.fromArray(tbl) return Vector3.new(tbl[1], tbl[2], tbl[3]) end ---@return number ---@return number ---@return number function Vector3:unpack() return self.x, self.y, self.z end ---@return Vec3Like function Vector3:toArray() return {self.x, self.y, self.z} end ---@return Vec3Like function Vector3:projectXZ() return Vector3.new(self.x, 0, self.z) end return Vector3