8 : rootBounds_(
makeAABB2(minX, minY, maxX, maxY)),
9 maxDepth_(std::max(1, maxDepth)),
10 maxPerNode_(std::max(1, maxPerNode)) {
11 if (!rootBounds_.
valid() || rootBounds_.
width() <= 0.f || rootBounds_.
height() <= 0.f) {
12 throw Exception(
"BSPTree2D: bounds must have positive area");
17BSPTree2D::Node *BSPTree2D::ensureRoot() {
19 root_ = std::make_unique<Node>();
20 root_->bounds = rootBounds_;
38 if (!
b.valid())
return false;
41 if (!insertInto(*ensureRoot(),
id,
b)) {
42 ensureRoot()->itemIds.push_back(
id);
56 return insert(
id, minX, minY, maxX, maxY);
59void BSPTree2D::rebuild() {
63 for (
const auto &kv : items_) {
64 if (!insertInto(*root_, kv.first, kv.second)) {
65 root_->itemIds.push_back(kv.first);
70bool BSPTree2D::insertInto(Node &node,
int id,
const AABB2 &bounds) {
71 if (!
node.bounds.containsAABB(bounds) &&
node.depth == 0) {
72 if (!
node.bounds.intersectsAABB(bounds))
return false;
73 }
else if (
node.depth > 0 && !
node.bounds.containsAABB(bounds)) {
78 node.itemIds.push_back(
id);
79 if (
static_cast<int>(
node.itemIds.size()) > maxPerNode_ &&
node.depth < maxDepth_) {
87 if (bounds.maxX <=
node.split &&
node.left) {
88 return insertInto(*
node.left,
id, bounds);
90 if (bounds.minX >=
node.split &&
node.right) {
91 return insertInto(*
node.right,
id, bounds);
94 if (bounds.maxY <=
node.split &&
node.left) {
95 return insertInto(*
node.left,
id, bounds);
97 if (bounds.minY >=
node.split &&
node.right) {
98 return insertInto(*
node.right,
id, bounds);
101 node.itemIds.push_back(
id);
105void BSPTree2D::split(Node &node) {
107 node.split = (
node.axis == 0) ?
node.bounds.centerX() :
node.bounds.centerY();
109 node.left = std::make_unique<Node>();
110 node.right = std::make_unique<Node>();
113 node.left->axis =
node.left->depth % 2;
114 node.right->axis =
node.right->depth % 2;
116 if (
node.axis == 0) {
128 std::vector<int> remain;
129 remain.reserve(
node.itemIds.size());
130 for (
int id :
node.itemIds) {
131 auto it = items_.find(
id);
132 if (it == items_.end())
continue;
133 const AABB2 &
b = it->second;
134 if (
node.axis == 0) {
135 if (
b.maxX <=
node.split) {
136 node.left->itemIds.push_back(
id);
137 }
else if (
b.minX >=
node.split) {
138 node.right->itemIds.push_back(
id);
140 remain.push_back(
id);
143 if (
b.maxY <=
node.split) {
144 node.left->itemIds.push_back(
id);
145 }
else if (
b.minY >=
node.split) {
146 node.right->itemIds.push_back(
id);
148 remain.push_back(
id);
152 node.itemIds.swap(remain);
155void BSPTree2D::collect(Node &node,
const AABB2 *rect,
float cx,
float cy,
float radius,
157 if (rect && !
node.bounds.intersectsAABB(*rect))
return;
158 if (useCircle && !
node.bounds.intersectsCircle(
cx,
cy, radius))
return;
160 for (
int id :
node.itemIds) {
161 auto it = items_.find(
id);
162 if (it == items_.end())
continue;
163 const AABB2 &
b = it->second;
166 hit =
b.intersectsAABB(*rect);
167 }
else if (useCircle) {
168 hit =
b.intersectsCircle(
cx,
cy, radius);
170 hit =
b.containsPoint(
cx,
cy);
175 if (!
node.isLeaf()) {
176 if (
node.left) collect(*
node.left, rect,
cx,
cy, radius, useCircle);
177 if (
node.right) collect(*
node.right, rect,
cx,
cy, radius, useCircle);
183 collect(*ensureRoot(),
nullptr,
x,
y, 0.f,
false);
190 collect(*ensureRoot(), &rect, 0.f, 0.f, 0.f,
false);
196 if (radius < 0.f) radius = 0.f;
197 collect(*ensureRoot(),
nullptr,
cx,
cy, radius,
true);
bool update(int id, float minX, float minY, float maxX, float maxY)
BSPTree2D(float minX, float minY, float maxX, float maxY, int maxDepth=12, int maxPerNode=8)
int queryPoint(float x, float y)
bool contains(int id) const
int queryRect(float minX, float minY, float maxX, float maxY)
int queryCircle(float cx, float cy, float radius)
bool insert(int id, float minX, float minY, float maxX, float maxY)
NodeDesc node(std::string id, std::vector< NodeDesc > children, std::string name)
AABB2 makeAABB2(float minX, float minY, float maxX, float maxY)