Skip to main content

irox_geometry/
vector.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5use crate::{Point, Point2D};
6use core::ops::DivAssign;
7use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
8use irox_tools::math::Matrix;
9use irox_tools::FloatIsh;
10use irox_units::units::angle::Angle;
11
12pub trait Vector2D<T: FloatIsh>: Default + Copy + Clone + PartialEq + PartialOrd {
13    fn vx(&self) -> T;
14    fn vy(&self) -> T;
15    fn magnitude(&self) -> T;
16    #[must_use]
17    fn normalize(&self) -> Self;
18    fn dot(&self, other: &Self) -> T;
19
20    fn angle(&self, other: &Self) -> Angle;
21    #[must_use]
22    fn perpendicular(&self) -> Self;
23    #[must_use]
24    fn rotate(&self, angle: Angle) -> Self;
25    #[must_use]
26    fn abs(&self) -> Self;
27    fn to_point(&self) -> Point<T>;
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
31pub struct Vector<T: FloatIsh> {
32    pub vx: T,
33    pub vy: T,
34}
35impl<T: FloatIsh> Vector<T> {
36    pub fn new(vx: T, vy: T) -> Self {
37        Self { vx, vy }
38    }
39    pub fn splat(v: T) -> Self {
40        Self::new(v, v)
41    }
42    pub fn to_matrix(&self) -> Matrix<2, 1, f64> {
43        Matrix::new([[self.vy.to_f64()], [self.vy.to_f64()]])
44    }
45}
46
47impl<T: FloatIsh> Vector2D<T> for Vector<T> {
48    fn vx(&self) -> T {
49        self.vx
50    }
51
52    fn vy(&self) -> T {
53        self.vy
54    }
55
56    fn magnitude(&self) -> T {
57        (self.vx * self.vx + self.vy * self.vy).sqrt()
58    }
59
60    fn normalize(&self) -> Self {
61        let mag = self.magnitude();
62        Self {
63            vx: self.vx / mag,
64            vy: self.vy / mag,
65        }
66    }
67
68    fn dot(&self, other: &Self) -> T {
69        self.vx * other.vx + self.vy * other.vy
70    }
71
72    fn angle(&self, other: &Self) -> Angle {
73        let rad = self.vy.atan2(self.vx) - other.vy.atan2(other.vx);
74        Angle::new_radians(rad.to_f64())
75    }
76
77    fn perpendicular(&self) -> Self {
78        self.rotate(Angle::new_degrees(90.))
79    }
80
81    fn rotate(&self, angle: Angle) -> Self {
82        let Matrix {
83            values: [[vx], [vy]],
84        } = Matrix::<2, 2, _>::rotation_counterclockwise(angle.as_radians().value())
85            .mul(self.to_matrix());
86        Self {
87            vx: T::from_f64(vx),
88            vy: T::from_f64(vy),
89        }
90    }
91
92    fn abs(&self) -> Self {
93        Self {
94            vx: self.vx.abs(),
95            vy: self.vy.abs(),
96        }
97    }
98    fn to_point(&self) -> Point<T> {
99        Point::new_point(self.vx, self.vy)
100    }
101}
102
103impl<T: FloatIsh> Add for Vector<T> {
104    type Output = Self;
105
106    fn add(self, rhs: Self) -> Self::Output {
107        Self {
108            vx: self.vx + rhs.vx,
109            vy: self.vy + rhs.vy,
110        }
111    }
112}
113impl<T: FloatIsh> AddAssign for Vector<T> {
114    fn add_assign(&mut self, rhs: Self) {
115        self.vx += rhs.vx;
116        self.vy += rhs.vy;
117    }
118}
119impl<T: FloatIsh> Mul<T> for Vector<T> {
120    type Output = Self;
121
122    fn mul(self, rhs: T) -> Self::Output {
123        Self {
124            vx: self.vx * rhs,
125            vy: self.vy * rhs,
126        }
127    }
128}
129impl<T: FloatIsh> MulAssign<T> for Vector<T> {
130    fn mul_assign(&mut self, rhs: T) {
131        self.vx *= rhs;
132        self.vy *= rhs;
133    }
134}
135impl<T: FloatIsh> Sub for Vector<T> {
136    type Output = Self;
137
138    fn sub(self, rhs: Self) -> Self::Output {
139        Self {
140            vx: self.vx - rhs.vx,
141            vy: self.vy - rhs.vy,
142        }
143    }
144}
145impl<T: FloatIsh> SubAssign for Vector<T> {
146    fn sub_assign(&mut self, rhs: Self) {
147        self.vx -= rhs.vx;
148        self.vy -= rhs.vy;
149    }
150}
151impl<T: FloatIsh> DivAssign<T> for Vector<T> {
152    fn div_assign(&mut self, rhs: T) {
153        self.vx /= rhs;
154        self.vy /= rhs;
155    }
156}