fey_math/
quad.rs

1use crate::{
2    Float, Line, Num, Rect, Vec2, impl_approx, impl_bytemuck, impl_casts, impl_interp, line,
3};
4use serde::{Deserialize, Serialize};
5
6pub type QuadF = Quad<f32>;
7pub type QuadI = Quad<i32>;
8
9/// A quad, represented by 4 points.
10#[repr(transparent)]
11#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
12pub struct Quad<T>(pub [Vec2<T>; 4]);
13
14impl_bytemuck!(Quad);
15
16impl_interp!(
17    NAME = Quad
18    INDICES = [0, 1, 2, 3]
19);
20
21impl_casts!(
22    NAME = Quad
23    FIELDS = (0)
24);
25
26impl_approx!(
27    NAME = Quad
28    FIELDS = (0)
29);
30
31/// Create a [`Quad`].
32#[inline]
33pub const fn quad<T>(a: Vec2<T>, b: Vec2<T>, c: Vec2<T>, d: Vec2<T>) -> Quad<T> {
34    Quad([a, b, c, d])
35}
36
37impl<T> Quad<T> {
38    /// Create a new quad.
39    #[inline]
40    pub const fn new(a: Vec2<T>, b: Vec2<T>, c: Vec2<T>, d: Vec2<T>) -> Self {
41        Self([a, b, c, d])
42    }
43}
44
45impl<T: Copy> Quad<T> {
46    /// First point of the quad.
47    #[inline]
48    pub const fn a(&self) -> Vec2<T> {
49        self.0[0]
50    }
51
52    /// Second point of the quad
53    #[inline]
54    pub const fn b(&self) -> Vec2<T> {
55        self.0[1]
56    }
57
58    /// Third point of the quad.
59    #[inline]
60    pub const fn c(&self) -> Vec2<T> {
61        self.0[2]
62    }
63
64    /// Third point of the quad.
65    #[inline]
66    pub const fn d(&self) -> Vec2<T> {
67        self.0[3]
68    }
69
70    /// The quad's 4 edges.
71    #[inline]
72    pub fn edges(&self) -> [Line<T>; 4] {
73        [
74            self.edge_ab(),
75            self.edge_bc(),
76            self.edge_cd(),
77            self.edge_da(),
78        ]
79    }
80
81    /// The quad's a -> b edge.
82    #[inline]
83    pub const fn edge_ab(&self) -> Line<T> {
84        line(self.a(), self.b())
85    }
86
87    /// The quad's b -> c edge.
88    #[inline]
89    pub const fn edge_bc(&self) -> Line<T> {
90        line(self.b(), self.c())
91    }
92
93    /// The quad's c -> d edge.
94    #[inline]
95    pub const fn edge_cd(&self) -> Line<T> {
96        line(self.c(), self.d())
97    }
98
99    /// The quad's d -> a edge.
100    #[inline]
101    pub const fn edge_da(&self) -> Line<T> {
102        line(self.d(), self.a())
103    }
104}
105
106impl<T: Num> Quad<T> {
107    /// A quad with all points set to `(0, 0)`.
108    pub const ZERO: Self = Self([Vec2::ZERO; 4]);
109
110    /// Create a quad equivalent to the provided rectangle.
111    pub fn from_rect(rect: Rect<T>) -> Self {
112        Self(rect.corners())
113    }
114
115    /// The vector a -> b of the quad.
116    #[inline]
117    pub fn ab(&self) -> Vec2<T> {
118        self.a() - self.b()
119    }
120
121    /// The vector b -> c of the quad.
122    #[inline]
123    pub fn bc(&self) -> Vec2<T> {
124        self.c() - self.b()
125    }
126
127    /// The vector c -> d of the quad.
128    #[inline]
129    pub fn cd(&self) -> Vec2<T> {
130        self.d() - self.c()
131    }
132
133    /// The vector d -> a of the quad.
134    #[inline]
135    pub fn da(&self) -> Vec2<T> {
136        self.a() - self.d()
137    }
138}
139
140impl<T: Float> Quad<T> {
141    /// Return the outward-facing normal of edge a -> b.
142    #[inline]
143    pub fn norm_ab(&self) -> Vec2<T> {
144        self.ab().turn_left().norm()
145    }
146
147    /// Return the outward-facing normal of edge b -> c.
148    #[inline]
149    pub fn norm_bc(&self) -> Vec2<T> {
150        self.bc().turn_left().norm()
151    }
152
153    /// Return the outward-facing normal of edge c -> a.
154    #[inline]
155    pub fn norm_cd(&self) -> Vec2<T> {
156        self.cd().turn_left().norm()
157    }
158
159    /// Return the outward-facing normal of edge c -> a.
160    #[inline]
161    pub fn norm_da(&self) -> Vec2<T> {
162        self.da().turn_left().norm()
163    }
164
165    /// Create a quad representing a "line segment" from `start` to
166    /// `end`, with the thickness of `width`. Useful for rendering.
167    #[inline]
168    pub fn line(line: impl Into<Line<T>>, width: T) -> Self {
169        let line = line.into();
170        let n2 = T::ONE + T::ONE;
171        let off = (line.end - line.start).turn_left().norm() * (width / n2);
172        Self([
173            line.start + off,
174            line.end + off,
175            line.end - off,
176            line.start - off,
177        ])
178    }
179
180    /// Create a quad representing a "line segment" from `start` to
181    /// `end`, with the provided start and end thickness. Useful for rendering.
182    #[inline]
183    pub fn stroke(line: impl Into<Line<T>>, start_width: T, end_width: T) -> Self {
184        let line = line.into();
185        let off = (line.end - line.start).turn_left().norm();
186        let off_a = off * (start_width * T::HALF);
187        let off_b = off * (end_width * T::HALF);
188        Self([
189            line.start + off_a,
190            line.end + off_b,
191            line.end - off_b,
192            line.start - off_a,
193        ])
194    }
195
196    #[inline]
197    pub fn transform_by(&self, f: impl FnMut(Vec2<T>) -> Vec2<T>) -> Self {
198        Self(self.0.map(f))
199    }
200}
201
202impl<T> AsRef<[Vec2<T>]> for Quad<T> {
203    #[inline]
204    fn as_ref(&self) -> &[Vec2<T>] {
205        self.0.as_slice()
206    }
207}
208
209impl<T> From<(Vec2<T>, Vec2<T>, Vec2<T>, Vec2<T>)> for Quad<T> {
210    #[inline]
211    fn from((a, b, c, d): (Vec2<T>, Vec2<T>, Vec2<T>, Vec2<T>)) -> Self {
212        Self([a, b, c, d])
213    }
214}
215
216impl<T> From<Quad<T>> for (Vec2<T>, Vec2<T>, Vec2<T>, Vec2<T>) {
217    #[inline]
218    fn from(Quad([a, b, c, d]): Quad<T>) -> Self {
219        (a, b, c, d)
220    }
221}
222
223impl<T> From<[Vec2<T>; 4]> for Quad<T> {
224    #[inline]
225    fn from(value: [Vec2<T>; 4]) -> Self {
226        Self(value)
227    }
228}
229
230impl<T> From<Quad<T>> for [Vec2<T>; 4] {
231    #[inline]
232    fn from(value: Quad<T>) -> Self {
233        value.0
234    }
235}
236
237impl<T: Num> From<Rect<T>> for Quad<T> {
238    #[inline]
239    fn from(value: Rect<T>) -> Self {
240        Self::from_rect(value)
241    }
242}