Skip to main content

ferroui_base/
vector.rs

1//! Defines a vector.
2
3use std::fmt;
4use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5use std::str::FromStr;
6
7use crate::utilities::math_utilities::{self, MathUtilities};
8use crate::utilities::span_helpers::InvariantF64;
9use crate::utilities::{FormatError, SpanStringTokenizer};
10use crate::Point;
11
12/// Defines a vector.
13#[derive(Clone, Copy, Debug, PartialEq, Default)]
14pub struct Vector {
15    /// The X component.
16    pub x: f64,
17    /// The Y component.
18    pub y: f64,
19}
20
21impl Vector {
22    /// The zero vector.
23    pub const ZERO: Vector = Vector::new(0.0, 0.0);
24
25    /// The one vector.
26    pub const ONE: Vector = Vector::new(1.0, 1.0);
27
28    /// The X unit vector.
29    pub const UNIT_X: Vector = Vector::new(1.0, 0.0);
30
31    /// The Y unit vector.
32    pub const UNIT_Y: Vector = Vector::new(0.0, 1.0);
33
34    /// Initializes a new instance of the [`Vector`] structure.
35    #[inline]
36    pub const fn new(x: f64, y: f64) -> Self {
37        Self { x, y }
38    }
39
40    /// Parses a [`Vector`] string (`"x, y"` or `"x y"`).
41    pub fn parse(s: &str) -> Result<Self, FormatError> {
42        SpanStringTokenizer::with_message(s, "Invalid Vector.")
43            .scope(|t| Ok(Vector::new(t.read_double()?, t.read_double()?)))
44    }
45
46    /// Length of the vector.
47    #[inline]
48    pub fn length(&self) -> f64 {
49        self.squared_length().sqrt()
50    }
51
52    /// Squared length of the vector.
53    #[inline]
54    pub fn squared_length(&self) -> f64 {
55        self.x * self.x + self.y * self.y
56    }
57
58    /// Check if two vectors are equal (exact comparison of the components).
59    #[inline]
60    pub fn equals(&self, other: Vector) -> bool {
61        self.x == other.x && self.y == other.y
62    }
63
64    /// Check if two vectors are nearly equal (numerically).
65    #[inline]
66    pub fn nearly_equals(&self, other: Vector) -> bool {
67        MathUtilities::are_close(self.x, other.x) && MathUtilities::are_close(self.y, other.y)
68    }
69
70    /// Returns a new vector with the specified X component.
71    #[inline]
72    pub const fn with_x(&self, x: f64) -> Vector {
73        Vector::new(x, self.y)
74    }
75
76    /// Returns a new vector with the specified Y component.
77    #[inline]
78    pub const fn with_y(&self, y: f64) -> Vector {
79        Vector::new(self.x, y)
80    }
81
82    /// Returns a normalized version of this vector. A zero-length vector yields NaN components.
83    #[inline]
84    pub fn normalize(&self) -> Vector {
85        Vector::normalize_vector(*self)
86    }
87
88    /// Returns a negated version of this vector.
89    #[inline]
90    pub fn negate(&self) -> Vector {
91        Vector::negate_vector(*self)
92    }
93
94    /// Returns the dot product of two vectors.
95    #[inline]
96    pub fn dot(a: Vector, b: Vector) -> f64 {
97        a.x * b.x + a.y * b.y
98    }
99
100    /// Returns the cross product of two vectors.
101    #[inline]
102    pub fn cross(a: Vector, b: Vector) -> f64 {
103        a.x * b.y - a.y * b.x
104    }
105
106    /// Normalizes the given vector (static form of [`normalize`](Self::normalize)).
107    #[inline]
108    pub fn normalize_vector(vector: Vector) -> Vector {
109        Vector::divide_scalar(vector, vector.length())
110    }
111
112    /// Divides the first vector by the second, component-wise.
113    #[inline]
114    pub fn divide(a: Vector, b: Vector) -> Vector {
115        Vector::new(a.x / b.x, a.y / b.y)
116    }
117
118    /// Divides the vector by the given scalar.
119    #[inline]
120    pub fn divide_scalar(vector: Vector, scalar: f64) -> Vector {
121        Vector::new(vector.x / scalar, vector.y / scalar)
122    }
123
124    /// Multiplies the first vector by the second, component-wise.
125    #[inline]
126    pub fn multiply(a: Vector, b: Vector) -> Vector {
127        Vector::new(a.x * b.x, a.y * b.y)
128    }
129
130    /// Multiplies the vector by the given scalar.
131    #[inline]
132    pub fn multiply_scalar(vector: Vector, scalar: f64) -> Vector {
133        Vector::new(vector.x * scalar, vector.y * scalar)
134    }
135
136    /// Adds the second to the first vector.
137    #[inline]
138    #[allow(clippy::should_implement_trait)] // the operator trait is implemented too
139    pub fn add(a: Vector, b: Vector) -> Vector {
140        Vector::new(a.x + b.x, a.y + b.y)
141    }
142
143    /// Subtracts the second from the first vector.
144    #[inline]
145    pub fn subtract(a: Vector, b: Vector) -> Vector {
146        Vector::new(a.x - b.x, a.y - b.y)
147    }
148
149    /// Negates the vector (static form of [`negate`](Self::negate)).
150    #[inline]
151    pub fn negate_vector(vector: Vector) -> Vector {
152        Vector::new(-vector.x, -vector.y)
153    }
154
155    /// Deconstructs the vector into its X and Y components.
156    #[inline]
157    pub const fn deconstruct(&self) -> (f64, f64) {
158        (self.x, self.y)
159    }
160
161    /// Returns a vector whose elements are the absolute values of each of the
162    /// specified vector's elements.
163    #[inline]
164    pub fn abs(&self) -> Vector {
165        Vector::new(self.x.abs(), self.y.abs())
166    }
167
168    /// Restricts a vector between a minimum and a maximum value.
169    #[inline]
170    pub fn clamp(value: Vector, min: Vector, max: Vector) -> Vector {
171        Vector::min(Vector::max(value, min), max)
172    }
173
174    /// Returns a vector whose elements are the maximum of each of the pairs of
175    /// elements in two specified vectors.
176    #[inline]
177    pub fn max(left: Vector, right: Vector) -> Vector {
178        Vector::new(
179            math_utilities::max(left.x, right.x),
180            math_utilities::max(left.y, right.y),
181        )
182    }
183
184    /// Returns a vector whose elements are the minimum of each of the pairs of
185    /// elements in two specified vectors.
186    #[inline]
187    pub fn min(left: Vector, right: Vector) -> Vector {
188        Vector::new(
189            math_utilities::min(left.x, right.x),
190            math_utilities::min(left.y, right.y),
191        )
192    }
193
194    /// Computes the Euclidean distance between the two given points.
195    #[inline]
196    pub fn distance(value1: Vector, value2: Vector) -> f64 {
197        Vector::distance_squared(value1, value2).sqrt()
198    }
199
200    /// Returns the Euclidean distance squared between two specified points.
201    #[inline]
202    pub fn distance_squared(value1: Vector, value2: Vector) -> f64 {
203        let difference = value1 - value2;
204        Vector::dot(difference, difference)
205    }
206}
207
208/// Converts the [`Vector`] to a [`Point`] (an explicit conversion in the reference API).
209impl From<Vector> for Point {
210    #[inline]
211    fn from(a: Vector) -> Point {
212        Point::new(a.x, a.y)
213    }
214}
215
216impl From<Vector> for (f64, f64) {
217    #[inline]
218    fn from(v: Vector) -> Self {
219        (v.x, v.y)
220    }
221}
222
223/// Calculates the dot product of two vectors.
224impl Mul for Vector {
225    type Output = f64;
226    #[inline]
227    fn mul(self, b: Vector) -> f64 {
228        Vector::dot(self, b)
229    }
230}
231
232impl Mul<f64> for Vector {
233    type Output = Vector;
234    #[inline]
235    fn mul(self, scale: f64) -> Vector {
236        Vector::multiply_scalar(self, scale)
237    }
238}
239
240impl Mul<Vector> for f64 {
241    type Output = Vector;
242    #[inline]
243    fn mul(self, vector: Vector) -> Vector {
244        Vector::multiply_scalar(vector, self)
245    }
246}
247
248impl Div<f64> for Vector {
249    type Output = Vector;
250    #[inline]
251    fn div(self, scale: f64) -> Vector {
252        Vector::divide_scalar(self, scale)
253    }
254}
255
256impl Neg for Vector {
257    type Output = Vector;
258    #[inline]
259    fn neg(self) -> Vector {
260        Vector::negate_vector(self)
261    }
262}
263
264impl Add for Vector {
265    type Output = Vector;
266    #[inline]
267    fn add(self, b: Vector) -> Vector {
268        Vector::add(self, b)
269    }
270}
271
272impl Sub for Vector {
273    type Output = Vector;
274    #[inline]
275    fn sub(self, b: Vector) -> Vector {
276        Vector::subtract(self, b)
277    }
278}
279
280impl AddAssign for Vector {
281    #[inline]
282    fn add_assign(&mut self, b: Vector) {
283        *self = *self + b;
284    }
285}
286
287impl SubAssign for Vector {
288    #[inline]
289    fn sub_assign(&mut self, b: Vector) {
290        *self = *self - b;
291    }
292}
293
294impl MulAssign<f64> for Vector {
295    #[inline]
296    fn mul_assign(&mut self, scale: f64) {
297        *self = *self * scale;
298    }
299}
300
301impl DivAssign<f64> for Vector {
302    #[inline]
303    fn div_assign(&mut self, scale: f64) {
304        *self = *self / scale;
305    }
306}
307
308impl FromStr for Vector {
309    type Err = FormatError;
310    #[inline]
311    fn from_str(s: &str) -> Result<Self, FormatError> {
312        Vector::parse(s)
313    }
314}
315
316impl fmt::Display for Vector {
317    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318        write!(f, "{}, {}", InvariantF64(self.x), InvariantF64(self.y))
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    #[test]
327    fn length_should_return_correct_length_of_vector() {
328        let vector = Vector::new(2.0, 4.0);
329        let length = ((2 * 2 + 4 * 4) as f64).sqrt();
330
331        assert_eq!(length, vector.length());
332    }
333
334    #[test]
335    fn length_squared_should_return_correct_length_of_vector() {
336        let vector_a = Vector::new(2.0, 4.0);
337        let squared_length_a = (2 * 2 + 4 * 4) as f64;
338
339        assert_eq!(squared_length_a, vector_a.squared_length());
340    }
341
342    #[test]
343    fn normalize_should_return_normalized_vector() {
344        // the length of a normalized vector must be 1
345
346        let vector_a = Vector::new(13.0, 84.0);
347        let vector_b = Vector::new(-34.0, 345.0);
348        let vector_c = Vector::new(-34.0, -84.0);
349
350        assert_eq!(1.0, vector_a.normalize().length());
351        assert_eq!(1.0, vector_b.normalize().length());
352        assert_eq!(1.0, vector_c.normalize().length());
353    }
354
355    #[test]
356    fn negate_should_return_negated_vector() {
357        let vector = Vector::new(2.0, 4.0);
358        let negated = Vector::new(-2.0, -4.0);
359
360        assert_eq!(negated, vector.negate());
361    }
362
363    #[test]
364    fn dot_should_return_correct_value() {
365        let a = Vector::new(-6.0, 8.0);
366        let b = Vector::new(5.0, 12.0);
367
368        assert_eq!(66.0, Vector::dot(a, b));
369    }
370
371    #[test]
372    fn cross_should_return_correct_value() {
373        let a = Vector::new(-6.0, 8.0);
374        let b = Vector::new(5.0, 12.0);
375
376        assert_eq!(-112.0, Vector::cross(a, b));
377    }
378
379    #[test]
380    fn divied_by_vector_should_return_correct_value() {
381        let a = Vector::new(10.0, 2.0);
382        let b = Vector::new(5.0, 2.0);
383
384        let expected = Vector::new(2.0, 1.0);
385
386        assert_eq!(expected, Vector::divide(a, b));
387    }
388
389    #[test]
390    fn divied_should_return_correct_value() {
391        let vector = Vector::new(10.0, 2.0);
392        let expected = Vector::new(5.0, 1.0);
393
394        assert_eq!(expected, Vector::divide_scalar(vector, 2.0));
395    }
396
397    #[test]
398    fn multiply_by_vector_should_return_correct_value() {
399        let a = Vector::new(10.0, 2.0);
400        let b = Vector::new(2.0, 2.0);
401
402        let expected = Vector::new(20.0, 4.0);
403
404        assert_eq!(expected, Vector::multiply(a, b));
405    }
406
407    #[test]
408    fn multiply_should_return_correct_value() {
409        let vector = Vector::new(10.0, 2.0);
410
411        let expected = Vector::new(20.0, 4.0);
412
413        assert_eq!(expected, Vector::multiply_scalar(vector, 2.0));
414    }
415
416    #[test]
417    fn scale_vector_should_be_commutative() {
418        let vector = Vector::new(10.0, 2.0);
419
420        let expected = vector * 2.0;
421
422        assert_eq!(expected, 2.0 * vector);
423    }
424
425    // Additional coverage (not part of the reference suite).
426
427    #[test]
428    fn parse_display_and_helpers() {
429        assert_eq!(Vector::parse("1,2").unwrap(), Vector::new(1.0, 2.0));
430        assert_eq!(Vector::parse("x").unwrap_err().message(), "Invalid Vector.");
431        assert_eq!(Vector::new(1.0, 2.5).to_string(), "1, 2.5");
432        assert_eq!(Vector::new(-1.0, 2.0).abs(), Vector::new(1.0, 2.0));
433        assert_eq!(
434            Vector::clamp(Vector::new(-5.0, 5.0), Vector::ZERO, Vector::ONE),
435            Vector::new(0.0, 1.0)
436        );
437        assert_eq!(Vector::distance(Vector::ZERO, Vector::new(3.0, 4.0)), 5.0);
438        assert_eq!(Vector::new(1.0, 2.0) * Vector::new(3.0, 4.0), 11.0);
439        assert_eq!(Point::from(Vector::UNIT_X), Point::new(1.0, 0.0));
440    }
441}