Improve & fix quaternions

This commit is contained in:
Lars Mueller 2021-02-02 15:44:58 +01:00
parent e56e9c1207
commit c323443975

@ -1,60 +1,61 @@
function multiply(q, w) function multiply(self, other)
return { return {
w[1] * q[1] - w[2] * q[2] - w[3] * q[3] - w[4] * q[4], other[1] * self[1] - other[2] * self[2] - other[3] * self[3] - other[4] * self[4],
w[1] * q[2] + w[2] * q[1] - w[3] * q[4] + w[4] * q[3], other[1] * self[2] + other[2] * self[1] - other[3] * self[4] + other[4] * self[3],
w[1] * q[3] + w[2] * q[4] + w[3] * q[1] - w[4] * q[2], other[1] * self[3] + other[2] * self[4] + other[3] * self[1] - other[4] * self[2],
w[1] * q[4] - w[2] * q[3] + w[3] * q[2] + w[4] * q[1] other[1] * self[4] - other[2] * self[3] + other[3] * self[2] + other[4] * self[1]
} }
end end
function normalize(q) function normalize(self)
local q_1, q_2, q_3, q_4 = q[1], q[2], q[3], q[4] local len = math.sqrt(self[1] ^ 2 + self[2] ^ 2 + self[3] ^ 2 + (q[4] ^ 4))
local len = math.sqrt(q_1 * q_1 + q_2 * q_2 + q_3 * q_3 + (q_4 ^ 4)) local res = {}
local r = {} for key, value in pairs(self) do
for key, value in pairs(q) do res[key] = value / len
r[key] = value / len
end end
return r return res
end end
function negate(q) function negate(self)
for k, v in pairs(q) do for key, value in pairs(self) do
q[k] = -v self[key] = -value
end end
end end
function dot(q, w) function dot(self, other)
return q[1] * w[1] + q[2] * w[2] + q[3] * w[3] + q[4] * w[4] return self[1] * other[1] + self[2] * other[2] + self[3] * other[3] + self[4] * other[4]
end end
-- assuming q & w are normalized --: self normalized quaternion
function slerp(q, w, r) --: other normalized quaternion
local d = dot(q, w) function slerp(self, other, ratio)
local d = dot(self, other)
if d < 0 then if d < 0 then
d = -d d = -d
negate(w) negate(other)
end end
-- Threshold beyond which linear interpolation is used -- Threshold beyond which linear interpolation is used
if d > 1 - 1e-10 then if d > 1 - 1e-10 then
return linear_interpolation(q, w, r) return modlib.vector.interpolate(self, other, ratio)
end end
local theta_0 = math.acos(d) local theta_0 = math.acos(d)
local theta = theta_0 * r local theta = theta_0 * ratio
local sin_theta = math.sin(theta) local sin_theta = math.sin(theta)
local sin_theta_0 = math.sin(theta_0) local sin_theta_0 = math.sin(theta_0)
local s_1 = sin_theta / sin_theta_0 local s_1 = sin_theta / sin_theta_0
local s_0 = math.cos(theta) - d * s_1 local s_0 = math.cos(theta) - d * s_1
return modlib.vector.add(modlib.vector.multiply_scalar(q, s_0), modlib.vector.multiply_scalar(w, s_1)) return modlib.vector.add(modlib.vector.multiply_scalar(self, s_0), modlib.vector.multiply_scalar(other, s_1))
end end
function to_rotation(q) --> {x, y, z} euler rotation in degrees
function to_euler_rotation(self)
local rotation = {} local rotation = {}
local sinr_cosp = 2 * (q[4] * q[1] + q[2] * q[3]) local sinr_cosp = 2 * (self[4] * self[1] + self[2] * self[3])
local cosr_cosp = 1 - 2 * (q[1] * q[1] + q[2] * q[2]) local cosr_cosp = 1 - 2 * (self[1] * self[1] + self[2] * self[2])
rotation.x = math.atan2(sinr_cosp, cosr_cosp) rotation.x = math.atan2(sinr_cosp, cosr_cosp)
local sinp = 2 * (q[4] * q[2] - q[3] * q[1]) local sinp = 2 * (self[4] * self[2] - self[3] * self[1])
if sinp <= -1 then if sinp <= -1 then
rotation.y = -math.pi/2 rotation.y = -math.pi/2
elseif sinp >= 1 then elseif sinp >= 1 then
@ -63,8 +64,8 @@ function to_rotation(q)
rotation.y = math.asin(sinp) rotation.y = math.asin(sinp)
end end
local siny_cosp = 2 * (q[4] * q[3] + q[1] * q[2]) local siny_cosp = 2 * (self[4] * self[3] + self[1] * self[2])
local cosy_cosp = 1 - 2 * (q[2] * q[2] + q[3] * q[3]) local cosy_cosp = 1 - 2 * (self[2] * self[2] + self[3] * self[3])
rotation.z = math.atan2(siny_cosp, cosy_cosp) rotation.z = math.atan2(siny_cosp, cosy_cosp)
return vector.apply(rotation, math.deg) return vector.apply(rotation, math.deg)