#![allow(dead_code)]
#[allow(dead_code)]
pub fn quat_identity() -> [f32; 4] {
[0.0, 0.0, 0.0, 1.0]
}
#[allow(dead_code)]
pub fn quat_mul(a: [f32; 4], b: [f32; 4]) -> [f32; 4] {
let [ax, ay, az, aw] = a;
let [bx, by, bz, bw] = b;
[
aw * bx + ax * bw + ay * bz - az * by,
aw * by - ax * bz + ay * bw + az * bx,
aw * bz + ax * by - ay * bx + az * bw,
aw * bw - ax * bx - ay * by - az * bz,
]
}
#[allow(dead_code)]
pub fn quat_normalize(q: [f32; 4]) -> [f32; 4] {
let len = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]).sqrt();
if len < 1e-9 {
return quat_identity();
}
let inv = 1.0 / len;
[q[0] * inv, q[1] * inv, q[2] * inv, q[3] * inv]
}
#[allow(dead_code)]
pub fn quat_conjugate(q: [f32; 4]) -> [f32; 4] {
[-q[0], -q[1], -q[2], q[3]]
}
#[allow(dead_code)]
pub fn quat_rotate_vec(q: [f32; 4], v: [f32; 3]) -> [f32; 3] {
let vq = [v[0], v[1], v[2], 0.0];
let qc = quat_conjugate(q);
let tmp = quat_mul(q, vq);
let res = quat_mul(tmp, qc);
[res[0], res[1], res[2]]
}
#[allow(dead_code)]
pub fn quat_from_axis_angle(axis: [f32; 3], angle: f32) -> [f32; 4] {
let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
if len < 1e-9 {
return quat_identity();
}
let inv = 1.0 / len;
let half = angle * 0.5;
let s = half.sin();
[axis[0] * inv * s, axis[1] * inv * s, axis[2] * inv * s, half.cos()]
}
#[allow(dead_code)]
pub fn quat_dot(a: [f32; 4], b: [f32; 4]) -> f32 {
a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
}
#[allow(dead_code)]
pub fn quat_slerp(a: [f32; 4], b: [f32; 4], t: f32) -> [f32; 4] {
let mut dot = quat_dot(a, b);
let b_adj = if dot < 0.0 {
dot = -dot;
[-b[0], -b[1], -b[2], -b[3]]
} else {
b
};
let (sa, sb) = if dot > 0.9995 {
(1.0 - t, t)
} else {
let theta = dot.acos();
let sin_theta = theta.sin();
(
((1.0 - t) * theta).sin() / sin_theta,
(t * theta).sin() / sin_theta,
)
};
quat_normalize([
a[0] * sa + b_adj[0] * sb,
a[1] * sa + b_adj[1] * sb,
a[2] * sa + b_adj[2] * sb,
a[3] * sa + b_adj[3] * sb,
])
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::PI;
const EPS: f32 = 1e-4;
#[test]
fn test_identity() {
let q = quat_identity();
assert_eq!(q, [0.0, 0.0, 0.0, 1.0]);
}
#[test]
fn test_normalize_identity() {
let q = quat_normalize([0.0, 0.0, 0.0, 2.0]);
assert!((q[3] - 1.0).abs() < EPS);
}
#[test]
fn test_conjugate() {
let q = [1.0f32, 2.0, 3.0, 4.0];
let c = quat_conjugate(q);
assert_eq!(c, [-1.0, -2.0, -3.0, 4.0]);
}
#[test]
fn test_mul_identity() {
let id = quat_identity();
let q = quat_from_axis_angle([0.0, 1.0, 0.0], PI / 4.0);
let r = quat_mul(id, q);
for i in 0..4 {
assert!((r[i] - q[i]).abs() < EPS);
}
}
#[test]
fn test_from_axis_angle_zero() {
let q = quat_from_axis_angle([0.0, 0.0, 0.0], 1.0);
assert!((q[3] - 1.0).abs() < EPS);
}
#[test]
fn test_from_axis_angle_90_y() {
let q = quat_from_axis_angle([0.0, 1.0, 0.0], PI / 2.0);
let expected_w = (PI / 4.0).cos();
assert!((q[3] - expected_w).abs() < EPS);
}
#[test]
fn test_dot_identity_self() {
let id = quat_identity();
assert!((quat_dot(id, id) - 1.0).abs() < EPS);
}
#[test]
fn test_slerp_t0() {
let a = quat_identity();
let b = quat_from_axis_angle([0.0, 1.0, 0.0], PI / 2.0);
let r = quat_slerp(a, b, 0.0);
for i in 0..4 {
assert!((r[i] - a[i]).abs() < EPS);
}
}
#[test]
fn test_slerp_t1() {
let a = quat_identity();
let b = quat_from_axis_angle([0.0, 1.0, 0.0], PI / 2.0);
let r = quat_slerp(a, b, 1.0);
for i in 0..4 {
assert!((r[i] - b[i]).abs() < EPS);
}
}
#[test]
fn test_rotate_vec_identity() {
let q = quat_identity();
let v = [1.0f32, 2.0, 3.0];
let r = quat_rotate_vec(q, v);
for i in 0..3 {
assert!((r[i] - v[i]).abs() < EPS);
}
}
}