use std::fmt;
use std::ops::{Mul, Neg};
use crate::{Vec3, EPS};
#[derive(Clone, Copy, PartialEq)]
pub struct UnitVec3(Vec3);
impl UnitVec3 {
pub const X: Self = Self(Vec3::X);
pub const Y: Self = Self(Vec3::Y);
pub const Z: Self = Self(Vec3::Z);
#[inline]
pub fn try_from_vec(v: Vec3) -> Option<Self> {
let len = v.length();
if len < EPS {
None
} else {
Some(Self(v / len))
}
}
#[inline]
pub fn new_unchecked(v: Vec3) -> Self {
Self(v)
}
#[inline]
pub fn as_vec(self) -> Vec3 {
self.0
}
#[inline]
pub fn dot(self, rhs: Self) -> f64 {
self.0.dot(rhs.0)
}
#[inline]
pub fn dot_vec(self, v: Vec3) -> f64 {
self.0.dot(v)
}
#[inline]
pub fn cross(self, rhs: Self) -> Vec3 {
self.0.cross(rhs.0)
}
#[inline]
pub fn flip(self) -> Self {
Self(-self.0)
}
#[inline]
pub fn angle_to(self, other: Self) -> f64 {
self.dot(other).clamp(-1.0, 1.0).acos()
}
pub fn perp_basis(self) -> (Self, Self) {
let u = self.0.any_perp().normalize();
let v_vec = self.0.cross(u);
(Self(u), Self(v_vec))
}
}
impl Neg for UnitVec3 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
self.flip()
}
}
impl Mul<f64> for UnitVec3 {
type Output = Vec3;
#[inline]
fn mul(self, s: f64) -> Vec3 {
self.0 * s
}
}
impl Mul<UnitVec3> for f64 {
type Output = Vec3;
#[inline]
fn mul(self, u: UnitVec3) -> Vec3 {
u.0 * self
}
}
impl From<UnitVec3> for Vec3 {
fn from(u: UnitVec3) -> Vec3 {
u.0
}
}
impl fmt::Debug for UnitVec3 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"UnitVec3({:.6}, {:.6}, {:.6})",
self.0.x, self.0.y, self.0.z
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unit_length() {
let v = Vec3::new(1.0, 2.0, 3.0);
let u = UnitVec3::try_from_vec(v).unwrap();
assert!((u.as_vec().length() - 1.0).abs() < 1e-12);
}
#[test]
fn zero_returns_none() {
assert!(UnitVec3::try_from_vec(Vec3::ZERO).is_none());
}
#[test]
fn perp_basis_orthogonal() {
let u = UnitVec3::Z;
let (a, b) = u.perp_basis();
assert!(a.dot(u).abs() < 1e-12);
assert!(b.dot(u).abs() < 1e-12);
assert!(a.dot(b).abs() < 1e-12);
}
}