use crate::{
epsilon_eq,
mat3::Mat3,
mat4::Mat4,
utils::epsilon_eq_default,
vec::{Vec3, Vec4},
};
use std::f32::consts::PI;
use std::{
cmp::PartialEq,
fmt,
ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign},
};
#[derive(Clone, Copy, Debug)]
pub struct Quat {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
impl Quat {
#[inline]
#[must_use]
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Quat {
Quat { x, y, z, w }
}
pub const IDENTITY: Quat = Quat {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
};
pub const ZERO: Quat = Quat {
x: 0.0,
y: 0.0,
z: 0.0,
w: 0.0,
};
pub const NAN: Quat = Quat {
x: f32::NAN,
y: f32::NAN,
z: f32::NAN,
w: f32::NAN,
};
pub const INFINITY: Quat = Quat {
x: f32::INFINITY,
y: f32::INFINITY,
z: f32::INFINITY,
w: f32::INFINITY,
};
#[must_use]
pub fn from_axis_angle(axis: Vec3, angle_rad: f32) -> Self {
let half_angle = angle_rad * 0.5;
let (s, c) = half_angle.sin_cos();
let axis = axis.normalize_or_zero();
Self {
x: axis.x * s,
y: axis.y * s,
z: axis.z * s,
w: c,
}
}
#[must_use]
pub fn from_euler_angles(roll: f32, pitch: f32, yaw: f32) -> Self {
let (half_roll_sin, half_roll_cos) = (roll * 0.5).sin_cos(); let (half_pitch_sin, half_pitch_cos) = (pitch * 0.5).sin_cos(); let (half_yaw_sin, half_yaw_cos) = (yaw * 0.5).sin_cos();
let x = half_roll_sin * half_pitch_cos * half_yaw_cos
- half_roll_cos * half_pitch_sin * half_yaw_sin;
let y = half_roll_cos * half_pitch_sin * half_yaw_cos
+ half_roll_sin * half_pitch_cos * half_yaw_sin;
let z = half_roll_cos * half_pitch_cos * half_yaw_sin
- half_roll_sin * half_pitch_sin * half_yaw_cos;
let w = half_roll_cos * half_pitch_cos * half_yaw_cos
+ half_roll_sin * half_pitch_sin * half_yaw_sin;
Quat { x, y, z, w }
}
#[must_use]
pub fn from_to_rotation(from: Vec3, to: Vec3) -> Self {
let dot = from.dot(to);
if dot > 0.999999 {
Self::IDENTITY
} else if dot < -0.999999 {
let mut axis = Vec3::new(0.0, 1.0, 0.0).cross(from);
if axis.squared_length() < 1e-6 {
axis = Vec3::new(1.0, 0.0, 0.0).cross(from);
}
return Self::from_axis_angle(axis.normalize(), PI);
} else {
let axis = from.cross(to);
let w = 1.0 + dot;
Self::new(axis.x, axis.y, axis.z, w).normalize()
}
}
#[inline]
#[must_use]
pub fn from_rotation_x(angle_rad: f32) -> Self {
let (s, c) = (angle_rad * 0.5).sin_cos();
Self {
x: s,
y: 0.0,
z: 0.0,
w: c,
}
}
#[inline]
#[must_use]
pub fn from_rotation_y(angle_rad: f32) -> Self {
let (s, c) = (angle_rad * 0.5).sin_cos();
Self {
x: 0.0,
y: s,
z: 0.0,
w: c,
}
}
#[inline]
#[must_use]
pub fn from_rotation_z(angle_rad: f32) -> Self {
let (s, c) = (angle_rad * 0.5).sin_cos();
Self {
x: 0.0,
y: 0.0,
z: s,
w: c,
}
}
#[must_use]
pub fn to_swing_twist(self, axis: Vec3) -> (Self, Self) {
let v = self.vector_part();
let p = axis.dot(v) * axis;
let twist = Self::new(p.x, p.y, p.z, self.w).normalize();
let swing = self * twist.conjugate();
(swing, twist)
}
#[must_use]
pub fn from_mat4(m: &Mat4) -> Self {
let mat3 = Mat3::new(m.col0.xyz(), m.col1.xyz(), m.col2.xyz());
Self::from_mat3(&mat3.orthonormalize())
}
#[must_use]
pub fn to_euler_angles(self) -> (f32, f32, f32) {
let Quat { x, y, z, w } = self;
let sinr_cosp = 2.0 * (w * x + y * z);
let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
let roll = sinr_cosp.atan2(cosr_cosp);
let sinp = 2.0 * (w * y - z * x);
let pitch = if sinp.abs() >= 1.0 {
sinp.signum() * std::f32::consts::FRAC_PI_2
} else {
sinp.asin()
};
let siny_cosp = 2.0 * (w * z + x * y);
let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
let yaw = siny_cosp.atan2(cosy_cosp);
(roll, pitch, yaw)
}
#[inline]
#[must_use]
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w).sqrt()
}
#[inline]
#[must_use]
pub fn squared_length(&self) -> f32 {
self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
}
#[must_use]
pub fn normalize(&self) -> Quat {
let len_sq = self.squared_length();
if len_sq > 0.0 {
let inv_len = 1.0 / len_sq.sqrt();
Self {
x: self.x * inv_len,
y: self.y * inv_len,
z: self.z * inv_len,
w: self.w * inv_len,
}
} else {
Self::IDENTITY
}
}
#[must_use]
pub fn try_normalize(&self) -> Option<Quat> {
let len_sq = self.squared_length();
if len_sq > 0.0 {
Some(*self * (1.0 / len_sq.sqrt()))
} else {
None
}
}
#[must_use]
pub fn is_normalized(&self) -> bool {
epsilon_eq_default(self.squared_length(), 1.0)
}
#[must_use]
pub fn is_nan(&self) -> bool {
self.x.is_nan() || self.y.is_nan() || self.z.is_nan() || self.w.is_nan()
}
#[must_use]
pub fn is_finite(&self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite() && self.w.is_finite()
}
#[inline]
#[must_use]
pub fn conjugate(&self) -> Quat {
Quat {
x: -self.x,
y: -self.y,
z: -self.z,
w: self.w,
}
}
#[must_use]
pub fn inverse(&self) -> Quat {
let len_sq = self.squared_length();
if len_sq > 0.0 {
self.conjugate() / len_sq
} else {
Self::IDENTITY
}
}
#[inline]
#[must_use]
pub fn dot(&self, other: Quat) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
}
#[must_use]
pub fn rotate_vec3(&self, v: Vec3) -> Vec3 {
let q_vec = Vec3::new(self.x, self.y, self.z);
let t = 2.0 * q_vec.cross(v);
v + (self.w * t) + q_vec.cross(t)
}
#[must_use]
pub fn angle_between(a: Quat, b: Quat) -> f32 {
2.0 * a.dot(b).clamp(-1.0, 1.0).acos()
}
#[must_use]
pub fn to_axis_angle(&self) -> (Vec3, f32) {
let quat = if self.w > 1.0 {
self.normalize()
} else {
*self
};
let angle = 2.0 * quat.w.acos();
let s = (1.0 - quat.w * quat.w).sqrt();
if s < 1e-6 {
(Vec3::new(1.0, 0.0, 0.0), 0.0)
} else {
(Vec3::new(quat.x / s, quat.y / s, quat.z / s), angle)
}
}
#[must_use]
pub fn look_rotation(forward: Vec3, up: Vec3) -> Quat {
let forward = forward.normalize();
let right = up.cross(forward).normalize();
let up = forward.cross(right);
Self::from_mat3(&Mat3::new(right, up, forward))
}
#[must_use]
pub fn to_mat3(&self) -> Mat3 {
let (x, y, z, w) = (self.x, self.y, self.z, self.w);
let x2 = x + x;
let y2 = y + y;
let z2 = z + z;
let xx = x * x2;
let xy = x * y2;
let xz = x * z2;
let yy = y * y2;
let yz = y * z2;
let zz = z * z2;
let wx = w * x2;
let wy = w * y2;
let wz = w * z2;
Mat3::new(
Vec3::new(1.0 - (yy + zz), xy + wz, xz - wy),
Vec3::new(xy - wz, 1.0 - (xx + zz), yz + wx),
Vec3::new(xz + wy, yz - wx, 1.0 - (xx + yy)),
)
}
#[must_use]
pub fn from_mat3(m: &Mat3) -> Self {
let trace = m.col0.x + m.col1.y + m.col2.z;
if trace > 0.0 {
let s = (trace + 1.0).sqrt() * 2.0;
Self {
w: 0.25 * s,
x: (m.col1.z - m.col2.y) / s,
y: (m.col2.x - m.col0.z) / s,
z: (m.col0.y - m.col1.x) / s,
}
} else if (m.col0.x > m.col1.y) && (m.col0.x > m.col2.z) {
let s = (1.0 + m.col0.x - m.col1.y - m.col2.z).sqrt() * 2.0;
Self {
w: (m.col1.z - m.col2.y) / s,
x: 0.25 * s,
y: (m.col2.x + m.col0.y) / s,
z: (m.col2.x + m.col0.z) / s,
}
} else if m.col1.y > m.col2.z {
let s = (1.0 + m.col1.y - m.col0.x - m.col2.z).sqrt() * 2.0;
Self {
w: (m.col2.x - m.col0.z) / s,
x: (m.col1.x + m.col0.y) / s,
y: 0.25 * s,
z: (m.col2.y + m.col1.z) / s,
}
} else {
let s = (1.0 + m.col2.z - m.col0.x - m.col1.y).sqrt() * 2.0;
Self {
w: (m.col0.y - m.col1.x) / s,
x: (m.col2.x + m.col0.z) / s,
y: (m.col2.y + m.col2.z) / s,
z: 0.25 * s,
}
}
}
#[inline]
pub fn from_vec4(v: Vec4) -> Quat {
Self {
x: v.x,
y: v.y,
z: v.z,
w: v.w,
}
}
#[inline]
pub fn to_vec4(&self) -> Vec4 {
Vec4::new(self.x, self.y, self.z, self.w)
}
#[inline]
pub fn vector_part(&self) -> Vec3 {
Vec3::new(self.x, self.y, self.z)
}
#[inline]
pub fn scalar_part(&self) -> f32 {
self.w
}
#[must_use]
pub fn approx_eq(&self, other: &Quat) -> bool {
epsilon_eq_default(self.x, other.x)
&& epsilon_eq_default(self.y, other.y)
&& epsilon_eq_default(self.z, other.z)
&& epsilon_eq_default(self.w, other.w)
}
#[must_use]
pub fn approx_eq_eps(&self, other: &Quat, epsilon: f32) -> bool {
epsilon_eq(self.x, other.x, epsilon)
&& epsilon_eq(self.y, other.y, epsilon)
&& epsilon_eq(self.z, other.z, epsilon)
&& epsilon_eq(self.w, other.w, epsilon)
}
#[must_use]
pub fn lerp(&self, b: Quat, t: f32) -> Quat {
*self * (1.0 - t) + b * t
}
#[must_use]
pub fn lerp_between(a: Quat, b: Quat, t: f32) -> Quat {
a * (1.0 - t) + b * t
}
#[must_use]
pub fn nlerp(&self, b: Quat, t: f32) -> Quat {
(*self * (1.0 - t) + b * t).normalize()
}
#[must_use]
pub fn nlerp_between(a: Quat, b: Quat, t: f32) -> Quat {
(a * (1.0 - t) + b * t).normalize()
}
#[must_use]
pub fn slerp(self, end: Self, t: f32) -> Self {
let mut dot = self.dot(end);
let mut end_adj = end;
if dot < 0.0 {
dot = -dot;
end_adj = -end;
}
if dot > 0.9995 {
return self.lerp(end_adj, t).normalize();
}
let theta_0 = dot.acos();
let theta = theta_0 * t;
let sin_theta = theta.sin();
let sin_theta_0 = theta_0.sin();
let s0 = (theta_0 - theta).sin() / sin_theta_0;
let s1 = sin_theta / sin_theta_0;
(self * s0) + (end_adj * s1)
}
pub fn exp(self) -> Quat {
let v = Vec3::new(self.x, self.y, self.z);
let len = v.length();
let (s, c) = len.sin_cos();
if len < 1e-6 {
Self {
x: v.x,
y: v.y,
z: v.z,
w: c,
}
} else {
let scale = s / len;
Self {
x: v.x * scale,
y: v.y * scale,
z: v.z * scale,
w: c,
}
}
}
pub fn log(self) -> Quat {
let v = Vec3::new(self.x, self.y, self.z);
let v_len_sq = v.squared_length();
let q_len_sq = self.squared_length();
if v_len_sq < 1e-12 {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
w: q_len_sq.sqrt().ln(),
}
} else {
let v_len = v_len_sq.sqrt();
let q_len = q_len_sq.sqrt();
let scale = v_len.atan2(self.w) / v_len;
Self {
x: v.x * scale,
y: v.y * scale,
z: v.z * scale,
w: q_len.ln(),
}
}
}
pub fn powf(self, exponent: f32) -> Quat {
(self.log() * exponent).exp()
}
}
impl Add for Quat {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self::new(
self.x + rhs.x,
self.y + rhs.y,
self.z + rhs.z,
self.w + rhs.w,
)
}
}
impl Sub for Quat {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self::new(
self.x - rhs.x,
self.y - rhs.y,
self.z - rhs.z,
self.w - rhs.w,
)
}
}
impl Mul for Quat {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Self {
w: self.w * rhs.w - self.x * rhs.x - self.y * rhs.y - self.z * rhs.z,
x: self.w * rhs.x + self.x * rhs.w + self.y * rhs.z - self.z * rhs.y,
y: self.w * rhs.y - self.x * rhs.z + self.y * rhs.w + self.z * rhs.x,
z: self.w * rhs.z + self.x * rhs.y - self.y * rhs.x + self.z * rhs.w,
}
}
}
impl Mul<Vec3> for Quat {
type Output = Vec3;
#[inline]
fn mul(self, rhs: Vec3) -> Vec3 {
self.rotate_vec3(rhs)
}
}
impl Mul<f32> for Quat {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self {
Self {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
w: self.w * rhs,
}
}
}
impl Div<f32> for Quat {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self {
Self {
x: self.x / rhs,
y: self.y / rhs,
z: self.z / rhs,
w: self.w / rhs,
}
}
}
impl Neg for Quat {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self {
x: -self.x,
y: -self.y,
z: -self.z,
w: -self.w,
}
}
}
impl AddAssign for Quat {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
self.w += rhs.w;
}
}
impl SubAssign for Quat {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
self.w -= rhs.w;
}
}
impl MulAssign for Quat {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl MulAssign<f32> for Quat {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
self.w *= rhs;
}
}
impl DivAssign<f32> for Quat {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.x /= rhs;
self.y /= rhs;
self.z /= rhs;
self.w /= rhs;
}
}
impl Default for Quat {
#[inline]
fn default() -> Self {
Self::IDENTITY
}
}
impl PartialEq for Quat {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w
}
}
impl Index<usize> for Quat {
type Output = f32;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
match index {
0 => &self.x,
1 => &self.y,
2 => &self.z,
3 => &self.w,
_ => panic!("Quat index out of bounds: {}", index),
}
}
}
impl IndexMut<usize> for Quat {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match index {
0 => &mut self.x,
1 => &mut self.y,
2 => &mut self.z,
3 => &mut self.w,
_ => panic!("Quat index out of bounds: {}", index),
}
}
}
impl fmt::Display for Quat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Quat({:.3}, {:.3}, {:.3}, {:.3})",
self.x, self.y, self.z, self.w
)
}
}
impl From<Vec4> for Quat {
#[inline]
fn from(v: Vec4) -> Self {
Self::new(v.x, v.y, v.z, v.w)
}
}
impl From<Quat> for Vec4 {
#[inline]
fn from(q: Quat) -> Self {
Self::new(q.x, q.y, q.z, q.w)
}
}
impl approx::AbsDiffEq for Quat {
type Epsilon = f32;
#[inline]
fn default_epsilon() -> f32 {
f32::EPSILON
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
f32::abs_diff_eq(&self.x, &other.x, epsilon)
&& f32::abs_diff_eq(&self.y, &other.y, epsilon)
&& f32::abs_diff_eq(&self.z, &other.z, epsilon)
&& f32::abs_diff_eq(&self.w, &other.w, epsilon)
}
}
impl approx::RelativeEq for Quat {
#[inline]
fn default_max_relative() -> f32 {
f32::EPSILON
}
#[inline]
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
f32::relative_eq(&self.x, &other.x, epsilon, max_relative)
&& f32::relative_eq(&self.y, &other.y, epsilon, max_relative)
&& f32::relative_eq(&self.z, &other.z, epsilon, max_relative)
&& f32::relative_eq(&self.w, &other.w, epsilon, max_relative)
}
}