use rand::Rng;
use crate::mat4::Mat4;
use crate::utils;
use crate::utils::epsilon_eq;
use crate::utils::epsilon_eq_default;
use crate::utils::is_near_zero_default;
use crate::vec::Vec3;
use core::fmt;
use std::cmp::PartialEq;
use std::ops::{
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
};
#[derive(Debug, Clone, Copy)]
pub struct Vec4 {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
impl Vec4 {
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Vec4 {
Vec4 { x, y, z, w }
}
pub fn to_array(self) -> [f32; 4] {
[self.x, self.y, self.z, self.w]
}
pub fn from_array(arr: [f32; 4]) -> Vec4 {
Vec4::new(arr[0], arr[1], arr[2], arr[3])
}
pub fn to_tuple(self) -> (f32, f32, f32, f32) {
(self.x, self.y, self.z, self.w)
}
pub fn from_tuple(tuple: (f32, f32, f32, f32)) -> Vec4 {
Vec4::new(tuple.0, tuple.1, tuple.2, tuple.3)
}
pub fn from_vec3_w(v: Vec3, w: f32) -> Vec4 {
Vec4::new(v.x, v.y, v.z, w)
}
pub fn xyz(self) -> Vec3 {
Vec3::new(self.x, self.y, self.z)
}
pub const ZERO: Self = Self {
x: 0.0,
y: 0.0,
z: 0.0,
w: 0.0,
};
pub const NAN: Self = Self {
x: f32::NAN,
y: f32::NAN,
z: f32::NAN,
w: f32::NAN,
};
pub const INFINITY: Self = Self {
x: f32::INFINITY,
y: f32::INFINITY,
z: f32::INFINITY,
w: f32::INFINITY,
};
pub const X: Self = Self {
x: 1.0,
y: 0.0,
z: 0.0,
w: 0.0,
};
pub const Y: Self = Self {
x: 0.0,
y: 1.0,
z: 0.0,
w: 0.0,
};
pub const Z: Self = Self {
x: 0.0,
y: 0.0,
z: 1.0,
w: 0.0,
};
pub const W: Self = Self {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
};
pub fn abs(self) -> Vec4 {
Vec4::new(self.x.abs(), self.y.abs(), self.z.abs(), self.w.abs())
}
pub fn signum(self) -> Self {
Vec4::new(
if self.x > 0.0 {
1.0
} else if self.x < 0.0 {
-1.0
} else {
0.0
},
if self.y > 0.0 {
1.0
} else if self.y < 0.0 {
-1.0
} else {
0.0
},
if self.z > 0.0 {
1.0
} else if self.z < 0.0 {
-1.0
} else {
0.0
},
if self.w > 0.0 {
1.0
} else if self.w < 0.0 {
-1.0
} else {
0.0
},
)
}
pub fn ieee_signum(self) -> Self {
Vec4::new(
self.x.signum(),
self.y.signum(),
self.z.signum(),
self.w.signum(),
)
}
pub fn clamp(self, min: f32, max: f32) -> Vec4 {
Vec4::new(
utils::clamp(self.x, min, max),
utils::clamp(self.y, min, max),
utils::clamp(self.z, min, max),
utils::clamp(self.w, min, max),
)
}
pub fn min(self, other: Vec4) -> Vec4 {
Vec4::new(
self.x.min(other.x),
self.y.min(other.y),
self.z.min(other.z),
self.w.min(other.w),
)
}
pub fn max(self, other: Vec4) -> Vec4 {
Vec4::new(
self.x.max(other.x),
self.y.max(other.y),
self.z.max(other.z),
self.w.max(other.w),
)
}
pub fn triple_product_4d(a: Vec4, b: Vec4, c: Vec4, d: Vec4) -> f32 {
let m = Mat4::new(
Vec4::new(a.x, b.x, c.x, d.x),
Vec4::new(a.y, b.y, c.y, d.y),
Vec4::new(a.z, b.z, c.z, d.z),
Vec4::new(a.w, b.w, c.w, d.w),
);
m[0][0]
* (m[1][1] * (m[2][2] * m[3][3] - m[2][3] * m[3][2])
- m[1][2] * (m[2][1] * m[3][3] - m[2][3] * m[3][1])
+ m[1][3] * (m[2][1] * m[3][2] - m[2][2] * m[3][1]))
- m[0][1]
* (m[1][0] * (m[2][2] * m[3][3] - m[2][3] * m[3][2])
- m[1][2] * (m[2][0] * m[3][3] - m[2][3] * m[3][0])
+ m[1][3] * (m[2][0] * m[3][2] - m[2][2] * m[3][0]))
+ m[0][2]
* (m[1][0] * (m[2][1] * m[3][3] - m[2][3] * m[3][1])
- m[1][1] * (m[2][0] * m[3][3] - m[2][3] * m[3][0])
+ m[1][3] * (m[2][0] * m[3][1] - m[2][1] * m[3][0]))
- m[0][3]
* (m[1][0] * (m[2][1] * m[3][2] - m[2][2] * m[3][1])
- m[1][1] * (m[2][0] * m[3][2] - m[2][2] * m[3][0])
+ m[1][2] * (m[2][0] * m[3][1] - m[2][1] * m[3][0]))
}
pub fn hypervolume_4d(a: Vec4, b: Vec4, c: Vec4, d: Vec4) -> f32 {
Vec4::triple_product_4d(a, b, c, d).abs()
}
pub fn perpendicular(self) -> Vec4 {
let mut v = if self.x.abs() <= self.y.abs()
&& self.x.abs() <= self.z.abs()
&& self.x.abs() <= self.w.abs()
{
Vec4::new(0.0, -self.z, self.y, -self.w)
} else if self.y.abs() <= self.z.abs() && self.y.abs() <= self.w.abs() {
Vec4::new(-self.z, 0.0, self.x, -self.w)
} else if self.z.abs() <= self.w.abs() {
Vec4::new(-self.y, self.x, 0.0, -self.w)
} else {
Vec4::new(-self.y, self.x, -self.z, 0.0)
};
v = v.normalize_or_zero();
if v.is_zero() {
Vec4::new(1.0, 0.0, 0.0, 0.0) } else {
v
}
}
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w).sqrt()
}
pub fn squared_length(&self) -> f32 {
self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
}
pub fn normalize_or_zero(&self) -> Vec4 {
let len = self.length();
if len == 0.0 {
Vec4::new(0.0, 0.0, 0.0, 0.0)
} else {
Vec4::new(self.x / len, self.y / len, self.z / len, self.w / len)
}
}
pub fn try_normalize(&self) -> Option<Vec4> {
let len = self.length();
if is_near_zero_default(len) {
None
} else {
Some(Vec4::new(
self.x / len,
self.y / len,
self.z / len,
self.w / len,
))
}
}
pub fn normalize(&self) -> Vec4 {
let len = self.length();
assert!(
!is_near_zero_default(len),
"Cannot normalize zero length vector"
);
Vec4::new(self.x / len, self.y / len, self.z / len, self.w / len)
}
pub fn is_normalized(self) -> bool {
epsilon_eq(self.length(), 1.0, 1e-6)
}
pub fn is_normalized_fast(self) -> bool {
epsilon_eq(self.squared_length(), 1.0, 1e-6)
}
pub fn dot(&self, other: Vec4) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
}
pub fn cross_xyz(&self, other: Vec4) -> Vec4 {
Vec4::new(
self.y * other.z - self.z * other.y, self.z * other.x - self.x * other.z, self.x * other.y - self.y * other.x, self.w,
)
}
pub fn angle_to(self, other: Vec4) -> f32 {
let dot = self.dot(other);
let len_product = self.length() * other.length();
if epsilon_eq_default(len_product, 0.0) {
return 0.0;
}
(dot / len_product).clamp(-1.0, 1.0).acos()
}
pub fn angle_between_radians(a: Vec4, b: Vec4) -> f32 {
a.dot(b).clamp(-1.0, 1.0).acos()
}
pub fn angle_between_degrees(a: Vec4, b: Vec4) -> f32 {
utils::radians_to_degrees(a.dot(b).clamp(-1.0, 1.0).acos())
}
pub fn lerp(self, target: Vec4, t: f32) -> Vec4 {
self * (1.0 - t) + target * t
}
pub fn lerp_clamped(self, target: Vec4, t: f32) -> Vec4 {
let t = utils::clamp(t, 0.0, 1.0);
self.lerp(target, t)
}
pub fn lerp_between(a: Vec4, b: Vec4, t: f32) -> Vec4 {
a * (1.0 - t) + b * t
}
pub fn lerp_between_clamped(a: Vec4, b: Vec4, t: f32) -> Vec4 {
let t = utils::clamp(t, 0.0, 1.0);
Vec4::lerp_between(a, b, t)
}
pub fn slerp(a: Vec4, b: Vec4, t: f32) -> Vec4 {
if a.length() < 1e-6 {
return b * t;
}
if b.length() < 1e-6 {
return a * (1.0 - t);
}
let a_norm = a.normalize_or_zero();
let b_norm = b.normalize_or_zero();
let dot = a_norm.dot(b_norm).clamp(-1.0, 1.0);
if dot > 1.0 - 1e-6 {
return Vec4::lerp(a, b, t);
}
let theta = dot.acos();
let sin_theta = theta.sin();
let wa = ((1.0 - t) * theta).sin() / sin_theta;
let wb = (t * theta).sin() / sin_theta;
(a_norm * wa + b_norm * wb) * (a.length() * (1.0 - t) + b.length() * t)
}
pub fn slerp_angle(a: Vec4, b: Vec4, t: f32) -> Vec4 {
if a.length() < 1e-6 || b.length() < 1e-6 {
return Vec4::lerp_between_clamped(a, b, t);
}
let a_norm = a.normalize();
let b_norm = b.normalize();
let dot = a_norm.dot(b_norm).clamp(-1.0, 1.0);
if dot > 1.0 - 1e-6 {
return Vec4::lerp_between_clamped(a, b, t);
}
let theta = dot.acos();
let sin_theta = theta.sin();
let wa = ((1.0 - t) * theta).sin() / sin_theta;
let wb = (t * theta).sin() / sin_theta;
(a_norm * wa + b_norm * wb) * (a.length() * (1.0 - t) + b.length() * t)
}
pub fn project(&self, onto: Vec4) -> Vec4 {
let denominator = onto.dot(onto);
if denominator == 0.0 {
Vec4::new(0.0, 0.0, 0.0, 0.0)
} else {
onto * (self.dot(onto) / denominator)
}
}
pub fn reject(&self, other: Vec4) -> Vec4 {
*self - self.project(other)
}
pub fn reflect(&self, normal: Vec4) -> Vec4 {
*self - normal * (2.0 * self.dot(normal))
}
pub fn mirror(&self, normal: Vec4) -> Vec4 {
*self - normal * (2.0 * self.dot(normal))
}
pub fn distance(&self, other: Vec4) -> f32 {
(*self - other).length()
}
pub fn squared_distance(&self, other: Vec4) -> f32 {
(*self - other).squared_length()
}
pub fn direction_to(&self, other: Vec4) -> Vec4 {
let delta = other - *self;
delta.normalize_or_zero()
}
pub fn direction_to_raw(&self, other: Vec4) -> Vec4 {
other - *self
}
pub fn move_towards(current: Vec4, target: Vec4, max_delta: f32) -> Vec4 {
let delta = target - current;
let distance = delta.length();
if distance <= max_delta || distance < f32::EPSILON {
target
} else {
current + delta / distance * max_delta
}
}
pub fn orthonormalize(a: Vec4, b: Vec4) -> (Vec4, Vec4) {
let a_norm = a.normalize();
let projection = b.project(a_norm);
let b_orthogonal = b - projection;
let b_norm = b_orthogonal.normalize();
(a_norm, b_norm)
}
pub fn rotate_around_axis(&self, axis: Vec4, angle: f32) -> Vec4 {
let axis = axis.normalize();
let cos = angle.cos();
let sin = angle.sin();
*self * cos + axis.cross_xyz(*self) * sin + axis * axis.dot(*self) * (1.0 - cos)
}
pub fn rotate_in_plane(self, a: usize, b: usize, angle: f32) -> Vec4 {
assert!(
a < 4 && b < 4 && a != b,
"Invalid acis indices for 4D plane rotation"
);
let sin = angle.sin();
let cos = angle.cos();
let mut components = [self.x, self.y, self.z, self.w];
let (va, vb) = (components[a], components[b]);
components[a] = va * cos - vb * sin;
components[b] = va * sin + vb * cos;
Vec4::new(components[0], components[1], components[2], components[3])
}
pub fn random_unit_vector() -> Vec4 {
let mut rng = rand::rng();
loop {
let x = rng.random_range(-1.0..=1.0);
let y = rng.random_range(-1.0..=1.0);
let z = rng.random_range(-1.0..=1.0);
let w = rng.random_range(-1.0..=1.0);
let v = Vec4::new(x, y, z, w);
let len_sq = v.squared_length();
if len_sq > 0.0 && len_sq <= 1.0 {
return v / len_sq.sqrt();
}
}
}
pub fn random_in_unit_sphere() -> Vec4 {
let mut rng = rand::rng();
loop {
let x = rng.random_range(-1.0..=1.0);
let y = rng.random_range(-1.0..=1.0);
let z = rng.random_range(-1.0..=1.0);
let w = rng.random_range(-1.0..=1.0);
let v = Vec4::new(x, y, z, w);
if v.squared_length() <= 1.0 {
return v;
}
}
}
pub fn barycentric(p: Vec4, a: Vec4, b: Vec4, c: Vec4) -> (f32, f32, f32) {
let v0 = b - a;
let v1 = c - a;
let v2 = p - a;
let d00 = v0.dot(v0);
let d01 = v0.dot(v1);
let d11 = v1.dot(v1);
let d20 = v2.dot(v0);
let d21 = v2.dot(v1);
let denom = d00 * d11 - d01 * d01;
if is_near_zero_default(denom) {
return (0.0, 0.0, 0.0); }
let v = (d11 * d20 - d01 * d21) / denom;
let w = (d00 * d21 - d01 * d20) / denom;
let u = 1.0 - v - w;
(u, v, w)
}
pub fn in_triangle(p: Vec4, a: Vec4, b: Vec4, c: Vec4) -> bool {
let (u, v, w) = Vec4::barycentric(p, a, b, c);
u >= 0.0 && v >= 0.0 && w >= 0.0 && u <= 1.0 && v <= 1.0 && w <= 1.0
}
pub fn approx_eq(&self, other: Vec4) -> bool {
epsilon_eq(self.x, other.x, 1e-6)
&& epsilon_eq(self.y, other.y, 1e-6)
&& epsilon_eq(self.z, other.z, 1e-6)
&& epsilon_eq(self.w, other.w, 1e-6)
}
pub fn approx_eq_eps(&self, other: Vec4, 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)
}
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite() && self.w.is_finite()
}
pub fn is_nan(self) -> bool {
self.x.is_nan() || self.y.is_nan() || self.z.is_nan() || self.w.is_nan()
}
pub fn is_zero(self) -> bool {
epsilon_eq_default(self.x, 0.0)
&& epsilon_eq_default(self.y, 0.0)
&& epsilon_eq_default(self.z, 0.0)
&& epsilon_eq_default(self.w, 0.0)
}
pub fn is_zero_eps(self, epsilon: f32) -> bool {
epsilon_eq(self.x, 0.0, epsilon)
&& epsilon_eq(self.y, 0.0, epsilon)
&& epsilon_eq(self.z, 0.0, epsilon)
&& epsilon_eq(self.w, 0.0, epsilon)
}
}
impl Add for Vec4 {
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 Add<f32> for Vec4 {
type Output = Self;
#[inline]
fn add(self, rhs: f32) -> Self::Output {
Self::new(self.x + rhs, self.y + rhs, self.z + rhs, self.w + rhs)
}
}
impl Add<Vec4> for f32 {
type Output = Vec4;
#[inline]
fn add(self, rhs: Vec4) -> Self::Output {
Vec4::new(self + rhs.x, self + rhs.y, self + rhs.z, self + rhs.w)
}
}
impl Sub for Vec4 {
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 Sub<f32> for Vec4 {
type Output = Self;
#[inline]
fn sub(self, rhs: f32) -> Self::Output {
Self::new(self.x - rhs, self.y - rhs, self.z - rhs, self.w - rhs)
}
}
impl Sub<Vec4> for f32 {
type Output = Vec4;
#[inline]
fn sub(self, rhs: Vec4) -> Self::Output {
Vec4::new(self - rhs.x, self - rhs.y, self - rhs.z, self - rhs.w)
}
}
impl Mul for Vec4 {
type Output = Self;
#[inline]
fn mul(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<f32> for Vec4 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self::new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs)
}
}
impl Mul<Vec4> for f32 {
type Output = Vec4;
#[inline]
fn mul(self, rhs: Vec4) -> Self::Output {
Vec4::new(self * rhs.x, self * rhs.y, self * rhs.z, self * rhs.w)
}
}
impl Div for Vec4 {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
Self::new(
self.x / rhs.x,
self.y / rhs.y,
self.z / rhs.z,
self.w / rhs.w,
)
}
}
impl Div<f32> for Vec4 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self::new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs)
}
}
impl Div<Vec4> for f32 {
type Output = Vec4;
#[inline]
fn div(self, rhs: Vec4) -> Self::Output {
Vec4::new(self / rhs.x, self / rhs.y, self / rhs.z, self / rhs.w)
}
}
impl Neg for Vec4 {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self::new(-self.x, -self.y, -self.z, -self.w)
}
}
impl AddAssign for Vec4 {
#[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 AddAssign<f32> for Vec4 {
#[inline]
fn add_assign(&mut self, rhs: f32) {
self.x += rhs;
self.y += rhs;
self.z += rhs;
self.w += rhs;
}
}
impl SubAssign for Vec4 {
#[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 SubAssign<f32> for Vec4 {
#[inline]
fn sub_assign(&mut self, rhs: f32) {
self.x -= rhs;
self.y -= rhs;
self.z -= rhs;
self.w -= rhs;
}
}
impl MulAssign for Vec4 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
self.z *= rhs.z;
self.w *= rhs.w;
}
}
impl MulAssign<f32> for Vec4 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
self.w *= rhs;
}
}
impl DivAssign for Vec4 {
#[inline]
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
self.z /= rhs.z;
self.w /= rhs.w;
}
}
impl DivAssign<f32> for Vec4 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.x /= rhs;
self.y /= rhs;
self.z /= rhs;
self.w /= rhs;
}
}
impl Default for Vec4 {
#[inline]
fn default() -> Self {
Self::ZERO }
}
impl PartialEq for Vec4 {
#[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 Vec4 {
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!("Vec4 index out of bounds: {}", index),
}
}
}
impl IndexMut<usize> for Vec4 {
#[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!("Vec4 index out of bounds: {}", index),
}
}
}
impl fmt::Display for Vec4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Vec4({:.3}, {:.3}, {:.3}, {:.3})",
self.x, self.y, self.z, self.w
)
}
}
impl From<(f32, f32, f32, f32)> for Vec4 {
#[inline]
fn from(t: (f32, f32, f32, f32)) -> Self {
Self::new(t.0, t.1, t.2, t.3)
}
}
impl From<Vec4> for (f32, f32, f32, f32) {
#[inline]
fn from(v: Vec4) -> Self {
(v.x, v.y, v.z, v.w)
}
}
impl From<[f32; 4]> for Vec4 {
#[inline]
fn from(arr: [f32; 4]) -> Self {
Self::new(arr[0], arr[1], arr[2], arr[3])
}
}
impl From<Vec4> for [f32; 4] {
#[inline]
fn from(v: Vec4) -> Self {
[v.x, v.y, v.z, v.w]
}
}
impl approx::AbsDiffEq for Vec4 {
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 Vec4 {
#[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)
}
}