graphics_shapes/
shape_box.rs

1use crate::prelude::*;
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Serialize};
4
5/// Stores shapes in a generic way so they can be stored in lists, drawn in bulk, etc
6///
7/// Implements all [Shape] methods except `from_points` and passes the call to the underlying shape
8/// but does not support shape specific methods such as [Rect::is_square]
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10#[derive(Debug, Clone, PartialEq)]
11pub enum ShapeBox {
12    Line(Line),
13    Rect(Rect),
14    Triangle(Triangle),
15    Circle(Circle),
16    Ellipse(Ellipse),
17    Polygon(Polygon),
18}
19
20macro_rules! per_shape_0 {
21    ($shape_box: expr, $method: path) => {
22        match $shape_box {
23            ShapeBox::Line(shape) => $method(shape),
24            ShapeBox::Rect(shape) => $method(shape),
25            ShapeBox::Circle(shape) => $method(shape),
26            ShapeBox::Triangle(shape) => $method(shape),
27            ShapeBox::Ellipse(shape) => $method(shape),
28            ShapeBox::Polygon(shape) => $method(shape),
29        }
30    };
31}
32
33macro_rules! per_shape_1 {
34    ($shape_box: expr, $method: path, $param1: expr) => {
35        match $shape_box {
36            ShapeBox::Line(shape) => $method(shape, $param1),
37            ShapeBox::Rect(shape) => $method(shape, $param1),
38            ShapeBox::Circle(shape) => $method(shape, $param1),
39            ShapeBox::Triangle(shape) => $method(shape, $param1),
40            ShapeBox::Ellipse(shape) => $method(shape, $param1),
41            ShapeBox::Polygon(shape) => $method(shape, $param1),
42        }
43    };
44}
45
46impl Shape for ShapeBox {
47    fn from_points(_: &[Coord]) -> Self
48    where
49        Self: Sized,
50    {
51        unimplemented!("ShapeBox can't be constructed from points")
52    }
53
54    fn rebuild(&self, points: &[Coord]) -> Self
55    where
56        Self: Sized,
57    {
58        match self {
59            ShapeBox::Line(_) => ShapeBox::Line(Line::from_points(points)),
60            ShapeBox::Rect(_) => ShapeBox::Rect(Rect::from_points(points)),
61            ShapeBox::Triangle(_) => ShapeBox::Triangle(Triangle::from_points(points)),
62            ShapeBox::Circle(_) => ShapeBox::Circle(Circle::from_points(points)),
63            ShapeBox::Ellipse(_) => ShapeBox::Ellipse(Ellipse::from_points(points)),
64            ShapeBox::Polygon(_) => ShapeBox::Polygon(Polygon::from_points(points)),
65        }
66    }
67
68    fn contains(&self, point: Coord) -> bool {
69        per_shape_1!(self, Shape::contains, point)
70    }
71
72    fn points(&self) -> Vec<Coord> {
73        per_shape_0!(self, Shape::points)
74    }
75
76    fn center(&self) -> Coord {
77        per_shape_0!(self, Shape::center)
78    }
79
80    fn outline_pixels(&self) -> Vec<Coord> {
81        per_shape_0!(self, Shape::outline_pixels)
82    }
83
84    fn filled_pixels(&self) -> Vec<Coord> {
85        per_shape_0!(self, Shape::filled_pixels)
86    }
87
88    /// Same as `clone()`
89    fn to_shape_box(&self) -> ShapeBox {
90        self.clone()
91    }
92}
93
94impl IntersectsShape for ShapeBox {
95    fn intersects_rect(&self, rect: &Rect) -> bool {
96        per_shape_1!(self, IntersectsShape::intersects_rect, rect)
97    }
98
99    fn intersects_circle(&self, circle: &Circle) -> bool {
100        per_shape_1!(self, IntersectsShape::intersects_circle, circle)
101    }
102
103    fn intersects_line(&self, line: &Line) -> bool {
104        per_shape_1!(self, IntersectsShape::intersects_line, line)
105    }
106
107    fn intersects_triangle(&self, triangle: &Triangle) -> bool {
108        per_shape_1!(self, IntersectsShape::intersects_triangle, triangle)
109    }
110
111    fn intersects_ellipse(&self, ellipse: &Ellipse) -> bool {
112        per_shape_1!(self, IntersectsShape::intersects_ellipse, ellipse)
113    }
114
115    fn intersects_polygon(&self, polygon: &Polygon) -> bool {
116        per_shape_1!(self, IntersectsShape::intersects_polygon, polygon)
117    }
118}
119
120impl ContainsShape for ShapeBox {
121    fn contains_rect(&self, rect: &Rect) -> bool {
122        per_shape_1!(self, ContainsShape::contains_rect, rect)
123    }
124
125    fn contains_circle(&self, circle: &Circle) -> bool {
126        per_shape_1!(self, ContainsShape::contains_circle, circle)
127    }
128
129    fn contains_line(&self, line: &Line) -> bool {
130        per_shape_1!(self, ContainsShape::contains_line, line)
131    }
132
133    fn contains_triangle(&self, triangle: &Triangle) -> bool {
134        per_shape_1!(self, ContainsShape::contains_triangle, triangle)
135    }
136
137    fn contains_ellipse(&self, ellipse: &Ellipse) -> bool {
138        per_shape_1!(self, ContainsShape::contains_ellipse, ellipse)
139    }
140
141    fn contains_polygon(&self, polygon: &Polygon) -> bool {
142        per_shape_1!(self, ContainsShape::contains_polygon, polygon)
143    }
144}
145
146impl IntersectsContains for ShapeBox {}
147
148macro_rules! shapebox_shape {
149    ($shape: ty, $variant: path) => {
150        impl From<$shape> for ShapeBox {
151            fn from(value: $shape) -> Self {
152                $variant(value)
153            }
154        }
155    };
156}
157
158shapebox_shape!(Line, ShapeBox::Line);
159shapebox_shape!(Rect, ShapeBox::Rect);
160shapebox_shape!(Triangle, ShapeBox::Triangle);
161shapebox_shape!(Circle, ShapeBox::Circle);
162shapebox_shape!(Ellipse, ShapeBox::Ellipse);
163shapebox_shape!(Polygon, ShapeBox::Polygon);
164
165#[cfg(test)]
166mod test {
167    use crate::prelude::*;
168    use crate::shape_box::ShapeBox;
169
170    #[test]
171    fn basic() {
172        let rect_box = ShapeBox::from(Rect::new((10, 10), (30, 30)));
173        assert!(rect_box.contains(coord!(12, 15)));
174        assert!(rect_box.intersects_line(&Line::new((18, 0), (0, 30))));
175    }
176}