use rand::Rng;
use crate::utils;
use crate::utils::epsilon_eq;
use crate::utils::epsilon_eq_default;
use crate::utils::is_near_zero_default;
use crate::vec::Vec2;
use crate::vec::Vec4;
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 Vec3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vec3 {
pub const fn new(x: f32, y: f32, z: f32) -> Vec3 {
Vec3 { x, y, z }
}
pub fn to_array(self) -> [f32; 3] {
[self.x, self.y, self.z]
}
pub fn from_array(arr: [f32; 3]) -> Vec3 {
Vec3::new(arr[0], arr[1], arr[2])
}
pub fn to_tuple(self) -> (f32, f32, f32) {
(self.x, self.y, self.z)
}
pub fn from_tuple(t: (f32, f32, f32)) -> Vec3 {
Vec3::new(t.0, t.1, t.2)
}
pub fn from_vec2_z(v: Vec2, z: f32) -> Vec3 {
Vec3::new(v.x, v.y, z)
}
pub const ZERO: Self = Self {
x: 0.0,
y: 0.0,
z: 0.0,
};
pub const NAN: Self = Self {
x: f32::NAN,
y: f32::NAN,
z: f32::NAN,
};
pub const INFINITY: Self = Self {
x: f32::INFINITY,
y: f32::INFINITY,
z: f32::INFINITY,
};
pub const X: Self = Self {
x: 1.0,
y: 0.0,
z: 0.0,
};
pub const Y: Self = Self {
x: 0.0,
y: 1.0,
z: 0.0,
};
pub const Z: Self = Self {
x: 0.0,
y: 0.0,
z: 1.0,
};
pub fn abs(self) -> Vec3 {
Vec3::new(self.x.abs(), self.y.abs(), self.z.abs())
}
pub fn signum(self) -> Self {
Vec3::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
},
)
}
pub fn ieee_signum(self) -> Self {
Vec3::new(self.x.signum(), self.y.signum(), self.z.signum())
}
pub fn extend(&self, w: f32) -> Vec4 {
Vec4::new(self.x, self.y, self.z, w)
}
pub fn clamp(self, min: f32, max: f32) -> Vec3 {
Vec3::new(
utils::clamp(self.x, min, max),
utils::clamp(self.y, min, max),
utils::clamp(self.z, min, max),
)
}
pub fn min(self, other: Vec3) -> Vec3 {
Vec3::new(
self.x.min(other.x),
self.y.min(other.y),
self.z.min(other.z),
)
}
pub fn max(self, other: Vec3) -> Vec3 {
Vec3::new(
self.x.max(other.x),
self.y.max(other.y),
self.z.max(other.z),
)
}
pub fn triple_product_vector(a: Vec3, b: Vec3, c: Vec3) -> Vec3 {
let ac = a.dot(c);
let bc = b.dot(c);
b * ac - a * bc
}
pub fn triple_product_scalar(a: Vec3, b: Vec3, c: Vec3) -> f32 {
a.dot(b.cross(c))
}
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
pub fn squared_length(&self) -> f32 {
self.x * self.x + self.y * self.y + self.z * self.z
}
pub fn try_normalize(&self) -> Option<Vec3> {
let len = self.length();
if is_near_zero_default(len) {
None
} else {
Some(Vec3::new(self.x / len, self.y / len, self.z / len))
}
}
pub fn normalize(&self) -> Vec3 {
let len = self.length();
assert!(
!is_near_zero_default(len),
"Cannot normalize zero length vector"
);
Vec3::new(self.x / len, self.y / len, self.z / len)
}
pub fn normalize_or_zero(&self) -> Vec3 {
let len = self.length();
if epsilon_eq_default(len, 0.0) {
Vec3::ZERO
} else {
Vec3::new(self.x / len, self.y / len, self.z / 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: Vec3) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z
}
pub fn cross(&self, other: Vec3) -> Vec3 {
Vec3::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, )
}
pub fn angle_to(self, other: Vec3) -> 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: Vec3, b: Vec3) -> f32 {
(a.dot(b) / (a.length() * b.length()))
.clamp(-1.0, 1.0)
.acos()
}
pub fn angle_between_degrees(a: Vec3, b: Vec3) -> f32 {
utils::radians_to_degrees(Vec3::angle_between_radians(a, b))
}
pub fn lerp(self, target: Vec3, t: f32) -> Vec3 {
self * (1.0 - t) + target * t
}
pub fn lerp_clamped(self, target: Vec3, t: f32) -> Vec3 {
let t = utils::clamp(t, 0.0, 1.0);
self.lerp(target, t)
}
pub fn lerp_between(a: Vec3, b: Vec3, t: f32) -> Vec3 {
a * (1.0 - t) + b * t
}
pub fn lerp_between_clamped(a: Vec3, b: Vec3, t: f32) -> Vec3 {
let t = utils::clamp(t, 0.0, 1.0);
Vec3::lerp_between(a, b, t)
}
pub fn slerp(a: Vec3, b: Vec3, t: f32) -> Vec3 {
if a.length() < 1e-6 {
return b * t;
}
if b.length() < 1e-6 {
return a * (1.0 - 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 Vec3::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 slerp_angle(a: Vec3, b: Vec3, t: f32) -> Vec3 {
if a.length() < 1e-6 || b.length() < 1e-6 {
return Vec3::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 Vec3::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: Vec3) -> Vec3 {
let denominator = onto.dot(onto);
if denominator == 0.0 {
Vec3::new(0.0, 0.0, 0.0)
} else {
onto * (self.dot(onto) / denominator)
}
}
pub fn reject(&self, other: Vec3) -> Vec3 {
*self - self.project(other)
}
pub fn reflect(&self, normal: Vec3) -> Vec3 {
*self - normal * (2.0 * self.dot(normal))
}
pub fn mirror(&self, normal: Vec3) -> Vec3 {
*self - normal * (2.0 * self.dot(normal))
}
pub fn distance(&self, other: Vec3) -> f32 {
(*self - other).length()
}
pub fn squared_distance(&self, other: Vec3) -> f32 {
(*self - other).squared_length()
}
pub fn direction_to(&self, other: Vec3) -> Vec3 {
let delta = other - *self;
delta.normalize()
}
pub fn direction_to_raw(&self, other: Vec3) -> Vec3 {
other - *self
}
pub fn move_towards(current: Vec3, target: Vec3, max_delta: f32) -> Vec3 {
let delta = target - current;
let distance = delta.length();
if distance <= max_delta || distance < f32::EPSILON {
target
} else {
current + delta / distance * max_delta
}
}
pub fn orthonormal_basis(&self) -> (Vec3, Vec3) {
let up = if self.x.abs() < 0.9 {
Vec3::new(1.0, 0.0, 0.0)
} else {
Vec3::new(0.0, 1.0, 0.0)
};
let v = self.cross(up).normalize();
let w = self.cross(v).normalize();
(v, w)
}
pub fn orthonormalize(a: Vec3, b: Vec3) -> (Vec3, Vec3) {
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: Vec3, angle: f32) -> Vec3 {
let axis = axis.normalize();
let cos = angle.cos();
let sin = angle.sin();
*self * cos + axis.cross(*self) * sin + axis * axis.dot(*self) * (1.0 - cos)
}
pub fn random_unit_vector() -> Vec3 {
let mut rng = rand::rng();
let theta = rng.random_range(0.0..std::f32::consts::TAU);
let z = rng.random_range(-1.0..1.0);
let r = (1.0f32 - z * z).sqrt();
Vec3::new(r * theta.cos(), r * theta.sin(), z)
}
pub fn barycentric(p: Vec3, a: Vec3, b: Vec3, c: Vec3) -> (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;
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: Vec3, a: Vec3, b: Vec3, c: Vec3) -> bool {
let (u, v, w) = Vec3::barycentric(p, a, b, c);
u >= 0.0 && v >= 0.0 && w >= 0.0
}
pub fn is_zero(self) -> bool {
utils::is_near_zero_default(self.length())
}
pub fn is_zero_eps(&self, epsilon: f32) -> bool {
utils::epsilon_eq(self.length(), 0.0, epsilon)
}
pub fn approx_eq(&self, other: Vec3) -> 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)
}
pub fn approx_eq_eps(&self, other: Vec3, epsilon: f32) -> bool {
epsilon_eq(self.x, other.x, epsilon)
&& epsilon_eq(self.y, other.y, epsilon)
&& epsilon_eq(self.z, other.z, epsilon)
}
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
}
pub fn is_nan(self) -> bool {
self.x.is_nan() || self.y.is_nan() || self.z.is_nan()
}
}
impl Add for Vec3 {
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)
}
}
impl Add<f32> for Vec3 {
type Output = Self;
#[inline]
fn add(self, rhs: f32) -> Self::Output {
Self::new(self.x + rhs, self.y + rhs, self.z + rhs)
}
}
impl Add<Vec3> for f32 {
type Output = Vec3;
#[inline]
fn add(self, rhs: Vec3) -> Self::Output {
Vec3::new(self + rhs.x, self + rhs.y, self + rhs.z)
}
}
impl Sub for Vec3 {
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)
}
}
impl Sub<f32> for Vec3 {
type Output = Self;
#[inline]
fn sub(self, rhs: f32) -> Self::Output {
Self::new(self.x - rhs, self.y - rhs, self.z - rhs)
}
}
impl Sub<Vec3> for f32 {
type Output = Vec3;
#[inline]
fn sub(self, rhs: Vec3) -> Self::Output {
Vec3::new(self - rhs.x, self - rhs.y, self - rhs.z)
}
}
impl Mul for Vec3 {
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)
}
}
impl Mul<f32> for Vec3 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self::new(self.x * rhs, self.y * rhs, self.z * rhs)
}
}
impl Mul<Vec3> for f32 {
type Output = Vec3;
#[inline]
fn mul(self, rhs: Vec3) -> Self::Output {
Vec3::new(self * rhs.x, self * rhs.y, self * rhs.z)
}
}
impl Div for Vec3 {
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)
}
}
impl Div<f32> for Vec3 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self::new(self.x / rhs, self.y / rhs, self.z / rhs)
}
}
impl Div<Vec3> for f32 {
type Output = Vec3;
#[inline]
fn div(self, rhs: Vec3) -> Self::Output {
Vec3::new(self / rhs.x, self / rhs.y, self / rhs.z)
}
}
impl Neg for Vec3 {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self::new(-self.x, -self.y, -self.z)
}
}
impl AddAssign for Vec3 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl AddAssign<f32> for Vec3 {
#[inline]
fn add_assign(&mut self, rhs: f32) {
self.x += rhs;
self.y += rhs;
self.z += rhs;
}
}
impl SubAssign for Vec3 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl SubAssign<f32> for Vec3 {
#[inline]
fn sub_assign(&mut self, rhs: f32) {
self.x -= rhs;
self.y -= rhs;
self.z -= rhs;
}
}
impl MulAssign for Vec3 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
self.z *= rhs.z;
}
}
impl MulAssign<f32> for Vec3 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
}
}
impl DivAssign for Vec3 {
#[inline]
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
self.z /= rhs.z;
}
}
impl DivAssign<f32> for Vec3 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.x /= rhs;
self.y /= rhs;
self.z /= rhs;
}
}
impl Default for Vec3 {
#[inline]
fn default() -> Self {
Self::ZERO }
}
impl PartialEq for Vec3 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y && self.z == other.z
}
}
impl Index<usize> for Vec3 {
type Output = f32;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
match index {
0 => &self.x,
1 => &self.y,
2 => &self.z,
_ => panic!("Vec3 index out of bounds: {}", index),
}
}
}
impl IndexMut<usize> for Vec3 {
#[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,
_ => panic!("Vec3 index out of bounds: {}", index),
}
}
}
impl fmt::Display for Vec3 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Vec3({:.3}, {:.3}, {:.3})", self.x, self.y, self.z)
}
}
impl From<(f32, f32, f32)> for Vec3 {
#[inline]
fn from(t: (f32, f32, f32)) -> Self {
Self::new(t.0, t.1, t.2)
}
}
impl From<Vec3> for (f32, f32, f32) {
#[inline]
fn from(v: Vec3) -> Self {
(v.x, v.y, v.z)
}
}
impl From<[f32; 3]> for Vec3 {
#[inline]
fn from(arr: [f32; 3]) -> Self {
Self::new(arr[0], arr[1], arr[2])
}
}
impl From<Vec3> for [f32; 3] {
#[inline]
fn from(v: Vec3) -> Self {
[v.x, v.y, v.z]
}
}
impl approx::AbsDiffEq for Vec3 {
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)
}
}
impl approx::RelativeEq for Vec3 {
#[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)
}
}