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 fn new<U: Into<T>>(x: U, y: U, z: U) -> Self {
326 let (x, y, z) = (x.into(), y.into(), z.into());
327 Self { x, y, z }
328 }
329
330 #[must_use]
332 #[inline]
333 pub fn dot(&self, target: Self) -> T {
334 let (x, y, z): (T, T, T) = target.into();
335 self.x.clone() * x + self.y.clone() * y + self.z.clone() * z
336 }
337
338 #[must_use]
340 #[inline]
341 pub fn cross(&self, target: Self) -> Self {
342 let (x, y, z): (T, T, T) = target.into();
343 Self {
344 x: self.y.clone() * z.clone() - self.z.clone() * y.clone(),
345 y: self.z.clone() * x.clone() - self.x.clone() * z,
346 z: self.x.clone() * y - self.y.clone() * x,
347 }
348 }
349
350 #[must_use]
352 #[inline]
353 pub fn max(&self, target: &Self) -> Self {
354 let x = if self.x > target.x {
355 self.x.clone()
356 } else {
357 target.x.clone()
358 };
359 let y = if self.y > target.y {
360 self.y.clone()
361 } else {
362 target.y.clone()
363 };
364 let z = if self.z > target.z {
365 self.z.clone()
366 } else {
367 target.z.clone()
368 };
369 Self { x, y, z }
370 }
371
372 #[must_use]
374 #[inline]
375 pub fn min(&self, target: &Self) -> Self {
376 let x = if self.x < target.x {
377 self.x.clone()
378 } else {
379 target.x.clone()
380 };
381 let y = if self.y < target.y {
382 self.y.clone()
383 } else {
384 target.y.clone()
385 };
386 let z = if self.z < target.z {
387 self.z.clone()
388 } else {
389 target.z.clone()
390 };
391 Self { x, y, z }
392 }
393
394 pub const fn x(&self) -> &T {
396 &self.x
397 }
398
399 pub const fn y(&self) -> &T {
401 &self.y
402 }
403
404 pub const fn z(&self) -> &T {
406 &self.z
407 }
408}
409
410impl<T: Vector3Coordinate> core::fmt::Display for Vector3<T> {
411 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
412 write!(f, "Vector3({}, {}, {})", self.x, self.y, self.z)
413 }
414}
415
416#[cfg(test)]
417mod tests {
418 use super::*;
419 use core::ops::Sub;
420
421 #[test]
422 fn angle() {
423 let angle = core::f64::consts::PI / 2.0;
424 let calc_angle = Vector3::<f64>::x_axis().angle(Vector3::<f64>::y_axis());
425 assert!(calc_angle.sub(angle) <= f64::EPSILON);
426 }
427
428 #[test]
429 fn create() {
430 let my_vec: Vector3<f64> = Vector3::new(1.3, 0.0, -5.35501);
431 assert!((my_vec.x() - 1.3f64).abs() <= f64::EPSILON);
432 assert!((my_vec.y() - 0.0f64).abs() <= f64::EPSILON);
433 assert!((my_vec.z() - -5.35501f64).abs() <= f64::EPSILON);
434 }
435
436 #[test]
437 fn sum() {
438 let vec1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
439 let vec2 = Vector3::new(5.0, 0.0, -1.0);
440 assert_eq!(vec1 + vec2, Vector3::new(6.0, 2.0, 2.0));
441 }
442
443 #[test]
444 fn normalization() {
445 let mut test_vec: Vector3<f64> = Vector3::new(1.0, 2.3, 100.123);
446 test_vec.normalize();
447 assert_eq!(
448 test_vec,
449 Vector3::new(
450 0.009_984_583_160_766_44,
451 0.022_964_541_269_762_81,
452 0.999_686_419_805_418_3
453 )
454 );
455 assert!((1.0 - test_vec.magnitude()).abs() <= f64::EPSILON);
456 }
457
458 #[test]
459 fn lerp() {
460 let start = Vector3::new(0.0, 0.0, 0.0);
461 let end = Vector3::new(1.0, 2.0, 3.0);
462 let lerp_result = start.lerp(&end, 0.75);
463 assert_eq!(lerp_result, Vector3::new(0.75, 1.5, 2.25));
464 }
465
466 #[test]
467 fn dot_product() {
468 let vec1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
469 let vec2 = Vector3::new(5.0, 0.0, -1.0);
470 let dot_result = vec1.dot(vec2);
471 assert!((dot_result - 2.0f64).abs() <= f64::EPSILON);
472 }
473
474 #[test]
475 fn cross_product() {
476 let vec1: Vector3<f64> = Vector3::new(1.0, 0.0, 0.0);
477 let vec2 = Vector3::new(0.0, 1.0, 0.0);
478 let cross_result = vec1.cross(vec2);
479 assert_eq!(cross_result, Vector3::new(0.0, 0.0, 1.0));
480 }
481
482 #[test]
483 fn max_components() {
484 let vec1: Vector3<f64> = Vector3::new(1.0, 5.0, 3.0);
485 let vec2 = Vector3::new(3.0, 2.0, 4.0);
486 let max_result = vec1.max(&vec2);
487 assert_eq!(max_result, Vector3::new(3.0, 5.0, 4.0));
488 }
489
490 #[test]
491 fn min_components() {
492 let vec1: Vector3<f64> = Vector3::new(1.0, 5.0, 3.0);
493 let vec2 = Vector3::new(3.0, 2.0, 4.0);
494 let min_result = vec1.min(&vec2);
495 assert_eq!(min_result, Vector3::new(1.0, 2.0, 3.0));
496 }
497
498 #[test]
499 fn fuzzy_equality() {
500 let vec1 = Vector3::new(1.0, 2.0, 3.0);
501 let vec2 = Vector3::new(1.01, 1.99, 3.01);
502 let epsilon = 0.02;
503 let fuzzy_equal_result = vec1.fuzzy_equal(&vec2, epsilon);
504 assert!(fuzzy_equal_result);
505 }
506
507 #[test]
508 fn distance() {
509 let v1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
510 let v2 = Vector3::new(4.0, 6.0, 8.0);
511 assert!((v1.distance(v2) - (50.0f64).sqrt()).abs() <= f64::EPSILON);
512 }
513
514 #[test]
515 fn project() {
516 let v: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
517 let on_normal = Vector3::new(1.0, 0.0, 0.0);
518 let expected = Vector3::new(1.0, 0.0, 0.0);
519 assert_eq!(v.project(on_normal), expected);
520 }
521
522 #[test]
523 fn reflect() {
524 let v: Vector3<f64> = Vector3::new(1.0, -1.0, 0.0);
525 let normal = Vector3::new(0.0, 1.0, 0.0);
526 let expected = Vector3::new(1.0, 1.0, 0.0);
527 assert_eq!(v.reflect(normal), expected);
528 }
529
530 #[test]
531 fn inverse() {
532 let v: Vector3<f64> = Vector3::new(2.0, 4.0, 8.0);
533 let expected = Vector3::new(0.5, 0.25, 0.125);
534 assert_eq!(v.inverse(), expected);
535 }
536
537 #[test]
538 fn abs() {
539 let v: Vector3<f64> = Vector3::new(-1.0, -2.0, 3.0);
540 let expected = Vector3::new(1.0, 2.0, 3.0);
541 assert_eq!(v.abs(), expected);
542 }
543
544 #[test]
545 fn ceil() {
546 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.0);
547 let expected = Vector3::new(2.0, 3.0, 3.0);
548 assert_eq!(v.ceil(), expected);
549 }
550
551 #[test]
552 fn floor() {
553 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.0);
554 let expected = Vector3::new(1.0, 2.0, 3.0);
555 assert_eq!(v.floor(), expected);
556 }
557
558 #[test]
559 fn round() {
560 let v: Vector3<f64> = Vector3::new(1.1, 2.9, 3.5);
561 let expected = Vector3::new(1.0, 3.0, 4.0);
562 assert_eq!(v.round(), expected);
563 }
564
565 #[test]
566 fn clamp() {
567 let v = Vector3::new(0.0, 5.0, 10.0);
568 let min = 1.0;
569 let max = 9.0;
570 let expected = Vector3::new(1.0, 5.0, 9.0);
571 assert_eq!(v.clamp(min, max), expected);
572 }
573
574 #[test]
575 fn rotated() {
576 let v = Vector3::new(1.0, 0.0, 0.0);
577 let axis = Vector3::new(0.0, 0.0, 1.0);
578 let angle = core::f64::consts::FRAC_PI_2;
579 let rotated = v.rotated(axis, angle);
580 let expected = Vector3::new(0.0, 1.0, 0.0);
581 assert!(rotated.fuzzy_equal(&expected, 1e-15));
582 }
583
584 #[test]
585 fn from_spherical() {
586 let radius = 1.0;
587 let polar = core::f64::consts::FRAC_PI_2;
588 let azimuth = 0.0;
589 let v = Vector3::from_spherical(radius, polar, azimuth);
590 let expected = Vector3::new(1.0, 0.0, 0.0);
591 assert!(v.fuzzy_equal(&expected, 1e-15));
592 }
593
594 #[test]
595 fn nan_dont_panic() {
596 let mut vec1: Vector3<f64> = Vector3::default();
597 vec1 /= f64::NAN;
598 }
599
600 #[test]
601 fn readme_example() {
602 let mut v1: Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
603 let mut v2: Vector3<f64> = Vector3::new(3.0, 1.0, 2.0);
604
605 let sum = v1 + v2;
607 let difference = v1 - v2;
608 let dot_product = v1.dot(v2);
609 let cross_product = v1.cross(v2);
610
611 let lerp_result = v1.lerp(&v2, 0.5);
613 let angle = v1.angle(v2);
614 let fuzzy_equal = v1.fuzzy_equal(&v2, 0.001);
615
616 println!("Sum: {sum}");
617 println!("Difference: {difference}");
618 println!("Dot product: {dot_product}");
619 println!("Cross product: {cross_product}");
620 println!("Lerp 50%: {lerp_result}");
621 println!("Angle: {angle}");
622 print!("Are they close enough?: {fuzzy_equal}");
623
624 v1.normalize();
625 v2.normalize();
626
627 println!("v1 normalized: {v1}");
628 println!("v2 normalized: {v2}");
629 }
630 #[test]
631 fn conversion_box() {
632 let correct = Vector3::new(1, 2, 3);
633 let x = String::from("Vector3(1,2,3)").into_boxed_str();
634 assert_eq!(x.parse::<Vector3<i32>>().unwrap(), correct);
635 }
636 #[test]
637 fn from_slice() {
638 let correct = Vector3::new(1, 2, 3);
639 let arr = [1, 2, 3].as_ref();
640 let x = Vector3::try_from(arr).unwrap();
641 assert_eq!(correct, x);
642 }
643
644 #[test]
645 fn from_box_slice() {
646 let correct = Vector3::new(1, 2, 3);
647 let arr: Box<[i32]> = Box::from([1, 2, 3].as_ref());
648 let x = Vector3::try_from(arr).unwrap();
649 assert_eq!(correct, x);
650 }
651}