use std::ops::Mul;
use crate::{
core::{matrix::Matrix, vec::Vector},
prelude::{Mat3x3f, Vec3f},
};
pub type Quat = Quaternion;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Quaternion {
w: f32,
i: f32,
j: f32,
k: f32,
}
impl Quaternion {
pub fn new(wijk: [f32; 4]) -> Self {
Self {
w: wijk[0],
i: wijk[1],
j: wijk[2],
k: wijk[3],
}
}
pub fn wxyz(w: f32, x: f32, y: f32, z: f32) -> Self {
Self {
w,
i: x,
j: y,
k: z,
}
}
pub fn identity() -> Self {
Self::wxyz(1., 0., 0., 0.)
}
pub fn angle_axis(degree: f32, axis: Vec3f) -> Self {
let rad = degree.to_radians();
let (cos, sin) = ((rad * 0.5).cos(), (rad * 0.5).sin());
let v = axis.normalize() * sin;
Self::wxyz(cos, v[0], v[1], v[2])
}
pub fn euler(x: f32, y: f32, z: f32) -> Self {
let mut q = if x == 0. {
Self::angle_axis(x, Vec3f::vec([1., 0., 0.]))
} else {
Self::identity()
};
if y != 0. {
let qy = Self::angle_axis(y, Vec3f::vec([0., 1., 0.]));
q = qy * q;
}
if z != 0. {
let qz = Self::angle_axis(z, Vec3f::vec([0., 0., 1.]));
q = qz * q;
}
q
}
pub fn normalize(&self) -> Self {
let (w, i, j, k) = (self.w, self.i, self.j, self.k);
let norm = i * i + j * j + k * k;
Self::wxyz(w / norm, i / norm, j / norm, k / norm)
}
pub fn rotate(&self, degree: f32, axis: Vec3f) -> Self {
let q = Self::angle_axis(degree, axis);
q * (*self)
}
pub fn transform_vec(&self, vec: Vec3f) -> Vec3f {
self.to_matrix().matmulvec(vec)
}
pub fn conjugate(&self) -> Self {
Self::wxyz(self.w, -self.i, -self.j, -self.k)
}
#[rustfmt::skip]
pub fn to_matrix(&self) -> Mat3x3f {
let (r,i,j,k) = (self.w,self.i,self.j,self.k);
Mat3x3f::mat([3,3],
[
1. - 2. * (j * j + k * k), 2. * (i * j - r * k), 2.* (i * k + r * j),
2. * (i * j + r * k), 1. - 2. * (i * i + k * k), 2. * (j * k - r * i),
2. * (i * k - r * j), 2. * (j * k + r * i), 1. - 2. * (i * i + j * j),
]
)
}
}
impl Mul<Quaternion> for Quaternion {
type Output = Quaternion;
fn mul(self, rhs: Quaternion) -> Self::Output {
let w = self.w * rhs.w - (self.i * rhs.i + self.j * rhs.j + self.k * rhs.k); let i = self.w * rhs.i + rhs.w * self.i + self.j * rhs.k - self.k * rhs.j; let j = self.w * rhs.j + rhs.w * self.j - self.i * rhs.k + self.k * rhs.i; let k = self.w * rhs.k + rhs.w * self.k + self.i * rhs.j - self.j * rhs.i; Self::wxyz(w, i, j, k)
}
}
#[test]
fn test_quaternion() {
let (d, a) = (60., Vec3f::vec([1., 0., 0.]));
let q = Quat::identity();
let qr = q.rotate(d, a);
assert_eq!(qr.w, 30f32.to_radians().cos());
let q = Quat::identity();
let qr = q.rotate(d, a).rotate(-d, a);
assert_eq!(q, qr);
let v = Vec3f::vec([0., 1., 0.]);
let vr = q.rotate(d, a).transform_vec(v);
assert_eq!(vr[1], 0.5f32);
}