fixed32_math/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/fixed32-math-rs
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5
6/*!
7# Vector and Rect Library
8
9This Rust crate that provides efficient 2D vector and rectangle operations using fixed-point arithmetic.
10Designed for applications where fixed precision is preferred, this crate is ideal for scenarios such as graphics programming,
11game development, and embedded systems where deterministic results are crucial.
12*/
13
14use core::fmt;
15use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
16
17use fixed32::Fp;
18
19/// Represents a vector in a 2D space.
20///
21#[derive(Default, PartialEq, Eq, Clone, Copy)]
22pub struct Vector {
23    pub x: Fp,
24    pub y: Fp,
25}
26
27impl Vector {
28    /// Creates a new `Vector` with the specified `x` and `y` components.
29    ///
30    /// # Parameters
31    /// - `x`: The x-coordinate of the vector.
32    /// - `y`: The y-coordinate of the vector.
33    ///
34    /// # Returns
35    /// A `Vector` instance with the given `x` and `y` components.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use fixed32::Fp;
41    /// use fixed32_math::Vector;
42    ///
43    /// let v = Vector::new(Fp::from(2), Fp::from(3));
44    /// assert_eq!(v.x, Fp::from(2));
45    /// assert_eq!(v.y, Fp::from(3));
46    /// ```
47    #[inline]
48    #[must_use]
49    pub const fn new(x: Fp, y: Fp) -> Self {
50        Self { x, y }
51    }
52
53    /// Returns a `Vector` pointing to the left (negative x-axis direction).
54    ///
55    /// This is a convenience method to create a vector that represents a direction
56    /// to the left in 2D space.
57    ///
58    /// # Returns
59    /// A `Vector` instance with `x` set to `-1` and `y` set to `0`.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use fixed32::Fp;
65    /// use fixed32_math::Vector;
66    ///
67    /// let left = Vector::left();
68    /// assert_eq!(left.x, Fp::neg_one());
69    /// assert_eq!(left.y, Fp::zero());
70    /// ```
71    #[inline]
72    #[must_use]
73    pub const fn left() -> Self {
74        Self {
75            x: Fp::neg_one(),
76            y: Fp::zero(),
77        }
78    }
79
80    /// Returns a `Vector` pointing to the right (positive x-axis direction).
81    ///
82    /// This is a convenience method to create a vector that represents a direction
83    /// to the right in 2D space.
84    ///
85    /// # Returns
86    /// A `Vector` instance with `x` set to `1` and `y` set to `0`.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use fixed32::Fp;
92    /// use fixed32_math::Vector;
93    ///
94    /// let right = Vector::right();
95    /// assert_eq!(right.x, Fp::one());
96    /// assert_eq!(right.y, Fp::zero());
97    /// ```
98    #[inline]
99    #[must_use]
100    pub const fn right() -> Self {
101        Self {
102            x: Fp::one(),
103            y: Fp::zero(),
104        }
105    }
106
107    /// Returns a `Vector` pointing upwards (positive y-axis direction).
108    ///
109    /// This is a convenience method to create a vector that represents a direction
110    /// upwards in 2D space.
111    ///
112    /// # Returns
113    /// A `Vector` instance with `x` set to `0` and `y` set to `1`.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use fixed32::Fp;
119    /// use fixed32_math::Vector;
120    ///
121    /// let up = Vector::up();
122    /// assert_eq!(up.x, Fp::zero());
123    /// assert_eq!(up.y, Fp::one());
124    /// ```
125    #[inline]
126    #[must_use]
127    pub const fn up() -> Self {
128        Self {
129            x: Fp::zero(),
130            y: Fp::one(),
131        }
132    }
133
134    /// Returns a `Vector` pointing downwards (negative y-axis direction).
135    ///
136    /// This is a convenience method to create a vector that represents a direction
137    /// downwards in 2D space.
138    ///
139    /// # Returns
140    /// A `Vector` instance with `x` set to `0` and `y` set to `-1`.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// use fixed32::Fp;
146    /// use fixed32_math::Vector;
147    ///
148    /// let down = Vector::down();
149    /// assert_eq!(down.x, Fp::zero());
150    /// assert_eq!(down.y, Fp::neg_one());
151    /// ```
152    #[inline]
153    #[must_use]
154    pub const fn down() -> Self {
155        Self {
156            x: Fp::zero(),
157            y: Fp::neg_one(),
158        }
159    }
160
161    /// Computes the squared length (magnitude) of the vector.
162    ///
163    /// This method calculates the squared length of the vector, which is the sum of the
164    /// squares of its `x` and `y` components. It is often used for performance reasons when
165    /// the actual length is not needed, as computing the square root can be costly.
166    ///
167    /// # Returns
168    /// The squared length of the vector as an [`Fp`] value.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use fixed32::Fp;
174    /// use fixed32_math::Vector;
175    ///
176    /// let v = Vector::new(Fp::from(3), Fp::from(4));
177    /// let sqr_len = v.sqr_len();
178    /// assert_eq!(sqr_len, Fp::from(25));
179    /// ```
180    #[must_use]
181    pub fn sqr_len(&self) -> Fp {
182        self.x * self.x + self.y * self.y
183    }
184
185    /// Returns the length of the vector.
186    #[must_use]
187    pub fn len(&self) -> Fp {
188        self.sqr_len().sqrt()
189    }
190
191    /// Returns a normalized vector with length 1. Returns `None` if the vector is zero-length.
192    #[must_use]
193    pub fn normalize(&self) -> Option<Self> {
194        let length = self.len();
195        if length.is_zero() {
196            None
197        } else {
198            Some(Self {
199                x: self.x / length,
200                y: self.y / length,
201            })
202        }
203    }
204
205    /// Computes the dot product of this vector with another.
206    #[must_use]
207    pub fn dot(&self, other: &Self) -> Fp {
208        self.x * other.x + self.y * other.y
209    }
210
211    /// Computes the magnitude of the cross product in 2D (which is a scalar value).
212    #[must_use]
213    pub fn cross(&self, other: &Self) -> Fp {
214        self.x * other.y - self.y * other.x
215    }
216
217    /// Scales the vector by another vector component-wise.
218    #[must_use]
219    pub fn scale(&self, factor: &Self) -> Self {
220        Self {
221            x: self.x * factor.x,
222            y: self.y * factor.y,
223        }
224    }
225
226    /// Rotates the vector by the given angle in radians.
227    #[must_use]
228    pub fn rotate(&self, angle: Fp) -> Self {
229        let cos_angle = angle.cos();
230        let sin_angle = angle.sin();
231        Self {
232            x: self.x * cos_angle - self.y * sin_angle,
233            y: self.x * sin_angle + self.y * cos_angle,
234        }
235    }
236
237    /// Returns the absolute value of each component of the vector.
238    #[must_use]
239    pub const fn abs(&self) -> Self {
240        Self {
241            x: self.x.abs(),
242            y: self.y.abs(),
243        }
244    }
245}
246
247impl fmt::Debug for Vector {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        write!(f, "vec:{},{}", self.x, self.y)
250    }
251}
252
253impl fmt::Display for Vector {
254    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255        write!(f, "vec:{},{}", self.x, self.y)
256    }
257}
258
259impl From<(i16, i16)> for Vector {
260    fn from(values: (i16, i16)) -> Self {
261        Self {
262            x: Fp::from(values.0),
263            y: Fp::from(values.1),
264        }
265    }
266}
267
268impl From<(f32, f32)> for Vector {
269    fn from(values: (f32, f32)) -> Self {
270        Self {
271            x: Fp::from(values.0),
272            y: Fp::from(values.1),
273        }
274    }
275}
276
277impl Sub for Vector {
278    type Output = Self;
279
280    fn sub(self, rhs: Self) -> Self::Output {
281        Self {
282            x: self.x - rhs.x,
283            y: self.y - rhs.y,
284        }
285    }
286}
287
288impl Add for Vector {
289    type Output = Self;
290
291    fn add(self, rhs: Self) -> Self::Output {
292        Self {
293            x: self.x + rhs.x,
294            y: self.y + rhs.y,
295        }
296    }
297}
298
299impl AddAssign for Vector {
300    fn add_assign(&mut self, rhs: Self) {
301        self.x += rhs.x;
302        self.y += rhs.y;
303    }
304}
305
306impl Mul<Self> for Vector {
307    type Output = Self;
308
309    fn mul(self, rhs: Self) -> Self::Output {
310        Self {
311            x: self.x * rhs.x,
312            y: self.y * rhs.y,
313        }
314    }
315}
316
317impl Mul<Vector> for Fp {
318    type Output = Vector;
319
320    fn mul(self, rhs: Vector) -> Self::Output {
321        Vector {
322            x: self * rhs.x,
323            y: self * rhs.y,
324        }
325    }
326}
327
328impl Mul<Fp> for Vector {
329    type Output = Self;
330
331    fn mul(self, rhs: Fp) -> Self::Output {
332        Self {
333            x: self.x * rhs,
334            y: self.y * rhs,
335        }
336    }
337}
338
339impl Div<Self> for Vector {
340    type Output = Self;
341
342    fn div(self, rhs: Self) -> Self::Output {
343        Self {
344            x: self.x / rhs.x,
345            y: self.y / rhs.y,
346        }
347    }
348}
349
350impl Div<Vector> for i16 {
351    type Output = Vector;
352
353    fn div(self, rhs: Vector) -> Self::Output {
354        Vector {
355            x: (self / rhs.x),
356            y: (self / rhs.y),
357        }
358    }
359}
360
361impl Div<Fp> for Vector {
362    type Output = Self;
363
364    fn div(self, rhs: Fp) -> Self::Output {
365        Self {
366            x: self.x / rhs,
367            y: self.y / rhs,
368        }
369    }
370}
371
372impl Div<i16> for Vector {
373    type Output = Self;
374
375    fn div(self, rhs: i16) -> Self::Output {
376        Self {
377            x: self.x / Fp::from(rhs),
378            y: self.y / Fp::from(rhs),
379        }
380    }
381}
382
383impl Mul<Vector> for i16 {
384    type Output = Vector;
385
386    fn mul(self, rhs: Vector) -> Self::Output {
387        Vector {
388            x: self * rhs.x,
389            y: self * rhs.y,
390        }
391    }
392}
393
394impl Mul<i16> for Vector {
395    type Output = Self;
396
397    fn mul(self, rhs: i16) -> Self::Output {
398        Self {
399            x: self.x * Fp::from(rhs),
400            y: self.y * Fp::from(rhs),
401        }
402    }
403}
404
405impl Neg for Vector {
406    type Output = Self;
407
408    fn neg(self) -> Self {
409        Self {
410            x: -self.x,
411            y: -self.y,
412        }
413    }
414}
415
416/// Represents a rectangle in a 2D space.
417///
418/// The `Rect` struct is defined by its position (`pos`) and size (`size`), both of which are
419/// represented as [`Vector`] instances. The position indicates the coordinates of the rectangle's
420/// bottom-left corner, and the size indicates the width and height of the rectangle.
421///
422/// # Examples
423///
424/// Creating a new rectangle:
425/// ```
426/// use fixed32::Fp;
427/// use fixed32_math::{Vector, Rect};
428///
429/// let pos = Vector::new(Fp::from(1), Fp::from(2));
430/// let size = Vector::new(Fp::from(3), Fp::from(4));
431/// let rect = Rect::new(pos, size);
432/// ```
433///
434/// Accessing the position and size:
435/// ```
436/// use fixed32::Fp;
437/// use fixed32_math::{Vector, Rect};
438///
439/// let pos = Vector::new(Fp::from(1), Fp::from(2));
440/// let size = Vector::new(Fp::from(3), Fp::from(4));
441/// let rect = Rect::new(pos, size);
442/// assert_eq!(rect.pos.x, Fp::from(1));
443/// assert_eq!(rect.size.y, Fp::from(4));
444/// ```
445#[derive(Default, PartialEq, Eq, Clone, Copy)]
446pub struct Rect {
447    pub pos: Vector,
448    pub size: Vector,
449}
450
451impl Rect {
452    /// Creates a new `Rect` with the specified position and size.
453    ///
454    /// # Parameters
455    /// - `pos`: The position of the rectangle's top-left corner as a [`Vector`].
456    /// - `size`: The size of the rectangle, including its width and height, as a [`Vector`].
457    ///
458    /// # Returns
459    /// A `Rect` instance with the given position and size.
460    ///
461    /// # Examples
462    ///
463    /// ```
464    /// use fixed32::Fp;
465    /// use fixed32_math::{Vector, Rect};
466    ///
467    /// let pos = Vector::new(Fp::from(1), Fp::from(2));
468    /// let size = Vector::new(Fp::from(3), Fp::from(4));
469    /// let rect = Rect::new(pos, size);
470    /// assert_eq!(rect.pos, pos);
471    /// assert_eq!(rect.size, size);
472    /// ```
473    #[inline]
474    #[must_use]
475    pub const fn new(pos: Vector, size: Vector) -> Self {
476        Self { pos, size }
477    }
478
479    #[must_use]
480    #[inline(always)]
481    pub fn top(self) -> Fp {
482        self.pos.y + self.size.y
483    }
484
485    #[inline(always)]
486    #[must_use]
487    pub const fn bottom(self) -> Fp {
488        self.pos.y
489    }
490
491    #[inline(always)]
492    #[must_use]
493    pub const fn left(self) -> Fp {
494        self.pos.x
495    }
496
497    #[inline(always)]
498    #[must_use]
499    pub fn right(self) -> Fp {
500        self.pos.x + self.size.x
501    }
502
503    /// Returns a new `Rect` with its position translated by the given vector.
504    ///
505    /// This method is useful for moving the rectangle while keeping its size unchanged.
506    ///
507    /// # Parameters
508    /// - `offset`: A vector indicating how much to translate the rectangle's position.
509    ///
510    /// # Returns
511    /// A new `Rect` with the position translated by the given vector. The size remains unchanged.
512    ///
513    /// # Examples
514    ///
515    /// ```
516    /// use fixed32::Fp;
517    /// use fixed32_math::{Rect, Vector};
518    ///
519    /// let pos = Vector::new(Fp::from(1), Fp::from(2));
520    /// let size = Vector::new(Fp::from(3), Fp::from(4));
521    /// let rect = Rect::new(pos, size);
522    /// let offset = Vector::new(Fp::from(1), Fp::from(-1));
523    /// let translated_rect = rect.move_by(offset);
524    /// assert_eq!(translated_rect.pos.x, Fp::from(2));
525    /// assert_eq!(translated_rect.pos.y, Fp::from(1));
526    /// assert_eq!(translated_rect.size, size);
527    /// ```
528    #[must_use]
529    pub fn move_by(self, offset: Vector) -> Self {
530        Self {
531            pos: self.pos + offset,
532            size: self.size,
533        }
534    }
535
536    /// Calculates the area of the rectangle.
537    #[must_use]
538    pub fn area(&self) -> Fp {
539        self.size.x * self.size.y
540    }
541
542    /// Calculates the perimeter of the rectangle.
543    #[must_use]
544    pub fn perimeter(&self) -> Fp {
545        2 * (self.size.x + self.size.y)
546    }
547
548    /// Calculates the intersection of two rectangles.
549    #[must_use]
550    pub fn intersection(&self, other: &Self) -> Option<Self> {
551        let x_overlap = Fp::max(self.pos.x, other.pos.x)
552            ..Fp::min(self.pos.x + self.size.x, other.pos.x + other.size.x);
553        let y_overlap = Fp::max(self.pos.y, other.pos.y)
554            ..Fp::min(self.pos.y + self.size.y, other.pos.y + other.size.y);
555
556        if x_overlap.is_empty() || y_overlap.is_empty() {
557            None
558        } else {
559            Some(Self {
560                pos: Vector {
561                    x: x_overlap.start,
562                    y: y_overlap.start,
563                },
564                size: Vector {
565                    x: x_overlap.end - x_overlap.start,
566                    y: y_overlap.end - y_overlap.start,
567                },
568            })
569        }
570    }
571
572    /// Calculates the union of two rectangles.
573    #[must_use]
574    pub fn union(&self, other: &Self) -> Self {
575        let x_min = Fp::min(self.pos.x, other.pos.x);
576        let y_min = Fp::min(self.pos.y, other.pos.y);
577        let x_max = Fp::max(self.pos.x + self.size.x, other.pos.x + other.size.x);
578        let y_max = Fp::max(self.pos.y + self.size.y, other.pos.y + other.size.y);
579
580        Self {
581            pos: Vector { x: x_min, y: y_min },
582            size: Vector {
583                x: x_max - x_min,
584                y: y_max - y_min,
585            },
586        }
587    }
588
589    /// Checks if a point is inside the rectangle.
590    #[must_use]
591    pub fn contains_point(&self, point: &Vector) -> bool {
592        point.x >= self.pos.x
593            && point.x < self.pos.x + self.size.x
594            && point.y >= self.pos.y
595            && point.y < self.pos.y + self.size.y
596    }
597
598    /// Checks if another rectangle is completely inside this rectangle.
599    #[must_use]
600    pub fn contains_rect(&self, other: &Self) -> bool {
601        self.contains_point(&other.pos) && self.contains_point(&(other.pos + other.size))
602    }
603
604    #[inline]
605    #[must_use]
606    pub fn is_overlapping(self, other: Self) -> bool {
607        !(self.right() < other.left()
608            || self.left() > other.right()
609            || self.bottom() > other.top()
610            || self.top() < other.bottom())
611    }
612
613    /// Expands the rectangle by a given offset.
614    #[must_use]
615    pub fn expanded(&self, offset: Vector) -> Self {
616        Self {
617            pos: self.pos - offset,
618            size: self.size + offset * Fp::from(2.0),
619        }
620    }
621
622    /// Contracts the rectangle by a given offset.
623    #[must_use]
624    pub fn contracted(&self, offset: Vector) -> Self {
625        Self {
626            pos: self.pos + offset,
627            size: self.size - offset * Fp::from(2.0),
628        }
629    }
630
631    /// Calculates the aspect ratio of the rectangle.
632    #[must_use]
633    pub fn aspect_ratio(&self) -> Fp {
634        self.size.x / self.size.y
635    }
636}
637
638impl fmt::Debug for Rect {
639    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640        write!(
641            f,
642            "rect:({:?},{:?},{:?},{:?})",
643            self.pos.x, self.pos.y, self.size.x, self.size.y
644        )
645    }
646}
647
648impl fmt::Display for Rect {
649    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
650        write!(
651            f,
652            "({}, {}, {}, {})",
653            self.pos.x, self.pos.y, self.size.x, self.size.y
654        )
655    }
656}
657
658impl From<(i16, i16, i16, i16)> for Rect {
659    fn from(values: (i16, i16, i16, i16)) -> Self {
660        Self {
661            pos: Vector::from((values.0, values.1)),
662            size: Vector::from((values.2, values.3)),
663        }
664    }
665}
666
667impl From<(f32, f32, f32, f32)> for Rect {
668    fn from(values: (f32, f32, f32, f32)) -> Self {
669        Self {
670            pos: Vector::from((values.0, values.1)),
671            size: Vector::from((values.2, values.3)),
672        }
673    }
674}