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()
154 .iter()
155 .map(|p| p.x)
156 .min()
157 .expect("shape has no points")
158 }
159
160 #[must_use]
162 fn right(&self) -> isize {
163 self.points()
164 .iter()
165 .map(|p| p.x)
166 .max()
167 .expect("shape has no points")
168 }
169
170 #[must_use]
172 fn top(&self) -> isize {
173 self.points()
174 .iter()
175 .map(|p| p.y)
176 .min()
177 .expect("shape has no points")
178 }
179
180 #[must_use]
182 fn bottom(&self) -> isize {
183 self.points()
184 .iter()
185 .map(|p| p.y)
186 .max()
187 .expect("shape has no points")
188 }
189
190 #[must_use]
191 fn top_left(&self) -> Coord {
192 coord!(self.left(), self.top())
193 }
194
195 #[must_use]
196 fn top_right(&self) -> Coord {
197 coord!(self.right(), self.top())
198 }
199
200 #[must_use]
201 fn bottom_left(&self) -> Coord {
202 coord!(self.left(), self.bottom())
203 }
204
205 #[must_use]
206 fn bottom_right(&self) -> Coord {
207 coord!(self.right(), self.bottom())
208 }
209
210 #[must_use]
212 fn scale(&self, factor: f32) -> Self
213 where
214 Self: Sized,
215 {
216 self.scale_around(factor, self.center())
217 }
218
219 #[must_use]
221 fn scale_around(&self, factor: f32, point: Coord) -> Self
222 where
223 Self: Sized,
224 {
225 let points = scale_points(point, &self.points(), factor);
226 self.rebuild(&points)
227 }
228
229 #[must_use]
232 fn outline_pixels(&self) -> Vec<Coord>;
233
234 #[must_use]
237 fn filled_pixels(&self) -> Vec<Coord>;
238
239 #[must_use]
241 fn to_shape_box(&self) -> ShapeBox;
242}
243
244pub trait IntersectsContains: Shape + ContainsShape + IntersectsShape + Sized {
246 #[must_use]
251 fn contains_shape(&self, other: &dyn Shape) -> Option<bool> {
252 if let Some(line) = other.as_any().downcast_ref::<Line>() {
253 return Some(self.contains_line(line));
254 }
255 if let Some(rect) = other.as_any().downcast_ref::<Rect>() {
256 return Some(self.contains_rect(rect));
257 }
258 if let Some(triangle) = other.as_any().downcast_ref::<Triangle>() {
259 return Some(self.contains_triangle(triangle));
260 }
261 if let Some(polygon) = other.as_any().downcast_ref::<Polygon>() {
262 return Some(self.contains_polygon(polygon));
263 }
264 if let Some(circle) = other.as_any().downcast_ref::<Circle>() {
265 return Some(self.contains_circle(circle));
266 }
267 if let Some(ellipse) = other.as_any().downcast_ref::<Ellipse>() {
268 return Some(self.contains_ellipse(ellipse));
269 }
270 if let Some(shapebox) = other.as_any().downcast_ref::<ShapeBox>() {
271 return Some(match shapebox {
272 ShapeBox::Line(line) => self.contains_line(line),
273 ShapeBox::Rect(rect) => self.contains_rect(rect),
274 ShapeBox::Triangle(triangle) => self.contains_triangle(triangle),
275 ShapeBox::Circle(circle) => self.contains_circle(circle),
276 ShapeBox::Ellipse(ellipse) => self.contains_ellipse(ellipse),
277 ShapeBox::Polygon(polygon) => self.contains_polygon(polygon),
278 });
279 }
280 None
281 }
282
283 #[must_use]
288 fn intersects_shape(&self, other: &dyn Shape) -> Option<bool> {
289 if let Some(line) = other.as_any().downcast_ref::<Line>() {
290 return Some(self.intersects_line(line));
291 }
292 if let Some(rect) = other.as_any().downcast_ref::<Rect>() {
293 return Some(self.intersects_rect(rect));
294 }
295 if let Some(triangle) = other.as_any().downcast_ref::<Triangle>() {
296 return Some(self.intersects_triangle(triangle));
297 }
298 if let Some(polygon) = other.as_any().downcast_ref::<Polygon>() {
299 return Some(self.intersects_polygon(polygon));
300 }
301 if let Some(circle) = other.as_any().downcast_ref::<Circle>() {
302 return Some(self.intersects_circle(circle));
303 }
304 if let Some(ellipse) = other.as_any().downcast_ref::<Ellipse>() {
305 return Some(self.intersects_ellipse(ellipse));
306 }
307 if let Some(shapebox) = other.as_any().downcast_ref::<ShapeBox>() {
308 return Some(match shapebox {
309 ShapeBox::Line(line) => self.intersects_line(line),
310 ShapeBox::Rect(rect) => self.intersects_rect(rect),
311 ShapeBox::Triangle(triangle) => self.intersects_triangle(triangle),
312 ShapeBox::Circle(circle) => self.intersects_circle(circle),
313 ShapeBox::Ellipse(ellipse) => self.intersects_ellipse(ellipse),
314 ShapeBox::Polygon(polygon) => self.intersects_polygon(polygon),
315 });
316 }
317 None
318 }
319}
320
321fn new_hash_set() -> FnvHashSet<Coord> {
322 FnvHashSet::default()
323}
324
325#[cfg(test)]
326mod test {
327 use crate::prelude::*;
328
329 pub fn check_points(expected: &[(isize, isize)], actual: &[Coord]) {
330 let mut expected: Vec<Coord> = expected.iter().map(|(x, y)| coord!(*x, *y)).collect();
331 let mut unexpected = vec![];
332 for point in actual {
333 if let Some(i) = expected.iter().position(|p| p == point) {
334 expected.remove(i);
335 } else {
336 unexpected.push(point);
337 }
338 }
339 let mut message = String::new();
340 if !expected.is_empty() {
341 message.push_str(&format!("Points not found: {:?}", expected));
342 }
343 if !unexpected.is_empty() {
344 message.push_str(&format!("Points unexpectedly found: {:?}", unexpected));
345 }
346 if !message.is_empty() {
347 panic!("{message}");
348 }
349 }
350
351 #[test]
352 fn generic_contains() {
353 let outer = Rect::new((0, 0), (10, 10));
354 let inner = Line::new((2, 2), (4, 4));
355
356 assert!(outer.contains_line(&inner));
357 assert_eq!(outer.contains_shape(&inner), Some(true));
358
359 let outside = Line::new((-3, 200), (-1, -1));
360 assert!(!outer.contains_line(&outside));
361 assert_eq!(outer.contains_shape(&outside), Some(false));
362 }
363
364 #[test]
365 fn shapebox_intersects() {
366 let line = Line::new((10, 10), (20, 20));
367 let rect = Rect::new((5, 5), (15, 15));
368 let shape_box = rect.to_shape_box();
369 assert_eq!(line.intersects_shape(&rect), Some(true));
370 assert_eq!(line.intersects_shape(&shape_box), Some(true));
371 }
372}