honeycomb_core/geometry/dim2/
vector.rs

1//! Custom spatial representation
2//!
3//! This module contains all code used to model vectors.
4
5use crate::geometry::{CoordsError, CoordsFloat};
6
7/// # 2D vector structure
8///
9/// ## Generics
10///
11/// - `T: CoordsFloat` -- Generic FP type for coordinates.
12///
13/// ## Example
14///
15/// ```
16/// # use honeycomb_core::geometry::CoordsError;
17/// # fn main() -> Result<(), CoordsError> {
18/// use honeycomb_core::geometry::Vector2;
19///
20/// let unit_x = Vector2::unit_x();
21/// let unit_y = Vector2::unit_y();
22///
23/// assert_eq!(unit_x.dot(&unit_y), 0.0);
24/// assert_eq!(unit_x.normal_dir()?, unit_y);
25///
26/// let two: f64 = 2.0;
27/// let x_plus_y: Vector2<f64> = unit_x + unit_y;
28///
29/// assert_eq!(x_plus_y.norm(), two.sqrt());
30/// assert_eq!(x_plus_y.unit_dir()?, Vector2(1.0 / two.sqrt(), 1.0 / two.sqrt()));
31/// # Ok(())
32/// # }
33/// ```
34#[derive(Debug, Clone, Copy, Default, PartialEq)]
35pub struct Vector2<T: CoordsFloat>(pub T, pub T);
36
37unsafe impl<T: CoordsFloat> Send for Vector2<T> {}
38unsafe impl<T: CoordsFloat> Sync for Vector2<T> {}
39
40impl<T: CoordsFloat> Vector2<T> {
41    /// Return a unit vector along the `x` axis.
42    #[must_use = "unused return value"]
43    pub fn unit_x() -> Self {
44        Self(T::one(), T::zero())
45    }
46
47    /// Return a unit vector along the `y` axis.
48    #[must_use = "unused return value"]
49    pub fn unit_y() -> Self {
50        Self(T::zero(), T::one())
51    }
52
53    /// Consume `self` to return inner values.
54    pub fn into_inner(self) -> (T, T) {
55        (self.0, self.1)
56    }
57
58    /// Cast `self` to `f32` coordinates.
59    pub fn to_f32(self) -> Option<Vector2<f32>> {
60        match (self.0.to_f32(), self.1.to_f32()) {
61            (Some(x), Some(y)) => Some(Vector2(x, y)),
62            _ => None,
63        }
64    }
65
66    /// Cast `self` to `f64` coordinates.
67    pub fn to_f64(self) -> Option<Vector2<f64>> {
68        match (self.0.to_f64(), self.1.to_f64()) {
69            (Some(x), Some(y)) => Some(Vector2(x, y)),
70            _ => None,
71        }
72    }
73
74    /// Return the value of the `x` coordinate of the vector.
75    pub fn x(&self) -> T {
76        self.0
77    }
78
79    /// Return the value of the `y` coordinate of the vector.
80    pub fn y(&self) -> T {
81        self.1
82    }
83
84    /// Compute the norm of `self`.
85    pub fn norm(&self) -> T {
86        self.0.hypot(self.1)
87    }
88
89    /// Compute the direction of `self` as a unit vector.
90    ///
91    /// # Errors
92    ///
93    /// This method will return an error if called on a null `Vector2`.
94    pub fn unit_dir(&self) -> Result<Self, CoordsError> {
95        let norm = self.norm();
96        if norm.is_zero() {
97            Err(CoordsError::InvalidUnitDir)
98        } else {
99            Ok(*self / norm)
100        }
101    }
102
103    /// Compute the direction of the normal vector to `self` as a unit vector.
104    ///
105    /// # Errors
106    ///
107    /// This method will return an error if called on a null `Vector2`.
108    pub fn normal_dir(&self) -> Result<Vector2<T>, CoordsError> {
109        Self(-self.1, self.0)
110            .unit_dir() // unit(-y, x)
111            .map_err(|_| CoordsError::InvalidNormDir)
112    }
113
114    /// Return the dot product between `self` and `other`.
115    pub fn dot(&self, other: &Vector2<T>) -> T {
116        self.0 * other.0 + self.1 * other.1
117    }
118}
119
120// Building trait
121
122impl<T: CoordsFloat> From<(T, T)> for Vector2<T> {
123    fn from((x, y): (T, T)) -> Self {
124        Self(x, y)
125    }
126}
127
128// Basic operations
129
130impl<T: CoordsFloat> std::ops::Add<Vector2<T>> for Vector2<T> {
131    type Output = Self;
132
133    fn add(self, rhs: Vector2<T>) -> Self::Output {
134        Self(self.0 + rhs.0, self.1 + rhs.1)
135    }
136}
137
138impl<T: CoordsFloat> std::ops::AddAssign<Vector2<T>> for Vector2<T> {
139    fn add_assign(&mut self, rhs: Vector2<T>) {
140        self.0 += rhs.0;
141        self.1 += rhs.1;
142    }
143}
144
145impl<T: CoordsFloat> std::ops::Sub<Vector2<T>> for Vector2<T> {
146    type Output = Self;
147
148    fn sub(self, rhs: Vector2<T>) -> Self::Output {
149        Self(self.0 - rhs.0, self.1 - rhs.1)
150    }
151}
152
153impl<T: CoordsFloat> std::ops::SubAssign<Vector2<T>> for Vector2<T> {
154    fn sub_assign(&mut self, rhs: Vector2<T>) {
155        self.0 -= rhs.0;
156        self.0 -= rhs.0;
157    }
158}
159
160impl<T: CoordsFloat> std::ops::Mul<T> for Vector2<T> {
161    type Output = Self;
162
163    fn mul(self, rhs: T) -> Self::Output {
164        Self(self.0 * rhs, self.1 * rhs)
165    }
166}
167
168impl<T: CoordsFloat> std::ops::MulAssign<T> for Vector2<T> {
169    fn mul_assign(&mut self, rhs: T) {
170        self.0 *= rhs;
171        self.1 *= rhs;
172    }
173}
174
175impl<T: CoordsFloat> std::ops::Div<T> for Vector2<T> {
176    type Output = Self;
177
178    fn div(self, rhs: T) -> Self::Output {
179        assert!(!rhs.is_zero());
180        Self(self.0 / rhs, self.1 / rhs)
181    }
182}
183
184impl<T: CoordsFloat> std::ops::DivAssign<T> for Vector2<T> {
185    fn div_assign(&mut self, rhs: T) {
186        assert!(!rhs.is_zero());
187        self.0 /= rhs;
188        self.1 /= rhs;
189    }
190}
191
192impl<T: CoordsFloat> std::ops::Neg for Vector2<T> {
193    type Output = Self;
194
195    fn neg(self) -> Self::Output {
196        Self(-self.0, -self.1)
197    }
198}