Skip to main content

stipple_geometry/
vec2.rs

1use core::ops::{Add, Div, Mul, Neg, Sub};
2
3/// A 2D vector (displacement) in logical pixels.
4///
5/// Distinct from [`Point`](crate::Point): a `Vec2` is a difference between
6/// points, not a position. Subtracting two points yields a `Vec2`; adding a
7/// `Vec2` to a point yields a point.
8#[derive(Clone, Copy, Debug, Default, PartialEq)]
9pub struct Vec2 {
10    pub dx: f64,
11    pub dy: f64,
12}
13
14impl Vec2 {
15    pub const ZERO: Self = Self { dx: 0.0, dy: 0.0 };
16
17    #[inline]
18    pub const fn new(dx: f64, dy: f64) -> Self {
19        Self { dx, dy }
20    }
21
22    /// Uniform vector with both components set to `v`.
23    #[inline]
24    pub const fn splat(v: f64) -> Self {
25        Self { dx: v, dy: v }
26    }
27
28    #[inline]
29    pub fn length(self) -> f64 {
30        self.length_squared().sqrt()
31    }
32
33    #[inline]
34    pub fn length_squared(self) -> f64 {
35        self.dx * self.dx + self.dy * self.dy
36    }
37
38    #[inline]
39    pub fn dot(self, other: Self) -> f64 {
40        self.dx * other.dx + self.dy * other.dy
41    }
42
43    /// Returns the unit vector in the same direction, or [`Vec2::ZERO`] if the
44    /// length is zero.
45    #[inline]
46    pub fn normalized(self) -> Self {
47        let len = self.length();
48        if len == 0.0 { Self::ZERO } else { self / len }
49    }
50}
51
52impl Add for Vec2 {
53    type Output = Vec2;
54    #[inline]
55    fn add(self, rhs: Vec2) -> Vec2 {
56        Vec2::new(self.dx + rhs.dx, self.dy + rhs.dy)
57    }
58}
59
60impl Sub for Vec2 {
61    type Output = Vec2;
62    #[inline]
63    fn sub(self, rhs: Vec2) -> Vec2 {
64        Vec2::new(self.dx - rhs.dx, self.dy - rhs.dy)
65    }
66}
67
68impl Neg for Vec2 {
69    type Output = Vec2;
70    #[inline]
71    fn neg(self) -> Vec2 {
72        Vec2::new(-self.dx, -self.dy)
73    }
74}
75
76impl Mul<f64> for Vec2 {
77    type Output = Vec2;
78    #[inline]
79    fn mul(self, rhs: f64) -> Vec2 {
80        Vec2::new(self.dx * rhs, self.dy * rhs)
81    }
82}
83
84impl Div<f64> for Vec2 {
85    type Output = Vec2;
86    #[inline]
87    fn div(self, rhs: f64) -> Vec2 {
88        Vec2::new(self.dx / rhs, self.dy / rhs)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn length_and_normalize() {
98        let v = Vec2::new(3.0, 4.0);
99        assert_eq!(v.length(), 5.0);
100        let n = v.normalized();
101        assert!((n.length() - 1.0).abs() < 1e-12);
102        assert_eq!(Vec2::ZERO.normalized(), Vec2::ZERO);
103    }
104
105    #[test]
106    fn arithmetic() {
107        assert_eq!(
108            Vec2::new(1.0, 2.0) + Vec2::new(3.0, 4.0),
109            Vec2::new(4.0, 6.0)
110        );
111        assert_eq!(Vec2::splat(2.0) * 3.0, Vec2::new(6.0, 6.0));
112        assert_eq!(-Vec2::new(1.0, -2.0), Vec2::new(-1.0, 2.0));
113    }
114}