lars/vector/
vector2.rs

1//! 2D vector math utilities.
2//!
3//! This module provides a simple 2D vector type (`Vec2`) with common mathematical
4//! operations used in geometry, graphics, and game development.
5//!
6//! It supports vector addition, subtraction, scaling, dot and cross products, and normalization.
7
8use crate::Mat2;
9use derive_more::{Add, Constructor, Div, Mul, Neg, Sub};
10use std::fmt;
11use std::ops::Mul;
12
13/// A 2-dimensional vector type
14///
15/// Provides common vector operations such as addition, subtraction, scalar and
16/// component-wise multiplication, normalization, dot and cross products.
17///
18/// # Examples
19/// ```
20///
21/// use lars::Vec2;
22/// let a = Vec2::new(3.0, 4.0);
23/// assert_eq!(a.mag(), 5.0);
24/// ```
25#[derive(Add, Sub, Div, Mul, Neg, Clone, Copy, Debug, PartialOrd, Constructor)]
26pub struct Vec2 {
27    /// X component of the vector.
28    pub x: f64,
29    /// Y component of the vector.
30    pub y: f64,
31}
32
33const EPSILON: f64 = 1e-9;
34
35impl PartialEq for Vec2 {
36    fn eq(&self, other: &Self) -> bool {
37        (self.x - other.x).abs() < EPSILON && (self.y - other.y).abs() < EPSILON
38    }
39}
40
41impl Vec2 {
42    /// A zero Vector (0.0, 0.0, 0.0)
43    pub const ZERO: Vec2 = Vec2 { x: 0.0, y: 0.0 };
44    /// A one Vector (1.0, 1.0, 1.0)
45    pub const ONE: Vec2 = Vec2 { x: 1.0, y: 1.0 };
46    /// A Unit Vector in X (1.0, 0.0, 0.0)
47    pub const UNIT_X: Vec2 = Vec2 { x: 1.0, y: 0.0 };
48    /// A Unit Vector in Y (0.0, 1.0, 0.0)
49    pub const UNIT_Y: Vec2 = Vec2 { x: 0.0, y: 1.0 };
50
51    /// Returns the **magnitude** (length) of the vector.
52    ///
53    /// # Examples
54    /// ```
55    ///
56    /// use lars::Vec2;
57    /// let v = Vec2::new(3.0, 4.0);
58    /// assert_eq!(v.mag(), 5.0);
59    /// ```
60    pub fn mag(&self) -> f64 {
61        (self.x * self.x + self.y * self.y).sqrt()
62    }
63
64    /// Returns the **dot product** between `self` and another [`Vec2`].
65    ///
66    /// The dot product measures the cosine of the angle between two vectors.
67    ///
68    /// # Examples
69    /// ```
70    ///
71    /// use lars::Vec2;
72    /// let a = Vec2::new(1.0, 2.0);
73    /// let b = Vec2::new(3.0, 4.0);
74    /// assert_eq!(a.dot(&b), 11.0);
75    /// ```
76    pub fn dot(&self, other: &Vec2) -> f64 {
77        (self.x * other.x) + (self.y * other.y)
78    }
79
80    /// Returns the **scalar 2D cross product** between `self` and another [`Vec2`].
81    ///
82    /// Unlike the 3D cross product, the 2D version returns a scalar equal to
83    /// the signed area of the parallelogram formed by the two vectors.
84    ///
85    /// # Examples
86    /// ```
87    ///
88    /// use lars::Vec2;
89    /// let a = Vec2::new(1.0, 0.0);
90    /// let b = Vec2::new(0.0, 1.0);
91    /// assert_eq!(a.cross(&b), 1.0);
92    /// ```
93    pub fn cross(&self, other: &Vec2) -> f64 {
94        self.x * other.y - self.y * other.x
95    }
96
97    /// Applies a function `f` to each component (`x`, `y`) of the vector.
98    ///
99    /// # Examples
100    /// ```
101    ///
102    /// use lars::Vec2;
103    /// let v = Vec2::new(1.0, 2.0);
104    /// let squared = v.map(|x| x * x);
105    /// assert_eq!(squared, Vec2::new(1.0, 4.0));
106    /// ```
107    pub fn map<F>(&self, f: F) -> Vec2
108    where
109        F: Fn(f64) -> f64,
110    {
111        let fx = f(self.x);
112        let fy = f(self.y);
113        Vec2 { x: fx, y: fy }
114    }
115
116    /// Returns a **normalized** (unit-length) version of the vector.
117    ///
118    /// # Panics
119    /// Panics if the vector has zero magnitude (division by zero).
120    ///
121    /// # Examples
122    /// ```
123    ///
124    /// use lars::Vec2;
125    /// let v = Vec2::new(3.0, 0.0);
126    /// assert_eq!(v.normalize(), Vec2::new(1.0, 0.0));
127    /// ```
128    pub fn normalize(&self) -> Vec2 {
129        let m = self.mag();
130        if m == 0.0 {
131            return Vec2::ZERO; // todo handle this better
132        }
133
134        self.map(|i| i / m)
135    }
136
137    // All functions below this point are variations of the above functions
138
139    ///  Returns the **magnitude**  of the vector, squared.
140    ///
141    /// # Examples
142    /// ```
143    ///
144    /// use lars::Vec2;
145    /// let v = Vec2::new(3.0, 4.0);
146    /// assert_eq!(v.mag_sq(), 25.0);
147    /// ```
148    pub fn mag_sq(&self) -> f64 {
149        self.x * self.x + self.y * self.y
150    }
151}
152
153/// Implements **scalar multiplication** for `f64 * Vec2`.
154///
155/// # Examples
156/// ```
157///
158/// use lars::Vec2;
159/// let v = Vec2::new(1.0, 2.0);
160/// let scaled = 2.0 * v;
161/// assert_eq!(scaled, Vec2::new(2.0, 4.0));
162/// ```
163impl Mul<Vec2> for f64 {
164    type Output = Vec2;
165    fn mul(self, vector: Vec2) -> Vec2 {
166        Vec2 {
167            x: self * vector.x,
168            y: self * vector.y,
169        }
170    }
171}
172
173/// Implements **component-wise multiplication** between two [`Vec2`]s.
174///
175/// # Examples
176/// ```
177///
178/// use lars::Vec2;
179/// let a = Vec2::new(1.0, 2.0);
180/// let b = Vec2::new(3.0, 4.0);
181/// assert_eq!(a * b, Vec2::new(3.0, 8.0));
182/// ```
183impl Mul<Vec2> for Vec2 {
184    type Output = Vec2;
185    fn mul(self, vector: Vec2) -> Vec2 {
186        Vec2 {
187            x: self.x * vector.x,
188            y: self.y * vector.y,
189        }
190    }
191}
192// Vec2 * Mat2
193impl Mul<Mat2> for Vec2 {
194    type Output = Vec2;
195    fn mul(self, m: Mat2) -> Vec2 {
196        let x = m.a * self.x + m.b * self.y;
197        let y = m.c * self.x + m.d * self.y;
198
199        Vec2::new(x, y)
200    }
201}
202
203/// Represents a 2D point in space.
204///
205/// Alias for [`Vec2`].
206pub type Point2D = Vec2;
207
208impl Point2D {
209    /// Finds the unsigned distance between `self` and another 3D point `Other`.
210    ///
211    /// #examples
212    ///
213    /// ```
214    /// use lars::Point2D;
215    /// let a = Point2D::new(1.0, 0.0);
216    /// let b = Point2D::new(0.0, 0.0);
217    /// assert_eq!(a.dist(&b), 1.0)
218    ///
219    ///
220    ///
221    ///
222    /// ```
223    pub fn dist(&self, other: &Point2D) -> f64 {
224        (*self - *other).mag().abs()
225    }
226    /// Finds the unsigned distance between `self` and another 3D point `Other`, squared
227    ///
228    /// #examples
229    ///
230    /// ```
231    ///
232    /// use lars::Point2D;
233    /// let a = Point2D::new(2.0, 0.0);
234    /// let b = Point2D::new(0.0, 0.0);
235    /// assert_eq!(a.dist_sq(&b), 4.0)
236    ///
237    /// ```
238    pub fn dist_sq(&self, other: &Point2D) -> f64 {
239        (*self - *other).mag_sq().abs()
240    }
241}
242
243/// displays the vector in the form (X, Y)
244impl fmt::Display for Vec2 {
245    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246        write!(f, "({}, {})", self.x, self.y)
247    }
248}
249
250/// Returns (0.0, 0.0)
251impl Default for Vec2 {
252    fn default() -> Self {
253        Self::ZERO
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260    #[test]
261    fn test_add() {
262        let v1 = Vec2::ZERO;
263        let v2 = Vec2::ONE;
264        assert_eq!(v1 + v2, Vec2::ONE);
265    }
266    #[test]
267    fn test_mag() {
268        let v = Vec2::new(3.0, 4.0);
269        assert_eq!(v.mag(), 5.0);
270    }
271
272    #[test]
273    fn test_mag_sq() {
274        let v = Vec2::new(3.0, 4.0);
275        assert_eq!(v.mag_sq(), 25.0);
276    }
277
278    #[test]
279    fn test_dist() {
280        let a = Point2D::new(1.0, 2.0);
281        let b = Point2D::new(1.0, 0.0);
282        assert_eq!(a.dist(&b), 2.0);
283    }
284
285    #[test]
286    fn test_dist_sq() {
287        let a = Point2D::new(1.0, 2.0);
288        let b = Point2D::new(1.0, 0.0);
289        assert_eq!(a.dist_sq(&b), 4.0);
290    }
291
292    #[test]
293    fn test_dot() {
294        let a = Vec2::new(1.0, 2.0);
295        let b = Vec2::new(3.0, 4.0);
296        assert_eq!(a.dot(&b), 11.0);
297    }
298
299    #[test]
300    fn test_cross() {
301        let a = Vec2::new(1.0, 0.0);
302        let b = Vec2::new(0.0, 1.0);
303        assert_eq!(a.cross(&b), 1.0);
304    }
305
306    #[test]
307    fn test_normalize() {
308        let v = Vec2::new(3.0, 4.0);
309        let n = v.normalize();
310        assert!((n.mag() - 1.0).abs() < 1e-10);
311    }
312
313    #[test]
314    fn test_scalar_mul() {
315        let v = Vec2::new(1.0, 2.0);
316        assert_eq!(2.0 * v, Vec2::new(2.0, 4.0));
317    }
318
319    #[test]
320    fn test_component_mul() {
321        let a = Vec2::new(2.0, 3.0);
322        let b = Vec2::new(4.0, 5.0);
323        assert_eq!(a * b, Vec2::new(8.0, 15.0));
324    }
325
326    #[test]
327    fn test_default() {
328        let v = Vec2::default();
329        assert_eq!(v, Vec2::ZERO);
330    }
331
332    #[test]
333    fn test_zero_length_normalize() {
334        let v = Vec2::ZERO;
335        let n = v.normalize();
336        assert_eq!(n, Vec2::ZERO);
337    }
338
339    #[test]
340    fn test_dot_perpendicular() {
341        let a = Vec2::UNIT_X;
342        let b = Vec2::UNIT_Y;
343        assert_eq!(a.dot(&b), 0.0);
344    }
345}
346