use crate::vec3::Vec3;
use core::marker::PhantomData;
use crate::math;
#[cfg(feature = "unit_vec")]
use crate::unit_vec::UnitVec3;
pub use crate::markers::{Local, Meters, Pixels, Screen, World};
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Radians;
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Degrees;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Vec4<Unit: Copy = (), Space: Copy = ()> {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
#[cfg_attr(feature = "serde", serde(skip))]
_unit: PhantomData<Unit>,
#[cfg_attr(feature = "serde", serde(skip))]
_space: PhantomData<Space>,
}
pub type Vec4f32 = Vec4<(),()>;
pub type Vec4Meters = Vec4<Meters,()>;
pub type Vec4Pixels = Vec4<Pixels,()>;
pub type Vec4World = Vec4<(),World>;
pub type Vec4Local = Vec4<(),Local>;
pub type Vec4Screen = Vec4<(),Screen>;
pub type Vec4MetersWorld = Vec4<Meters,World>;
pub type Vec4PixelsScreen = Vec4<Pixels,Screen>;
impl<Unit: Copy, Space: Copy> Vec4<Unit, Space> {
pub const ZERO: Self = Self {
x: 0.0,
y: 0.0,
z: 0.0,
w: 0.0,
_unit: PhantomData,
_space: PhantomData,
};
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self { x, y, z, w, _unit: PhantomData, _space: PhantomData }
}
pub fn xyz(self) -> Vec3<Unit, Space> {
Vec3::new(self.x, self.y, self.z)
}
#[inline]
pub const fn dot(self, other: Self) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
}
pub const fn wzyx(self) -> Self {
Self {
x: self.w,
y: self.z,
z: self.y,
w: self.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)
}
}
pub fn reflect(self, normal: Self) -> Self {
let reflected_xyz = self.xyz().reflect(normal.xyz());
Self::new(reflected_xyz.x, reflected_xyz.y, reflected_xyz.z, self.w)
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn reflect_unit(self, normal: UnitVec3<Unit, Space>) -> Self {
let reflected_xyz = self.xyz().reflect(normal.as_vec());
Self::new(reflected_xyz.x, reflected_xyz.y, reflected_xyz.z, self.w)
}
pub fn refract(self, normal: Self, eta: f32) -> Self {
let refracted_xyz = self.xyz().refract(normal.xyz(), eta);
Self::new(refracted_xyz.x, refracted_xyz.y, refracted_xyz.z, self.w)
}
pub fn try_refract(self, normal: Self, eta: f32) -> Option<Self> {
match self.xyz().try_refract(normal.xyz(), eta) {
Some(refracted_xyz) => Some(Self::new(
refracted_xyz.x,
refracted_xyz.y,
refracted_xyz.z,
self.w,
)),
None => None,
}
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn refract_unit(self, normal: UnitVec3<Unit, Space>, eta: f32) -> Self {
let refracted_xyz = self.xyz().refract(normal.as_vec(), eta);
Self::new(refracted_xyz.x, refracted_xyz.y, refracted_xyz.z, self.w)
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn try_refract_unit(self, normal: UnitVec3<Unit, Space>, eta: f32) -> Option<Self> {
match self.xyz().try_refract(normal.as_vec(), eta) {
Some(refracted_xyz) => Some(Self::new(
refracted_xyz.x,
refracted_xyz.y,
refracted_xyz.z,
self.w,
)),
None => None,
}
}
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,
w: self.w + (b.w - self.w) * 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 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 const fn clamp(self, min: Self, max: Self) -> Self {
Self {
x: if self.x < min.x { min.x } else if self.x > max.x { max.x } else { self.x },
y: if self.y < min.y { min.y } else if self.y > max.y { max.y } else { self.y },
z: if self.z < min.z { min.z } else if self.z > max.z { max.z } else { self.z },
w: if self.w < min.w { min.w } else if self.w > max.w { max.w } else { self.w },
_unit: PhantomData,
_space: PhantomData,
}
}
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 },
w: if self.w < other.w { self.w } else { other.w },
_unit: PhantomData,
_space: PhantomData,
}
}
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 },
w: if self.w > other.w { self.w } else { other.w },
_unit: PhantomData,
_space: PhantomData,
}
}
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_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite() && self.w.is_finite()
}
}
impl Vec4<Meters,()> {
pub fn to_pixels(self, scale: f32) -> Vec4<Pixels,()> {
Vec4 {
x: self.x * scale,
y: self.y * scale,
z: self.z * scale,
w: self.w * scale,
_unit: PhantomData,
_space: PhantomData,
}
}
}
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
impl<Unit: Copy, Space: Copy> Add for Vec4<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,
w: self.w + rhs.w,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> AddAssign for Vec4<Unit, Space> {
#[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<Unit: Copy, Space: Copy> Sub for Vec4<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,
w: self.w - rhs.w,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> SubAssign for Vec4<Unit, Space> {
#[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<Unit: Copy, Space: Copy> Mul<f32> for Vec4<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,
w: self.w * rhs,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> Mul<Vec4<Unit, Space>> for f32 {
type Output = Vec4<Unit, Space>;
#[inline]
fn mul(self, rhs: Vec4<Unit, Space>) -> Self::Output {
Vec4 {
x: self * rhs.x,
y: self * rhs.y,
z: self * rhs.z,
w: self * rhs.w,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> MulAssign<f32> for Vec4<Unit, Space> {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
self.w *= rhs;
}
}
impl<Unit: Copy, Space: Copy> Mul<Vec4<Unit, Space>> for Vec4<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,
w: self.w * rhs.w,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> MulAssign<Vec4<Unit, Space>> for Vec4<Unit, Space> {
#[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<Unit: Copy, Space: Copy> Div<f32> for Vec4<Unit, Space> {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
let inv_rhs = 1.0 / rhs;
Self {
x: self.x * inv_rhs,
y: self.y * inv_rhs,
z: self.z * inv_rhs,
w: self.w * inv_rhs,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> DivAssign<f32> for Vec4<Unit, Space> {
#[inline]
fn div_assign(&mut self, rhs: f32) {
let inv_rhs = 1.0 / rhs;
self.x *= inv_rhs;
self.y *= inv_rhs;
self.z *= inv_rhs;
self.w *= inv_rhs;
}
}
impl<Unit: Copy, Space: Copy> Div<Vec4<Unit, Space>> for Vec4<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,
w: self.w / rhs.w,
_unit: PhantomData,
_space: PhantomData,
}
}
}
impl<Unit: Copy, Space: Copy> DivAssign<Vec4<Unit, Space>> for Vec4<Unit, Space> {
#[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<Unit: Copy, Space: Copy> Neg for Vec4<Unit, Space> {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self {
x: -self.x,
y: -self.y,
z: -self.z,
w: -self.w,
_unit: PhantomData,
_space: PhantomData,
}
}
}