ferravex_math/
shapes.rs

1use crate::{Vec2, Vec4, vec2};
2
3/// Axis-aligned 2D rectangle. Width and height should be nonnegative.
4#[repr(C)]
5#[derive(Clone, Copy, Debug, Default, PartialEq)]
6pub struct Rect {
7    pub x: f32,
8    pub y: f32,
9    pub w: f32,
10    pub h: f32,
11}
12impl Rect {
13    pub const ZERO: Self = Self::new(0.0, 0.0, 0.0, 0.0);
14    pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
15        Self { x, y, w, h }
16    }
17    pub const fn point(self) -> Vec2 {
18        vec2(self.x, self.y)
19    }
20    pub const fn size(self) -> Vec2 {
21        vec2(self.w, self.h)
22    }
23    pub fn center(self) -> Vec2 {
24        self.point() + self.size() * 0.5
25    }
26    pub const fn left(self) -> f32 {
27        self.x
28    }
29    pub const fn right(self) -> f32 {
30        self.x + self.w
31    }
32    pub const fn top(self) -> f32 {
33        self.y
34    }
35    pub const fn bottom(self) -> f32 {
36        self.y + self.h
37    }
38    pub fn is_finite(self) -> bool {
39        [self.x, self.y, self.w, self.h]
40            .into_iter()
41            .all(f32::is_finite)
42    }
43    pub fn contains(self, p: Vec2) -> bool {
44        self.is_finite()
45            && self.w >= 0.0
46            && self.h >= 0.0
47            && p.x >= self.x
48            && p.x <= self.right()
49            && p.y >= self.y
50            && p.y <= self.bottom()
51    }
52    /// Positive-area overlap. Touching edges are not solid penetration.
53    pub fn overlaps(self, other: &Self) -> bool {
54        self.w > 0.0
55            && self.h > 0.0
56            && other.w > 0.0
57            && other.h > 0.0
58            && self.x < other.right()
59            && self.right() > other.x
60            && self.y < other.bottom()
61            && self.bottom() > other.y
62    }
63    pub fn intersect(self, other: Self) -> Option<Self> {
64        if !self.overlaps(&other) {
65            return None;
66        }
67        let point = self.point().max(other.point());
68        let end = vec2(self.right(), self.bottom()).min(vec2(other.right(), other.bottom()));
69        Some(Self::new(
70            point.x,
71            point.y,
72            end.x - point.x,
73            end.y - point.y,
74        ))
75    }
76    pub fn combine_with(self, other: Self) -> Self {
77        let p = self.point().min(other.point());
78        let end = vec2(self.right(), self.bottom()).max(vec2(other.right(), other.bottom()));
79        Self::new(p.x, p.y, end.x - p.x, end.y - p.y)
80    }
81    pub fn move_to(&mut self, p: Vec2) {
82        self.x = p.x;
83        self.y = p.y;
84    }
85    pub fn offset(self, delta: Vec2) -> Self {
86        Self::new(self.x + delta.x, self.y + delta.y, self.w, self.h)
87    }
88    pub fn normalized(self) -> Self {
89        Self::new(
90            self.x.min(self.right()),
91            self.y.min(self.bottom()),
92            self.w.abs(),
93            self.h.abs(),
94        )
95    }
96}
97
98/// Unpremultiplied RGBA floating-point channels. No implicit gamma conversion.
99#[repr(C)]
100#[derive(Clone, Copy, Debug, Default, PartialEq)]
101pub struct Color {
102    pub r: f32,
103    pub g: f32,
104    pub b: f32,
105    pub a: f32,
106}
107impl Color {
108    pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
109        Self { r, g, b, a }
110    }
111    pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
112        Self::new(
113            r as f32 / 255.0,
114            g as f32 / 255.0,
115            b as f32 / 255.0,
116            a as f32 / 255.0,
117        )
118    }
119    /// Opaque 0xRRGGBB color.
120    pub const fn from_hex(rgb: u32) -> Self {
121        Self::from_rgba((rgb >> 16) as u8, (rgb >> 8) as u8, rgb as u8, 255)
122    }
123    pub const fn to_vec(self) -> Vec4 {
124        Vec4::new(self.r, self.g, self.b, self.a)
125    }
126    pub const fn to_array(self) -> [f32; 4] {
127        [self.r, self.g, self.b, self.a]
128    }
129    pub fn to_rgba(self) -> [u8; 4] {
130        self.to_array()
131            .map(|c| (c.clamp(0.0, 1.0) * 255.0).round() as u8)
132    }
133    pub const fn with_alpha(self, alpha: f32) -> Self {
134        Self { a: alpha, ..self }
135    }
136    pub fn lerp(self, other: Self, t: f32) -> Self {
137        Self::from(self.to_vec().lerp(other.to_vec(), t))
138    }
139    pub fn premultiplied(self) -> Self {
140        Self::new(self.r * self.a, self.g * self.a, self.b * self.a, self.a)
141    }
142}
143impl From<Vec4> for Color {
144    fn from(v: Vec4) -> Self {
145        Self::new(v.x, v.y, v.z, v.w)
146    }
147}
148impl From<[f32; 4]> for Color {
149    fn from(v: [f32; 4]) -> Self {
150        Self::new(v[0], v[1], v[2], v[3])
151    }
152}
153impl From<Color> for [f32; 4] {
154    fn from(v: Color) -> Self {
155        v.to_array()
156    }
157}
158impl From<Color> for Vec4 {
159    fn from(v: Color) -> Self {
160        v.to_vec()
161    }
162}