1use std::fmt;
4use std::ops::{Mul, Neg};
5
6use crate::{Vec3, EPS};
7
8#[derive(Clone, Copy, PartialEq)]
13pub struct UnitVec3(Vec3);
14
15impl UnitVec3 {
16 pub const X: Self = Self(Vec3::X);
18 pub const Y: Self = Self(Vec3::Y);
20 pub const Z: Self = Self(Vec3::Z);
22
23 #[inline]
25 pub fn try_from_vec(v: Vec3) -> Option<Self> {
26 let len = v.length();
27 if len < EPS { None } else { Some(Self(v / len)) }
28 }
29
30 #[inline]
36 pub fn new_unchecked(v: Vec3) -> Self { Self(v) }
37
38 #[inline]
40 pub fn as_vec(self) -> Vec3 { self.0 }
41
42 #[inline]
44 pub fn dot(self, rhs: Self) -> f64 { self.0.dot(rhs.0) }
45
46 #[inline]
48 pub fn dot_vec(self, v: Vec3) -> f64 { self.0.dot(v) }
49
50 #[inline]
52 pub fn cross(self, rhs: Self) -> Vec3 { self.0.cross(rhs.0) }
53
54 #[inline]
56 pub fn flip(self) -> Self { Self(-self.0) }
57
58 #[inline]
60 pub fn angle_to(self, other: Self) -> f64 {
61 self.dot(other).clamp(-1.0, 1.0).acos()
62 }
63
64 pub fn perp_basis(self) -> (Self, Self) {
67 let u = self.0.any_perp().normalize();
68 let v_vec = self.0.cross(u);
69 (Self(u), Self(v_vec))
70 }
71}
72
73impl Neg for UnitVec3 {
74 type Output = Self;
75 #[inline] fn neg(self) -> Self { self.flip() }
76}
77
78impl Mul<f64> for UnitVec3 {
79 type Output = Vec3;
80 #[inline] fn mul(self, s: f64) -> Vec3 { self.0 * s }
81}
82impl Mul<UnitVec3> for f64 {
83 type Output = Vec3;
84 #[inline] fn mul(self, u: UnitVec3) -> Vec3 { u.0 * self }
85}
86
87impl From<UnitVec3> for Vec3 {
88 fn from(u: UnitVec3) -> Vec3 { u.0 }
89}
90
91impl fmt::Debug for UnitVec3 {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write!(f, "UnitVec3({:.6}, {:.6}, {:.6})", self.0.x, self.0.y, self.0.z)
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn unit_length() {
103 let v = Vec3::new(1.0, 2.0, 3.0);
104 let u = UnitVec3::try_from_vec(v).unwrap();
105 assert!((u.as_vec().length() - 1.0).abs() < 1e-12);
106 }
107
108 #[test]
109 fn zero_returns_none() {
110 assert!(UnitVec3::try_from_vec(Vec3::ZERO).is_none());
111 }
112
113 #[test]
114 fn perp_basis_orthogonal() {
115 let u = UnitVec3::Z;
116 let (a, b) = u.perp_basis();
117 assert!(a.dot(u).abs() < 1e-12);
118 assert!(b.dot(u).abs() < 1e-12);
119 assert!(a.dot(b).abs() < 1e-12);
120 }
121}