1use std::fmt;
4use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5use std::str::FromStr;
6
7use crate::utilities::math_utilities;
8use crate::utilities::span_helpers::InvariantF64;
9use crate::utilities::{FormatError, SpanStringTokenizer};
10
11#[derive(Clone, Copy, Debug, PartialEq, Default)]
13pub struct Vector3D {
14 pub x: f64,
15 pub y: f64,
16 pub z: f64,
17}
18
19impl Vector3D {
20 #[inline]
21 pub const fn new(x: f64, y: f64, z: f64) -> Self {
22 Self { x, y, z }
23 }
24
25 pub fn parse(s: &str) -> Result<Self, FormatError> {
27 SpanStringTokenizer::with_message(s, "Invalid Vector.").scope(|t| {
28 Ok(Vector3D::new(
29 t.read_double()?,
30 t.read_double()?,
31 t.read_double()?,
32 ))
33 })
34 }
35
36 #[inline]
38 pub fn dot(vector1: Vector3D, vector2: Vector3D) -> f64 {
39 (vector1.x * vector2.x) + (vector1.y * vector2.y) + (vector1.z * vector2.z)
40 }
41
42 #[inline]
44 #[allow(clippy::should_implement_trait)] pub fn add(left: Vector3D, right: Vector3D) -> Vector3D {
46 Vector3D::new(left.x + right.x, left.y + right.y, left.z + right.z)
47 }
48
49 #[inline]
51 pub fn substract(left: Vector3D, right: Vector3D) -> Vector3D {
52 Vector3D::new(left.x - right.x, left.y - right.y, left.z - right.z)
53 }
54
55 #[inline]
57 pub fn multiply(left: Vector3D, right: Vector3D) -> Vector3D {
58 Vector3D::new(left.x * right.x, left.y * right.y, left.z * right.z)
59 }
60
61 #[inline]
63 pub fn multiply_scalar(left: Vector3D, right: f64) -> Vector3D {
64 Vector3D::new(left.x * right, left.y * right, left.z * right)
65 }
66
67 #[inline]
69 pub fn divide(left: Vector3D, right: Vector3D) -> Vector3D {
70 Vector3D::new(left.x / right.x, left.y / right.y, left.z / right.z)
71 }
72
73 #[inline]
75 pub fn divide_scalar(left: Vector3D, right: f64) -> Vector3D {
76 Vector3D::new(left.x / right, left.y / right, left.z / right)
77 }
78
79 #[inline]
82 pub fn abs(&self) -> Vector3D {
83 Vector3D::new(self.x.abs(), self.y.abs(), self.z.abs())
84 }
85
86 #[inline]
88 pub fn clamp(value: Vector3D, min: Vector3D, max: Vector3D) -> Vector3D {
89 Vector3D::min(Vector3D::max(value, min), max)
90 }
91
92 #[inline]
95 pub fn max(left: Vector3D, right: Vector3D) -> Vector3D {
96 Vector3D::new(
97 math_utilities::max(left.x, right.x),
98 math_utilities::max(left.y, right.y),
99 math_utilities::max(left.z, right.z),
100 )
101 }
102
103 #[inline]
106 pub fn min(left: Vector3D, right: Vector3D) -> Vector3D {
107 Vector3D::new(
108 math_utilities::min(left.x, right.x),
109 math_utilities::min(left.y, right.y),
110 math_utilities::min(left.z, right.z),
111 )
112 }
113
114 #[inline]
116 pub fn length(&self) -> f64 {
117 Vector3D::dot(*self, *self).sqrt()
118 }
119
120 #[inline]
122 pub fn normalize(value: Vector3D) -> Vector3D {
123 Vector3D::divide_scalar(value, value.length())
124 }
125
126 #[inline]
128 pub fn distance_squared(value1: Vector3D, value2: Vector3D) -> f64 {
129 let difference = Vector3D::substract(value1, value2);
130 Vector3D::dot(difference, difference)
131 }
132
133 #[inline]
135 pub fn distance(value1: Vector3D, value2: Vector3D) -> f64 {
136 Vector3D::distance_squared(value1, value2).sqrt()
137 }
138
139 #[inline]
141 pub const fn deconstruct(&self) -> (f64, f64, f64) {
142 (self.x, self.y, self.z)
143 }
144}
145
146impl From<Vector3D> for (f64, f64, f64) {
147 #[inline]
148 fn from(v: Vector3D) -> Self {
149 (v.x, v.y, v.z)
150 }
151}
152
153impl Add for Vector3D {
154 type Output = Vector3D;
155 #[inline]
156 fn add(self, right: Vector3D) -> Vector3D {
157 Vector3D::add(self, right)
158 }
159}
160
161impl Sub for Vector3D {
162 type Output = Vector3D;
163 #[inline]
164 fn sub(self, right: Vector3D) -> Vector3D {
165 Vector3D::substract(self, right)
166 }
167}
168
169impl Neg for Vector3D {
170 type Output = Vector3D;
171 #[inline]
172 fn neg(self) -> Vector3D {
173 Vector3D::new(-self.x, -self.y, -self.z)
174 }
175}
176
177impl Mul<f64> for Vector3D {
178 type Output = Vector3D;
179 #[inline]
180 fn mul(self, right: f64) -> Vector3D {
181 Vector3D::multiply_scalar(self, right)
182 }
183}
184
185impl AddAssign for Vector3D {
186 #[inline]
187 fn add_assign(&mut self, right: Vector3D) {
188 *self = *self + right;
189 }
190}
191
192impl SubAssign for Vector3D {
193 #[inline]
194 fn sub_assign(&mut self, right: Vector3D) {
195 *self = *self - right;
196 }
197}
198
199impl MulAssign<f64> for Vector3D {
200 #[inline]
201 fn mul_assign(&mut self, right: f64) {
202 *self = *self * right;
203 }
204}
205
206impl FromStr for Vector3D {
207 type Err = FormatError;
208 #[inline]
209 fn from_str(s: &str) -> Result<Self, FormatError> {
210 Vector3D::parse(s)
211 }
212}
213
214impl fmt::Display for Vector3D {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 write!(
217 f,
218 "Vector3D {{ X = {}, Y = {}, Z = {} }}",
219 InvariantF64(self.x),
220 InvariantF64(self.y),
221 InvariantF64(self.z)
222 )
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
230
231 #[test]
232 fn parse_and_display() {
233 assert_eq!(
234 Vector3D::parse("1, 2.5 -3").unwrap(),
235 Vector3D::new(1.0, 2.5, -3.0)
236 );
237 assert_eq!(
238 Vector3D::parse("1,2").unwrap_err().message(),
239 "Invalid Vector."
240 );
241 assert_eq!(
242 Vector3D::new(1.0, 2.5, -3.0).to_string(),
243 "Vector3D { X = 1, Y = 2.5, Z = -3 }"
244 );
245 }
246
247 #[test]
248 fn arithmetic() {
249 let a = Vector3D::new(1.0, 2.0, 3.0);
250 let b = Vector3D::new(4.0, 5.0, 6.0);
251 assert_eq!(a + b, Vector3D::new(5.0, 7.0, 9.0));
252 assert_eq!(b - a, Vector3D::new(3.0, 3.0, 3.0));
253 assert_eq!(-a, Vector3D::new(-1.0, -2.0, -3.0));
254 assert_eq!(a * 2.0, Vector3D::new(2.0, 4.0, 6.0));
255 assert_eq!(Vector3D::dot(a, b), 32.0);
256 assert_eq!(Vector3D::multiply(a, b), Vector3D::new(4.0, 10.0, 18.0));
257 assert_eq!(Vector3D::divide(b, a), Vector3D::new(4.0, 2.5, 2.0));
258 assert_eq!(Vector3D::new(2.0, 3.0, 6.0).length(), 7.0);
259 assert_eq!(
260 Vector3D::normalize(Vector3D::new(0.0, 0.0, 5.0)),
261 Vector3D::new(0.0, 0.0, 1.0)
262 );
263 assert_eq!(Vector3D::distance(a, Vector3D::new(1.0, 2.0, 5.0)), 2.0);
264 assert_eq!(
265 Vector3D::clamp(
266 Vector3D::new(-1.0, 0.5, 9.0),
267 Vector3D::default(),
268 Vector3D::new(1.0, 1.0, 1.0)
269 ),
270 Vector3D::new(0.0, 0.5, 1.0)
271 );
272 assert_eq!(Vector3D::new(-1.0, 2.0, -3.0).abs(), a);
273 }
274}