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 {
28 None
29 } else {
30 Some(Self(v / len))
31 }
32 }
33
34 #[inline]
40 pub fn new_unchecked(v: Vec3) -> Self {
41 Self(v)
42 }
43
44 #[inline]
46 pub fn as_vec(self) -> Vec3 {
47 self.0
48 }
49
50 #[inline]
52 pub fn dot(self, rhs: Self) -> f64 {
53 self.0.dot(rhs.0)
54 }
55
56 #[inline]
58 pub fn dot_vec(self, v: Vec3) -> f64 {
59 self.0.dot(v)
60 }
61
62 #[inline]
64 pub fn cross(self, rhs: Self) -> Vec3 {
65 self.0.cross(rhs.0)
66 }
67
68 #[inline]
70 pub fn flip(self) -> Self {
71 Self(-self.0)
72 }
73
74 #[inline]
76 pub fn angle_to(self, other: Self) -> f64 {
77 self.dot(other).clamp(-1.0, 1.0).acos()
78 }
79
80 pub fn perp_basis(self) -> (Self, Self) {
83 let u = self.0.any_perp().normalize();
84 let v_vec = self.0.cross(u);
85 (Self(u), Self(v_vec))
86 }
87}
88
89impl Neg for UnitVec3 {
90 type Output = Self;
91 #[inline]
92 fn neg(self) -> Self {
93 self.flip()
94 }
95}
96
97impl Mul<f64> for UnitVec3 {
98 type Output = Vec3;
99 #[inline]
100 fn mul(self, s: f64) -> Vec3 {
101 self.0 * s
102 }
103}
104impl Mul<UnitVec3> for f64 {
105 type Output = Vec3;
106 #[inline]
107 fn mul(self, u: UnitVec3) -> Vec3 {
108 u.0 * self
109 }
110}
111
112impl From<UnitVec3> for Vec3 {
113 fn from(u: UnitVec3) -> Vec3 {
114 u.0
115 }
116}
117
118impl fmt::Debug for UnitVec3 {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(
121 f,
122 "UnitVec3({:.6}, {:.6}, {:.6})",
123 self.0.x, self.0.y, self.0.z
124 )
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn unit_length() {
134 let v = Vec3::new(1.0, 2.0, 3.0);
135 let u = UnitVec3::try_from_vec(v).unwrap();
136 assert!((u.as_vec().length() - 1.0).abs() < 1e-12);
137 }
138
139 #[test]
140 fn zero_returns_none() {
141 assert!(UnitVec3::try_from_vec(Vec3::ZERO).is_none());
142 }
143
144 #[test]
145 fn perp_basis_orthogonal() {
146 let u = UnitVec3::Z;
147 let (a, b) = u.perp_basis();
148 assert!(a.dot(u).abs() < 1e-12);
149 assert!(b.dot(u).abs() < 1e-12);
150 assert!(a.dot(b).abs() < 1e-12);
151 }
152}