use core::convert::From;
use core::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign};
use num_traits::{MulAdd, MulAddAssign, One, Signed, Zero, float::FloatCore};
use crate::math_methods::TrigonometricMethods;
use crate::sqrt_methods::SqrtMethods;
use crate::{QuaternionMath, Vector3d};
pub type Quaternionf32 = Quaternion<f32>;
pub type Quaternionf64 = Quaternion<f64>;
pub type RollPitchYawf32 = RollPitchYaw<f32>;
pub type RollPitchYawf64 = RollPitchYaw<f64>;
pub type RollPitchf32 = RollPitch<f32>;
pub type RollPitchf64 = RollPitch<f64>;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct RollPitchYaw<T> {
pub roll: T,
pub pitch: T,
pub yaw: T,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct RollPitch<T> {
pub roll: T,
pub pitch: T,
}
#[repr(C, align(16))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Quaternion<T> {
pub w: T,
pub x: T,
pub y: T,
pub z: T,
}
impl<T> Default for Quaternion<T>
where
T: Copy + Zero + One,
{
#[inline(always)]
fn default() -> Self {
Self { w: T::one(), x: T::zero(), y: T::zero(), z: T::zero() }
}
}
impl<T> Quaternion<T>
where
T: Copy,
{
#[inline(always)]
pub const fn new(w: T, x: T, y: T, z: T) -> Self {
Self { w, x, y, z }
}
}
impl<T> Zero for Quaternion<T>
where
T: Copy + Zero + PartialEq + QuaternionMath,
{
#[inline(always)]
fn zero() -> Self {
Self { w: T::zero(), x: T::zero(), y: T::zero(), z: T::zero() }
}
#[inline(always)]
fn is_zero(&self) -> bool {
self.w == T::zero() && self.x == T::zero() && self.y == T::zero() && self.z == T::zero()
}
}
impl<T> One for Quaternion<T>
where
T: Copy + Zero + One + PartialEq + Sub<Output = T> + Mul<Output = T> + QuaternionMath,
{
#[inline(always)]
fn one() -> Self {
Self { w: T::one(), x: T::zero(), y: T::zero(), z: T::zero() }
}
#[inline(always)]
fn is_one(&self) -> bool {
self.w == T::one() && self.x == T::zero() && self.y == T::zero() && self.z == T::zero()
}
}
impl<T> Neg for Quaternion<T>
where
T: Copy + QuaternionMath,
{
type Output = Self;
#[inline(always)]
fn neg(self) -> Self {
T::q_neg(self)
}
}
impl<T> Add for Quaternion<T>
where
T: Copy + QuaternionMath,
{
type Output = Self;
#[inline(always)]
fn add(self, other: Self) -> Self {
T::q_add(self, other)
}
}
impl<T> AddAssign for Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl<T> MulAdd<T> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
type Output = Self;
#[inline(always)]
fn mul_add(self, k: T, other: Self) -> Self {
T::q_mul_add(self, k, other)
}
}
impl<T> MulAddAssign<T> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
fn mul_add_assign(&mut self, k: T, other: Self) {
*self = self.mul_add(k, other);
}
}
impl<T> Sub for Quaternion<T>
where
T: Copy + QuaternionMath,
{
type Output = Self;
#[inline(always)]
fn sub(self, other: Self) -> Self {
self + (-other)
}
}
impl<T> SubAssign for Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl Mul<Quaternion<f32>> for f32 {
type Output = Quaternion<f32>;
#[inline(always)]
fn mul(self, other: Quaternion<f32>) -> Quaternion<f32> {
f32::q_mul_scalar(other, self)
}
}
impl Mul<Quaternion<f64>> for f64 {
type Output = Quaternion<f64>;
#[inline(always)]
fn mul(self, other: Quaternion<f64>) -> Quaternion<f64> {
f64::q_mul_scalar(other, self)
}
}
impl<T> Mul<T> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
type Output = Self;
#[inline(always)]
fn mul(self, k: T) -> Self {
T::q_mul_scalar(self, k)
}
}
impl<T> MulAssign<T> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
fn mul_assign(&mut self, k: T) {
*self = *self * k;
}
}
impl<T> Div<T> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
type Output = Self;
#[inline(always)]
fn div(self, k: T) -> Self {
T::q_div_scalar(self, k)
}
}
impl<T> DivAssign<T> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
fn div_assign(&mut self, k: T) {
*self = self.div(k);
}
}
impl<T> Mul<Quaternion<T>> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
type Output = Self;
#[inline(always)]
fn mul(self, other: Self) -> Self {
T::q_mul(self, other)
}
}
impl<T> MulAssign<Quaternion<T>> for Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
fn mul_assign(&mut self, other: Self) {
*self = self.mul(other);
}
}
impl<T> Index<usize> for Quaternion<T> {
type Output = T;
#[inline(always)]
fn index(&self, index: usize) -> &T {
match index {
0 => &self.w,
1 => &self.x,
2 => &self.y,
_ => &self.z, }
}
}
impl<T> IndexMut<usize> for Quaternion<T> {
#[inline(always)]
fn index_mut(&mut self, index: usize) -> &mut T {
match index {
0 => &mut self.w,
1 => &mut self.x,
2 => &mut self.y,
_ => &mut self.z, }
}
}
impl<T> Quaternion<T>
where
T: Copy + Signed,
{
#[inline(always)]
pub fn abs(self) -> Self {
Self { w: self.w.abs(), x: self.x.abs(), y: self.y.abs(), z: self.z.abs() }
}
#[inline(always)]
pub fn abs_in_place(&mut self) -> &mut Self {
*self = self.abs();
self
}
}
impl<T> Quaternion<T>
where
T: Copy + FloatCore,
{
#[inline(always)]
pub fn clamp(self, min: T, max: T) -> Self {
Self {
w: self.w.clamp(min, max),
x: self.x.clamp(min, max),
y: self.y.clamp(min, max),
z: self.z.clamp(min, max),
}
}
#[inline(always)]
pub fn clamp_in_place(&mut self, min: T, max: T) -> &mut Self {
*self = self.clamp(min, max);
self
}
}
impl<T> Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
pub fn norm_squared(self) -> T {
T::q_norm_squared(self)
}
}
impl<T> Quaternion<T>
where
T: Copy + SqrtMethods + QuaternionMath,
{
#[inline(always)]
pub fn norm(self) -> T {
Self::norm_squared(self).sqrt()
}
}
impl<T> Quaternion<T>
where
T: Copy + Zero + PartialEq + SqrtMethods + QuaternionMath,
{
#[inline(always)]
pub fn normalize(self) -> Self {
let norm_squared = self.norm_squared();
if norm_squared == T::zero() {
return self;
}
self * norm_squared.sqrt_reciprocal()
}
#[inline(always)]
pub fn normalize_in_place(&mut self) -> &mut Self {
*self = self.normalize();
self
}
#[inline(always)]
pub fn normalized_unchecked(self) -> Self {
let norm_squared = self.norm_squared();
self * norm_squared.sqrt_reciprocal()
}
#[inline(always)]
pub fn normalize_unchecked_in_place(&mut self) -> &mut Self {
*self = self.normalized_unchecked();
self
}
}
impl<T> Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
pub fn is_normalized(self) -> bool {
T::q_is_normalized(self)
}
}
impl<T> Quaternion<T>
where
T: Copy + Add<Output = T> + Mul<Output = T>,
{
#[inline(always)]
pub fn sum(self) -> T {
self.w + self.x + self.y + self.z
}
#[inline(always)]
pub fn product(self) -> T {
self.w * self.x * self.y * self.z
}
}
impl<T> Quaternion<T>
where
T: Copy + One + Add<Output = T> + Div<Output = T>,
{
#[inline(always)]
pub fn mean(self) -> T {
let four = T::one() + T::one() + T::one() + T::one();
(self.w + self.x + self.y + self.z) / four
}
}
impl<T> Quaternion<T>
where
T: Copy + Zero + One + Sub<Output = T> + Div<Output = T> + SqrtMethods,
{
pub fn rotate(self, v: &Vector3d<T>) -> Vector3d<T> {
let two: T = T::one() + T::one();
let half = T::one() / two;
let x2: T = self.x * self.x;
let y2: T = self.y * self.y;
let z2: T = self.z * self.z;
Vector3d::<T> {
x: v.x * (half - y2 - z2)
+ v.y * (self.x * self.y - self.w * self.z)
+ v.z * (self.w * self.y + self.x * self.z),
y: v.x * (self.w * self.z + self.x * self.y)
+ v.y * (half - x2 - z2)
+ v.z * (self.y * self.z - self.w * self.x),
z: v.x * (self.x * self.z - self.w * self.y)
+ v.y * (self.w * self.x + self.y * self.z)
+ v.z * (half - x2 - y2),
} }
pub fn cos_roll(self) -> T {
let half = T::one() / (T::one() + T::one());
let a: T = self.w * self.x + self.y * self.z;
let b: T = half - self.x * self.x - self.y * self.y;
b * (a * a + b * b).sqrt_reciprocal()
}
pub fn sin_pitch(self) -> T {
let two: T = T::one() + T::one();
(self.w * self.y - self.x * self.z) * two
}
pub fn cos_pitch(self) -> T {
let s: T = self.sin_pitch();
(T::one() - s * s).sqrt()
}
pub fn tan_pitch(self) -> T {
let s: T = self.sin_pitch();
s * (T::one() - s * s).sqrt_reciprocal()
}
pub fn cos_yaw(self) -> T {
let half = T::one() / (T::one() + T::one());
let a: T = self.w * self.z + self.x * self.y;
let b: T = half - self.y * self.y - self.z * self.z;
b * (a * a + b * b).sqrt_reciprocal()
}
pub fn sin_yaw(self) -> T {
let half = T::one() / (T::one() + T::one());
let a: T = self.w * self.z + self.x * self.y;
let b: T = half - self.y * self.y - self.z * self.z;
a * (a * a + b * b).sqrt_reciprocal()
}
pub fn sin_roll(self) -> T {
let half = T::one() / (T::one() + T::one());
let a: T = self.w * self.x + self.y * self.z;
let b: T = half - self.x * self.x - self.y * self.y;
a * (a * a + b * b).sqrt_reciprocal()
}
}
impl<T> Quaternion<T>
where
T: Copy + Zero + One + PartialOrd + Neg<Output = T> + Sub<Output = T> + Div<Output = T> + SqrtMethods,
{
pub fn sin_roll_clipped(self) -> T {
let half = T::one() / (T::one() + T::one());
let a: T = self.w * self.x + self.y * self.z;
let b: T = half - self.x * self.x - self.y * self.y;
if b < T::zero() {
if a < T::zero() {
return -T::one();
}
return T::one();
}
a * (a * a + b * b).sqrt_reciprocal()
}
pub fn sin_pitch_clipped(self) -> T {
let d = self.w * self.w - self.y * self.y;
let half_sin_pitch = self.w * self.y - self.x * self.z;
if d < T::zero() {
if half_sin_pitch < T::zero() {
return -T::one();
}
return T::one();
}
let two: T = T::one() + T::one();
two * half_sin_pitch
}
}
impl<T> Quaternion<T>
where
T: Copy + Zero + One + Neg<Output = T> + Sub<Output = T> + Div<Output = T> + TrigonometricMethods,
{
pub fn rotate_x(&mut self, theta: T) -> &mut Self {
let two = T::one() + T::one();
let (sin, cos) = (theta / two).sin_cos();
let wt: T = self.w * cos - self.x * sin;
self.x = self.w * sin + self.x * cos;
let yt: T = self.y * cos + self.z * sin;
self.z = self.z * cos - self.y * sin;
self.w = wt;
self.y = yt;
self
}
pub fn rotate_y(&mut self, theta: T) -> &mut Self {
let two = T::one() + T::one();
let (sin, cos) = (theta / two).sin_cos();
let wt: T = self.w * cos - self.y * sin;
let xt: T = self.x * cos - self.z * sin;
self.y = self.w * sin + self.y * cos;
self.z = self.x * sin - self.z * cos;
self.w = wt;
self.x = xt;
self
}
pub fn rotate_z(&mut self, theta: T) -> &mut Self {
let two = T::one() + T::one();
let (sin, cos) = (theta / two).sin_cos();
let wt: T = self.w * cos - self.z * sin;
let xt: T = self.x * cos - self.y * sin;
self.y = self.x * sin + self.y * cos;
self.z = self.z * cos - self.w * sin;
self.w = wt;
self.x = xt;
self
}
#[inline(always)]
pub fn calculate_roll_radians(self) -> T {
let half = T::one() / (T::one() + T::one());
(self.w * self.x + self.y * self.z).atan2(half - self.x * self.x - self.y * self.y)
}
#[inline(always)]
pub fn calculate_pitch_radians(self) -> T {
(self.w * self.y - self.x * self.z).asin()
}
#[inline(always)]
pub fn calculate_yaw_radians(self) -> T {
let half = T::one() / (T::one() + T::one());
(self.w * self.z + self.x * self.y).atan2(half - self.y * self.y - self.z * self.z)
}
pub fn from_roll_pitch_yaw_angles_radians(roll_radians: T, pitch_radians: T, yaw_radians: T) -> Self {
let half: T = T::one() / (T::one() + T::one());
let (sin_half_roll, cos_half_roll) = (roll_radians * half).sin_cos();
let (sin_half_pitch, cos_half_pitch) = (pitch_radians * half).sin_cos();
let (sin_half_yaw, cos_half_yaw) = (yaw_radians * half).sin_cos();
Self {
w: cos_half_roll * cos_half_pitch * cos_half_yaw + sin_half_roll * sin_half_pitch * sin_half_yaw,
x: sin_half_roll * cos_half_pitch * cos_half_yaw - cos_half_roll * sin_half_pitch * sin_half_yaw,
y: cos_half_roll * sin_half_pitch * cos_half_yaw + sin_half_roll * cos_half_pitch * sin_half_yaw,
z: cos_half_roll * cos_half_pitch * sin_half_yaw - sin_half_roll * sin_half_pitch * cos_half_yaw,
}
}
pub fn from_roll_pitch_angles_radians(roll_radians: T, pitch_radians: T) -> Self {
let half: T = T::one() / (T::one() + T::one());
let (sin_half_roll, cos_half_roll) = (roll_radians * half).sin_cos();
let (sin_half_pitch, cos_half_pitch) = (pitch_radians * half).sin_cos();
Self {
w: cos_half_roll * cos_half_pitch,
x: sin_half_roll * cos_half_pitch,
y: cos_half_roll * sin_half_pitch,
z: -sin_half_roll * sin_half_pitch,
}
}
}
impl<T> Quaternion<T>
where
T: Copy + QuaternionMath,
{
#[inline(always)]
pub fn conjugate(self) -> Self {
T::q_conjugate(self)
}
}
impl<T> Quaternion<T>
where
T: Copy + One + Neg<Output = T> + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{
#[inline(always)]
pub fn imaginary(self) -> Vector3d<T> {
Vector3d::<T> { x: self.x, y: self.y, z: self.z }
}
#[inline(always)]
pub fn direction_cosine_matrix_z(self) -> Vector3d<T> {
let two = T::one() + T::one();
Vector3d::<T> {
x: (self.w * self.y + self.x * self.z) * two,
y: (self.y * self.z - self.w * self.x) * two,
z: self.w * self.w,
}
}
#[inline(always)]
pub fn gravity(self) -> Vector3d<T> {
let two = T::one() + T::one();
Vector3d::<T> {
x: (self.x * self.z - self.w * self.y) * two,
y: (self.w * self.x + self.y * self.z) * two,
z: (self.w * self.w + self.z * self.z) * two - T::one(),
}
}
#[inline(always)]
pub fn half_gravity(self) -> Vector3d<T> {
let half: T = T::one() / (T::one() + T::one());
Vector3d::<T> {
x: self.x * self.z - self.w * self.y,
y: self.w * self.x + self.y * self.z,
z: self.w * self.w + self.z * self.z - half,
}
}
}
impl<T> Quaternion<T>
where
T: Copy + TrigonometricMethods + FloatCore,
{
#[inline(always)]
pub fn calculate_roll_degrees(self) -> T {
self.calculate_roll_radians().to_degrees()
}
#[inline(always)]
pub fn calculate_pitch_degrees(self) -> T {
self.calculate_pitch_radians().to_degrees()
}
#[inline(always)]
pub fn calculate_yaw_degrees(self) -> T {
self.calculate_yaw_radians().to_degrees()
}
#[inline(always)]
pub fn from_roll_pitch_yaw_angles_degrees(roll_degrees: T, pitch_degrees: T, yaw_degrees: T) -> Self {
Self::from_roll_pitch_yaw_angles_radians(
roll_degrees.to_radians(),
pitch_degrees.to_radians(),
yaw_degrees.to_radians(),
)
}
#[inline(always)]
pub fn from_roll_pitch_angles_degrees(roll_degrees: T, pitch_degrees: T) -> Self {
Self::from_roll_pitch_angles_radians(roll_degrees.to_radians(), pitch_degrees.to_radians())
}
}
impl<T> From<(T, T, T, T)> for Quaternion<T>
where
T: Copy,
{
#[inline(always)]
fn from((w, x, y, z): (T, T, T, T)) -> Self {
Self { w, x, y, z }
}
}
impl<T> From<[T; 4]> for Quaternion<T>
where
T: Copy,
{
#[inline(always)]
fn from(q: [T; 4]) -> Self {
Self { w: q[0], x: q[1], y: q[2], z: q[3] }
}
}
impl<T> From<Quaternion<T>> for [T; 4] {
#[inline(always)]
fn from(q: Quaternion<T>) -> Self {
[q.w, q.x, q.y, q.z]
}
}
impl<T> From<(T, T)> for Quaternion<T>
where
T: Copy + TrigonometricMethods + FloatCore,
{
#[inline(always)]
fn from((roll_radians, pitch_radians): (T, T)) -> Self {
Quaternion::from_roll_pitch_angles_radians(roll_radians, pitch_radians)
}
}
impl<T> From<(T, T, T)> for Quaternion<T>
where
T: Copy + TrigonometricMethods + FloatCore,
{
#[inline(always)]
fn from((roll_radians, pitch_radians, yaw_radians): (T, T, T)) -> Self {
Quaternion::from_roll_pitch_yaw_angles_radians(roll_radians, pitch_radians, yaw_radians)
}
}
impl<T> From<RollPitchYaw<T>> for Quaternion<T>
where
T: Copy + TrigonometricMethods + FloatCore,
{
fn from(angles: RollPitchYaw<T>) -> Self {
Quaternion::from_roll_pitch_yaw_angles_radians(angles.roll, angles.pitch, angles.yaw)
}
}
impl<T> From<RollPitch<T>> for Quaternion<T>
where
T: Copy + TrigonometricMethods + FloatCore,
{
#[inline(always)]
fn from(angles: RollPitch<T>) -> Self {
Quaternion::from_roll_pitch_angles_radians(angles.roll, angles.pitch)
}
}