quadtree/
shapes.rs

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    // Helper method to update the bounding box
59    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        // Increase radius by 1 in calculations to add a minimal margin.
187        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}