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}