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}