Quat = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 } Quat.__index = Quat Quat.__name = "Quat" --- Constructs a new Quaternion from x, y, z, and w. ---@param x number ---@param y number ---@param z number ---@param w number ---@return Quat function Quat:new(x, y, z, w) local q = {} setmetatable(q, Quat) q.x = x q.y = y q.z = z q.w = w return q end Quat.IDENTITY = Quat:new(0, 0, 0, 1) function Quat:copy() return Quat:new(self.x, self.y, self.z, self.w) end --- Creates a quaternion from the angle, in radians, around the x axis. --- @param rad number --- @return Quat function Quat:from_rotation_x(rad) local sin = math.sin(rad * 0.5) local cos = math.cos(rad * 0.5) return Quat:new(sin, 0, 0, cos) end --- Creates a quaternion from the angle, in radians, around the y axis. --- @param rad number --- @return Quat function Quat:from_rotation_y(rad) local sin = math.sin(rad * 0.5) local cos = math.cos(rad * 0.5) return Quat:new(0, sin, 0, cos) end --- Creates a quaternion from the angle, in radians, around the z axis. --- @param rad number --- @return Quat function Quat:from_rotation_z(rad) local sin = math.sin(rad * 0.5) local cos = math.cos(rad * 0.5) return Quat:new(0, 0, sin, cos) end --- Computes the dot product of `self`. ---@param rhs Quat ---@return number function Quat:dot(rhs) return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w) end --- Computes the length of `self`. ---@return number function Quat:length() return math.sqrt(self:dot(self)) end --- Compute the length of `self` squared. ---@return number function Quat:length_squared() return self:length() ^ 2 end --- Normalizes `self` and returns the new Quat ---@return unknown function Quat:normalize() local length = self:length() return self / length end --- Multiplies two Quaternions together. Keep in mind that Quaternion multiplication is NOT --- commutative so the order in which you multiply the quaternions matters. ---@param rhs Quat ---@return Quat function Quat:mult_quat(rhs) local x1, y1, z1, w1 = self.x, self.y, self.z, self.w local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w local x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2 local y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2 local z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2 local w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * x2 return Quat:new(x, y, z, w) end --- Multiplies `self` by a Vec3, returning the rotated Vec3 ---@param vec Vec3 ---@return Vec3 function Quat:mult_vec3(vec) local vec_quat = Quat:new(vec.x, vec.y, vec.z, 0) local quat = self:mult_quat(vec_quat) return Vec3:new(quat.x, quat.y, quat.z) end --- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`. --- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result --- will be equal to `rhs` --- @param rhs Quat --- @param alpha number --- @return Quat function Quat:lerp(rhs, alpha) -- ensure alpha is [0, 1] local alpha = math.max(0, math.min(1, alpha)) local x1, y1, z1, w1 = self.x, self.y, self.z, self.w local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w local x = (1 - alpha) * x1 + alpha * x2 local y = (1 - alpha) * y1 + alpha * y2 local z = (1 - alpha) * z1 + alpha * z2 local w = (1 - alpha) * w1 + alpha * w2 return Quat:new(x, y, z, w):normalize() end function Quat:__add(rhs) return Quat:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z, self.w + rhs.w) end function Quat:__sub(rhs) return Quat:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z, self.w - rhs.w) end function Quat:__mul(rhs) if type(rhs) == "number" then return Quat:new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs) elseif type(rhs) == "table" then local name = rhs.__name if name == "Vec3" then return self:mult_vec3(rhs) elseif name == "Quat" then return self:mult_quat(rhs) else assert(false, "Unknown usertype of rhs" .. name) end else assert(false, "Unknown type of rhs" .. type(rhs)) end end function Quat:__div(rhs) if type(rhs) == "number" then return Quat:new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs) else assert(rhs.__name == "Quat", "Attempted to divide Quat by unknown type " .. rhs.__name) return Quat:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z, self.w / rhs.w) end end function Quat:__eq(rhs) return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z and self.w == rhs.w end function Quat:__lt(rhs) return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z and self.w < rhs.w end function Quat:__le(rhs) return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z and self.w <= rhs.w end function Quat:__tostring() return "Quat(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ", " .. self.w .. ")" end