1#![forbid(unsafe_code)]
20
21use crate::coord::Coord;
22use crate::general_math::{rotate_points, scale_points};
23use crate::prelude::*;
24use crate::shape_box::ShapeBox;
25use fnv::FnvHashSet;
26use std::any::Any;
27
28pub mod circle;
29#[macro_use]
30pub mod coord;
31pub mod contains;
32pub mod ellipse;
33pub mod general_math;
34pub mod intersection;
35pub mod lerp;
36pub mod line;
37pub mod polygon;
38pub mod rect;
39pub mod shape_box;
40pub mod triangle;
41
42pub mod prelude {
43    pub use crate::circle::*;
44    pub use crate::contains::ContainsShape;
45    pub use crate::coord;
46    pub use crate::coord::*;
47    pub use crate::ellipse::*;
48    pub use crate::intersection::IntersectsShape;
49    pub use crate::lerp::*;
50    pub use crate::line::*;
51    pub use crate::polygon::*;
52    pub use crate::rect::*;
53    pub use crate::triangle::*;
54    pub use crate::IntersectsContains;
55    pub use crate::Shape;
56}
57
58pub trait AnyToAny: 'static {
59    fn as_any(&self) -> &dyn Any;
60}
61
62impl<T: 'static> AnyToAny for T {
63    fn as_any(&self) -> &dyn Any {
64        self
65    }
66}
67
68pub trait Shape: AnyToAny {
69    #[must_use]
71    fn from_points(points: &[Coord]) -> Self
72    where
73        Self: Sized;
74
75    #[must_use]
77    fn rebuild(&self, points: &[Coord]) -> Self
78    where
79        Self: Sized;
80
81    #[must_use]
83    fn translate_by(&self, delta: Coord) -> Self
84    where
85        Self: Sized,
86    {
87        let points: Vec<Coord> = self.points().iter().map(|p| *p + delta).collect();
88        self.rebuild(&points)
89    }
90
91    #[must_use]
97    fn move_to(&self, point: Coord) -> Self
98    where
99        Self: Sized,
100    {
101        let diff = (point) - self.points()[0];
102        self.translate_by(diff)
103    }
104
105    #[must_use]
111    fn move_center_to(&self, point: Coord) -> Self
112    where
113        Self: Sized,
114    {
115        let diff = point - self.center();
116        self.translate_by(diff)
117    }
118
119    #[must_use]
121    fn contains(&self, point: Coord) -> bool;
122
123    #[must_use]
125    fn points(&self) -> Vec<Coord>;
126
127    #[must_use]
129    fn rotate(&self, degrees: isize) -> Self
130    where
131        Self: Sized,
132    {
133        self.rotate_around(degrees, self.center())
134    }
135
136    #[must_use]
138    fn rotate_around(&self, degrees: isize, point: Coord) -> Self
139    where
140        Self: Sized,
141    {
142        let points = rotate_points(point, &self.points(), degrees);
143        self.rebuild(&points)
144    }
145
146    #[must_use]
148    fn center(&self) -> Coord;
149
150    #[must_use]
152    fn left(&self) -> isize {
153        self.points().iter().map(|p| p.x).min().unwrap()
154    }
155
156    #[must_use]
158    fn right(&self) -> isize {
159        self.points().iter().map(|p| p.x).max().unwrap()
160    }
161
162    #[must_use]
164    fn top(&self) -> isize {
165        self.points().iter().map(|p| p.y).min().unwrap()
166    }
167
168    #[must_use]
170    fn bottom(&self) -> isize {
171        self.points().iter().map(|p| p.y).max().unwrap()
172    }
173
174    #[must_use]
175    fn top_left(&self) -> Coord {
176        coord!(self.left(), self.top())
177    }
178
179    #[must_use]
180    fn top_right(&self) -> Coord {
181        coord!(self.right(), self.top())
182    }
183
184    #[must_use]
185    fn bottom_left(&self) -> Coord {
186        coord!(self.left(), self.bottom())
187    }
188
189    #[must_use]
190    fn bottom_right(&self) -> Coord {
191        coord!(self.right(), self.bottom())
192    }
193
194    #[must_use]
196    fn scale(&self, factor: f32) -> Self
197    where
198        Self: Sized,
199    {
200        self.scale_around(factor, self.center())
201    }
202
203    #[must_use]
205    fn scale_around(&self, factor: f32, point: Coord) -> Self
206    where
207        Self: Sized,
208    {
209        let points = scale_points(point, &self.points(), factor);
210        self.rebuild(&points)
211    }
212
213    #[must_use]
216    fn outline_pixels(&self) -> Vec<Coord>;
217
218    #[must_use]
221    fn filled_pixels(&self) -> Vec<Coord>;
222
223    #[must_use]
225    fn to_shape_box(&self) -> ShapeBox;
226}
227
228pub trait IntersectsContains: Shape + ContainsShape + IntersectsShape + Sized {
230    #[must_use]
235    fn contains_shape(&self, other: &dyn Shape) -> Option<bool> {
236        if let Some(line) = other.as_any().downcast_ref::<Line>() {
237            return Some(self.contains_line(line));
238        }
239        if let Some(rect) = other.as_any().downcast_ref::<Rect>() {
240            return Some(self.contains_rect(rect));
241        }
242        if let Some(triangle) = other.as_any().downcast_ref::<Triangle>() {
243            return Some(self.contains_triangle(triangle));
244        }
245        if let Some(polygon) = other.as_any().downcast_ref::<Polygon>() {
246            return Some(self.contains_polygon(polygon));
247        }
248        if let Some(circle) = other.as_any().downcast_ref::<Circle>() {
249            return Some(self.contains_circle(circle));
250        }
251        if let Some(ellipse) = other.as_any().downcast_ref::<Ellipse>() {
252            return Some(self.contains_ellipse(ellipse));
253        }
254        if let Some(shapebox) = other.as_any().downcast_ref::<ShapeBox>() {
255            return Some(match shapebox {
256                ShapeBox::Line(line) => self.contains_line(line),
257                ShapeBox::Rect(rect) => self.contains_rect(rect),
258                ShapeBox::Triangle(triangle) => self.contains_triangle(triangle),
259                ShapeBox::Circle(circle) => self.contains_circle(circle),
260                ShapeBox::Ellipse(ellipse) => self.contains_ellipse(ellipse),
261                ShapeBox::Polygon(polygon) => self.contains_polygon(polygon),
262            });
263        }
264        None
265    }
266
267    #[must_use]
272    fn intersects_shape(&self, other: &dyn Shape) -> Option<bool> {
273        if let Some(line) = other.as_any().downcast_ref::<Line>() {
274            return Some(self.intersects_line(line));
275        }
276        if let Some(rect) = other.as_any().downcast_ref::<Rect>() {
277            return Some(self.intersects_rect(rect));
278        }
279        if let Some(triangle) = other.as_any().downcast_ref::<Triangle>() {
280            return Some(self.intersects_triangle(triangle));
281        }
282        if let Some(polygon) = other.as_any().downcast_ref::<Polygon>() {
283            return Some(self.intersects_polygon(polygon));
284        }
285        if let Some(circle) = other.as_any().downcast_ref::<Circle>() {
286            return Some(self.intersects_circle(circle));
287        }
288        if let Some(ellipse) = other.as_any().downcast_ref::<Ellipse>() {
289            return Some(self.intersects_ellipse(ellipse));
290        }
291        if let Some(shapebox) = other.as_any().downcast_ref::<ShapeBox>() {
292            return Some(match shapebox {
293                ShapeBox::Line(line) => self.intersects_line(line),
294                ShapeBox::Rect(rect) => self.intersects_rect(rect),
295                ShapeBox::Triangle(triangle) => self.intersects_triangle(triangle),
296                ShapeBox::Circle(circle) => self.intersects_circle(circle),
297                ShapeBox::Ellipse(ellipse) => self.intersects_ellipse(ellipse),
298                ShapeBox::Polygon(polygon) => self.intersects_polygon(polygon),
299            });
300        }
301        None
302    }
303}
304
305fn new_hash_set() -> FnvHashSet<Coord> {
306    FnvHashSet::default()
307}
308
309#[cfg(test)]
310mod test {
311    use crate::prelude::*;
312
313    pub fn check_points(expected: &[(isize, isize)], actual: &[Coord]) {
314        let mut expected: Vec<Coord> = expected.iter().map(|(x, y)| coord!(*x, *y)).collect();
315        let mut unexpected = vec![];
316        for point in actual {
317            if let Some(i) = expected.iter().position(|p| p == point) {
318                expected.remove(i);
319            } else {
320                unexpected.push(point);
321            }
322        }
323        let mut message = String::new();
324        if !expected.is_empty() {
325            message.push_str(&format!("Points not found: {:?}", expected));
326        }
327        if !unexpected.is_empty() {
328            message.push_str(&format!("Points unexpectedly found: {:?}", unexpected));
329        }
330        if !message.is_empty() {
331            panic!("{message}");
332        }
333    }
334
335    #[test]
336    fn generic_contains() {
337        let outer = Rect::new((0, 0), (10, 10));
338        let inner = Line::new((2, 2), (4, 4));
339
340        assert!(outer.contains_line(&inner));
341        assert_eq!(outer.contains_shape(&inner), Some(true));
342
343        let outside = Line::new((-3, 200), (-1, -1));
344        assert!(!outer.contains_line(&outside));
345        assert_eq!(outer.contains_shape(&outside), Some(false));
346    }
347
348    #[test]
349    fn shapebox_intersects() {
350        let line = Line::new((10, 10), (20, 20));
351        let rect = Rect::new((5, 5), (15, 15));
352        let shape_box = rect.to_shape_box();
353        assert_eq!(line.intersects_shape(&rect), Some(true));
354        assert_eq!(line.intersects_shape(&shape_box), Some(true));
355    }
356}