bvh2d/bvh2d/
bvh2d_impl.rs1use crate::aabb::{Bounded, AABB};
2use crate::bvh2d::iter::BVH2dTraverseIterator;
3use crate::utils::{concatenate_vectors, joint_aabb_of_shapes, Bucket};
4use crate::Point2;
5use crate::EPSILON;
6use std::f32;
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11#[derive(Copy, Clone, Debug)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[allow(clippy::upper_case_acronyms)]
14pub(crate) enum BVH2dNode {
15 Leaf {
16 shape_index: usize,
17 },
18
19 Node {
20 child_l_index: usize,
21 child_l_aabb: AABB,
22 child_r_index: usize,
23 child_r_aabb: AABB,
24 },
25}
26
27impl BVH2dNode {
28 const DUMMY: BVH2dNode = { BVH2dNode::Leaf { shape_index: 0 } };
29
30 fn build<T: Bounded>(shapes: &[T], indices: &[usize], nodes: &mut Vec<BVH2dNode>) -> usize {
31 #[inline]
33 fn grow_convex_hull(convex_hull: (AABB, AABB), shape_aabb: &AABB) -> (AABB, AABB) {
34 let center = &shape_aabb.center();
35 let convex_hull_aabbs = &convex_hull.0;
36 let convex_hull_centroids = &convex_hull.1;
37 (
38 convex_hull_aabbs.join(shape_aabb),
39 convex_hull_centroids.grow(center),
40 )
41 }
42
43 if indices.len() == 1 {
45 let shape_index = indices[0];
46 let node_index = nodes.len();
47 nodes.push(BVH2dNode::Leaf { shape_index });
48 return node_index;
50 }
51
52 let mut convex_hull = (AABB::EMPTY, AABB::EMPTY);
53 for index in indices {
54 convex_hull = grow_convex_hull(convex_hull, &shapes[*index].aabb());
55 }
56 let (aabb_bounds, centroid_bounds) = convex_hull;
57
58 let node_index = nodes.len();
61 nodes.push(BVH2dNode::DUMMY);
62
63 let split_axis = centroid_bounds.largest_axis();
65 let split_axis_size = centroid_bounds.max[split_axis] - centroid_bounds.min[split_axis];
66
67 let (child_l_index, child_l_aabb, child_r_index, child_r_aabb) = if split_axis_size
69 < EPSILON
70 {
71 let (child_l_indices, child_r_indices) = indices.split_at(indices.len() / 2);
74 let child_l_aabb = joint_aabb_of_shapes(child_l_indices, shapes);
75 let child_r_aabb = joint_aabb_of_shapes(child_r_indices, shapes);
76
77 let child_l_index = BVH2dNode::build(shapes, child_l_indices, nodes);
79 let child_r_index = BVH2dNode::build(shapes, child_r_indices, nodes);
80 (child_l_index, child_l_aabb, child_r_index, child_r_aabb)
81 } else {
82 const NUM_BUCKETS: usize = 4;
83 const BUCKET_START_CAPACITY: usize = 20;
84 let mut buckets = [Bucket::EMPTY; NUM_BUCKETS];
85 let mut bucket_assignments: [Vec<usize>; NUM_BUCKETS] = [
86 Vec::with_capacity(BUCKET_START_CAPACITY),
87 Vec::with_capacity(BUCKET_START_CAPACITY),
88 Vec::with_capacity(BUCKET_START_CAPACITY),
89 Vec::with_capacity(BUCKET_START_CAPACITY),
90 ];
91
92 for idx in indices {
95 let shape = &shapes[*idx];
96 let shape_aabb = shape.aabb();
97 let shape_center = shape_aabb.center();
98
99 let bucket_num_relative =
101 (shape_center[split_axis] - centroid_bounds.min[split_axis]) / split_axis_size;
102
103 let bucket_num = (bucket_num_relative * (NUM_BUCKETS as f32 - 0.01)) as usize;
105
106 buckets[bucket_num].add_aabb(&shape_aabb);
108 bucket_assignments[bucket_num].push(*idx);
109 }
110
111 let mut min_bucket = 0;
113 let mut min_cost = f32::INFINITY;
114 let mut child_l_aabb = AABB::EMPTY;
115 let mut child_r_aabb = AABB::EMPTY;
116 for i in 0..(NUM_BUCKETS - 1) {
117 let (l_buckets, r_buckets) = buckets.split_at(i + 1);
118 let child_l = l_buckets.iter().fold(Bucket::EMPTY, Bucket::join_bucket);
119 let child_r = r_buckets.iter().fold(Bucket::EMPTY, Bucket::join_bucket);
120
121 let cost = (child_l.size as f32 * child_l.aabb.surface_area()
122 + child_r.size as f32 * child_r.aabb.surface_area())
123 / aabb_bounds.surface_area();
124 if cost < min_cost {
125 min_bucket = i;
126 min_cost = cost;
127 child_l_aabb = child_l.aabb;
128 child_r_aabb = child_r.aabb;
129 }
130 }
131
132 let (l_assignments, r_assignments) = bucket_assignments.split_at_mut(min_bucket + 1);
134 let child_l_indices = concatenate_vectors(l_assignments);
135 let child_r_indices = concatenate_vectors(r_assignments);
136
137 let child_l_index = BVH2dNode::build(shapes, &child_l_indices, nodes);
139 let child_r_index = BVH2dNode::build(shapes, &child_r_indices, nodes);
140 (child_l_index, child_l_aabb, child_r_index, child_r_aabb)
141 };
142
143 debug_assert!(!child_l_aabb.is_empty());
145 debug_assert!(!child_r_aabb.is_empty());
146 nodes[node_index] = BVH2dNode::Node {
147 child_l_aabb,
148 child_l_index,
149 child_r_aabb,
150 child_r_index,
151 };
152
153 node_index
154 }
155}
156
157#[allow(clippy::upper_case_acronyms)]
158#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
159#[derive(Clone, Debug)]
160pub struct BVH2d {
161 pub(crate) nodes: Vec<BVH2dNode>,
162}
163
164impl BVH2d {
165 pub fn build<Shape: Bounded>(shapes: &[Shape]) -> BVH2d {
166 let indices = (0..shapes.len()).collect::<Vec<usize>>();
167 let expected_node_count = shapes.len() * 2;
168 let mut nodes = Vec::with_capacity(expected_node_count);
169 BVH2dNode::build(shapes, &indices, &mut nodes);
170 BVH2d { nodes }
171 }
172
173 pub fn contains_iterator<'a>(&'a self, point: &'a Point2) -> BVH2dTraverseIterator<'a> {
174 BVH2dTraverseIterator::new(self, point)
175 }
176}