1use rand::Rng;
2use std::fmt::Debug;
3
4pub trait Shape: Debug {
5 fn bounding_box(&self) -> Rectangle;
6 fn as_any(&self) -> &dyn std::any::Any;
7}
8
9#[derive(Debug, Copy, Clone)]
10pub struct Circle {
11 pub x: f32,
12 pub y: f32,
13 pub radius: f32,
14 pub bounding_box: Rectangle,
15}
16
17impl Circle {
18 pub fn new(x: f32, y: f32, radius: f32) -> Self {
19 let bounding_box = Rectangle {
20 x: x,
21 y: y,
22 width: radius * 2.0,
23 height: radius * 2.0,
24 };
25 Self {
26 x,
27 y,
28 radius,
29 bounding_box,
30 }
31 }
32
33 pub fn x(&self) -> f32 {
34 self.x
35 }
36
37 pub fn y(&self) -> f32 {
38 self.y
39 }
40
41 pub fn radius(&self) -> f32 {
42 self.radius
43 }
44
45 pub fn update(&mut self, x: f32, y: f32) {
46 self.x = x;
47 self.y = y;
48 self.update_bounding_box();
49 }
50
51 pub fn update_with_radius(&mut self, x: f32, y: f32, radius: f32) {
52 self.x = x;
53 self.y = y;
54 self.radius = radius;
55 self.update_bounding_box();
56 }
57
58 fn update_bounding_box(&mut self) {
60 self.bounding_box = Rectangle {
61 x: self.x,
62 y: self.y,
63 width: self.radius * 2.0,
64 height: self.radius * 2.0,
65 };
66 }
67}
68
69impl Default for Circle {
70 fn default() -> Self {
71 let default_rectangle = Rectangle::default();
72 Self {
73 x: 0.0,
74 y: 0.0,
75 radius: 0.0,
76 bounding_box: default_rectangle,
77 }
78 }
79}
80
81impl Shape for Circle {
82 fn bounding_box(&self) -> Rectangle {
83 self.bounding_box
84 }
85
86 fn as_any(&self) -> &dyn std::any::Any {
87 self
88 }
89}
90
91#[derive(Debug, Copy, Clone)]
92pub struct Rectangle {
93 pub x: f32,
94 pub y: f32,
95 pub width: f32,
96 pub height: f32,
97}
98
99impl Rectangle {
100 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
101 Self {
102 x,
103 y,
104 width,
105 height,
106 }
107 }
108
109 pub fn width(&self) -> f32 {
110 self.width
111 }
112
113 pub fn height(&self) -> f32 {
114 self.height
115 }
116
117 pub fn left(&self) -> f32 {
118 self.x - self.width / 2.0
119 }
120
121 pub fn right(&self) -> f32 {
122 self.x + self.width / 2.0
123 }
124
125 pub fn top(&self) -> f32 {
126 self.y - self.height / 2.0
127 }
128
129 pub fn bottom(&self) -> f32 {
130 self.y + self.height / 2.0
131 }
132
133 pub fn top_left(&self) -> (f32, f32) {
134 (self.left(), self.top())
135 }
136
137 pub fn top_right(&self) -> (f32, f32) {
138 (self.right(), self.top())
139 }
140
141 pub fn bottom_left(&self) -> (f32, f32) {
142 (self.left(), self.bottom())
143 }
144
145 pub fn bottom_right(&self) -> (f32, f32) {
146 (self.right(), self.bottom())
147 }
148
149 pub fn distance_to_point(&self, x: f32, y: f32) -> f32 {
150 let dx = (x - self.x).abs() - self.width / 2.0;
151 let dy = (y - self.y).abs() - self.height / 2.0;
152 f32::max(dx, 0.0).powi(2) + f32::max(dy, 0.0).powi(2)
153 }
154
155 pub fn contains_circle(&self, x: f32, y: f32, radius: f32) -> bool {
156 let dx = (x - self.x).abs();
157 let dy = (y - self.y).abs();
158 let half_width = self.width / 2.0;
159 let half_height = self.height / 2.0;
160 if dx > half_width + radius || dy > half_height + radius {
161 return false;
162 }
163 if dx <= half_width || dy <= half_height {
164 return true;
165 }
166 let corner_distance_sq = (dx - half_width).powi(2) + (dy - half_height).powi(2);
167 corner_distance_sq <= radius.powi(2)
168 }
169
170 pub fn contains_point(&self, x: f32, y: f32) -> bool {
171 x >= self.left() && x <= self.right() && y >= self.top() && y <= self.bottom()
172 }
173
174 pub fn expand_to_include(&mut self, other: &Rectangle) {
175 let left = f32::min(self.left(), other.left());
176 let right = f32::max(self.right(), other.right());
177 let top = f32::min(self.top(), other.top());
178 let bottom = f32::max(self.bottom(), other.bottom());
179 self.x = (left + right) / 2.0;
180 self.y = (top + bottom) / 2.0;
181 self.width = right - left;
182 self.height = bottom - top;
183 }
184
185 pub fn get_random_circle_coords_inside<R: Rng>(&self, radius: f32, rng: &mut R) -> (f32, f32) {
186 let radius = radius + 1.0;
188 (
189 self._safe_randf32(rng, self.left() + radius, self.right() - radius),
190 self._safe_randf32(rng, self.top() + radius, self.bottom() - radius),
191 )
192 }
193
194 fn _safe_randf32<R: Rng>(&self, rng: &mut R, min: f32, max: f32) -> f32 {
195 if min > max {
196 return min;
197 }
198 rng.gen_range(min..=max)
199 }
200}
201
202impl Default for Rectangle {
203 fn default() -> Self {
204 Self {
205 x: 0.0,
206 y: 0.0,
207 width: 0.0,
208 height: 0.0,
209 }
210 }
211}
212
213impl Shape for Rectangle {
214 fn bounding_box(&self) -> Rectangle {
215 *self
216 }
217
218 fn as_any(&self) -> &dyn std::any::Any {
219 self
220 }
221}
222
223#[derive(Clone, Debug)]
224pub enum ShapeEnum {
225 Circle(Circle),
226 Rectangle(Rectangle),
227}
228
229impl Shape for ShapeEnum {
230 fn bounding_box(&self) -> Rectangle {
231 match self {
232 ShapeEnum::Circle(circle) => circle.bounding_box(),
233 ShapeEnum::Rectangle(rectangle) => rectangle.bounding_box(),
234 }
235 }
236
237 fn as_any(&self) -> &dyn std::any::Any {
238 match self {
239 ShapeEnum::Circle(circle) => circle.as_any(),
240 ShapeEnum::Rectangle(rectangle) => rectangle.as_any(),
241 }
242 }
243}