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
65pub trait Vector3Arg<T: Vector3Coordinate> {
103 fn borrow_vec(&self) -> &Vector3<T>;
105}
106
107impl<T: Vector3Coordinate> Vector3Arg<T> for &Vector3<T> {
108 #[inline]
109 fn borrow_vec(&self) -> &Vector3<T> {
110 self
111 }
112}
113
114impl<T: Vector3Coordinate + Copy> Vector3Arg<T> for Vector3<T> {
115 #[inline]
116 fn borrow_vec(&self) -> &Self {
117 self
118 }
119}
120
121#[cfg(any(feature = "std", feature = "libm"))]
122#[allow(clippy::needless_pass_by_value)]
123impl<T: Vector3Coordinate + num_traits::Float> Vector3<T> {
124 #[must_use]
143 #[inline]
144 pub fn fuzzy_equal(self, target: impl Vector3Arg<T>, epsilon: T) -> bool {
145 let target = target.borrow_vec();
146 assert!(epsilon.is_sign_positive());
147 (self.x - target.x).abs() <= epsilon
149 && (self.y - target.y).abs() <= epsilon
150 && (self.z - target.z).abs() <= epsilon
151 }
152
153 #[must_use]
166 #[inline]
167 pub fn lerp(self, target: impl Vector3Arg<T>, alpha: T) -> Self {
168 let target = target.borrow_vec();
169 Self {
170 x: self.x.lerp(target.x, alpha),
171 y: self.y.lerp(target.y, alpha),
172 z: self.z.lerp(target.z, alpha),
173 }
174 }
175
176 #[must_use]
187 #[inline]
188 pub fn magnitude(self) -> T {
189 (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
190 }
191
192 #[must_use]
194 #[inline]
195 pub fn angle(self, target: impl Vector3Arg<T>) -> T {
196 let target = target.borrow_vec();
197 let dot_product = self.dot(target);
198 let magnitude_product = self.magnitude() * target.magnitude();
199 (dot_product / magnitude_product).acos()
200 }
201
202 #[must_use]
204 #[inline]
205 pub fn angle_deg(self, target: impl Vector3Arg<T>) -> T
206 where
207 T: From<f64>,
208 {
209 const COEFF: f64 = 180.0 / core::f64::consts::PI;
210 self.angle(target) * From::from(COEFF)
211 }
212
213 #[inline]
215 pub fn normalize(&mut self) {
216 *self /= self.magnitude();
217 }
218
219 #[must_use]
221 #[inline]
222 pub fn normalized(self) -> Self {
223 self / self.magnitude()
224 }
225
226 #[must_use]
237 #[inline]
238 pub fn distance(self, target: impl Vector3Arg<T>) -> T {
239 let target = target.borrow_vec();
240 (self - *target).magnitude()
241 }
242
243 #[must_use]
245 #[inline]
246 pub fn project(self, on_normal: impl Vector3Arg<T>) -> Self {
247 let on_normal = on_normal.borrow_vec();
248 *on_normal * (self.dot(on_normal) / on_normal.dot(on_normal))
249 }
250
251 #[must_use]
253 #[inline]
254 pub fn reflect(self, normal: impl Vector3Arg<T>) -> Self {
255 let normal = normal.borrow_vec();
256 let two = T::one() + T::one();
257 self - (*normal * (self.dot(normal) * two))
258 }
259
260 #[must_use]
262 #[inline]
263 pub fn inverse(self) -> Self {
264 let one = T::one();
265 Self {
266 x: one / self.x,
267 y: one / self.y,
268 z: one / self.z,
269 }
270 }
271
272 #[must_use]
274 #[inline]
275 pub fn abs(self) -> Self {
276 Self {
277 x: self.x.abs(),
278 y: self.y.abs(),
279 z: self.z.abs(),
280 }
281 }
282
283 #[must_use]
285 #[inline]
286 pub fn ceil(self) -> Self {
287 Self {
288 x: self.x.ceil(),
289 y: self.y.ceil(),
290 z: self.z.ceil(),
291 }
292 }
293
294 #[must_use]
296 #[inline]
297 pub fn floor(self) -> Self {
298 Self {
299 x: self.x.floor(),
300 y: self.y.floor(),
301 z: self.z.floor(),
302 }
303 }
304
305 #[must_use]
307 #[inline]
308 pub fn round(self) -> Self {
309 Self {
310 x: self.x.round(),
311 y: self.y.round(),
312 z: self.z.round(),
313 }
314 }
315
316 #[must_use]
318 #[inline]
319 pub fn clamp(self, min: T, max: T) -> Self {
320 Self {
321 x: clamp(self.x, min, max),
322 y: clamp(self.y, min, max),
323 z: clamp(self.z, min, max),
324 }
325 }
326
327 #[must_use]
329 #[inline]
330 pub fn rotated(self, axis: impl Vector3Arg<T>, angle: T) -> Self {
331 let axis = axis.borrow_vec();
332 let (sin, cos) = angle.sin_cos();
333 let axis_normalized = axis.normalized();
334
335 let term1 = self * cos;
336 let term2 = axis_normalized.cross(self) * sin;
337 let term3_scalar = axis_normalized.dot(self) * (T::one() - cos);
338 let term3 = axis_normalized * term3_scalar;
339
340 term1 + term2 + term3
341 }
342
343 #[must_use]
351 #[inline]
352 pub fn from_spherical(radius: T, polar: T, azimuth: T) -> Self {
353 let (sin_polar, cos_polar) = polar.sin_cos();
354 let (sin_azimuth, cos_azimuth) = azimuth.sin_cos();
355 Self {
356 x: radius * sin_polar * cos_azimuth,
357 y: radius * sin_polar * sin_azimuth,
358 z: radius * cos_polar,
359 }
360 }
361
362 #[cfg(feature = "random")]
364 #[must_use]
365 #[inline]
366 pub fn random() -> Self
367 where
368 rand::distr::StandardUniform: rand::prelude::Distribution<T>,
369 {
370 RNG.with_borrow_mut(|thread| Self {
371 x: thread.random(),
372 y: thread.random(),
373 z: thread.random(),
374 })
375 }
376
377 #[cfg(feature = "random")]
379 #[must_use]
380 #[inline]
381 pub fn random_range(
382 range_x: impl SampleRange<T>,
383 range_y: impl SampleRange<T>,
384 range_z: impl SampleRange<T>,
385 ) -> Self
386 where
387 rand::distr::StandardUniform: rand::prelude::Distribution<T>,
388 T: SampleUniform,
389 {
390 RNG.with_borrow_mut(|thread| Self {
391 x: thread.random_range(range_x),
392 y: thread.random_range(range_y),
393 z: thread.random_range(range_z),
394 })
395 }
396}
397
398#[allow(clippy::needless_pass_by_value)]
399impl<T: Vector3Coordinate> Vector3<T> {
400 pub fn new<U: Into<T>, V: Into<T>, W: Into<T>>(x: U, y: V, z: W) -> Self {
427 let (x, y, z) = (x.into(), y.into(), z.into());
428 Self { x, y, z }
429 }
430
431 #[must_use]
434 #[inline]
435 pub fn dot(&self, target: impl Vector3Arg<T>) -> T {
436 let target = target.borrow_vec();
437 let (x, y, z): (T, T, T) = target.clone().into();
438 self.x.clone() * x + self.y.clone() * y + self.z.clone() * z
439 }
440
441 #[must_use]
444 #[inline]
445 pub fn cross(&self, target: impl Vector3Arg<T>) -> Self {
446 let target = target.borrow_vec();
447 let (x, y, z): (T, T, T) = target.clone().into();
448 Self {
449 x: self.y.clone() * z.clone() - self.z.clone() * y.clone(),
450 y: self.z.clone() * x.clone() - self.x.clone() * z,
451 z: self.x.clone() * y - self.y.clone() * x,
452 }
453 }
454
455 #[must_use]
457 #[inline]
458 pub fn max(&self, target: impl Vector3Arg<T>) -> Self {
459 let target = target.borrow_vec();
460 let x = if self.x > target.x {
461 self.x.clone()
462 } else {
463 target.x.clone()
464 };
465 let y = if self.y > target.y {
466 self.y.clone()
467 } else {
468 target.y.clone()
469 };
470 let z = if self.z > target.z {
471 self.z.clone()
472 } else {
473 target.z.clone()
474 };
475 Self { x, y, z }
476 }
477
478 #[must_use]
480 #[inline]
481 pub fn min(&self, target: impl Vector3Arg<T>) -> Self {
482 let target = target.borrow_vec();
483 let x = if self.x < target.x {
484 self.x.clone()
485 } else {
486 target.x.clone()
487 };
488 let y = if self.y < target.y {
489 self.y.clone()
490 } else {
491 target.y.clone()
492 };
493 let z = if self.z < target.z {
494 self.z.clone()
495 } else {
496 target.z.clone()
497 };
498 Self { x, y, z }
499 }
500
501 pub const fn x(&self) -> &T {
503 &self.x
504 }
505
506 pub const fn y(&self) -> &T {
508 &self.y
509 }
510
511 pub const fn z(&self) -> &T {
513 &self.z
514 }
515}
516
517impl<T: Vector3Coordinate> core::fmt::Display for Vector3<T> {
518 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
519 write!(f, "Vector3({}, {}, {})", self.x, self.y, self.z)
520 }
521}
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526 use core::ops::Sub;
527
528 #[test]
529 fn angle() {
530 let angle = core::f64::consts::PI / 2.0;
531 let calc_angle = Vector3::<f64>::x_axis().angle(Vector3::<f64>::y_axis());
532 assert!(calc_angle.sub(angle) <= f64::EPSILON);
533 }
534
535 #[test]
536 fn create() {
537 let my_vec: Vector3<f64> = Vector3::new(1.3, 0.0, -5.35501);
538 assert!((my_vec.x() - 1.3f64).abs() <= f64::EPSILON);
539 assert!((my_vec.y() - 0.0f64).abs() <= f64::EPSILON);
540 assert!((my_vec.z() - -5.35501f64).abs() <= f64::EPSILON);
541 }
542
543 #[test]
544 fn sum() {
545 let vec1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
546 let vec2 = Vector3::new(5.0, 0.0, -1.0);
547 assert_eq!(vec1 + vec2, Vector3::new(6.0, 2.0, 2.0));
548 }
549
550 #[test]
551 fn normalization() {
552 let mut test_vec: Vector3<f64> = Vector3::new(1.0, 2.3, 100.123);
553 test_vec.normalize();
554 assert_eq!(
555 test_vec,
556 Vector3::new(
557 0.009_984_583_160_766_44,
558 0.022_964_541_269_762_81,
559 0.999_686_419_805_418_3
560 )
561 );
562 assert!((1.0 - test_vec.magnitude()).abs() <= f64::EPSILON);
563 }
564
565 #[test]
566 fn lerp() {
567 let start = Vector3::new(0.0, 0.0, 0.0);
568 let end = Vector3::new(1.0, 2.0, 3.0);
569 let lerp_result = start.lerp(end, 0.75);
570 assert_eq!(lerp_result, Vector3::new(0.75, 1.5, 2.25));
571 }
572
573 #[test]
574 fn dot_product() {
575 let vec1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
576 let vec2 = Vector3::new(5.0, 0.0, -1.0);
577 let dot_result = vec1.dot(vec2);
578 assert!((dot_result - 2.0f64).abs() <= f64::EPSILON);
579 }
580
581 #[test]
582 fn cross_product() {
583 let vec1: Vector3<f64> = Vector3::new(1.0, 0.0, 0.0);
584 let vec2 = Vector3::new(0.0, 1.0, 0.0);
585 let cross_result = vec1.cross(vec2);
586 assert_eq!(cross_result, Vector3::new(0.0, 0.0, 1.0));
587 }
588
589 #[test]
590 fn max_components() {
591 let vec1: Vector3<f64> = Vector3::new(1.0, 5.0, 3.0);
592 let vec2 = Vector3::new(3.0, 2.0, 4.0);
593 let max_result = vec1.max(vec2);
594 assert_eq!(max_result, Vector3::new(3.0, 5.0, 4.0));
595 }
596
597 #[test]
598 fn min_components() {
599 let vec1: Vector3<f64> = Vector3::new(1.0, 5.0, 3.0);
600 let vec2 = Vector3::new(3.0, 2.0, 4.0);
601 let min_result = vec1.min(vec2);
602 assert_eq!(min_result, Vector3::new(1.0, 2.0, 3.0));
603 }
604
605 #[test]
606 fn fuzzy_equality() {
607 let vec1 = Vector3::new(1.0, 2.0, 3.0);
608 let vec2 = Vector3::new(1.01, 1.99, 3.01);
609 let epsilon = 0.02;
610 let fuzzy_equal_result = vec1.fuzzy_equal(vec2, epsilon);
611 assert!(fuzzy_equal_result);
612 }
613
614 #[test]
615 fn distance() {
616 let v1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
617 let v2 = Vector3::new(4.0, 6.0, 8.0);
618 assert!((v1.distance(v2) - (50.0f64).sqrt()).abs() <= f64::EPSILON);
619 }
620
621 #[test]
622 fn project() {
623 let v: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
624 let on_normal = Vector3::new(1.0, 0.0, 0.0);
625 let expected = Vector3::new(1.0, 0.0, 0.0);
626 assert_eq!(v.project(on_normal), expected);
627 }
628
629 #[test]
630 fn reflect() {
631 let v: Vector3<f64> = Vector3::new(1.0, -1.0, 0.0);
632 let normal = Vector3::new(0.0, 1.0, 0.0);
633 let expected = Vector3::new(1.0, 1.0, 0.0);
634 assert_eq!(v.reflect(normal), expected);
635 }
636
637 #[test]
638 fn inverse() {
639 let v: Vector3<f64> = Vector3::new(2.0, 4.0, 8.0);
640 let expected = Vector3::new(0.5, 0.25, 0.125);
641 assert_eq!(v.inverse(), expected);
642 }
643
644 #[test]
645 fn abs() {
646 let v: Vector3<f64> = Vector3::new(-1.0, -2.0, 3.0);
647 let expected = Vector3::new(1.0, 2.0, 3.0);
648 assert_eq!(v.abs(), expected);
649 }
650
651 #[test]
652 fn ceil() {
653 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.0);
654 let expected = Vector3::new(2.0, 3.0, 3.0);
655 assert_eq!(v.ceil(), expected);
656 }
657
658 #[test]
659 fn floor() {
660 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.0);
661 let expected = Vector3::new(1.0, 2.0, 3.0);
662 assert_eq!(v.floor(), expected);
663 }
664
665 #[test]
666 fn round() {
667 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.5);
668 let expected = Vector3::new(1.0, 3.0, 4.0);
669 assert_eq!(v.round(), expected);
670 }
671
672 #[test]
673 fn clamp() {
674 let v = Vector3::new(0.0, 5.0, 10.0);
675 let min = 1.0;
676 let max = 9.0;
677 let expected = Vector3::new(1.0, 5.0, 9.0);
678 assert_eq!(v.clamp(min, max), expected);
679 }
680
681 #[test]
682 fn rotated() {
683 let v = Vector3::new(1.0, 0.0, 0.0);
684 let axis = Vector3::new(0.0, 0.0, 1.0);
685 let angle = core::f64::consts::FRAC_PI_2;
686 let rotated = v.rotated(axis, angle);
687 let expected = Vector3::new(0.0, 1.0, 0.0);
688 assert!(rotated.fuzzy_equal(expected, 1e-15));
689 }
690
691 #[test]
692 fn from_spherical() {
693 let radius = 1.0;
694 let polar = core::f64::consts::FRAC_PI_2;
695 let azimuth = 0.0;
696 let v = Vector3::from_spherical(radius, polar, azimuth);
697 let expected = Vector3::new(1.0, 0.0, 0.0);
698 assert!(v.fuzzy_equal(expected, 1e-15));
699 }
700
701 #[test]
702 fn nan_dont_panic() {
703 let mut vec1: Vector3<f64> = Vector3::default();
704 vec1 /= f64::NAN;
705 }
706
707 #[test]
708 fn readme_example() {
709 type Vector3 = super::Vector3<f64>;
710
711 let v1 = Vector3::new(1, 2.5, 3);
712 let v2 = Vector3::from([9.0, 1.0, 4.0]);
713
714 let v3 = (v1 + v2) - (v1 - v2);
715 let v3 = v3.cross(v2);
716 let v3 = v3 * v3.dot(v1);
717 let v3 = v3 * 10.0 / 3.2;
718 let v3 = v3.normalized() + Vector3::one();
719 let v3 = v3.lerp(Vector3::zero(), 0.25);
720 let v3 = v3.floor() + Vector3::one();
721
722 println!("{v3}");
723 println!("{}", v3.angle(Vector3::z_axis()));
724 println!("{}", v3.fuzzy_equal(Vector3::z_axis(), 2.0));
725 }
726 #[test]
727 fn conversion_box() {
728 let correct = Vector3::new(1, 2, 3);
729 let x = String::from("Vector3(1,2,3)").into_boxed_str();
730 assert_eq!(x.parse::<Vector3<i32>>().unwrap(), correct);
731 }
732 #[test]
733 fn from_slice() {
734 let correct = Vector3::new(1, 2, 3);
735 let arr = [1, 2, 3].as_ref();
736 let x = Vector3::try_from(arr).unwrap();
737 assert_eq!(correct, x);
738 }
739
740 #[test]
741 fn from_box_slice() {
742 let correct = Vector3::new(1, 2, 3);
743 let arr: Box<[i32]> = Box::from([1, 2, 3].as_ref());
744 let x = Vector3::try_from(arr).unwrap();
745 assert_eq!(correct, x);
746 }
747}