fey_math/
dyn_shape.rs

1use crate::{
2    Angle, Circle, Float, Line, Polygon, Polygonal, Projection, Quad, Radians, Ray, RayHit, Rect,
3    Shape, Triangle, Vec2,
4};
5
6pub type DynShapeF = DynShape<f32>;
7
8/// A circle, triangle, rect, quad, or polygon.
9///
10/// This itself implements [`Shape<T>`] and defers to each variant's
11/// implementation of the trait's methods.
12///
13/// This is convenient when you want lists of shapes that can be one
14/// of any of the variants, rather than all being the same. For example,
15/// colliders for entities in a core engine.
16#[derive(Debug, Clone)]
17pub enum DynShape<T> {
18    Circle(Circle<T>),
19    Triangle(Triangle<T>),
20    Rect(Rect<T>),
21    Quad(Quad<T>),
22    Polygon(Polygon<T>),
23}
24
25impl<T: Float> DynShape<T> {
26    /// Returns true if this shape overlaps the other.
27    #[inline]
28    pub fn overlaps(&self, other: &Self) -> bool {
29        match other {
30            Self::Circle(sh) => self.overlaps_circ(sh),
31            Self::Triangle(sh) => self.overlaps_poly(sh),
32            Self::Rect(sh) => self.overlaps_rect(sh),
33            Self::Quad(sh) => self.overlaps_poly(sh),
34            Self::Polygon(sh) => self.overlaps_poly(sh),
35        }
36    }
37
38    /// If this shape overlaps the other, returns a *push-out* vector
39    /// that can be used to translate it so they no longer overlap.
40    #[inline]
41    pub fn extract_from(&self, other: &Self) -> Option<Vec2<T>> {
42        match other {
43            Self::Circle(sh) => self.extract_from_circ(sh),
44            Self::Triangle(sh) => self.extract_from_poly(sh),
45            Self::Rect(sh) => self.extract_from_poly(sh),
46            Self::Quad(sh) => self.extract_from_poly(sh),
47            Self::Polygon(sh) => self.extract_from_poly(sh),
48        }
49    }
50
51    /// Plot the vertices of the shape. For polygons, it will be the vertices
52    /// of the polygon, and for circles it will be `CAP` points distributed
53    /// evenly along the circle's radius.
54    pub fn hull_points(&self, circle_seg_len: T, plot: impl FnMut(Vec2<T>)) {
55        match self {
56            Self::Circle(sh) => sh.hull_points(circle_seg_len, Radians(T::ZERO), plot),
57            Self::Triangle(sh) => sh.0.iter().copied().for_each(plot),
58            Self::Rect(sh) => sh.corners().into_iter().for_each(plot),
59            Self::Quad(sh) => sh.0.iter().copied().for_each(plot),
60            Self::Polygon(sh) => sh.points().iter().copied().for_each(plot),
61        }
62    }
63
64    /// Plot the vertices of the shape.
65    pub fn hull_points_n(&self, circle_count: T, plot: impl FnMut(Vec2<T>)) {
66        match self {
67            Self::Circle(sh) => sh.hull_points_n(circle_count, Radians(T::ZERO), plot),
68            Self::Triangle(sh) => sh.0.iter().copied().for_each(plot),
69            Self::Rect(sh) => sh.corners().into_iter().for_each(plot),
70            Self::Quad(sh) => sh.0.iter().copied().for_each(plot),
71            Self::Polygon(sh) => sh.points().iter().copied().for_each(plot),
72        }
73    }
74
75    /// Plot the edges of the shape. For polygons, it will be the edges of
76    /// the polygon, and for circles it will be `CAP` edgds distributed
77    /// evenly along the circle's radius.
78    pub fn hull_edges(&self, circle_seg_len: T, plot: impl FnMut(Line<T>)) {
79        match self {
80            Self::Circle(sh) => sh.hull_edges(circle_seg_len, Radians(T::ZERO), plot),
81            Self::Triangle(sh) => sh.visit_edges(plot),
82            Self::Rect(sh) => sh.visit_edges(plot),
83            Self::Quad(sh) => sh.visit_edges(plot),
84            Self::Polygon(sh) => sh.visit_edges(plot),
85        }
86    }
87
88    /// Plot the edges of the shape. For polygons, it will be the edges of
89    /// the polygon, and for circles it will be `CAP` edgds distributed
90    /// evenly along the circle's radius.
91    pub fn hull_edges_n(&self, circle_count: T, plot: impl FnMut(Line<T>)) {
92        match self {
93            Self::Circle(sh) => sh.hull_edges_n(circle_count, Radians(T::ZERO), plot),
94            Self::Triangle(sh) => sh.visit_edges(plot),
95            Self::Rect(sh) => sh.visit_edges(plot),
96            Self::Quad(sh) => sh.visit_edges(plot),
97            Self::Polygon(sh) => sh.visit_edges(plot),
98        }
99    }
100
101    pub fn transform_into(
102        &self,
103        into: &mut Self,
104        circle_out: CircleOut<T>,
105        rect_out: RectOut,
106        f: impl FnMut(Vec2<T>) -> Vec2<T>,
107    ) {
108        match self {
109            Self::Circle(sh) => match circle_out {
110                CircleOut::Circle => {
111                    *into = Self::Circle(sh.transform_by_retain(f));
112                }
113                CircleOut::SegCount { count, angle } => {
114                    if let Self::Polygon(into) = into {
115                        sh.transform_by_into_n(count, angle, into, f);
116                    } else {
117                        *into = Self::Polygon(sh.transform_by_n(count, angle, f));
118                    }
119                }
120                CircleOut::SegLen { len, angle } => {
121                    if let Self::Polygon(into) = into {
122                        sh.transform_by_into(len, angle, into, f);
123                    } else {
124                        *into = Self::Polygon(sh.transform_by(len, angle, f));
125                    }
126                }
127            },
128            Self::Triangle(sh) => {
129                if let Self::Triangle(into) = into {
130                    *into = sh.transform_by(f);
131                } else {
132                    *into = Self::Triangle(sh.transform_by(f));
133                }
134            }
135            Self::Rect(sh) => match rect_out {
136                RectOut::Rect => {
137                    if let Self::Rect(into) = into {
138                        *into = sh.transform_by_retain(f);
139                    } else {
140                        *into = Self::Rect(sh.transform_by_retain(f));
141                    }
142                }
143                RectOut::Quad => {
144                    if let Self::Quad(into) = into {
145                        *into = sh.transform_by(f);
146                    } else {
147                        *into = Self::Quad(sh.transform_by(f));
148                    }
149                }
150            },
151            Self::Quad(sh) => {
152                if let Self::Quad(into) = into {
153                    *into = sh.transform_by(f);
154                } else {
155                    *into = Self::Quad(sh.transform_by(f));
156                }
157            }
158            Self::Polygon(sh) => {
159                if let Self::Polygon(into) = into {
160                    sh.transform_by_into(into, f);
161                } else {
162                    *into = Self::Polygon(sh.transform_by(f));
163                }
164            }
165        }
166    }
167}
168
169macro_rules! delegate {
170    ($this:ident, $call:ident, $($arg:ident),*) => {
171        match $this {
172            Self::Circle(sh) => sh.$call($($arg),*),
173            Self::Triangle(sh) => sh.$call($($arg),*),
174            Self::Rect(sh) => sh.$call($($arg),*),
175            Self::Quad(sh) => sh.$call($($arg),*),
176            Self::Polygon(sh) => sh.$call($($arg),*),
177        }
178    }
179}
180
181impl<T: Float> Shape<T> for DynShape<T> {
182    #[inline]
183    fn centroid(&self) -> Vec2<T> {
184        delegate!(self, centroid,)
185    }
186
187    #[inline]
188    fn contains(&self, p: Vec2<T>) -> bool {
189        delegate!(self, contains, p)
190    }
191
192    #[inline]
193    fn bounds(&self) -> Rect<T> {
194        delegate!(self, bounds,)
195    }
196
197    #[inline]
198    fn project_onto_axis(&self, axis: Vec2<T>) -> Projection<T> {
199        delegate!(self, project_onto_axis, axis)
200    }
201
202    #[inline]
203    fn project_point(&self, p: Vec2<T>) -> Vec2<T> {
204        delegate!(self, project_point, p)
205    }
206
207    #[inline]
208    fn rayhit(&self, ray: &Ray<T>) -> bool {
209        delegate!(self, rayhit, ray)
210    }
211
212    #[inline]
213    fn raycast(&self, ray: &Ray<T>) -> Option<RayHit<T>> {
214        delegate!(self, raycast, ray)
215    }
216
217    #[inline]
218    fn overlaps_rect(&self, rect: &Rect<T>) -> bool {
219        delegate!(self, overlaps_rect, rect)
220    }
221
222    #[inline]
223    fn overlaps_circ(&self, circ: &Circle<T>) -> bool {
224        delegate!(self, overlaps_circ, circ)
225    }
226
227    #[inline]
228    fn overlaps_poly<P: Polygonal<T>>(&self, poly: &P) -> bool {
229        delegate!(self, overlaps_poly, poly)
230    }
231
232    #[inline]
233    fn extract_from_circ(&self, circ: &Circle<T>) -> Option<Vec2<T>> {
234        delegate!(self, extract_from_circ, circ)
235    }
236
237    #[inline]
238    fn extract_from_poly<P: Polygonal<T>>(&self, poly: &P) -> Option<Vec2<T>> {
239        delegate!(self, extract_from_poly, poly)
240    }
241
242    #[inline]
243    fn is_convex(&self) -> bool {
244        delegate!(self, is_convex,)
245    }
246}
247
248impl<T> From<Circle<T>> for DynShape<T> {
249    #[inline]
250    fn from(value: Circle<T>) -> Self {
251        Self::Circle(value)
252    }
253}
254
255impl<T> From<Triangle<T>> for DynShape<T> {
256    #[inline]
257    fn from(value: Triangle<T>) -> Self {
258        Self::Triangle(value)
259    }
260}
261
262impl<T> From<Rect<T>> for DynShape<T> {
263    #[inline]
264    fn from(value: Rect<T>) -> Self {
265        Self::Rect(value)
266    }
267}
268
269impl<T> From<Quad<T>> for DynShape<T> {
270    #[inline]
271    fn from(value: Quad<T>) -> Self {
272        Self::Quad(value)
273    }
274}
275
276impl<T> From<Polygon<T>> for DynShape<T> {
277    #[inline]
278    fn from(value: Polygon<T>) -> Self {
279        Self::Polygon(value)
280    }
281}
282
283#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
284pub enum CircleOut<T> {
285    Circle,
286    SegCount { count: T, angle: Radians<T> },
287    SegLen { len: T, angle: Radians<T> },
288}
289
290impl<T> CircleOut<T> {
291    #[inline]
292    pub fn seg_count(count: T, angle: impl Angle<T>) -> Self {
293        Self::SegCount {
294            count,
295            angle: angle.to_radians(),
296        }
297    }
298
299    #[inline]
300    pub fn seg_len(len: T, angle: impl Angle<T>) -> Self {
301        Self::SegLen {
302            len,
303            angle: angle.to_radians(),
304        }
305    }
306}
307
308#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
309pub enum RectOut {
310    Rect,
311    Quad,
312}