coordinates/traits.rs
1use num_traits::{Float, Num};
2
3pub trait Positional<T: Float>
4where
5 Self: Magnitude<T> + Dot<T> + Copy,
6{
7 /// Angle between two points in space.
8 ///
9 /// # Returns
10 ///
11 /// The result is a positive value (in radians) aside from if
12 /// `self.magnitude() == 0` or `other.magnitude() == 0` where it will return
13 /// NaN (acos(infinity) is undefined)
14 ///
15 /// # Examples
16 ///
17 /// ## In 2D
18 /// ```
19 /// # use coordinates::prelude::*;
20 /// let right = Vector2::<f64>::RIGHT;
21 /// let up = Vector2::<f64>::UP;
22 ///
23 /// assert!((right.angle_to(&up) - std::f64::consts::FRAC_PI_2).abs() < std::f64::EPSILON);
24 /// ```
25 ///
26 /// ## In 3D
27 /// ```
28 /// # use coordinates::prelude::*;
29 /// let right= Vector3::<f64>::RIGHT;
30 /// let up = Vector3::<f64>::UP;
31 ///
32 /// assert!((right.angle_to(&up) - std::f64::consts::FRAC_PI_2).abs() < std::f64::EPSILON);
33 /// ```
34 #[must_use]
35 fn angle_to(&self, other: &Self) -> T {
36 (self.dot(other) / (self.magnitude() * other.magnitude())).acos()
37 }
38}
39
40pub trait Magnitude<T: Float>
41where
42 Self: Sized + std::ops::Div<T, Output = Self> + Copy,
43{
44 /// Returns the exact magnitude of the vector
45 ///
46 /// This is the same as getting the euclidean distance from the origin to the
47 /// tip of the vector, i.e. `sqrt(x^2 + y^2 + z^2)` for a cartesian coordinate
48 #[must_use]
49 fn magnitude(&self) -> T;
50
51 /// Returns the magnitude of a vector in as few operations as possible
52 ///
53 /// # Note:
54 ///
55 /// This operation is not appropriate for comparing vectors of different type.
56 /// e.g. the spherical coordinates `(10,0,0)` will have a smaller
57 /// `quick_magnitude()` than the cartesian coordinate `(0,5,0)` despite
58 /// having a larger real magnitude
59 #[must_use]
60 fn quick_magnitude(&self) -> T;
61
62 #[must_use]
63 fn normalize(self) -> Self {
64 self / self.magnitude()
65 }
66}
67
68pub trait Dot<T: Num> {
69 #[must_use]
70 fn dot(&self, rhs: &Self) -> T;
71}
72
73pub trait Cross3D {
74 #[must_use]
75 fn cross(&self, rhs: &Self) -> Self;
76}
77
78pub trait TrigConsts
79where
80 Self: Sized,
81{
82 const ZERO: Self;
83 const ONE: Self;
84 const NEG_ONE: Self;
85 const TAU: Self;
86 const FRAC_3PI_2: Self;
87 const PI: Self;
88 const FRAC_PI_2: Self;
89 const FRAC_PI_3: Self;
90 const FRAC_PI_4: Self;
91 const FRAC_PI_6: Self;
92 const FRAC_PI_8: Self;
93 const FRAC_1_PI: Self;
94 const FRAC_2_PI: Self;
95 const FRAC_2_SQRT_PI: Self;
96}
97
98macro_rules! impl_trig_consts {
99 ($($t : ident) +) => {
100 // Find constants of rust primitives.
101 $(impl_trig_consts!{$t, std::$t::consts})+
102 };
103 ($t : ident, $path : path) => {
104 // Find constants as children of the path
105 // e.g. `std::f32::consts{TAU,PI,...}`
106 impl_trig_consts!{$t, $path, TAU, PI, FRAC_PI_2, FRAC_PI_3,FRAC_PI_4,FRAC_PI_6,FRAC_PI_8, FRAC_1_PI, FRAC_2_PI, FRAC_2_SQRT_PI}
107 };
108 ($t : ident, $path : path, $($constant : ident),+)=> {
109 impl TrigConsts for $t{
110 // For each provided constant expand to a usable function
111 // e.g. `impl_constants!(f32, std::f32, TAU)`
112 // expands to `const TAU: f32 = std::f32::consts::TAU`
113 $(
114 const $constant : Self = { use $path as consts; consts::$constant };
115 )+
116 const NEG_ONE : Self = -1.0;
117 const ZERO: Self = 0.0;
118 const ONE: Self = 1.0;
119 const FRAC_3PI_2: Self = { use $path as consts; consts::PI + consts::FRAC_PI_2 };
120 }
121
122 };
123}
124
125impl_trig_consts!(f32 f64);