pub use crate::markers::{Local, Meters, Pixels, Screen, World};
use core::marker::PhantomData;
use crate::angle::{Degrees, Radians};
use crate::math;
#[cfg(feature = "unit_vec")]
use crate::unit_vec::UnitVec3;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub struct Vec3<Unit: Copy = (), Space: Copy = ()> {
pub x: f32,
pub y: f32,
pub z: f32,
#[cfg_attr(feature = "serde", serde(skip))]
_unit: PhantomData<Unit>,
#[cfg_attr(feature = "serde", serde(skip))]
_space: PhantomData<Space>,
}
pub type Vec3f32 = Vec3<(),()>;
pub type Vec3Meters = Vec3<Meters,()>;
pub type Vec3Pixels = Vec3<Pixels,()>;
pub type Vec3World = Vec3<(),World>;
pub type Vec3Local = Vec3<(),Local>;
pub type Vec3Screen = Vec3<(),Screen>;
pub type Vec3MetersWorld = Vec3<Meters,World>;
pub type Vec3PixelsScreen = Vec3<Pixels,Screen>;
impl<Unit: Copy, Space: Copy> Vec3<Unit, Space> {
pub const ZERO: Self = Self { x: 0.0, y: 0.0, z: 0.0, _unit: PhantomData, _space: PhantomData };
#[inline]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z, _unit: PhantomData, _space: PhantomData }
}
#[inline]
pub const fn dot(self, other: Self) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z
}
#[inline]
pub const fn yxz(self) -> Self {
Self { x: self.y, y: self.x, z: self.z, _unit: PhantomData, _space: PhantomData }
}
#[inline]
pub const fn cross(self, rhs: Self) -> Self {
Self {
x: self.y * rhs.z - self.z * rhs.y,
y: self.z * rhs.x - self.x * rhs.z,
z: self.x * rhs.y - self.y * rhs.x,
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub const fn length_squared(self) -> f32 {
self.dot(self)
}
#[inline]
pub fn length(self) -> f32 {
math::sqrt(self.length_squared())
}
#[inline]
pub fn normalize(self) -> Self {
let len_sq = self.length_squared();
if len_sq == 0.0 {
Self::ZERO
} else {
self / math::sqrt(len_sq)
}
}
#[inline]
pub fn try_normalize(self) -> Option<Self> {
let len_sq = self.length_squared();
if len_sq > 0.0 {
Some(self * (1.0 / math::sqrt(len_sq)))
} else {
None
}
}
#[inline]
pub fn checked_div_scalar(self, rhs: f32) -> Option<Self> {
if rhs == 0.0 || !rhs.is_finite() {
None
} else {
Some(self / rhs)
}
}
#[inline]
pub fn reflect(self, normal: Self) -> Self {
self - normal * (2.0 * self.dot(normal))
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn reflect_unit(self, normal: UnitVec3<Unit, Space>) -> Self {
self.reflect(normal.as_vec())
}
pub fn refract(self, normal: Self, eta: f32) -> Self {
let n_dot_i = normal.dot(self);
let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
if k < 0.0 {
Self::ZERO
} else {
self * eta - normal * (eta * n_dot_i * math::sqrt(k))
}
}
pub fn try_refract(self, normal: Self, eta: f32) -> Option<Self> {
let n_dot_i = normal.dot(self);
let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i);
if k < 0.0 {
None
} else {
Some(self * eta - normal * (eta * n_dot_i * math::sqrt(k)))
}
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn refract_unit(self, normal: UnitVec3<Unit, Space>, eta: f32) -> Self {
self.refract(normal.as_vec(), eta)
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn try_refract_unit(self, normal: UnitVec3<Unit, Space>, eta: f32) -> Option<Self> {
self.try_refract(normal.as_vec(), eta)
}
pub fn reflect_incident(i: Self, n: Self) -> Self {
i - n * (2.0 * i.dot(n))
}
pub fn refract_gl(i: Self, n: Self, eta: f32) -> Self {
let dot_ni = i.dot(n);
let k = 1.0 - eta * eta * (1.0 - dot_ni * dot_ni);
if k < 0.0 {
Self::ZERO
} else {
i * eta - n * (eta * dot_ni + math::sqrt(k))
}
}
pub fn try_refract_gl(i: Self, n: Self, eta: f32) -> Option<Self> {
let dot_ni = i.dot(n);
let k = 1.0 - eta * eta * (1.0 - dot_ni * dot_ni);
if k < 0.0 {
None
} else {
Some(i * eta - n * (eta * dot_ni + math::sqrt(k)))
}
}
#[inline]
pub fn refract_incident(i: Self, n: Self, eta: f32) -> Self {
Self::refract_gl(i, n, eta)
}
#[inline]
pub fn try_refract_incident(i: Self, n: Self, eta: f32) -> Option<Self> {
Self::try_refract_gl(i, n, eta)
}
#[inline]
pub fn lerp(self, b: Self, t: f32) -> Self {
Self {
x: self.x + (b.x - self.x) * t,
y: self.y + (b.y - self.y) * t,
z: self.z + (b.z - self.z) * t,
_unit: PhantomData,
_space: PhantomData,
}
}
pub fn angle_between(self, other: Self) -> f32 {
let dot = self.dot(other);
let len_product = self.length() * other.length();
if len_product == 0.0 {
0.0
} else {
math::acos((dot / len_product).clamp(-1.0, 1.0))
}
}
pub fn project_onto(self, other: Self) -> Self {
let other_len_sq = other.length_squared();
if other_len_sq == 0.0 {
Self::ZERO
} else {
other * (self.dot(other) / other_len_sq)
}
}
pub fn perp(self) -> Self {
Self {
x: -self.y,
y: self.x,
z: self.z,
_unit: PhantomData,
_space: PhantomData,
}
}
pub fn normalize_or_zero(self) -> Self {
let len = self.length();
if len == 0.0 { Self::ZERO } else { self / len }
}
pub fn distance(self, other: Self) -> f32 {
(self - other).length()
}
pub fn clamp(self, min: Self, max: Self) -> Self {
Self {
x: self.x.max(min.x).min(max.x),
y: self.y.max(min.y).min(max.y),
z: self.z.max(min.z).min(max.z),
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub const fn min(self, other: Self) -> Self {
Self {
x: if self.x < other.x { self.x } else { other.x },
y: if self.y < other.y { self.y } else { other.y },
z: if self.z < other.z { self.z } else { other.z },
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub const fn max(self, other: Self) -> Self {
Self {
x: if self.x > other.x { self.x } else { other.x },
y: if self.y > other.y { self.y } else { other.y },
z: if self.z > other.z { self.z } else { other.z },
_unit: PhantomData,
_space: PhantomData,
}
}
pub fn is_nan(self) -> bool {
self.x.is_nan() || self.y.is_nan() || self.z.is_nan()
}
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
}
pub fn rotate_axis(self, axis: Vec3<Unit, Space>, angle: Radians) -> Self {
let cos = math::cos(angle.0);
let sin = math::sin(angle.0);
let one_minus_cos = 1.0 - cos;
let k = axis;
let v = Vec3::new(self.x, self.y, self.z);
let dot = k.dot(v);
let cross = k.cross(v);
let rotated = v * cos + cross * sin + k * (dot * one_minus_cos);
Self {
x: rotated.x,
y: rotated.y,
z: rotated.z,
_unit: core::marker::PhantomData,
_space: core::marker::PhantomData,
}
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn rotate_unit_axis(self, axis: UnitVec3<Unit, Space>, angle: Radians) -> Self {
self.rotate_axis(axis.as_vec(), angle)
}
pub fn rotate(self, angle: Radians) -> Self {
let z_axis = Vec3::new(0.0, 0.0, 1.0);
self.rotate_axis(z_axis, angle)
}
pub fn rotate_deg(self, angle: Degrees) -> Self {
self.rotate(angle.to_radians())
}
}
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
impl<Unit: Copy, Space: Copy> Add for Vec3<Unit, Space> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> AddAssign for Vec3<Unit, Space> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl<Unit: Copy, Space: Copy> Sub for Vec3<Unit, Space> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> SubAssign for Vec3<Unit, Space> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl<Unit: Copy, Space: Copy> Mul<f32> for Vec3<Unit, Space> {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> Mul<Vec3<Unit, Space>> for f32 {
type Output = Vec3<Unit, Space>;
#[inline]
fn mul(self, rhs: Vec3<Unit, Space>) -> Self::Output {
Vec3 {
x: self * rhs.x,
y: self * rhs.y,
z: self * rhs.z,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> MulAssign<f32> for Vec3<Unit, Space> {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
}
}
impl<Unit: Copy, Space: Copy> Mul<Vec3<Unit, Space>> for Vec3<Unit, Space> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
z: self.z * rhs.z,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> MulAssign<Vec3<Unit, Space>> for Vec3<Unit, Space> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
self.z *= rhs.z;
}
}
impl<Unit: Copy, Space: Copy> Div<f32> for Vec3<Unit, Space> {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self {
x: self.x / rhs,
y: self.y / rhs,
z: self.z / rhs,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> DivAssign<f32> for Vec3<Unit, Space> {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.x /= rhs;
self.y /= rhs;
self.z /= rhs;
}
}
impl<Unit: Copy, Space: Copy> Div<Vec3<Unit, Space>> for Vec3<Unit, Space> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
Self {
x: self.x / rhs.x,
y: self.y / rhs.y,
z: self.z / rhs.z,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> DivAssign<Vec3<Unit, Space>> for Vec3<Unit, Space> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
self.z /= rhs.z;
}
}
impl<Unit: Copy, Space: Copy> Neg for Vec3<Unit, Space> {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self {
x: -self.x,
y: -self.y,
z: -self.z,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Space: Copy> Vec3<Meters, Space> {
pub fn to_pixels(self, scale: f32) -> Vec3<Pixels, Space> {
Vec3 { x: self.x * scale, y: self.y * scale, z: self.z * scale, _unit: PhantomData, _space: PhantomData }
}
}