outram_foam_basic_lib/primitives/
vector.rs1use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
28
29#[derive(Debug, Clone, Copy, PartialEq, Default)]
32pub struct Vector3 {
33 pub x: f64,
34 pub y: f64,
35 pub z: f64,
36}
37
38impl Vector3 {
39 pub const ZERO: Self = Self {
41 x: 0.0,
42 y: 0.0,
43 z: 0.0,
44 };
45 pub const ONE: Self = Self {
47 x: 1.0,
48 y: 1.0,
49 z: 1.0,
50 };
51 pub const X: Self = Self {
53 x: 1.0,
54 y: 0.0,
55 z: 0.0,
56 };
57 pub const Y: Self = Self {
59 x: 0.0,
60 y: 1.0,
61 z: 0.0,
62 };
63 pub const Z: Self = Self {
65 x: 0.0,
66 y: 0.0,
67 z: 1.0,
68 };
69
70 #[inline]
72 pub fn new(x: f64, y: f64, z: f64) -> Self {
73 Self { x, y, z }
74 }
75
76 #[inline]
78 pub fn mag_sqr(self) -> f64 {
79 self.x * self.x + self.y * self.y + self.z * self.z
80 }
81
82 #[inline]
84 pub fn mag(self) -> f64 {
85 self.mag_sqr().sqrt()
86 }
87
88 #[inline]
90 pub fn dist_sqr(self, other: Self) -> f64 {
91 (other - self).mag_sqr()
92 }
93
94 #[inline]
96 pub fn dist(self, other: Self) -> f64 {
97 self.dist_sqr(other).sqrt()
98 }
99
100 #[inline]
102 pub fn dot(self, other: Self) -> f64 {
103 self.x * other.x + self.y * other.y + self.z * other.z
104 }
105
106 #[inline]
108 pub fn cross(self, other: Self) -> Self {
109 Self {
110 x: self.y * other.z - self.z * other.y,
111 y: self.z * other.x - self.x * other.z,
112 z: self.x * other.y - self.y * other.x,
113 }
114 }
115
116 pub fn normalise(self, tol: f64) -> Self {
118 let s = self.mag();
119 if s < tol {
120 Self::ZERO
121 } else {
122 self / s
123 }
124 }
125
126 #[inline]
128 pub fn remove_collinear(self, unit_vec: Self) -> Self {
129 self - self.dot(unit_vec) * unit_vec
130 }
131
132 #[inline]
134 pub fn lerp(a: Self, b: Self, t: f64) -> Self {
135 let ot = 1.0 - t;
136 Self {
137 x: ot * a.x + t * b.x,
138 y: ot * a.y + t * b.y,
139 z: ot * a.z + t * b.z,
140 }
141 }
142}
143
144impl Neg for Vector3 {
147 type Output = Self;
148 #[inline]
149 fn neg(self) -> Self {
150 Self {
151 x: -self.x,
152 y: -self.y,
153 z: -self.z,
154 }
155 }
156}
157
158impl Add for Vector3 {
159 type Output = Self;
160 #[inline]
161 fn add(self, r: Self) -> Self {
162 Self {
163 x: self.x + r.x,
164 y: self.y + r.y,
165 z: self.z + r.z,
166 }
167 }
168}
169
170impl Sub for Vector3 {
171 type Output = Self;
172 #[inline]
173 fn sub(self, r: Self) -> Self {
174 Self {
175 x: self.x - r.x,
176 y: self.y - r.y,
177 z: self.z - r.z,
178 }
179 }
180}
181
182impl Mul<f64> for Vector3 {
183 type Output = Self;
184 #[inline]
185 fn mul(self, s: f64) -> Self {
186 Self {
187 x: self.x * s,
188 y: self.y * s,
189 z: self.z * s,
190 }
191 }
192}
193
194impl Mul<Vector3> for f64 {
195 type Output = Vector3;
196 #[inline]
197 fn mul(self, v: Vector3) -> Vector3 {
198 v * self
199 }
200}
201
202impl Div<f64> for Vector3 {
203 type Output = Self;
204 #[inline]
205 fn div(self, s: f64) -> Self {
206 Self {
207 x: self.x / s,
208 y: self.y / s,
209 z: self.z / s,
210 }
211 }
212}
213
214impl AddAssign for Vector3 {
215 #[inline]
216 fn add_assign(&mut self, r: Self) {
217 *self = *self + r;
218 }
219}
220
221impl SubAssign for Vector3 {
222 #[inline]
223 fn sub_assign(&mut self, r: Self) {
224 *self = *self - r;
225 }
226}
227
228impl MulAssign<f64> for Vector3 {
229 #[inline]
230 fn mul_assign(&mut self, s: f64) {
231 *self = *self * s;
232 }
233}
234
235impl DivAssign<f64> for Vector3 {
236 #[inline]
237 fn div_assign(&mut self, s: f64) {
238 *self = *self / s;
239 }
240}
241
242#[inline]
246pub fn mag_sqr(v: Vector3) -> f64 {
247 v.mag_sqr()
248}
249
250#[inline]
252pub fn mag(v: Vector3) -> f64 {
253 v.mag()
254}
255
256#[inline]
258pub fn dot(a: Vector3, b: Vector3) -> f64 {
259 a.dot(b)
260}
261
262#[inline]
264pub fn cross(a: Vector3, b: Vector3) -> Vector3 {
265 a.cross(b)
266}
267
268#[inline]
270pub fn lerp(a: Vector3, b: Vector3, t: f64) -> Vector3 {
271 Vector3::lerp(a, b, t)
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn dot_and_cross() {
280 let x = Vector3::X;
281 let y = Vector3::Y;
282 let z = Vector3::Z;
283 assert_eq!(dot(x, x), 1.0);
284 assert_eq!(dot(x, y), 0.0);
285 assert_eq!(cross(x, y), z);
286 assert_eq!(cross(y, z), x);
287 assert_eq!(cross(z, x), y);
288 }
289
290 #[test]
291 fn cross_orthogonal_to_both_inputs() {
292 let a = Vector3::new(1.0, 2.0, 3.0);
294 let b = Vector3::new(4.0, 5.0, 6.0);
295 let c = cross(a, b);
296 assert!(dot(c, a).abs() < 1e-14, "cross(a,b)·a = {}", dot(c, a));
297 assert!(dot(c, b).abs() < 1e-14, "cross(a,b)·b = {}", dot(c, b));
298 }
299
300 #[test]
301 fn mag_and_normalise() {
302 let v = Vector3::new(3.0, 4.0, 0.0);
303 assert_eq!(v.mag(), 5.0);
304 let n = v.normalise(1e-15);
305 assert!((n.mag() - 1.0).abs() < 1e-14);
306 }
307
308 #[test]
309 fn lerp() {
310 let a = Vector3::new(0.0, 0.0, 0.0);
311 let b = Vector3::new(2.0, 4.0, 6.0);
312 let m = Vector3::lerp(a, b, 0.5);
313 assert_eq!(m, Vector3::new(1.0, 2.0, 3.0));
314 }
315
316 #[test]
317 fn remove_collinear() {
318 let v = Vector3::new(3.0, 4.0, 0.0);
319 let n = v.normalise(1e-15);
320 let r = v.remove_collinear(n);
321 assert!(r.mag() < 1e-14);
322 }
323}