use crate::{
euler::EulerRot::{self, *},
nums::{bool32x4, f32x4},
BVec3x4, FloatEx, Mat3x4, Mat4x4, Quatx4, UnitVec2x4, UnitVec3x4, UnitVec4x4, Vec3x4, Vec4x4,
};
use crate::{nums::num_traits::*, UnitQuat};
use auto_ops_det::{impl_op_ex, impl_op_ex_commutative};
#[cfg(not(target_arch = "spirv"))]
use core::fmt;
use core::iter::Product;
use core::ops::{self, Add, Mul, Neg, Sub};
union VecUnionCast {
v: Quatx4,
uv: UnitQuatx4,
}
impl Quatx4 {
#[inline]
pub fn as_unit_quatx4_unchecked(self) -> UnitQuatx4 {
unsafe { VecUnionCast { v: self }.uv }
}
}
impl UnitQuatx4 {
#[inline]
pub fn as_quatx4(self) -> Quatx4 {
unsafe { VecUnionCast { uv: self }.v }
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct UnitQuatx4 {
pub x: f32x4,
pub y: f32x4,
pub z: f32x4,
pub w: f32x4,
}
impl UnitQuatx4 {
pub const IDENTITY: Self =
Self::from_xyzw_unchecked(f32x4::ZERO, f32x4::ZERO, f32x4::ZERO, f32x4::ONE);
#[inline]
pub const fn from_xyzw_unchecked(x: f32x4, y: f32x4, z: f32x4, w: f32x4) -> Self {
Self { x, y, z, w }
}
#[inline]
pub const fn from_array_unchecked(a: [f32x4; 4]) -> Self {
Self::from_xyzw_unchecked(a[0], a[1], a[2], a[3])
}
#[inline]
pub fn lane_select(cond: bool32x4, if_true: Self, if_false: Self) -> Self {
Self::from_unit_vec4(UnitVec4x4::lane_select(
cond,
if_true.into(),
if_false.into(),
))
}
#[inline]
pub fn splat_soa(q: UnitQuat) -> Self {
Self {
x: f32x4::splat(q.x),
y: f32x4::splat(q.y),
z: f32x4::splat(q.z),
w: f32x4::splat(q.w),
}
}
#[inline]
pub fn extract_lane(&self, i: usize) -> UnitQuat {
UnitQuat::from_xyzw_unchecked(
self.x.extract(i),
self.y.extract(i),
self.z.extract(i),
self.w.extract(i),
)
}
#[inline]
pub fn replace_lane(&mut self, i: usize, q: UnitQuat) {
let mut nv = self.as_quatx4();
nv.replace_lane(i, q.as_quat());
*self = nv.as_unit_quatx4_unchecked();
}
#[inline]
pub fn split_soa(self) -> [UnitQuat; 4] {
[
self.extract_lane(0),
self.extract_lane(1),
self.extract_lane(2),
self.extract_lane(3),
]
}
#[inline]
pub fn compose_soa(a: &[UnitQuat; 4]) -> UnitQuatx4 {
let mut v = Self::default();
v.replace_lane(0, a[0]);
v.replace_lane(1, a[1]);
v.replace_lane(2, a[2]);
v.replace_lane(3, a[3]);
v
}
#[inline]
pub fn from_unit_vec4(v: UnitVec4x4) -> Self {
let q: Self = Self {
x: v.x,
y: v.y,
z: v.z,
w: v.w,
};
glam_assert!(q.is_normalized(), "{:?} is not normalized.", q);
q
}
#[inline]
pub fn from_slice_unchecked(slice: &[f32x4]) -> Self {
Self::from_xyzw_unchecked(slice[0], slice[1], slice[2], slice[3])
}
#[inline]
pub fn write_to_slice(self, slice: &mut [f32x4]) {
slice[0] = self.x;
slice[1] = self.y;
slice[2] = self.z;
slice[3] = self.w;
}
#[inline]
pub fn from_axis_angle(axis: UnitVec3x4, angle: f32x4) -> Self {
#[cfg_attr(
not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)),
allow(clippy::let_and_return)
)]
let q = Quatx4::from_axis_angle(axis, angle).as_unit_quatx4_unchecked();
glam_assert!(q.is_normalized(), "{:?} is not normalized.", q);
q
}
#[inline]
pub fn from_scaled_axis(v: Vec3x4) -> Self {
#[cfg_attr(
not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)),
allow(clippy::let_and_return)
)]
let q = Quatx4::from_scaled_axis(v).as_unit_quatx4_unchecked();
glam_assert!(q.is_normalized(), "{:?} is not normalized.", q);
q
}
#[inline]
pub fn from_rotation_x(angle: f32x4) -> Self {
glam_assert!(angle.is_finite(), "angle {:?} is not finite.", angle);
let (s, c) = (angle * f32x4::const_splat(0.5)).sin_cosf();
Self::from_xyzw_unchecked(s, f32x4::ZERO, f32x4::ZERO, c)
}
#[inline]
pub fn from_rotation_y(angle: f32x4) -> Self {
glam_assert!(angle.is_finite(), "angle {:?} is not finite.", angle);
let (s, c) = (angle * f32x4::const_splat(0.5)).sin_cosf();
Self::from_xyzw_unchecked(f32x4::ZERO, s, f32x4::ZERO, c)
}
#[inline]
pub fn from_rotation_z(angle: f32x4) -> Self {
glam_assert!(angle.is_finite(), "angle {:?} is not finite.", angle);
let (s, c) = (angle * f32x4::const_splat(0.5)).sin_cosf();
Self::from_xyzw_unchecked(f32x4::ZERO, f32x4::ZERO, s, c)
}
#[inline]
pub fn from_euler(euler: EulerRot, a: f32x4, b: f32x4, c: f32x4) -> Self {
let (sa, ca) = (a * f32x4::const_splat(0.5)).sin_cosf();
let (sb, cb) = (b * f32x4::const_splat(0.5)).sin_cosf();
let (sc, cc) = (c * f32x4::const_splat(0.5)).sin_cosf();
match euler {
ZYX => Self::from_xyzw_unchecked(
ca * cb * sc - cc * sa * sb,
ca * cc * sb + cb * sa * sc,
cb * cc * sa - ca * sb * sc,
ca * cb * cc + sa * sb * sc,
),
ZXY => Self::from_xyzw_unchecked(
ca * cc * sb - cb * sa * sc,
cc * sa * sb + ca * cb * sc,
cb * cc * sa + ca * sb * sc,
ca * cb * cc - sa * sb * sc,
),
YXZ => Self::from_xyzw_unchecked(
ca * cc * sb + cb * sa * sc,
cb * cc * sa - ca * sb * sc,
ca * cb * sc - cc * sa * sb,
ca * cb * cc + sa * sb * sc,
),
YZX => Self::from_xyzw_unchecked(
cc * sa * sb + ca * cb * sc,
cb * cc * sa + ca * sb * sc,
ca * cc * sb - cb * sa * sc,
ca * cb * cc - sa * sb * sc,
),
XYZ => Self::from_xyzw_unchecked(
cb * cc * sa + ca * sb * sc,
ca * cc * sb - cb * sa * sc,
cc * sa * sb + ca * cb * sc,
ca * cb * cc - sa * sb * sc,
),
XZY => Self::from_xyzw_unchecked(
cb * cc * sa - ca * sb * sc,
ca * cb * sc - cc * sa * sb,
ca * cc * sb + cb * sa * sc,
ca * cb * cc + sa * sb * sc,
),
ZYZ => Self::from_xyzw_unchecked(
ca * sb * sc - cc * sa * sb,
ca * cc * sb + sa * sb * sc,
cb * cc * sa + ca * cb * sc,
ca * cb * cc - cb * sa * sc,
),
ZXZ => Self::from_xyzw_unchecked(
ca * cc * sb + sa * sb * sc,
cc * sa * sb - ca * sb * sc,
cb * cc * sa + ca * cb * sc,
ca * cb * cc - cb * sa * sc,
),
YXY => Self::from_xyzw_unchecked(
ca * cc * sb + sa * sb * sc,
cb * cc * sa + ca * cb * sc,
ca * sb * sc - cc * sa * sb,
ca * cb * cc - cb * sa * sc,
),
YZY => Self::from_xyzw_unchecked(
cc * sa * sb - ca * sb * sc,
cb * cc * sa + ca * cb * sc,
ca * cc * sb + sa * sb * sc,
ca * cb * cc - cb * sa * sc,
),
XYX => Self::from_xyzw_unchecked(
cb * cc * sa + ca * cb * sc,
ca * cc * sb + sa * sb * sc,
cc * sa * sb - ca * sb * sc,
ca * cb * cc - cb * sa * sc,
),
XZX => Self::from_xyzw_unchecked(
cb * cc * sa + ca * cb * sc,
ca * sb * sc - cc * sa * sb,
ca * cc * sb + sa * sb * sc,
ca * cb * cc - cb * sa * sc,
),
}
.renormalize()
}
#[inline]
pub fn from_euler_default(a: f32x4, b: f32x4, c: f32x4) -> Self {
Self::from_euler(EulerRot::default(), a, b, c)
}
#[inline]
pub(crate) fn from_rotation_axes(x_axis: Vec3x4, y_axis: Vec3x4, z_axis: Vec3x4) -> Self {
#[cfg_attr(
not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)),
allow(clippy::let_and_return)
)]
let q = Quatx4::from_rotation_axes(x_axis, y_axis, z_axis).as_unit_quatx4_unchecked();
glam_assert!(q.is_normalized(), "{:?} is not normalized.", q);
q
}
#[inline]
pub fn from_mat3(mat: &Mat3x4) -> Self {
Self::from_rotation_axes(mat.x_axis, mat.y_axis, mat.z_axis)
}
#[inline]
pub fn from_mat4(mat: &Mat4x4) -> Self {
Self::from_rotation_axes(
mat.x_axis.truncate(),
mat.y_axis.truncate(),
mat.z_axis.truncate(),
)
}
#[inline]
pub fn from_affine3(a: &crate::Affine3x4) -> Self {
#[allow(clippy::useless_conversion)]
Self::from_rotation_axes(
a.matrix3.x_axis.into(),
a.matrix3.y_axis.into(),
a.matrix3.z_axis.into(),
)
}
#[inline]
pub fn from_rotation_arc(from: UnitVec3x4, to: UnitVec3x4) -> Self {
#[cfg_attr(
not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)),
allow(clippy::let_and_return)
)]
let q = Quatx4::from_rotation_arc(from, to).as_unit_quatx4_unchecked();
glam_assert!(q.is_normalized(), "{:?} is not normalized.", q);
q
}
#[inline]
pub fn from_rotation_arc_colinear(from: UnitVec3x4, to: UnitVec3x4) -> Self {
#[cfg_attr(
not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)),
allow(clippy::let_and_return)
)]
let q = Quatx4::from_rotation_arc_colinear(from, to).as_unit_quatx4_unchecked();
glam_assert!(q.is_normalized(), "{:?} is not normalized.", q);
q
}
#[inline]
pub fn from_rotation_arc_2d(from: UnitVec2x4, to: UnitVec2x4) -> Self {
#[cfg_attr(
not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)),
allow(clippy::let_and_return)
)]
let q = Quatx4::from_rotation_arc_2d(from, to).as_unit_quatx4_unchecked();
glam_assert!(q.is_normalized(), "{:?} is not normalized.", q);
q
}
#[inline]
pub fn to_axis_angle(self) -> (UnitVec3x4, f32x4) {
const EPSILON: f64 = 1.0e-8;
const EPSILON_SQUARED: f32x4 = f32x4::const_splat((EPSILON * EPSILON) as f32);
let w = self.w;
let v3 = self.xyz();
let sin_theta_abs = v3.length();
let angle_valid = sin_theta_abs.ge(EPSILON_SQUARED);
let axis0 = v3 * sin_theta_abs.recip();
let axis1 = Vec3x4::X;
let mask = BVec3x4::new(angle_valid, angle_valid, angle_valid);
let a = Vec3x4::select(mask, axis0, axis1);
let angle = f32x4::select(
(sin_theta_abs * w.signumf()).atan2f(w.absf()) * f32x4::const_splat(2.0),
angle_valid,
f32x4::ZERO,
);
(a.as_unit_vec3x4_unchecked(), angle)
}
#[inline]
pub fn to_scaled_axis(self) -> Vec3x4 {
let (axis, angle) = self.to_axis_angle();
axis * angle
}
#[inline]
pub fn to_euler(self, euler: EulerRot) -> (f32x4, f32x4, f32x4) {
const PI: f32x4 = f32x4::PI;
#[allow(non_snake_case)]
let TWO_PI: f32x4 = PI + PI;
#[allow(clippy::excessive_precision)]
const SQRT_2: f32x4 = f32x4::const_splat(1.41421356237309504880168872420969808_f32);
let not_proper = euler.not_proper();
let (i, j, k) = euler.map_sequence();
let levi_civita_sig = f32x4::const_splat(euler.levi_civita_sig());
let q = [self.w, self.x, self.y, self.z];
let (a, b, c, d) = if not_proper {
(
(q[0] - q[j]) / SQRT_2,
(q[k] * levi_civita_sig + q[i]) / SQRT_2,
(q[0] + q[j]) / SQRT_2,
(q[k] * levi_civita_sig - q[i]) / SQRT_2,
)
} else {
(q[0], q[i], q[j], q[k] * levi_civita_sig)
};
let cos_theta_2 = f32x4::const_splat(2.0) * (a * a + b * b) - f32x4::ONE;
let theta_positive = b.atan2f(a);
let theta_negative = -d.atan2f(c);
let mut theta_1: f32x4;
let mut theta_2: f32x4;
let mut theta_3: f32x4;
let cond0 = (cos_theta_2 + f32x4::EPSILON).ge(f32x4::ONE);
let cond1 = (cos_theta_2 - f32x4::EPSILON).le(-f32x4::ONE);
theta_1 = f32x4::select(f32x4::ZERO, cond0 | cond1, theta_positive + theta_negative);
theta_2 = f32x4::select(
f32x4::ZERO,
cond0,
f32x4::select(PI, cond1, cos_theta_2.acosf()),
);
theta_3 = f32x4::select(
f32x4::TWO * theta_positive,
cond0,
f32x4::select(
-f32x4::TWO * theta_negative,
cond1,
theta_positive - theta_negative,
),
);
if not_proper {
theta_3 *= levi_civita_sig;
theta_2 -= PI / f32x4::const_splat(2.0);
}
theta_1 %= TWO_PI;
theta_3 %= TWO_PI;
theta_1 = f32x4::select(
theta_1,
theta_1.absf().ge(PI),
theta_1 - theta_1.signumf() * TWO_PI,
);
theta_3 = f32x4::select(
theta_3,
theta_3.absf().ge(PI),
theta_3 - theta_3.signumf() * TWO_PI,
);
(theta_3, theta_2, theta_1)
}
#[inline]
pub fn to_euler_default(self) -> (f32x4, f32x4, f32x4) {
self.to_euler(EulerRot::default())
}
#[inline]
pub fn to_array(&self) -> [f32x4; 4] {
[self.x, self.y, self.z, self.w]
}
#[inline]
pub fn xyz(self) -> Vec3x4 {
Vec3x4::new(self.x, self.y, self.z)
}
#[must_use]
#[inline]
pub fn conjugate(self) -> Self {
Self {
x: -self.x,
y: -self.y,
z: -self.z,
w: self.w,
}
}
#[must_use]
#[inline]
pub fn inverse(self) -> Self {
self.conjugate()
}
#[inline]
pub fn dot(self, rhs: Self) -> f32x4 {
Vec4x4::from(self).dot(Vec4x4::from(rhs))
}
#[inline]
pub fn is_normalized(self) -> bool32x4 {
Vec4x4::from(self).is_normalized()
}
#[must_use]
#[inline]
pub fn renormalize(self) -> Self {
Self::from_unit_vec4(Vec4x4::from(self).normalize_to_unit())
}
#[inline]
pub fn is_near_identity(self) -> bool32x4 {
let threshold_angle = f32x4::const_splat(0.002_847_144_6);
let positive_w_angle = self.w.absf().acos_approx() * f32x4::const_splat(2.0);
positive_w_angle.lt(threshold_angle)
}
#[inline]
pub fn angle_between(self, rhs: Self) -> f32x4 {
self.dot(rhs).absf().acos_approx() * f32x4::const_splat(2.0)
}
#[inline]
pub fn abs_diff_eq(self, rhs: Self, max_abs_diff: f32x4) -> bool32x4 {
Vec4x4::from(self).abs_diff_eq(Vec4x4::from(rhs), max_abs_diff)
}
#[inline]
#[doc(alias = "mix")]
pub fn lerp(self, end: Self, s: f32x4) -> Self {
let start = self;
let dot = start.dot(end);
let bias = f32x4::ONE.select(dot.ge(f32x4::ZERO), f32x4::NEG_ONE);
let interpolated = start
.as_quatx4()
.add(end.mul(bias).sub(start.as_quatx4()).mul(s));
interpolated.normalize_to_unit()
}
#[inline]
pub fn slerp(self, mut end: Self, s: f32x4) -> Self {
const DOT_THRESHOLD: f32x4 = f32x4::const_splat(0.9995);
let mut dot = self.dot(end);
let cond = dot.lt(f32x4::ZERO);
end = Self::lane_select(cond, -end, end);
dot = (-dot).select(cond, dot);
let cond = dot.gt(DOT_THRESHOLD);
let res1 = self.lerp(end, s);
let theta = dot.acos_approx();
let scale1 = (theta * (f32x4::ONE - s)).sinf();
let scale2 = (theta * s).sinf();
let theta_sin = theta.sinf();
let res2 = self
.mul(scale1)
.add(end.mul(scale2))
.mul(theta_sin.recip())
.as_unit_quatx4_unchecked();
Self::lane_select(cond, res1, res2)
}
#[inline]
pub fn mul_vec3(self, rhs: Vec3x4) -> Vec3x4 {
let w = self.w;
let b = self.xyz();
let b2 = b.dot(b);
rhs.mul(w * w - b2)
.add(b.mul(rhs.dot(b) * f32x4::const_splat(2.0)))
.add(b.cross(rhs).mul(w * f32x4::const_splat(2.0)))
}
#[inline]
pub fn mul_unit_quat(self, rhs: Self) -> Self {
let (x0, y0, z0, w0) = self.into();
let (x1, y1, z1, w1) = rhs.into();
Self::from_xyzw_unchecked(
w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1,
w0 * y1 - x0 * z1 + y0 * w1 + z0 * x1,
w0 * z1 + x0 * y1 - y0 * x1 + z0 * w1,
w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1,
)
}
}
#[cfg(not(target_arch = "spirv"))]
impl fmt::Debug for UnitQuatx4 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple(stringify!(UnitQuatx4))
.field(&self.x)
.field(&self.y)
.field(&self.z)
.field(&self.w)
.finish()
}
}
#[cfg(not(target_arch = "spirv"))]
impl fmt::Display for UnitQuatx4 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "[{}, {}, {}, {}]", self.x, self.y, self.z, self.w)
}
}
impl_op_ex!(+ |a: &UnitQuatx4, b: &UnitQuatx4| -> Quatx4{
Quatx4::from_vec4(Vec4x4::from(a) + Vec4x4::from(b))
});
impl_op_ex!(-|a: &UnitQuatx4, b: &UnitQuatx4| -> Quatx4 {
Quatx4::from_vec4(Vec4x4::from(a) - Vec4x4::from(b))
});
impl_op_ex_commutative!(*|a: &UnitQuatx4, b: &f32x4| -> Quatx4 {
Quatx4::from_vec4(Vec4x4::from(a) * b)
});
impl_op_ex!(/ |a: &UnitQuatx4, b: &f32x4| -> Quatx4{
Quatx4::from_vec4(Vec4x4::from(a) / b)
});
impl_op_ex!(*|a: &UnitQuatx4, b: &UnitQuatx4| -> UnitQuatx4 { a.mul_unit_quat(*b) });
impl_op_ex!(*= |a: &mut UnitQuatx4, b: &UnitQuatx4| {
*a = a.mul_unit_quat(*b)
});
impl_op_ex!(*|a: &UnitQuatx4, b: &Quatx4| -> Quatx4 { a.as_quatx4().mul_quat(*b) });
impl_op_ex!(*|a: &Quatx4, b: &UnitQuatx4| -> Quatx4 { a.mul_quat(b.as_quatx4()) });
impl_op_ex!(*= |a: &mut Quatx4, b: &UnitQuatx4| {
*a = a.mul_quat(b.as_quatx4())
});
impl_op_ex!(*|a: &UnitQuatx4, b: &Vec3x4| -> Vec3x4 { a.mul_vec3(*b) });
impl_op_ex!(*|a: &UnitQuatx4, b: &UnitVec3x4| -> UnitVec3x4 {
a.mul_vec3(b.as_vec3x4()).as_unit_vec3x4_unchecked()
});
impl Neg for UnitQuatx4 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
(self * f32x4::NEG_ONE).as_unit_quatx4_unchecked()
}
}
impl Default for UnitQuatx4 {
#[inline]
fn default() -> Self {
Self::IDENTITY
}
}
impl PartialEq for UnitQuatx4 {
#[inline]
fn eq(&self, rhs: &Self) -> bool {
Vec4x4::from(*self).eq(&Vec4x4::from(*rhs)).all()
}
}
#[cfg(not(target_arch = "spirv"))]
impl AsRef<[f32x4; 4]> for UnitQuatx4 {
#[inline]
fn as_ref(&self) -> &[f32x4; 4] {
unsafe { &*(self as *const Self as *const [f32x4; 4]) }
}
}
impl<'a> Product<&'a Self> for UnitQuatx4 {
fn product<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
iter.fold(Self::IDENTITY, |a, &b| a * b)
}
}
impl From<UnitQuatx4> for Vec4x4 {
#[inline]
fn from(q: UnitQuatx4) -> Self {
Self::new(q.x, q.y, q.z, q.w)
}
}
impl From<UnitQuatx4> for UnitVec4x4 {
#[inline]
fn from(q: UnitQuatx4) -> Self {
Vec4x4::from(q).as_unit_vec4x4_unchecked()
}
}
impl From<UnitQuatx4> for (f32x4, f32x4, f32x4, f32x4) {
#[inline]
fn from(q: UnitQuatx4) -> Self {
(q.x, q.y, q.z, q.w)
}
}
impl From<UnitQuatx4> for [f32x4; 4] {
#[inline]
fn from(q: UnitQuatx4) -> Self {
[q.x, q.y, q.z, q.w]
}
}
impl From<&UnitQuatx4> for Vec4x4 {
#[inline]
fn from(q: &UnitQuatx4) -> Self {
Self::new(q.x, q.y, q.z, q.w)
}
}
impl From<&UnitQuatx4> for UnitVec4x4 {
#[inline]
fn from(q: &UnitQuatx4) -> Self {
Vec4x4::from(q).as_unit_vec4x4_unchecked()
}
}
impl From<&UnitQuatx4> for (f32x4, f32x4, f32x4, f32x4) {
#[inline]
fn from(q: &UnitQuatx4) -> Self {
(q.x, q.y, q.z, q.w)
}
}
impl From<&UnitQuatx4> for [f32x4; 4] {
#[inline]
fn from(q: &UnitQuatx4) -> Self {
[q.x, q.y, q.z, q.w]
}
}