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]
108 #[inline]
109 pub fn lerp(&self, target: &Self, alpha: T) -> Self {
110 Self {
111 x: self.x.lerp(target.x, alpha),
112 y: self.y.lerp(target.y, alpha),
113 z: self.z.lerp(target.z, alpha),
114 }
115 }
116
117 #[must_use]
128 #[inline]
129 pub fn magnitude(&self) -> T {
130 (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
131 }
132
133 #[must_use]
135 #[inline]
136 pub fn angle(&self, target: &Self) -> T {
137 let dot_product = self.dot(target);
138 let magnitude_product = self.magnitude() * target.magnitude();
139 (dot_product / magnitude_product).acos()
140 }
141
142 #[must_use]
144 #[inline]
145 pub fn angle_deg(&self, target: &Self) -> T
146 where
147 T: From<f64>,
148 {
149 const COEFF: f64 = 180.0 / core::f64::consts::PI;
150 self.angle(target) * From::from(COEFF)
151 }
152
153 #[inline]
155 pub fn normalize(&mut self) {
156 *self /= self.magnitude();
157 }
158
159 #[must_use]
161 #[inline]
162 pub fn normalized(&self) -> Self {
163 *self / self.magnitude()
164 }
165
166 #[must_use]
177 #[inline]
178 pub fn distance(&self, target: &Self) -> T {
179 (*self - *target).magnitude()
180 }
181
182 #[must_use]
184 #[inline]
185 pub fn project(&self, on_normal: &Self) -> Self {
186 *on_normal * (self.dot(on_normal) / on_normal.dot(on_normal))
187 }
188
189 #[must_use]
191 #[inline]
192 pub fn reflect(&self, normal: &Self) -> Self {
193 let two = T::one() + T::one();
194 *self - (*normal * (self.dot(normal) * two))
195 }
196
197 #[must_use]
199 #[inline]
200 pub fn inverse(&self) -> Self {
201 let one = T::one();
202 Self {
203 x: one / self.x,
204 y: one / self.y,
205 z: one / self.z,
206 }
207 }
208
209 #[must_use]
211 #[inline]
212 pub fn abs(&self) -> Self {
213 Self {
214 x: self.x.abs(),
215 y: self.y.abs(),
216 z: self.z.abs(),
217 }
218 }
219
220 #[must_use]
222 #[inline]
223 pub fn ceil(&self) -> Self {
224 Self {
225 x: self.x.ceil(),
226 y: self.y.ceil(),
227 z: self.z.ceil(),
228 }
229 }
230
231 #[must_use]
233 #[inline]
234 pub fn floor(&self) -> Self {
235 Self {
236 x: self.x.floor(),
237 y: self.y.floor(),
238 z: self.z.floor(),
239 }
240 }
241
242 #[must_use]
244 #[inline]
245 pub fn round(&self) -> Self {
246 Self {
247 x: self.x.round(),
248 y: self.y.round(),
249 z: self.z.round(),
250 }
251 }
252
253 #[must_use]
255 #[inline]
256 pub fn clamp(&self, min: T, max: T) -> Self {
257 Self {
258 x: clamp(self.x, min, max),
259 y: clamp(self.y, min, max),
260 z: clamp(self.z, min, max),
261 }
262 }
263
264 #[must_use]
266 #[inline]
267 pub fn rotated(&self, axis: &Self, angle: T) -> Self {
268 let (sin, cos) = angle.sin_cos();
269 let axis_normalized = axis.normalized();
270
271 let term1 = *self * cos;
272 let term2 = axis_normalized.cross(self) * sin;
273 let term3_scalar = axis_normalized.dot(self) * (T::one() - cos);
274 let term3 = axis_normalized * term3_scalar;
275
276 term1 + term2 + term3
277 }
278
279 #[must_use]
287 #[inline]
288 pub fn from_spherical(radius: T, polar: T, azimuth: T) -> Self {
289 let (sin_polar, cos_polar) = polar.sin_cos();
290 let (sin_azimuth, cos_azimuth) = azimuth.sin_cos();
291 Self {
292 x: radius * sin_polar * cos_azimuth,
293 y: radius * sin_polar * sin_azimuth,
294 z: radius * cos_polar,
295 }
296 }
297
298 #[cfg(feature = "random")]
300 #[must_use]
301 #[inline]
302 pub fn random() -> Self
303 where
304 rand::distr::StandardUniform: rand::prelude::Distribution<T>,
305 {
306 RNG.with_borrow_mut(|thread| Self {
307 x: thread.random(),
308 y: thread.random(),
309 z: thread.random(),
310 })
311 }
312
313 #[cfg(feature = "random")]
315 #[must_use]
316 #[inline]
317 pub fn random_range(
318 range_x: impl SampleRange<T>,
319 range_y: impl SampleRange<T>,
320 range_z: impl SampleRange<T>,
321 ) -> Self
322 where
323 rand::distr::StandardUniform: rand::prelude::Distribution<T>,
324 T: SampleUniform,
325 {
326 RNG.with_borrow_mut(|thread| Self {
327 x: thread.random_range(range_x),
328 y: thread.random_range(range_y),
329 z: thread.random_range(range_z),
330 })
331 }
332}
333
334impl<T: Vector3Coordinate> Vector3<T> {
335 pub fn new<U: Into<T>, V: Into<T>, W: Into<T>>(x: U, y: V, z: W) -> Self {
362 let (x, y, z) = (x.into(), y.into(), z.into());
363 Self { x, y, z }
364 }
365
366 #[must_use]
369 #[inline]
370 pub fn dot(&self, target: &Self) -> T {
371 let (x, y, z): (T, T, T) = target.clone().into();
372 self.x.clone() * x + self.y.clone() * y + self.z.clone() * z
373 }
374
375 #[must_use]
378 #[inline]
379 pub fn cross(&self, target: &Self) -> Self {
380 let (x, y, z): (T, T, T) = target.clone().into();
381 Self {
382 x: self.y.clone() * z.clone() - self.z.clone() * y.clone(),
383 y: self.z.clone() * x.clone() - self.x.clone() * z,
384 z: self.x.clone() * y - self.y.clone() * x,
385 }
386 }
387
388 #[must_use]
390 #[inline]
391 pub fn max(&self, target: &Self) -> Self {
392 let x = if self.x > target.x {
393 self.x.clone()
394 } else {
395 target.x.clone()
396 };
397 let y = if self.y > target.y {
398 self.y.clone()
399 } else {
400 target.y.clone()
401 };
402 let z = if self.z > target.z {
403 self.z.clone()
404 } else {
405 target.z.clone()
406 };
407 Self { x, y, z }
408 }
409
410 #[must_use]
412 #[inline]
413 pub fn min(&self, target: &Self) -> Self {
414 let x = if self.x < target.x {
415 self.x.clone()
416 } else {
417 target.x.clone()
418 };
419 let y = if self.y < target.y {
420 self.y.clone()
421 } else {
422 target.y.clone()
423 };
424 let z = if self.z < target.z {
425 self.z.clone()
426 } else {
427 target.z.clone()
428 };
429 Self { x, y, z }
430 }
431
432 pub const fn x(&self) -> &T {
434 &self.x
435 }
436
437 pub const fn y(&self) -> &T {
439 &self.y
440 }
441
442 pub const fn z(&self) -> &T {
444 &self.z
445 }
446}
447
448impl<T: Vector3Coordinate> core::fmt::Display for Vector3<T> {
449 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
450 write!(f, "Vector3({}, {}, {})", self.x, self.y, self.z)
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457 use core::ops::Sub;
458
459 #[test]
460 fn angle() {
461 let angle = core::f64::consts::PI / 2.0;
462 let calc_angle = Vector3::<f64>::x_axis().angle(&Vector3::<f64>::y_axis());
463 assert!(calc_angle.sub(angle) <= f64::EPSILON);
464 }
465
466 #[test]
467 fn create() {
468 let my_vec: Vector3<f64> = Vector3::new(1.3, 0.0, -5.35501);
469 assert!((my_vec.x() - 1.3f64).abs() <= f64::EPSILON);
470 assert!((my_vec.y() - 0.0f64).abs() <= f64::EPSILON);
471 assert!((my_vec.z() - -5.35501f64).abs() <= f64::EPSILON);
472 }
473
474 #[test]
475 fn sum() {
476 let vec1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
477 let vec2 = Vector3::new(5.0, 0.0, -1.0);
478 assert_eq!(vec1 + vec2, Vector3::new(6.0, 2.0, 2.0));
479 }
480
481 #[test]
482 fn normalization() {
483 let mut test_vec: Vector3<f64> = Vector3::new(1.0, 2.3, 100.123);
484 test_vec.normalize();
485 assert_eq!(
486 test_vec,
487 Vector3::new(
488 0.009_984_583_160_766_44,
489 0.022_964_541_269_762_81,
490 0.999_686_419_805_418_3
491 )
492 );
493 assert!((1.0 - test_vec.magnitude()).abs() <= f64::EPSILON);
494 }
495
496 #[test]
497 fn lerp() {
498 let start = Vector3::new(0.0, 0.0, 0.0);
499 let end = Vector3::new(1.0, 2.0, 3.0);
500 let lerp_result = start.lerp(&end, 0.75);
501 assert_eq!(lerp_result, Vector3::new(0.75, 1.5, 2.25));
502 }
503
504 #[test]
505 fn dot_product() {
506 let vec1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
507 let vec2 = Vector3::new(5.0, 0.0, -1.0);
508 let dot_result = vec1.dot(&vec2);
509 assert!((dot_result - 2.0f64).abs() <= f64::EPSILON);
510 }
511
512 #[test]
513 fn cross_product() {
514 let vec1: Vector3<f64> = Vector3::new(1.0, 0.0, 0.0);
515 let vec2 = Vector3::new(0.0, 1.0, 0.0);
516 let cross_result = vec1.cross(&vec2);
517 assert_eq!(cross_result, Vector3::new(0.0, 0.0, 1.0));
518 }
519
520 #[test]
521 fn max_components() {
522 let vec1: Vector3<f64> = Vector3::new(1.0, 5.0, 3.0);
523 let vec2 = Vector3::new(3.0, 2.0, 4.0);
524 let max_result = vec1.max(&vec2);
525 assert_eq!(max_result, Vector3::new(3.0, 5.0, 4.0));
526 }
527
528 #[test]
529 fn min_components() {
530 let vec1: Vector3<f64> = Vector3::new(1.0, 5.0, 3.0);
531 let vec2 = Vector3::new(3.0, 2.0, 4.0);
532 let min_result = vec1.min(&vec2);
533 assert_eq!(min_result, Vector3::new(1.0, 2.0, 3.0));
534 }
535
536 #[test]
537 fn fuzzy_equality() {
538 let vec1 = Vector3::new(1.0, 2.0, 3.0);
539 let vec2 = Vector3::new(1.01, 1.99, 3.01);
540 let epsilon = 0.02;
541 let fuzzy_equal_result = vec1.fuzzy_equal(&vec2, epsilon);
542 assert!(fuzzy_equal_result);
543 }
544
545 #[test]
546 fn distance() {
547 let v1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
548 let v2 = Vector3::new(4.0, 6.0, 8.0);
549 assert!((v1.distance(&v2) - (50.0f64).sqrt()).abs() <= f64::EPSILON);
550 }
551
552 #[test]
553 fn project() {
554 let v: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
555 let on_normal = Vector3::new(1.0, 0.0, 0.0);
556 let expected = Vector3::new(1.0, 0.0, 0.0);
557 assert_eq!(v.project(&on_normal), expected);
558 }
559
560 #[test]
561 fn reflect() {
562 let v: Vector3<f64> = Vector3::new(1.0, -1.0, 0.0);
563 let normal = Vector3::new(0.0, 1.0, 0.0);
564 let expected = Vector3::new(1.0, 1.0, 0.0);
565 assert_eq!(v.reflect(&normal), expected);
566 }
567
568 #[test]
569 fn inverse() {
570 let v: Vector3<f64> = Vector3::new(2.0, 4.0, 8.0);
571 let expected = Vector3::new(0.5, 0.25, 0.125);
572 assert_eq!(v.inverse(), expected);
573 }
574
575 #[test]
576 fn abs() {
577 let v: Vector3<f64> = Vector3::new(-1.0, -2.0, 3.0);
578 let expected = Vector3::new(1.0, 2.0, 3.0);
579 assert_eq!(v.abs(), expected);
580 }
581
582 #[test]
583 fn ceil() {
584 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.0);
585 let expected = Vector3::new(2.0, 3.0, 3.0);
586 assert_eq!(v.ceil(), expected);
587 }
588
589 #[test]
590 fn floor() {
591 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.0);
592 let expected = Vector3::new(1.0, 2.0, 3.0);
593 assert_eq!(v.floor(), expected);
594 }
595
596 #[test]
597 fn round() {
598 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.5);
599 let expected = Vector3::new(1.0, 3.0, 4.0);
600 assert_eq!(v.round(), expected);
601 }
602
603 #[test]
604 fn clamp() {
605 let v = Vector3::new(0.0, 5.0, 10.0);
606 let min = 1.0;
607 let max = 9.0;
608 let expected = Vector3::new(1.0, 5.0, 9.0);
609 assert_eq!(v.clamp(min, max), expected);
610 }
611
612 #[test]
613 fn rotated() {
614 let v = Vector3::new(1.0, 0.0, 0.0);
615 let axis = Vector3::new(0.0, 0.0, 1.0);
616 let angle = core::f64::consts::FRAC_PI_2;
617 let rotated = v.rotated(&axis, angle);
618 let expected = Vector3::new(0.0, 1.0, 0.0);
619 assert!(rotated.fuzzy_equal(&expected, 1e-15));
620 }
621
622 #[test]
623 fn from_spherical() {
624 let radius = 1.0;
625 let polar = core::f64::consts::FRAC_PI_2;
626 let azimuth = 0.0;
627 let v = Vector3::from_spherical(radius, polar, azimuth);
628 let expected = Vector3::new(1.0, 0.0, 0.0);
629 assert!(v.fuzzy_equal(&expected, 1e-15));
630 }
631
632 #[test]
633 fn nan_dont_panic() {
634 let mut vec1: Vector3<f64> = Vector3::default();
635 vec1 /= f64::NAN;
636 }
637
638 #[test]
639 fn readme_example() {
640 type Vector3 = super::Vector3<f64>;
641
642 let v1 = Vector3::new(1, 2.5, 3);
643 let v2 = Vector3::from([9.0, 1.0, 4.0]);
644
645 let v3 = (v1 + v2) - (v1 - v2);
646 let v3 = v3.cross(&v2);
647 let v3 = v3 * v3.dot(&v1);
648 let v3 = v3 * 10.0 / 3.2;
649 let v3 = v3.normalized();
650 let v3 = v3.lerp(&Vector3::zero(), 0.25);
651 let v3 = v3.floor();
652
653 println!("{v3}");
654 println!("{}", v3.angle(&Vector3::z_axis()));
655 println!("{}", v3.fuzzy_equal(&Vector3::z_axis(), 2.0));
656 }
657 #[test]
658 fn conversion_box() {
659 let correct = Vector3::new(1, 2, 3);
660 let x = String::from("Vector3(1,2,3)").into_boxed_str();
661 assert_eq!(x.parse::<Vector3<i32>>().unwrap(), correct);
662 }
663 #[test]
664 fn from_slice() {
665 let correct = Vector3::new(1, 2, 3);
666 let arr = [1, 2, 3].as_ref();
667 let x = Vector3::try_from(arr).unwrap();
668 assert_eq!(correct, x);
669 }
670
671 #[test]
672 fn from_box_slice() {
673 let correct = Vector3::new(1, 2, 3);
674 let arr: Box<[i32]> = Box::from([1, 2, 3].as_ref());
675 let x = Vector3::try_from(arr).unwrap();
676 assert_eq!(correct, x);
677 }
678}