use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
use num_traits::Float;
#[derive(Debug, Copy, Clone)]
pub struct Quaternion<T: Float> {
pub w: T,
pub x: T,
pub y: T,
pub z: T,
}
pub fn q<T>(w: T, x: T, y: T, z: T) -> Quaternion<T>
where
T: Float,
{
Quaternion { w, x, y, z }
}
pub fn qi<T>(w: i32, x: i32, y: i32, z: i32) -> Quaternion<T>
where
T: Float,
{
q(T::from(w).unwrap(), T::from(x).unwrap(), T::from(y).unwrap(), T::from(z).unwrap())
}
impl<T> Quaternion<T>
where
T: Float,
{
pub fn id() -> Self {
Quaternion {
w: T::one(),
x: T::zero(),
y: T::zero(),
z: T::zero(),
}
}
pub fn new(w: T, x: T, y: T, z: T) -> Self {
Quaternion { w, x, y, z }
}
pub fn scale(&self, t: T) -> Quaternion<T> {
Quaternion {
w: self.w * t,
x: self.x * t,
y: self.y * t,
z: self.z * t,
}
}
pub fn scale_mut(&mut self, t: T) {
self.w = self.w * t;
self.x = self.x * t;
self.y = self.y * t;
self.z = self.z * t;
}
pub fn inverse(&self) -> Self {
self.scale(T::from(1.).unwrap() / self.square_length()).conjugate()
}
pub fn inverse_mut(&mut self) {
self.scale_mut(T::from(1.).unwrap() / self.square_length());
self.conjugate_mut();
}
pub fn dot(&self, other: &Quaternion<T>) -> T {
self.w * other.w + self.x * other.x + self.y * other.y + self.z * other.z
}
pub fn conjugate(&self) -> Self {
Quaternion {
w: self.w,
x: -self.x,
y: -self.y,
z: -self.z,
}
}
pub fn conjugate_mut(&mut self) {
self.x = -self.x;
self.y = -self.y;
self.z = -self.z;
}
#[inline(always)]
pub fn square_length(&self) -> T {
self.w * self.w + self.x * self.x + self.y * self.y + self.z * self.z
}
#[inline(always)]
pub fn length(&self) -> T {
self.square_length().sqrt()
}
}
impl<T> Default for Quaternion<T>
where
T: Float,
{
fn default() -> Self {
Quaternion {
w: T::zero(),
x: T::zero(),
y: T::zero(),
z: T::zero(),
}
}
}
impl<T> PartialEq for Quaternion<T>
where
T: Float,
{
fn eq(&self, other: &Self) -> bool {
(*self - *other).square_length() < T::epsilon()
}
}
impl<T> Eq for Quaternion<T> where T: Float {}
impl<T> Add for Quaternion<T>
where
T: Float,
{
type Output = Self;
fn add(self, other: Self) -> Self {
Quaternion {
w: self.w + other.w,
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl<T> AddAssign for Quaternion<T>
where
T: Float,
{
fn add_assign(&mut self, other: Self) {
self.w = self.w + other.w;
self.x = self.x + other.x;
self.y = self.y + other.y;
self.z = self.z + other.z;
}
}
impl<T> Mul for Quaternion<T>
where
T: Float,
{
type Output = Self;
fn mul(self, other: Self) -> Self {
Quaternion {
w: self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
x: self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
y: self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x,
z: self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w,
}
}
}
impl<T> MulAssign for Quaternion<T>
where
T: Float,
{
fn mul_assign(&mut self, other: Self) {
let w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z;
let x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y;
let y = self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x;
let z = self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w;
self.w = w;
self.x = x;
self.y = y;
self.z = z;
}
}
impl<T> Div for Quaternion<T>
where
T: Float,
{
type Output = Self;
fn div(self, b: Self) -> Self::Output {
self.mul(b.inverse())
}
}
impl<T> Sub for Quaternion<T>
where
T: Float,
{
type Output = Self;
fn sub(self, other: Self) -> Self {
Quaternion {
w: self.w - other.w,
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl<T> SubAssign for Quaternion<T>
where
T: Float,
{
fn sub_assign(&mut self, other: Self) {
self.w = self.w - other.w;
self.x = self.x - other.x;
self.y = self.y - other.y;
self.z = self.z - other.z;
}
}
impl<T> Neg for Quaternion<T>
where
T: Float,
{
type Output = Self;
fn neg(self) -> Self::Output {
Quaternion {
w: -self.w,
x: -self.x,
y: -self.y,
z: -self.z,
}
}
}
impl<T> Quaternion<T>
where
T: Float,
{
#[inline(always)]
pub fn from_euler_angles(x: T, y: T, z: T) -> Quaternion<T> {
let two: T = T::one() + T::one();
let half_x = x / two;
let half_y = y / two;
let half_z = z / two;
let cos_x_2 = half_x.cos();
let cos_y_2 = half_y.cos();
let cos_z_2 = half_z.cos();
let sin_x_2 = half_x.sin();
let sin_y_2 = half_y.sin();
let sin_z_2 = half_z.sin();
Quaternion {
w: cos_x_2 * cos_y_2 * cos_z_2 + sin_x_2 * sin_y_2 * sin_z_2,
x: sin_x_2 * cos_y_2 * cos_z_2 + cos_x_2 * sin_y_2 * sin_z_2,
y: cos_x_2 * sin_y_2 * cos_z_2 + sin_x_2 * cos_y_2 * sin_z_2,
z: cos_x_2 * cos_y_2 * sin_z_2 + sin_x_2 * sin_y_2 * cos_z_2,
}
}
}