Skip to main content

vec3_rs/
lib.rs

1//! This crate provides a simple implementation of 3D vectors in Rust. Supports any numeric type trough num-traits.
2
3#![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
29/// Trait representing accepted coordonate kind `T` for `Vector3<T>`.
30pub 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/// Represents a vector in 3D space.
57#[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/// Trait for accepting either a reference to a Vector3 or a Vector3 by value (if it is Copy).
66///
67/// # Examples
68///
69/// `Copy` types (like `f64`) can be passed by value or reference:
70/// ```
71/// use vec3_rs::Vector3;
72/// let v1: Vector3<f64> = Vector3::new(1.0, 0.0, 0.0);
73/// let v2: Vector3<f64> = Vector3::new(0.0, 1.0, 0.0);
74///
75/// // Both work perfectly because `f64` is `Copy`
76/// let _ = v1.dot(v2);
77/// let _ = v1.dot(&v2);
78/// let _ = v1.angle(v2);
79/// let _ = v1.angle(&v2);
80/// ```
81///
82/// Non-`Copy` types can ONLY be passed by reference:
83/// ```compile_fail
84/// use vec3_rs::{Vector3, Vector3Coordinate};
85///
86/// fn test_non_copy_by_value<T: Vector3Coordinate>(v1: Vector3<T>, v2: Vector3<T>) {
87///     // This fails to compile because `T` is not guaranteed to be `Copy`.
88///     // `Vector3Arg` restricts pass-by-value strictly to `Copy` types.
89///     v1.dot(v2);
90/// }
91/// ```
92///
93/// But passing by reference works fine for generic non-`Copy` types:
94/// ```
95/// use vec3_rs::{Vector3, Vector3Coordinate};
96///
97/// fn test_non_copy_by_ref<T: Vector3Coordinate>(v1: Vector3<T>, v2: Vector3<T>) {
98///     // This works because passing by reference never consumes the target!
99///     v1.dot(&v2);
100/// }
101/// ```
102pub trait Vector3Arg<T: Vector3Coordinate> {
103    /// Returns a reference to the underlying Vector3.
104    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    /// Checks if this vector is approximately equal to another vector within a given epsilon.
125    ///
126    /// # Panics
127    ///
128    /// Panics if `epsilon` is negative.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use vec3_rs::Vector3;
134    ///
135    /// let v1 = Vector3::new(0.1, 0.2, 0.3);
136    /// let v2 = Vector3::new(0.101, 0.199, 0.299);
137    ///
138    /// let epsilon = 0.01;
139    /// let is_approx_equal = v1.fuzzy_equal(v2, epsilon);
140    /// assert!(is_approx_equal)
141    /// ```
142    #[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        // unrolled for performance
148        (self.x - target.x).abs() <= epsilon
149            && (self.y - target.y).abs() <= epsilon
150            && (self.z - target.z).abs() <= epsilon
151    }
152
153    /// Linearly interpolates between this vector and another vector by a given ratio.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// type Vector3 = vec3_rs::Vector3<f64>;
159    ///
160    /// let origin = Vector3::zero();
161    /// let x_axis = Vector3::x_axis();
162    /// assert_eq!(origin.lerp(x_axis, 0.5), Vector3::new(0.5, 0, 0))
163    ///
164    /// ```
165    #[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    /// Computes the magnitude (length) of the vector.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// type Vector3 = vec3_rs::Vector3<f64>;
182    ///
183    /// let mut vector3 = Vector3::new(12354, 7324, -7765).normalized();
184    /// assert_eq!(vector3.magnitude(), 1.0);
185    /// ```
186    #[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    /// Computes the angle in **radians** (0..2pi) between this vector and another vector.
193    #[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    /// Computes the angle in **degrees** (0.0..360.0) between this vector and another vector.
203    #[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    /// Scales the vector such that its magnitude becomes 1.
214    #[inline]
215    pub fn normalize(&mut self) {
216        *self /= self.magnitude();
217    }
218
219    /// Copies the vector and scales it such that its magnitude becomes 1.
220    #[must_use]
221    #[inline]
222    pub fn normalized(self) -> Self {
223        self / self.magnitude()
224    }
225
226    /// Computes the distance between this vector and another vector.
227    ///
228    /// # Examples
229    ///
230    /// ```
231    /// type Vector3 = vec3_rs::Vector3<f64>;
232    ///
233    /// let left = -Vector3::x_axis();
234    /// let right = Vector3::x_axis();
235    /// assert_eq!(left.distance(right), 2.0);
236    #[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    /// Projects this vector onto another vector.
244    #[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    /// Reflects this vector off a surface defined by a normal.
252    #[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    /// Inverts the components of the vector.
261    #[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    /// Returns a new vector with the absolute value of each component.
273    #[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    /// Returns a new vector with the ceiling of each component.
284    #[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    /// Returns a new vector with the floor of each component.
295    #[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    /// Returns a new vector with the rounded value of each component.
306    #[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    /// Returns a new vector with each component clamped to a given range.
317    #[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    /// Rotates the vector around an axis by a given angle in radians.
328    #[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    /// Creates a new `Vector3` from spherical coordinates.
344    ///
345    /// # Arguments
346    ///
347    /// * `radius` - The distance from the origin.
348    /// * `polar` - The polar angle (the angle from the z-axis, in radians).
349    /// * `azimuth` - The azimuth angle (the angle from the x-axis in the xy-plane, in radians).
350    #[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    /// Generates a random Vector3 with components in the range [0.0, 1.0).
363    #[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    /// Generates a random Vector3 with components in the given ranges.
378    #[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    /// Creates a new Vector3 with the specified coordinates.
401    ///
402    /// # Examples
403    ///
404    /// ```
405    /// type Vector3 = vec3_rs::Vector3<f64>;
406    ///
407    /// let vector3 = Vector3::new(1.0, 2.0, 3.0);
408    /// let also_ok = Vector3::new(1, 2, 3);
409    /// assert_eq!(vector3, also_ok);
410    ///
411    /// // can also be created from arrays and tuples
412    /// // but no automatic number conversion
413    /// let from_array = Vector3::from([1.0, 2.0, 3.0]);
414    /// let from_tuple = Vector3::from((1.0, 2.0, 3.0));
415    /// assert_eq!(vector3, from_array);
416    /// assert_eq!(vector3, from_tuple);
417    ///
418    /// // conversion is fallible with unknown sized data types
419    /// let slice: &[f64] = [1.0, 2.0, 3.0].as_ref();
420    /// let from_slice = Vector3::try_from(slice).unwrap();
421    /// let from_vec = Vector3::try_from(vec![1.0, 2.0, 3.0]).unwrap();
422    /// assert_eq!(vector3, from_slice);
423    /// assert_eq!(vector3, from_vec);
424    ///
425    /// ```
426    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    /// Computes the dot product between this vector and another vector.
432    /// <https://en.wikipedia.org/wiki/Dot_product>
433    #[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    /// Computes the cross product between this vector and another vector.
442    /// <https://en.wikipedia.org/wiki/Cross_product>
443    #[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    /// Computes the component-wise maximum of this vector and another vector.
456    #[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    /// Computes the component-wise minimum of this vector and another vector.
479    #[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    /// Retrieves the X component of the vector.
502    pub const fn x(&self) -> &T {
503        &self.x
504    }
505
506    /// Retrieves the Y component of the vector.
507    pub const fn y(&self) -> &T {
508        &self.y
509    }
510
511    /// Retrieves the Z component of the vector.
512    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}