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);
}
}