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    /// Return the value of the `x` coordinate of the vector.
59    pub fn x(&self) -> T {
60        self.0
61    }
62
63    /// Return the value of the `y` coordinate of the vector.
64    pub fn y(&self) -> T {
65        self.1
66    }
67
68    /// Compute the norm of `self`.
69    pub fn norm(&self) -> T {
70        self.0.hypot(self.1)
71    }
72
73    /// Compute the direction of `self` as a unit vector.
74    ///
75    /// # Errors
76    ///
77    /// This method will return an error if called on a null `Vector2`.
78    pub fn unit_dir(&self) -> Result<Self, CoordsError> {
79        let norm = self.norm();
80        if norm.is_zero() {
81            Err(CoordsError::InvalidUnitDir)
82        } else {
83            Ok(*self / norm)
84        }
85    }
86
87    /// Compute the direction of the normal vector to `self` as a unit vector.
88    ///
89    /// # Errors
90    ///
91    /// This method will return an error if called on a null `Vector2`.
92    pub fn normal_dir(&self) -> Result<Vector2<T>, CoordsError> {
93        Self(-self.1, self.0)
94            .unit_dir() // unit(-y, x)
95            .map_err(|_| CoordsError::InvalidNormDir)
96    }
97
98    /// Return the dot product between `self` and `other`.
99    pub fn dot(&self, other: &Vector2<T>) -> T {
100        self.0 * other.0 + self.1 * other.1
101    }
102}
103
104// Building trait
105
106impl<T: CoordsFloat> From<(T, T)> for Vector2<T> {
107    fn from((x, y): (T, T)) -> Self {
108        Self(x, y)
109    }
110}
111
112// Basic operations
113
114impl<T: CoordsFloat> std::ops::Add<Vector2<T>> for Vector2<T> {
115    type Output = Self;
116
117    fn add(self, rhs: Vector2<T>) -> Self::Output {
118        Self(self.0 + rhs.0, self.1 + rhs.1)
119    }
120}
121
122impl<T: CoordsFloat> std::ops::AddAssign<Vector2<T>> for Vector2<T> {
123    fn add_assign(&mut self, rhs: Vector2<T>) {
124        self.0 += rhs.0;
125        self.1 += rhs.1;
126    }
127}
128
129impl<T: CoordsFloat> std::ops::Sub<Vector2<T>> for Vector2<T> {
130    type Output = Self;
131
132    fn sub(self, rhs: Vector2<T>) -> Self::Output {
133        Self(self.0 - rhs.0, self.1 - rhs.1)
134    }
135}
136
137impl<T: CoordsFloat> std::ops::SubAssign<Vector2<T>> for Vector2<T> {
138    fn sub_assign(&mut self, rhs: Vector2<T>) {
139        self.0 -= rhs.0;
140        self.0 -= rhs.0;
141    }
142}
143
144impl<T: CoordsFloat> std::ops::Mul<T> for Vector2<T> {
145    type Output = Self;
146
147    fn mul(self, rhs: T) -> Self::Output {
148        Self(self.0 * rhs, self.1 * rhs)
149    }
150}
151
152impl<T: CoordsFloat> std::ops::MulAssign<T> for Vector2<T> {
153    fn mul_assign(&mut self, rhs: T) {
154        self.0 *= rhs;
155        self.1 *= rhs;
156    }
157}
158
159impl<T: CoordsFloat> std::ops::Div<T> for Vector2<T> {
160    type Output = Self;
161
162    fn div(self, rhs: T) -> Self::Output {
163        assert!(!rhs.is_zero());
164        Self(self.0 / rhs, self.1 / rhs)
165    }
166}
167
168impl<T: CoordsFloat> std::ops::DivAssign<T> for Vector2<T> {
169    fn div_assign(&mut self, rhs: T) {
170        assert!(!rhs.is_zero());
171        self.0 /= rhs;
172        self.1 /= rhs;
173    }
174}
175
176impl<T: CoordsFloat> std::ops::Neg for Vector2<T> {
177    type Output = Self;
178
179    fn neg(self) -> Self::Output {
180        Self(-self.0, -self.1)
181    }
182}