1#![cfg_attr(not(feature = "std"), no_std)]
4
5mod consts;
6mod convert;
7#[cfg(any(feature = "std", feature = "libm"))]
8mod float_lerp;
9mod ops;
10mod ops_scalar;
11
12#[cfg(any(feature = "std", feature = "libm"))]
13use float_lerp::Lerp;
14#[cfg(any(feature = "std", feature = "libm"))]
15use num_traits::clamp;
16#[cfg(feature = "random")]
17use rand::{
18 RngExt,
19 distr::uniform::{SampleRange, SampleUniform},
20 make_rng,
21 rngs::SmallRng,
22};
23
24#[cfg(feature = "random")]
25thread_local! {
26 static RNG: std::cell::RefCell<SmallRng> = std::cell::RefCell::new(make_rng());
27}
28
29pub trait Vector3Coordinate:
31 num_traits::Num
32 + num_traits::ToPrimitive
33 + PartialOrd
34 + core::fmt::Display
35 + core::ops::AddAssign
36 + core::ops::SubAssign
37 + core::ops::MulAssign
38 + core::ops::DivAssign
39 + Clone
40{
41}
42
43impl<T> Vector3Coordinate for T where
44 T: num_traits::Num
45 + num_traits::ToPrimitive
46 + PartialOrd
47 + core::fmt::Display
48 + core::ops::AddAssign
49 + core::ops::SubAssign
50 + core::ops::MulAssign
51 + core::ops::DivAssign
52 + Clone
53{
54}
55
56#[derive(Debug, PartialEq, Eq, Default, Clone, Copy, Hash)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct Vector3<T: Vector3Coordinate> {
60 x: T,
61 y: T,
62 z: T,
63}
64
65#[cfg(any(feature = "std", feature = "libm"))]
66impl<T: Vector3Coordinate + num_traits::Float> Vector3<T> {
67 #[must_use]
86 #[inline]
87 pub fn fuzzy_equal(&self, target: &Self, epsilon: T) -> bool {
88 assert!(epsilon.is_sign_positive());
89 (self.x - target.x).abs() <= epsilon
91 && (self.y - target.y).abs() <= epsilon
92 && (self.z - target.z).abs() <= epsilon
93 }
94
95 #[must_use]
97 #[inline]
98 pub fn lerp(&self, target: &Self, alpha: T) -> Self {
99 Self {
100 x: self.x.lerp(target.x, alpha),
101 y: self.y.lerp(target.y, alpha),
102 z: self.z.lerp(target.z, alpha),
103 }
104 }
105
106 #[must_use]
108 #[inline]
109 pub fn magnitude(&self) -> T {
110 (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
111 }
112
113 #[must_use]
115 #[inline]
116 pub fn angle(&self, target: Self) -> T {
117 let dot_product = self.dot(target);
118 let magnitude_product = self.magnitude() * target.magnitude();
119 (dot_product / magnitude_product).acos()
120 }
121
122 #[must_use]
124 #[inline]
125 pub fn angle_deg(&self, target: Self) -> T
126 where
127 T: From<f64>,
128 {
129 const COEFF: f64 = 180.0 / core::f64::consts::PI;
130 self.angle(target) * From::from(COEFF)
131 }
132
133 #[inline]
135 pub fn normalize(&mut self) {
136 *self /= self.magnitude();
137 }
138
139 #[must_use]
141 #[inline]
142 pub fn distance(&self, target: Self) -> T {
143 (*self - target).magnitude()
144 }
145
146 #[must_use]
148 #[inline]
149 pub fn project(&self, on_normal: Self) -> Self {
150 on_normal * (self.dot(on_normal) / on_normal.dot(on_normal))
151 }
152
153 #[must_use]
155 #[inline]
156 pub fn reflect(&self, normal: Self) -> Self {
157 let two = T::one() + T::one();
158 *self - (normal * (self.dot(normal) * two))
159 }
160
161 #[must_use]
163 #[inline]
164 pub fn inverse(&self) -> Self {
165 let one = T::one();
166 Self {
167 x: one / self.x,
168 y: one / self.y,
169 z: one / self.z,
170 }
171 }
172
173 #[must_use]
175 #[inline]
176 pub fn abs(&self) -> Self {
177 Self {
178 x: self.x.abs(),
179 y: self.y.abs(),
180 z: self.z.abs(),
181 }
182 }
183
184 #[must_use]
186 #[inline]
187 pub fn ceil(&self) -> Self {
188 Self {
189 x: self.x.ceil(),
190 y: self.y.ceil(),
191 z: self.z.ceil(),
192 }
193 }
194
195 #[must_use]
197 #[inline]
198 pub fn floor(&self) -> Self {
199 Self {
200 x: self.x.floor(),
201 y: self.y.floor(),
202 z: self.z.floor(),
203 }
204 }
205
206 #[must_use]
208 #[inline]
209 pub fn round(&self) -> Self {
210 Self {
211 x: self.x.round(),
212 y: self.y.round(),
213 z: self.z.round(),
214 }
215 }
216
217 #[must_use]
219 #[inline]
220 pub fn clamp(&self, min: T, max: T) -> Self {
221 Self {
222 x: clamp(self.x, min, max),
223 y: clamp(self.y, min, max),
224 z: clamp(self.z, min, max),
225 }
226 }
227
228 #[must_use]
230 #[inline]
231 pub fn rotated(&self, axis: Self, angle: T) -> Self {
232 let (sin, cos) = angle.sin_cos();
233 let axis_normalized = axis / axis.magnitude();
234
235 let term1 = *self * cos;
236 let term2 = axis_normalized.cross(*self) * sin;
237 let term3_scalar = axis_normalized.dot(*self) * (T::one() - cos);
238 let term3 = axis_normalized * term3_scalar;
239
240 term1 + term2 + term3
241 }
242
243 #[must_use]
251 #[inline]
252 pub fn from_spherical(radius: T, polar: T, azimuth: T) -> Self {
253 let (sin_polar, cos_polar) = polar.sin_cos();
254 let (sin_azimuth, cos_azimuth) = azimuth.sin_cos();
255 Self {
256 x: radius * sin_polar * cos_azimuth,
257 y: radius * sin_polar * sin_azimuth,
258 z: radius * cos_polar,
259 }
260 }
261
262 #[cfg(feature = "random")]
264 #[must_use]
265 #[inline]
266 pub fn random() -> Self
267 where
268 rand::distr::StandardUniform: rand::prelude::Distribution<T>,
269 {
270 RNG.with_borrow_mut(|thread| Self {
271 x: thread.random(),
272 y: thread.random(),
273 z: thread.random(),
274 })
275 }
276
277 #[cfg(feature = "random")]
279 #[must_use]
280 #[inline]
281 pub fn random_range(
282 range_x: impl SampleRange<T>,
283 range_y: impl SampleRange<T>,
284 range_z: impl SampleRange<T>,
285 ) -> Self
286 where
287 rand::distr::StandardUniform: rand::prelude::Distribution<T>,
288 T: SampleUniform,
289 {
290 RNG.with_borrow_mut(|thread| Self {
291 x: thread.random_range(range_x),
292 y: thread.random_range(range_y),
293 z: thread.random_range(range_z),
294 })
295 }
296}
297
298impl<T: Vector3Coordinate> Vector3<T> {
299 pub const fn new(x: T, y: T, z: T) -> Self {
309 Self { x, y, z }
310 }
311
312 #[must_use]
314 #[inline]
315 pub fn dot(&self, target: Self) -> T {
316 let (x, y, z): (T, T, T) = target.into();
317 self.x.clone() * x + self.y.clone() * y + self.z.clone() * z
318 }
319
320 #[must_use]
322 #[inline]
323 pub fn cross(&self, target: Self) -> Self {
324 let (x, y, z): (T, T, T) = target.into();
325 Self {
326 x: self.y.clone() * z.clone() - self.z.clone() * y.clone(),
327 y: self.z.clone() * x.clone() - self.x.clone() * z,
328 z: self.x.clone() * y - self.y.clone() * x,
329 }
330 }
331
332 #[must_use]
334 #[inline]
335 pub fn max(&self, target: &Self) -> Self {
336 let x = if self.x > target.x {
337 self.x.clone()
338 } else {
339 target.x.clone()
340 };
341 let y = if self.y > target.y {
342 self.y.clone()
343 } else {
344 target.y.clone()
345 };
346 let z = if self.z > target.z {
347 self.z.clone()
348 } else {
349 target.z.clone()
350 };
351 Self { x, y, z }
352 }
353
354 #[must_use]
356 #[inline]
357 pub fn min(&self, target: &Self) -> Self {
358 let x = if self.x < target.x {
359 self.x.clone()
360 } else {
361 target.x.clone()
362 };
363 let y = if self.y < target.y {
364 self.y.clone()
365 } else {
366 target.y.clone()
367 };
368 let z = if self.z < target.z {
369 self.z.clone()
370 } else {
371 target.z.clone()
372 };
373 Self { x, y, z }
374 }
375
376 pub const fn x(&self) -> &T {
378 &self.x
379 }
380
381 pub const fn y(&self) -> &T {
383 &self.y
384 }
385
386 pub const fn z(&self) -> &T {
388 &self.z
389 }
390}
391
392impl<T: Vector3Coordinate> core::fmt::Display for Vector3<T> {
393 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
394 write!(f, "Vector3({}, {}, {})", self.x, self.y, self.z)
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use core::ops::Sub;
402
403 #[test]
404 fn angle() {
405 let angle = core::f64::consts::PI / 2.0;
406 let calc_angle = Vector3::<f64>::x_axis().angle(Vector3::<f64>::y_axis());
407 assert!(calc_angle.sub(angle) <= f64::EPSILON);
408 }
409
410 #[test]
411 fn create() {
412 let my_vec: Vector3<f64> = Vector3::new(1.3, 0.0, -5.35501);
413 assert!((my_vec.x() - 1.3f64).abs() <= f64::EPSILON);
414 assert!((my_vec.y() - 0.0f64).abs() <= f64::EPSILON);
415 assert!((my_vec.z() - -5.35501f64).abs() <= f64::EPSILON);
416 }
417
418 #[test]
419 fn sum() {
420 let vec1 = Vector3::new(1.0, 2.0, 3.0);
421 let vec2 = Vector3::new(5.0, 0.0, -1.0);
422 assert_eq!(vec1 + vec2, Vector3::new(6.0, 2.0, 2.0));
423 }
424
425 #[test]
426 fn normalization() {
427 let mut test_vec: Vector3<f64> = Vector3::new(1.0, 2.3, 100.123);
428 test_vec.normalize();
429 assert_eq!(
430 test_vec,
431 Vector3::new(
432 0.009_984_583_160_766_44,
433 0.022_964_541_269_762_81,
434 0.999_686_419_805_418_3
435 )
436 );
437 assert!((1.0 - test_vec.magnitude()).abs() <= f64::EPSILON);
438 }
439
440 #[test]
441 fn lerp() {
442 let start = Vector3::new(0.0, 0.0, 0.0);
443 let end = Vector3::new(1.0, 2.0, 3.0);
444 let lerp_result = start.lerp(&end, 0.75);
445 assert_eq!(lerp_result, Vector3::new(0.75, 1.5, 2.25));
446 }
447
448 #[test]
449 fn dot_product() {
450 let vec1 = Vector3::new(1.0, 2.0, 3.0);
451 let vec2 = Vector3::new(5.0, 0.0, -1.0);
452 let dot_result = vec1.dot(vec2);
453 assert!((dot_result - 2.0f64).abs() <= f64::EPSILON);
454 }
455
456 #[test]
457 fn cross_product() {
458 let vec1 = Vector3::new(1.0, 0.0, 0.0);
459 let vec2 = Vector3::new(0.0, 1.0, 0.0);
460 let cross_result = vec1.cross(vec2);
461 assert_eq!(cross_result, Vector3::new(0.0, 0.0, 1.0));
462 }
463
464 #[test]
465 fn max_components() {
466 let vec1 = Vector3::new(1.0, 5.0, 3.0);
467 let vec2 = Vector3::new(3.0, 2.0, 4.0);
468 let max_result = vec1.max(&vec2);
469 assert_eq!(max_result, Vector3::new(3.0, 5.0, 4.0));
470 }
471
472 #[test]
473 fn min_components() {
474 let vec1 = Vector3::new(1.0, 5.0, 3.0);
475 let vec2 = Vector3::new(3.0, 2.0, 4.0);
476 let min_result = vec1.min(&vec2);
477 assert_eq!(min_result, Vector3::new(1.0, 2.0, 3.0));
478 }
479
480 #[test]
481 fn fuzzy_equality() {
482 let vec1 = Vector3::new(1.0, 2.0, 3.0);
483 let vec2 = Vector3::new(1.01, 1.99, 3.01);
484 let epsilon = 0.02;
485 let fuzzy_equal_result = vec1.fuzzy_equal(&vec2, epsilon);
486 assert!(fuzzy_equal_result);
487 }
488
489 #[test]
490 fn distance() {
491 let v1 = Vector3::new(1.0, 2.0, 3.0);
492 let v2 = Vector3::new(4.0, 6.0, 8.0);
493 assert!((v1.distance(v2) - (50.0f64).sqrt()).abs() <= f64::EPSILON);
494 }
495
496 #[test]
497 fn project() {
498 let v = Vector3::new(1.0, 2.0, 3.0);
499 let on_normal = Vector3::new(1.0, 0.0, 0.0);
500 let expected = Vector3::new(1.0, 0.0, 0.0);
501 assert_eq!(v.project(on_normal), expected);
502 }
503
504 #[test]
505 fn reflect() {
506 let v = Vector3::new(1.0, -1.0, 0.0);
507 let normal = Vector3::new(0.0, 1.0, 0.0);
508 let expected = Vector3::new(1.0, 1.0, 0.0);
509 assert_eq!(v.reflect(normal), expected);
510 }
511
512 #[test]
513 fn inverse() {
514 let v = Vector3::new(2.0, 4.0, 8.0);
515 let expected = Vector3::new(0.5, 0.25, 0.125);
516 assert_eq!(v.inverse(), expected);
517 }
518
519 #[test]
520 fn abs() {
521 let v = Vector3::new(-1.0, -2.0, 3.0);
522 let expected = Vector3::new(1.0, 2.0, 3.0);
523 assert_eq!(v.abs(), expected);
524 }
525
526 #[test]
527 fn ceil() {
528 let v = Vector3::new(1.1, 2.9, 3.0);
529 let expected = Vector3::new(2.0, 3.0, 3.0);
530 assert_eq!(v.ceil(), expected);
531 }
532
533 #[test]
534 fn floor() {
535 let v = Vector3::new(1.1, 2.9, 3.0);
536 let expected = Vector3::new(1.0, 2.0, 3.0);
537 assert_eq!(v.floor(), expected);
538 }
539
540 #[test]
541 fn round() {
542 let v = Vector3::new(1.1, 2.9, 3.5);
543 let expected = Vector3::new(1.0, 3.0, 4.0);
544 assert_eq!(v.round(), expected);
545 }
546
547 #[test]
548 fn clamp() {
549 let v = Vector3::new(0.0, 5.0, 10.0);
550 let min = 1.0;
551 let max = 9.0;
552 let expected = Vector3::new(1.0, 5.0, 9.0);
553 assert_eq!(v.clamp(min, max), expected);
554 }
555
556 #[test]
557 fn rotated() {
558 let v = Vector3::new(1.0, 0.0, 0.0);
559 let axis = Vector3::new(0.0, 0.0, 1.0);
560 let angle = core::f64::consts::FRAC_PI_2;
561 let rotated = v.rotated(axis, angle);
562 let expected = Vector3::new(0.0, 1.0, 0.0);
563 assert!(rotated.fuzzy_equal(&expected, 1e-15));
564 }
565
566 #[test]
567 fn from_spherical() {
568 let radius = 1.0;
569 let polar = core::f64::consts::FRAC_PI_2;
570 let azimuth = 0.0;
571 let v = Vector3::from_spherical(radius, polar, azimuth);
572 let expected = Vector3::new(1.0, 0.0, 0.0);
573 assert!(v.fuzzy_equal(&expected, 1e-15));
574 }
575
576 #[test]
577 fn nan_dont_panic() {
578 let mut vec1: Vector3<f64> = Vector3::default();
579 vec1 /= f64::NAN;
580 }
581
582 #[test]
583 fn readme_example() {
584 let mut v1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
585 let mut v2: Vector3<f64> = Vector3::new(3.0, 1.0, 2.0);
586
587 let sum = v1 + v2;
589 let difference = v1 - v2;
590 let dot_product = v1.dot(v2);
591 let cross_product = v1.cross(v2);
592
593 let lerp_result = v1.lerp(&v2, 0.5);
595 let angle = v1.angle(v2);
596 let fuzzy_equal = v1.fuzzy_equal(&v2, 0.001);
597
598 println!("Sum: {sum}");
599 println!("Difference: {difference}");
600 println!("Dot product: {dot_product}");
601 println!("Cross product: {cross_product}");
602 println!("Lerp 50%: {lerp_result}");
603 println!("Angle: {angle}");
604 print!("Are they close enough?: {fuzzy_equal}");
605
606 v1.normalize();
607 v2.normalize();
608
609 println!("v1 normalized: {v1}");
610 println!("v2 normalized: {v2}");
611 }
612 #[test]
613 fn conversion_box() {
614 let correct = Vector3::new(1, 2, 3);
615 let x = String::from("Vector3(1,2,3)").into_boxed_str();
616 assert_eq!(x.parse::<Vector3<i32>>().unwrap(), correct);
617 }
618 #[test]
619 fn from_slice() {
620 let correct = Vector3::new(1, 2, 3);
621 let arr = [1, 2, 3].as_ref();
622 let x = Vector3::try_from(arr).unwrap();
623 assert_eq!(correct, x);
624 }
625
626 #[test]
627 fn from_box_slice() {
628 let correct = Vector3::new(1, 2, 3);
629 let arr: Box<[i32]> = Box::from([1, 2, 3].as_ref());
630 let x = Vector3::try_from(arr).unwrap();
631 assert_eq!(correct, x);
632 }
633}