Skip to content

Commit 7bd14dc

Browse files
committed
feat: rect quadtree rs optimizations
1 parent 62a69c1 commit 7bd14dc

File tree

1 file changed

+24
-29
lines changed

1 file changed

+24
-29
lines changed

src/rect_quadtree.rs

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,16 @@ fn child_index_for_rect(boundary: &Rect, r: &Rect) -> Option<usize> {
2626
let cx = 0.5 * (boundary.min_x + boundary.max_x);
2727
let cy = 0.5 * (boundary.min_y + boundary.max_y);
2828

29-
// Does r fit entirely on one side of the vertical split?
30-
let in_left = r.max_x <= cx;
31-
let in_right = r.min_x >= cx;
32-
33-
// Does r fit entirely on one side of the horizontal split?
34-
let in_bottom = r.max_y <= cy;
35-
let in_top = r.min_y >= cy;
36-
37-
let x_side = if in_left { Some(0) } else if in_right { Some(1) } else { None };
38-
let y_side = if in_bottom { Some(0) } else if in_top { Some(1) } else { None };
39-
40-
match (x_side, y_side) {
41-
(Some(0), Some(0)) => Some(0),
42-
(Some(1), Some(0)) => Some(1),
43-
(Some(0), Some(1)) => Some(2),
44-
(Some(1), Some(1)) => Some(3),
45-
_ => None, // Spans split line(s) so it must live at this node
29+
// fits entirely on one side per axis?
30+
let fits_x = r.max_x <= cx || r.min_x >= cx;
31+
let fits_y = r.max_y <= cy || r.min_y >= cy;
32+
if !(fits_x && fits_y) {
33+
return None;
4634
}
35+
// right=1 if min_x >= cx, top=1 if min_y >= cy
36+
let ix = (r.min_x >= cx) as usize;
37+
let iy = (r.min_y >= cy) as usize;
38+
Some(ix | (iy << 1)) // 0..3
4739
}
4840

4941
#[inline(always)]
@@ -120,10 +112,10 @@ impl RectQuadTree {
120112
let cy = 0.5 * (self.boundary.min_y + self.boundary.max_y);
121113

122114
let quads = [
123-
Rect { min_x: self.boundary.min_x, min_y: self.boundary.min_y, max_x: cx, max_y: cy }, // 0
124-
Rect { min_x: cx, min_y: self.boundary.min_y, max_x: self.boundary.max_x, max_y: cy }, // 1
125-
Rect { min_x: self.boundary.min_x, min_y: cy, max_x: cx, max_y: self.boundary.max_y }, // 2
126-
Rect { min_x: cx, min_y: cy, max_x: self.boundary.max_x, max_y: self.boundary.max_y }, // 3
115+
Rect { min_x: self.boundary.min_x, min_y: self.boundary.min_y, max_x: cx, max_y: cy },
116+
Rect { min_x: cx, min_y: self.boundary.min_y, max_x: self.boundary.max_x, max_y: cy },
117+
Rect { min_x: self.boundary.min_x, min_y: cy, max_x: cx, max_y: self.boundary.max_y },
118+
Rect { min_x: cx, min_y: cy, max_x: self.boundary.max_x, max_y: self.boundary.max_y },
127119
];
128120

129121
let d = self.depth + 1;
@@ -134,16 +126,19 @@ impl RectQuadTree {
134126
RectQuadTree::new_child(quads[3], self.capacity, d, self.max_depth),
135127
];
136128

137-
// Move any items that fully fit a child
138-
let mut stay: Vec<RectItem> = Vec::new();
139-
for it in self.items.drain(..) {
140-
if let Some(idx) = child_index_for_rect(&self.boundary, &it.rect) {
129+
// Move any items that fully fit a child, in place
130+
let mut i = 0;
131+
while i < self.items.len() {
132+
let rect = self.items[i].rect;
133+
if let Some(idx) = child_index_for_rect(&self.boundary, &rect) {
134+
let it = self.items.swap_remove(i);
141135
kids[idx].insert(it);
136+
// do not advance i, we swapped in a new element at i
142137
} else {
143-
stay.push(it);
138+
i += 1;
144139
}
145140
}
146-
self.items = stay;
141+
147142
self.children = Some(Box::new(kids));
148143
}
149144

@@ -159,7 +154,7 @@ impl RectQuadTree {
159154
#[derive(Copy, Clone)]
160155
enum Mode { Filter, ReportAll }
161156

162-
let mut out: Vec<(u64, Rect)> = Vec::with_capacity(128);
157+
let mut out: SmallVec<[(u64, Rect); 128]> = SmallVec::new();
163158
let mut stack: SmallVec<[(&RectQuadTree, Mode); 64]> = SmallVec::new();
164159
stack.push((self, Mode::Filter));
165160

@@ -213,7 +208,7 @@ impl RectQuadTree {
213208
}
214209
}
215210

216-
out
211+
out.into_vec()
217212
}
218213

219214
/// Convenience if you only want ids.

0 commit comments

Comments
 (0)