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) {
72 if (!
node.bounds.intersectsAABB(
bounds))
return false;
78 node.itemIds.push_back(
id);
79 if (
static_cast<int>(
node.itemIds.size()) > maxPerNode_ &&
node.depth < maxDepth_) {
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) {
175 if (!
node.isLeaf()) {
183 collect(*ensureRoot(),
nullptr,
x,
y, 0.f,
false);
190 collect(*ensureRoot(), &rect, 0.f, 0.f, 0.f,
false);
197 collect(*ensureRoot(),
nullptr,
cx,
cy,
radius,
true);
EVENGINE_API_FOUNDATION public API.
void clear()
Removes all stored entries.
bool update(int id, float minX, float minY, float maxX, float maxY)
Moves an existing item to a new AABB; false if unknown or out of bounds.
BSPTree2D(float minX, float minY, float maxX, float maxY, int maxDepth=12, int maxPerNode=8)
Creates a 2D BSP/kd tree covering the given bounds.
int queryPoint(float x, float y)
Finds items overlapping a point; fills the result buffer.
bool contains(int id) const
True if the id is currently stored.
int queryRect(float minX, float minY, float maxX, float maxY)
Finds items overlapping an AABB; fills the result buffer.
int queryCircle(float cx, float cy, float radius)
Finds items overlapping a circle; fills the result buffer.
bool remove(int id)
Removes an item by id; false if unknown.
bool insert(int id, float minX, float minY, float maxX, float maxY)
Inserts an item AABB; false if out of bounds or id already present.
void clear()
Removes all stored entries.
int getCount() const
Number of stored ids.
void addUnique(int id)
Appends id if not already present.
AABB2 makeAABB2(float minX, float minY, float maxX, float maxY)
Builds a normalized 2D AABB (swaps inverted mins/maxes).
Axis-aligned 2D bounding box (min/max inclusive).
bool valid() const
True when min <= max on every axis.
float width() const
Extent along X (max - min).
float height() const
Extent along Y (max - min).