1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq, Default)]
9#[allow(dead_code)]
10pub struct Rect2 {
11 pub min_x: f32,
12 pub min_y: f32,
13 pub max_x: f32,
14 pub max_y: f32,
15}
16
17impl Rect2 {
18 #[allow(dead_code)]
19 pub fn new(min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> Self {
20 Self {
21 min_x,
22 min_y,
23 max_x,
24 max_y,
25 }
26 }
27
28 #[allow(dead_code)]
29 pub fn contains_point(&self, x: f32, y: f32) -> bool {
30 (self.min_x..=self.max_x).contains(&x) && (self.min_y..=self.max_y).contains(&y)
31 }
32
33 #[allow(dead_code)]
34 pub fn overlaps(&self, other: &Rect2) -> bool {
35 self.min_x <= other.max_x
36 && self.max_x >= other.min_x
37 && self.min_y <= other.max_y
38 && self.max_y >= other.min_y
39 }
40
41 fn area(&self) -> f32 {
42 (self.max_x - self.min_x).max(0.0) * (self.max_y - self.min_y).max(0.0)
43 }
44
45 fn merge(&self, other: &Rect2) -> Rect2 {
46 Rect2::new(
47 self.min_x.min(other.min_x),
48 self.min_y.min(other.min_y),
49 self.max_x.max(other.max_x),
50 self.max_y.max(other.max_y),
51 )
52 }
53}
54
55#[derive(Debug, Clone, Default)]
57#[allow(dead_code)]
58pub struct RTreeEntry {
59 pub bounds: Rect2,
60 pub id: usize,
61}
62
63#[derive(Debug, Clone)]
65enum RNode {
66 Leaf { entry: RTreeEntry },
67 Internal { bounds: Rect2, children: Vec<RNode> },
68}
69
70impl RNode {
71 fn bounds(&self) -> &Rect2 {
72 match self {
73 RNode::Leaf { entry } => &entry.bounds,
74 RNode::Internal { bounds, .. } => bounds,
75 }
76 }
77
78 fn query_overlap(&self, query: &Rect2, result: &mut Vec<usize>) {
79 if !self.bounds().overlaps(query) {
80 return;
81 }
82 match self {
83 RNode::Leaf { entry } => {
84 if entry.bounds.overlaps(query) {
85 result.push(entry.id);
86 }
87 }
88 RNode::Internal { children, .. } => {
89 for child in children {
90 child.query_overlap(query, result);
91 }
92 }
93 }
94 }
95
96 fn query_point(&self, x: f32, y: f32, result: &mut Vec<usize>) {
97 if !self.bounds().contains_point(x, y) {
98 return;
99 }
100 match self {
101 RNode::Leaf { entry } => {
102 if entry.bounds.contains_point(x, y) {
103 result.push(entry.id);
104 }
105 }
106 RNode::Internal { children, .. } => {
107 for child in children {
108 child.query_point(x, y, result);
109 }
110 }
111 }
112 }
113}
114
115#[derive(Debug, Clone, Default)]
117#[allow(dead_code)]
118pub struct RTree2D {
119 entries: Vec<RTreeEntry>,
120 root: Option<RNode>,
121}
122
123fn build_node_rtree(entries: Vec<RTreeEntry>, max_children: usize) -> RNode {
124 if entries.len() <= max_children {
125 if entries.len() == 1 {
126 return RNode::Leaf {
127 entry: entries.into_iter().next().unwrap_or_default(),
128 };
129 }
130 let bounds = entries
131 .iter()
132 .fold(entries[0].bounds, |acc, e| acc.merge(&e.bounds));
133 let children = entries
134 .into_iter()
135 .map(|e| RNode::Leaf { entry: e })
136 .collect();
137 return RNode::Internal { bounds, children };
138 }
139 let mut sorted = entries;
141 sorted.sort_by(|a, b| {
142 let ca = (a.bounds.min_x + a.bounds.max_x) * 0.5;
143 let cb = (b.bounds.min_x + b.bounds.max_x) * 0.5;
144 ca.partial_cmp(&cb).unwrap_or(std::cmp::Ordering::Equal)
145 });
146 let chunk_size = sorted.len().div_ceil(max_children);
147 let mut children = Vec::new();
148 for chunk in sorted.chunks(chunk_size.max(1)) {
149 children.push(build_node_rtree(chunk.to_vec(), max_children));
150 }
151 let bounds = children
152 .iter()
153 .fold(*children[0].bounds(), |acc, c| acc.merge(c.bounds()));
154 RNode::Internal { bounds, children }
155}
156
157impl RTree2D {
158 #[allow(dead_code)]
160 pub fn new() -> Self {
161 Self::default()
162 }
163
164 #[allow(dead_code)]
166 pub fn build(entries: Vec<RTreeEntry>) -> Self {
167 if entries.is_empty() {
168 return Self {
169 entries: vec![],
170 root: None,
171 };
172 }
173 let root = Some(build_node_rtree(entries.clone(), 4));
174 Self { entries, root }
175 }
176
177 #[allow(dead_code)]
179 pub fn query_overlap(&self, query: &Rect2) -> Vec<usize> {
180 let mut result = Vec::new();
181 if let Some(root) = &self.root {
182 root.query_overlap(query, &mut result);
183 }
184 result
185 }
186
187 #[allow(dead_code)]
189 pub fn query_point(&self, x: f32, y: f32) -> Vec<usize> {
190 let mut result = Vec::new();
191 if let Some(root) = &self.root {
192 root.query_point(x, y, &mut result);
193 }
194 result
195 }
196
197 #[allow(dead_code)]
199 pub fn len(&self) -> usize {
200 self.entries.len()
201 }
202
203 #[allow(dead_code)]
205 pub fn is_empty(&self) -> bool {
206 self.entries.is_empty()
207 }
208}
209
210#[allow(dead_code)]
212pub fn rtree_entry(id: usize, min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> RTreeEntry {
213 RTreeEntry {
214 bounds: Rect2::new(min_x, min_y, max_x, max_y),
215 id,
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 fn sample_tree() -> RTree2D {
224 let entries = vec![
225 rtree_entry(0, 0.0, 0.0, 1.0, 1.0),
226 rtree_entry(1, 2.0, 2.0, 3.0, 3.0),
227 rtree_entry(2, 0.5, 0.5, 1.5, 1.5),
228 rtree_entry(3, 10.0, 10.0, 11.0, 11.0),
229 ];
230 RTree2D::build(entries)
231 }
232
233 #[test]
234 fn empty_tree_queries_return_empty() {
235 let t = RTree2D::new();
236 assert!(t
237 .query_overlap(&Rect2::new(0.0, 0.0, 10.0, 10.0))
238 .is_empty());
239 }
240
241 #[test]
242 fn query_point_inside_entry() {
243 let t = sample_tree();
244 let r = t.query_point(0.5, 0.5);
245 assert!(r.contains(&0));
246 }
247
248 #[test]
249 fn query_point_outside_all() {
250 let t = sample_tree();
251 let r = t.query_point(5.0, 5.0);
252 assert!(r.is_empty());
253 }
254
255 #[test]
256 fn query_overlap_finds_overlapping() {
257 let t = sample_tree();
258 let r = t.query_overlap(&Rect2::new(0.8, 0.8, 2.2, 2.2));
259 assert!(r.contains(&2));
260 assert!(r.contains(&1));
261 }
262
263 #[test]
264 fn query_overlap_miss() {
265 let t = sample_tree();
266 let r = t.query_overlap(&Rect2::new(20.0, 20.0, 21.0, 21.0));
267 assert!(r.is_empty());
268 }
269
270 #[test]
271 fn len_correct() {
272 let t = sample_tree();
273 assert_eq!(t.len(), 4);
274 }
275
276 #[test]
277 fn is_empty_true() {
278 let t = RTree2D::new();
279 assert!(t.is_empty());
280 }
281
282 #[test]
283 fn is_empty_false() {
284 let t = sample_tree();
285 assert!(!t.is_empty());
286 }
287
288 #[test]
289 fn rect_overlaps_self() {
290 let r = Rect2::new(0.0, 0.0, 1.0, 1.0);
291 assert!(r.overlaps(&r));
292 }
293
294 #[test]
295 fn rect_area_correct() {
296 let r = Rect2::new(0.0, 0.0, 2.0, 3.0);
297 assert!((r.area() - 6.0).abs() < 1e-5);
298 }
299}