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