1189 lines
38 KiB
C
1189 lines
38 KiB
C
|
/**
|
||
|
@file PointKDTree.h
|
||
|
|
||
|
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
|
||
|
|
||
|
@created 2004-01-11
|
||
|
@edited 2012-07-30
|
||
|
|
||
|
Copyright 2000-2012, Morgan McGuire.
|
||
|
All rights reserved.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#ifndef G3D_PointKDTree_h
|
||
|
#define G3D_PointKDTree_h
|
||
|
|
||
|
#include "G3D/platform.h"
|
||
|
#include "G3D/Array.h"
|
||
|
#include "G3D/Table.h"
|
||
|
#include "G3D/Vector2.h"
|
||
|
#include "G3D/Vector3.h"
|
||
|
#include "G3D/Vector4.h"
|
||
|
#include "G3D/AABox.h"
|
||
|
#include "G3D/Sphere.h"
|
||
|
#include "G3D/Box.h"
|
||
|
#include "G3D/BinaryInput.h"
|
||
|
#include "G3D/BinaryOutput.h"
|
||
|
#include "G3D/CollisionDetection.h"
|
||
|
#include "G3D/Frustum.h"
|
||
|
#include "G3D/PositionTrait.h"
|
||
|
#include <algorithm>
|
||
|
|
||
|
namespace G3D {
|
||
|
|
||
|
/**
|
||
|
A set data structure that supports spatial queries using an axis-aligned
|
||
|
BSP tree for speed.
|
||
|
|
||
|
PointKDTree allows you to quickly find points in 3D that lie within
|
||
|
a box or sphere. For large sets of objects it is much faster
|
||
|
than testing each object for a collision. See also G3D::KDTree; this class
|
||
|
is optimized for point sets, e.g.,for use in photon mapping and mesh processing.
|
||
|
|
||
|
<B>Template Parameters</B>
|
||
|
|
||
|
<br>
|
||
|
|
||
|
<br>The template parameter <I>T</I> must be one for which
|
||
|
the following functions are overloaded:
|
||
|
|
||
|
<pre>
|
||
|
T::T(); <I>(public constructor of no arguments)</I>
|
||
|
|
||
|
template<> struct PositionTrait<class T> {
|
||
|
static void getPosition(const T& v, G3D::Vector3& p);};
|
||
|
|
||
|
template <> struct HashTrait<class T> {
|
||
|
static size_t hashCode(const T& key);};
|
||
|
|
||
|
template<> struct EqualsTrait<class T> {
|
||
|
static bool equals(const T& a, const T& b); };
|
||
|
</pre>
|
||
|
|
||
|
<p>
|
||
|
|
||
|
G3D provides these for the Vector2, Vector3, and Vector4 classes.
|
||
|
If you use a custom class, or a pointer to a custom class, you will need
|
||
|
to define those functions.
|
||
|
|
||
|
<B>Moving %Set Members</B>
|
||
|
<DT>It is important that objects do not move without updating the
|
||
|
PointKDTree. If the position of an object is about
|
||
|
to change, PointKDTree::remove it before they change and
|
||
|
PointKDTree::insert it again afterward. For objects
|
||
|
where the hashCode and == operator are invariant with respect
|
||
|
to the 3D position,
|
||
|
you can use the PointKDTree::update method as a shortcut to
|
||
|
insert/remove an object in one step after it has moved.
|
||
|
|
||
|
|
||
|
Note: Do not mutate any value once it has been inserted into PointKDTree. Values
|
||
|
are copied interally. All PointKDTree iterators convert to pointers to constant
|
||
|
values to reinforce this.
|
||
|
|
||
|
If you want to mutate the objects you intend to store in a PointKDTree
|
||
|
simply insert <I>pointers</I> to your objects instead of the objects
|
||
|
themselves, and ensure that the above operations are defined. (And
|
||
|
actually, because values are copied, if your values are large you may
|
||
|
want to insert pointers anyway, to save space and make the balance
|
||
|
operation faster.)
|
||
|
|
||
|
<B>Dimensions</B>
|
||
|
Although designed as a 3D-data structure, you can use the PointKDTree
|
||
|
for data distributed along 2 or 1 axes by simply returning bounds
|
||
|
that are always zero along one or more dimensions.
|
||
|
|
||
|
*/
|
||
|
template<class T,
|
||
|
class PositionFunc = PositionTrait<T>,
|
||
|
class HashFunc = HashTrait<T>,
|
||
|
class EqualsFunc = EqualsTrait<T> >
|
||
|
class PointKDTree {
|
||
|
protected:
|
||
|
#define TreeType PointKDTree<T, PositionFunc, HashFunc, EqualsFunc>
|
||
|
|
||
|
// Unlike the KDTree, the PointKDTree assumes that T elements are
|
||
|
// small and keeps the handle and cached position together instead of
|
||
|
// placing them in separate bounds arrays. Also note that a copy of T
|
||
|
// is kept in the member table and that there is no indirection.
|
||
|
class Handle {
|
||
|
private:
|
||
|
Vector3 m_position;
|
||
|
|
||
|
public:
|
||
|
T value;
|
||
|
|
||
|
inline Handle() {}
|
||
|
inline Handle(const T& v) : value(v) {
|
||
|
PositionFunc::getPosition(v, m_position);
|
||
|
}
|
||
|
|
||
|
/** Used by makeNode to create fake handles for partitioning. */
|
||
|
void setPosition(const Vector3& v) {
|
||
|
m_position = v;
|
||
|
}
|
||
|
|
||
|
inline const Vector3& position() const {
|
||
|
return m_position;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/** Returns the bounds of the sub array. Used by makeNode. */
|
||
|
static AABox computeBounds(
|
||
|
const Array<Handle>& point) {
|
||
|
|
||
|
if (point.size() == 0) {
|
||
|
return AABox(Vector3::inf(), Vector3::inf());
|
||
|
}
|
||
|
|
||
|
AABox bounds(point[0].position());
|
||
|
|
||
|
for (int p = 0; p < point.size(); ++p) {
|
||
|
bounds.merge(point[p].position());
|
||
|
}
|
||
|
|
||
|
return bounds;
|
||
|
}
|
||
|
|
||
|
class Node {
|
||
|
public:
|
||
|
|
||
|
/** Spatial bounds on all values at this node and its children, based purely on
|
||
|
the parent's splitting planes. May be infinite */
|
||
|
AABox splitBounds;
|
||
|
|
||
|
Vector3::Axis splitAxis;
|
||
|
|
||
|
/** Location along the specified axis */
|
||
|
float splitLocation;
|
||
|
|
||
|
/** child[0] contains all values strictly
|
||
|
smaller than splitLocation along splitAxis.
|
||
|
|
||
|
child[1] contains all values strictly
|
||
|
larger.
|
||
|
|
||
|
Both may be NULL if there are not enough
|
||
|
values to bother recursing.
|
||
|
*/
|
||
|
Node* child[2];
|
||
|
|
||
|
/** Values if this is a leaf node). */
|
||
|
Array<Handle> valueArray;
|
||
|
|
||
|
/** Creates node with NULL children */
|
||
|
Node() {
|
||
|
splitAxis = Vector3::X_AXIS;
|
||
|
splitLocation = 0;
|
||
|
splitBounds = AABox(-Vector3::inf(), Vector3::inf());
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
child[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Doesn't clone children.
|
||
|
*/
|
||
|
Node(const Node& other) : valueArray(other.valueArray) {
|
||
|
splitAxis = other.splitAxis;
|
||
|
splitLocation = other.splitLocation;
|
||
|
splitBounds = other.splitBounds;
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
child[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Copies the specified subarray of pt into point, NULLs the children.
|
||
|
Assumes a second pass will set splitBounds. */
|
||
|
Node(const Array<Handle>& pt) {
|
||
|
splitAxis = Vector3::X_AXIS;
|
||
|
splitLocation = 0;
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
child[i] = NULL;
|
||
|
}
|
||
|
valueArray = pt;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Deletes the children (but not the values) */
|
||
|
~Node() {
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
delete child[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Returns true if this node is a leaf (no children) */
|
||
|
inline bool isLeaf() const {
|
||
|
return (child[0] == NULL) && (child[1] == NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Recursively appends all handles and children's handles
|
||
|
to the array.
|
||
|
*/
|
||
|
void getHandles(Array<Handle>& handleArray) const {
|
||
|
handleArray.append(valueArray);
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
if (child[i] != NULL) {
|
||
|
child[i]->getHandles(handleArray);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void verifyNode(const Vector3& lo, const Vector3& hi) {
|
||
|
// debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n",
|
||
|
// splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z);
|
||
|
|
||
|
debugAssert(lo == splitBounds.low());
|
||
|
debugAssert(hi == splitBounds.high());
|
||
|
|
||
|
# ifdef G3D_DEBUG
|
||
|
for (int i = 0; i < valueArray.length(); ++i) {
|
||
|
const Vector3& b = valueArray[i].position();
|
||
|
debugAssert(splitBounds.contains(b));
|
||
|
}
|
||
|
# endif
|
||
|
|
||
|
if (child[0] || child[1]) {
|
||
|
debugAssert(lo[splitAxis] < splitLocation);
|
||
|
debugAssert(hi[splitAxis] > splitLocation);
|
||
|
}
|
||
|
|
||
|
Vector3 newLo = lo;
|
||
|
newLo[splitAxis] = splitLocation;
|
||
|
Vector3 newHi = hi;
|
||
|
newHi[splitAxis] = splitLocation;
|
||
|
|
||
|
if (child[0] != NULL) {
|
||
|
child[0]->verifyNode(lo, newHi);
|
||
|
}
|
||
|
|
||
|
if (child[1] != NULL) {
|
||
|
child[1]->verifyNode(newLo, hi);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Stores the locations of the splitting planes (the structure but not the content)
|
||
|
so that the tree can be quickly rebuilt from a previous configuration without
|
||
|
calling balance.
|
||
|
*/
|
||
|
static void serializeStructure(const Node* n, BinaryOutput& bo) {
|
||
|
if (n == NULL) {
|
||
|
bo.writeUInt8(0);
|
||
|
} else {
|
||
|
bo.writeUInt8(1);
|
||
|
n->splitBounds.serialize(bo);
|
||
|
serialize(n->splitAxis, bo);
|
||
|
bo.writeFloat32(n->splitLocation);
|
||
|
for (int c = 0; c < 2; ++c) {
|
||
|
serializeStructure(n->child[c], bo);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Clears the member table */
|
||
|
static Node* deserializeStructure(BinaryInput& bi) {
|
||
|
if (bi.readUInt8() == 0) {
|
||
|
return NULL;
|
||
|
} else {
|
||
|
Node* n = new Node();
|
||
|
n->splitBounds.deserialize(bi);
|
||
|
deserialize(n->splitAxis, bi);
|
||
|
n->splitLocation = bi.readFloat32();
|
||
|
for (int c = 0; c < 2; ++c) {
|
||
|
n->child[c] = deserializeStructure(bi);
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Returns the deepest node that completely contains bounds. */
|
||
|
Node* findDeepestContainingNode(const Vector3& point) {
|
||
|
|
||
|
// See which side of the splitting plane the bounds are on
|
||
|
if (point[splitAxis] < splitLocation) {
|
||
|
// Point is on the low side. Recurse into the child
|
||
|
// if it exists.
|
||
|
if (child[0] != NULL) {
|
||
|
return child[0]->findDeepestContainingNode(point);
|
||
|
}
|
||
|
} else if (point[splitAxis] > splitLocation) {
|
||
|
// Point is on the high side, recurse into the child
|
||
|
// if it exists.
|
||
|
if (child[1] != NULL) {
|
||
|
return child[1]->findDeepestContainingNode(point);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// There was no containing child, so this node is the
|
||
|
// deepest containing node.
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** Appends all members that intersect the box.
|
||
|
If useSphere is true, members are tested against the sphere instead. */
|
||
|
void getIntersectingMembers(
|
||
|
const AABox& sphereBounds,
|
||
|
const Sphere& sphere,
|
||
|
Array<T>& members) const {
|
||
|
|
||
|
// Test all values at this node. Extract the
|
||
|
// underlying C array for speed
|
||
|
const int N = valueArray.size();
|
||
|
const Handle* handleArray = valueArray.getCArray();
|
||
|
|
||
|
const float r2 = square(sphere.radius);
|
||
|
|
||
|
// Copy the sphere center so that it is on the stack near the radius
|
||
|
const Vector3 center = sphere.center;
|
||
|
for (int v = 0; v < N; ++v) {
|
||
|
if ((center - handleArray[v].position()).squaredLength() <= r2) {
|
||
|
members.append(handleArray[v].value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the left child overlaps the box, recurse into it
|
||
|
if (child[0] && (sphereBounds.low()[splitAxis] < splitLocation)) {
|
||
|
child[0]->getIntersectingMembers(sphereBounds, sphere, members);
|
||
|
}
|
||
|
|
||
|
// If the right child overlaps the box, recurse into it
|
||
|
if (child[1] && (sphereBounds.high()[splitAxis] > splitLocation)) {
|
||
|
child[1]->getIntersectingMembers(sphereBounds, sphere, members);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Appends all members that intersect the box.
|
||
|
If useSphere is true, members are tested against the sphere instead.
|
||
|
|
||
|
Implemented using both box and sphere tests to simplify the implementation
|
||
|
of a future beginSphereInteresection iterator using the same underlying
|
||
|
BoxIterator class.
|
||
|
*/
|
||
|
void getIntersectingMembers(
|
||
|
const AABox& box,
|
||
|
const Sphere& sphere,
|
||
|
Array<T>& members,
|
||
|
bool useSphere) const {
|
||
|
|
||
|
// Test all values at this node
|
||
|
for (int v = 0; v < valueArray.size(); ++v) {
|
||
|
if ((useSphere && sphere.contains(valueArray[v].position())) ||
|
||
|
(! useSphere && box.contains(valueArray[v].position()))) {
|
||
|
members.append(valueArray[v].value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the left child overlaps the box, recurse into it
|
||
|
if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) {
|
||
|
child[0]->getIntersectingMembers(box, sphere, members, useSphere);
|
||
|
}
|
||
|
|
||
|
// If the right child overlaps the box, recurse into it
|
||
|
if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) {
|
||
|
child[1]->getIntersectingMembers(box, sphere, members, useSphere);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Recurse through the tree, assigning splitBounds fields.
|
||
|
*/
|
||
|
void assignSplitBounds(const AABox& myBounds) {
|
||
|
splitBounds = myBounds;
|
||
|
|
||
|
# ifdef G3D_DEBUG
|
||
|
if (child[0] || child[1]) {
|
||
|
debugAssert(splitBounds.high()[splitAxis] > splitLocation);
|
||
|
debugAssert(splitBounds.low()[splitAxis] < splitLocation);
|
||
|
}
|
||
|
# endif
|
||
|
|
||
|
AABox childBounds[2];
|
||
|
myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]);
|
||
|
|
||
|
for (int c = 0; c < 2; ++c) {
|
||
|
if (child[c]) {
|
||
|
child[c]->assignSplitBounds(childBounds[c]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class AxisComparator {
|
||
|
private:
|
||
|
Vector3::Axis sortAxis;
|
||
|
|
||
|
public:
|
||
|
|
||
|
AxisComparator(Vector3::Axis s) : sortAxis(s) {}
|
||
|
|
||
|
inline int operator()(const Handle& A, const Handle& B) const {
|
||
|
if (A.position()[sortAxis] > B.position()[sortAxis]) {
|
||
|
return -1;
|
||
|
} else if (A.position()[sortAxis] < B.position()[sortAxis]) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
Recursively subdivides the subarray.
|
||
|
|
||
|
The source array will be cleared after it is used
|
||
|
|
||
|
Call assignSplitBounds() on the root node after making a tree.
|
||
|
*/
|
||
|
Node* makeNode(
|
||
|
Array<Handle>& source,
|
||
|
Array<Handle>& temp,
|
||
|
int valuesPerNode,
|
||
|
int numMeanSplits) {
|
||
|
|
||
|
Node* node = NULL;
|
||
|
|
||
|
if (source.size() <= valuesPerNode) {
|
||
|
// Make a new leaf node
|
||
|
node = new Node(source);
|
||
|
|
||
|
// Set the pointers in the memberTable
|
||
|
for (int i = 0; i < source.size(); ++i) {
|
||
|
memberTable.set(source[i].value, node);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// Make a new internal node
|
||
|
node = new Node();
|
||
|
|
||
|
const AABox bounds = computeBounds(source);
|
||
|
const Vector3 extent = bounds.high() - bounds.low();
|
||
|
|
||
|
Vector3::Axis splitAxis = extent.primaryAxis();
|
||
|
|
||
|
float splitLocation;
|
||
|
|
||
|
Array<Handle> lt, gt;
|
||
|
|
||
|
if (numMeanSplits <= 0) {
|
||
|
source.medianPartition(lt, node->valueArray, gt, temp, AxisComparator(splitAxis));
|
||
|
splitLocation = node->valueArray[0].position()[splitAxis];
|
||
|
|
||
|
if ((node->valueArray.size() > source.size() / 2) &&
|
||
|
(source.size() > 10)) {
|
||
|
// Our median split put an awful lot of points on the splitting plane. Try a mean
|
||
|
// split instead
|
||
|
numMeanSplits = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (numMeanSplits > 0) {
|
||
|
// Compute the mean along the axis
|
||
|
|
||
|
splitLocation = (bounds.high()[splitAxis] +
|
||
|
bounds.low()[splitAxis]) / 2.0f;
|
||
|
|
||
|
Handle splitHandle;
|
||
|
Vector3 v;
|
||
|
v[splitAxis] = splitLocation;
|
||
|
splitHandle.setPosition(v);
|
||
|
|
||
|
source.partition(splitHandle, lt, node->valueArray, gt, AxisComparator(splitAxis));
|
||
|
}
|
||
|
|
||
|
# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
|
||
|
for (int i = 0; i < lt.size(); ++i) {
|
||
|
const Vector3& v = lt[i].position();
|
||
|
debugAssert(v[splitAxis] < splitLocation);
|
||
|
}
|
||
|
for (int i = 0; i < gt.size(); ++i) {
|
||
|
debugAssert(gt[i].position()[splitAxis] > splitLocation);
|
||
|
}
|
||
|
for (int i = 0; i < node->valueArray.size(); ++i) {
|
||
|
debugAssert(node->valueArray[i].position()[splitAxis] == splitLocation);
|
||
|
}
|
||
|
# endif
|
||
|
|
||
|
node->splitAxis = splitAxis;
|
||
|
node->splitLocation = splitLocation;
|
||
|
|
||
|
// Throw away the source array to save memory
|
||
|
source.fastClear();
|
||
|
|
||
|
if (lt.size() > 0) {
|
||
|
node->child[0] = makeNode(lt, temp, valuesPerNode, numMeanSplits - 1);
|
||
|
}
|
||
|
|
||
|
if (gt.size() > 0) {
|
||
|
node->child[1] = makeNode(gt, temp, valuesPerNode, numMeanSplits - 1);
|
||
|
}
|
||
|
|
||
|
// Add the values stored at this interior node to the member table
|
||
|
for(int i = 0; i < node->valueArray.size(); ++i) {
|
||
|
memberTable.set(node->valueArray[i].value, node);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return node;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Recursively clone the passed in node tree, setting
|
||
|
pointers for members in the memberTable as appropriate.
|
||
|
called by the assignment operator.
|
||
|
*/
|
||
|
Node* cloneTree(Node* src) {
|
||
|
Node* dst = new Node(*src);
|
||
|
|
||
|
// Make back pointers
|
||
|
for (int i = 0; i < dst->valueArray.size(); ++i) {
|
||
|
memberTable.set(dst->valueArray[i].value, dst);
|
||
|
}
|
||
|
|
||
|
// Clone children
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
if (src->child[i] != NULL) {
|
||
|
dst->child[i] = cloneTree(src->child[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dst;
|
||
|
}
|
||
|
|
||
|
/** Maps members to the node containing them */
|
||
|
typedef Table<T, Node*, HashFunc, EqualsFunc> MemberTable;
|
||
|
MemberTable memberTable;
|
||
|
|
||
|
Node* root;
|
||
|
|
||
|
public:
|
||
|
|
||
|
/** To construct a balanced tree, insert the elements and then call
|
||
|
PointKDTree::balance(). */
|
||
|
PointKDTree() : root(NULL) {}
|
||
|
|
||
|
|
||
|
PointKDTree(const PointKDTree& src) : root(NULL) {
|
||
|
*this = src;
|
||
|
}
|
||
|
|
||
|
|
||
|
PointKDTree& operator=(const PointKDTree& src) {
|
||
|
delete root;
|
||
|
// Clone tree takes care of filling out the memberTable.
|
||
|
root = cloneTree(src.root);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
~PointKDTree() {
|
||
|
clear();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Throws out all elements of the set and erases the structure of the tree.
|
||
|
*/
|
||
|
void clear() {
|
||
|
memberTable.clear();
|
||
|
delete root;
|
||
|
root = NULL;
|
||
|
}
|
||
|
|
||
|
/** Removes all elements of the set while maintaining the structure of the tree */
|
||
|
void clearData() {
|
||
|
memberTable.clear();
|
||
|
Array<Node*> stack;
|
||
|
stack.push(root);
|
||
|
while (stack.size() > 0) {
|
||
|
Node* node = stack.pop();
|
||
|
node->valueArray.fastClear();
|
||
|
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
if (node->child[i] != NULL) {
|
||
|
stack.push(node->child[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
size_t size() const {
|
||
|
return memberTable.size();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Inserts an object into the set if it is not
|
||
|
already present. O(log n) time. Does not
|
||
|
cause the tree to be balanced.
|
||
|
*/
|
||
|
void insert(const T& value) {
|
||
|
if (contains(value)) {
|
||
|
// Already in the set
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Handle h(value);
|
||
|
|
||
|
if (root == NULL) {
|
||
|
// This is the first node; create a root node
|
||
|
root = new Node();
|
||
|
}
|
||
|
|
||
|
Node* node = root->findDeepestContainingNode(h.position());
|
||
|
|
||
|
// Insert into the node
|
||
|
node->valueArray.append(h);
|
||
|
|
||
|
// Insert into the node table
|
||
|
memberTable.set(value, node);
|
||
|
}
|
||
|
|
||
|
/** Inserts each elements in the array in turn. If the tree
|
||
|
begins empty (no structure and no elements), this is faster
|
||
|
than inserting each element in turn. You still need to balance
|
||
|
the tree at the end.*/
|
||
|
void insert(const Array<T>& valueArray) {
|
||
|
// Pre-size the member table to avoid multiple allocations
|
||
|
memberTable.setSizeHint(valueArray.size() + size());
|
||
|
|
||
|
if (root == NULL) {
|
||
|
// Optimized case for an empty tree; don't bother
|
||
|
// searching or reallocating the root node's valueArray
|
||
|
// as we incrementally insert.
|
||
|
root = new Node();
|
||
|
root->valueArray.resize(valueArray.size());
|
||
|
for (int i = 0; i < valueArray.size(); ++i) {
|
||
|
// Insert in opposite order so that we have the exact same
|
||
|
// data structure as if we inserted each (i.e., order is reversed
|
||
|
// from array).
|
||
|
root->valueArray[valueArray.size() - i - 1] = Handle(valueArray[i]);
|
||
|
memberTable.set(valueArray[i], root);
|
||
|
}
|
||
|
} else {
|
||
|
// Insert at appropriate tree depth.
|
||
|
for (int i = 0; i < valueArray.size(); ++i) {
|
||
|
insert(valueArray[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Returns true if this object is in the set, otherwise
|
||
|
returns false. O(1) time.
|
||
|
*/
|
||
|
bool contains(const T& value) {
|
||
|
return memberTable.containsKey(value);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Removes an object from the set in O(1) time.
|
||
|
It is an error to remove members that are not already
|
||
|
present. May unbalance the tree.
|
||
|
|
||
|
Removing an element never causes a node (split plane) to be removed...
|
||
|
nodes are only changed when the tree is rebalanced. This behavior
|
||
|
is desirable because it allows the split planes to be serialized,
|
||
|
and then deserialized into an empty tree which can be repopulated.
|
||
|
*/
|
||
|
void remove(const T& value) {
|
||
|
debugAssertM(contains(value),
|
||
|
"Tried to remove an element from a "
|
||
|
"PointKDTree that was not present");
|
||
|
|
||
|
Array<Handle>& list = memberTable[value]->valueArray;
|
||
|
|
||
|
// Find the element and remove it
|
||
|
for (int i = list.length() - 1; i >= 0; --i) {
|
||
|
if (list[i].value == value) {
|
||
|
list.fastRemove(i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
memberTable.remove(value);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
If the element is in the set, it is removed.
|
||
|
The element is then inserted.
|
||
|
|
||
|
This is useful when the == and hashCode methods
|
||
|
on <I>T</I> are independent of the bounds. In
|
||
|
that case, you may call update(v) to insert an
|
||
|
element for the first time and call update(v)
|
||
|
again every time it moves to keep the tree
|
||
|
up to date.
|
||
|
*/
|
||
|
void update(const T& value) {
|
||
|
if (contains(value)) {
|
||
|
remove(value);
|
||
|
}
|
||
|
insert(value);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Rebalances the tree (slow). Call when objects
|
||
|
have moved substantially from their original positions
|
||
|
(which unbalances the tree and causes the spatial
|
||
|
queries to be slow).
|
||
|
|
||
|
@param valuesPerNode Maximum number of elements to put at
|
||
|
a node.
|
||
|
|
||
|
@param numMeanSplits numMeanSplits = 0 gives a
|
||
|
fully axis aligned BSP-tree, where the balance operation attempts to balance
|
||
|
the tree so that every splitting plane has an equal number of left
|
||
|
and right children (i.e. it is a <B>median</B> split along that axis).
|
||
|
This tends to maximize average performance; all querries will return in the same amount of time.
|
||
|
|
||
|
You can override this behavior by
|
||
|
setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT
|
||
|
creates a full oct-tree, which tends to optimize peak performance (some areas of the scene will terminate after few recursive splits) at the expense of
|
||
|
peak performance.
|
||
|
*/
|
||
|
void balance(int valuesPerNode = 40, int numMeanSplits = 3) {
|
||
|
if (root == NULL) {
|
||
|
// Tree is empty
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Array<Handle> handleArray;
|
||
|
root->getHandles(handleArray);
|
||
|
|
||
|
// Delete the old tree
|
||
|
clear();
|
||
|
|
||
|
Array<Handle> temp;
|
||
|
root = makeNode(handleArray, temp, valuesPerNode, numMeanSplits);
|
||
|
temp.fastClear();
|
||
|
|
||
|
// Walk the tree, assigning splitBounds. We start with unbounded
|
||
|
// space.
|
||
|
root->assignSplitBounds(AABox::maxFinite());
|
||
|
|
||
|
# ifdef _DEBUG
|
||
|
root->verifyNode(Vector3::minFinite(), Vector3::maxFinite());
|
||
|
# endif
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
/**
|
||
|
Returns the elements
|
||
|
|
||
|
@param parentMask The mask that this node returned from culledBy.
|
||
|
*/
|
||
|
static void getIntersectingMembers(
|
||
|
const Array<Plane>& plane,
|
||
|
Array<T>& members,
|
||
|
Node* node,
|
||
|
uint32 parentMask) {
|
||
|
|
||
|
int dummy;
|
||
|
|
||
|
if (parentMask == 0) {
|
||
|
// None of these planes can cull anything
|
||
|
for (int v = node->valueArray.size() - 1; v >= 0; --v) {
|
||
|
members.append(node->valueArray[v].value);
|
||
|
}
|
||
|
|
||
|
// Iterate through child nodes
|
||
|
for (int c = 0; c < 2; ++c) {
|
||
|
if (node->child[c]) {
|
||
|
getIntersectingMembers(plane, members, node->child[c], 0);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
if (node->valueArray.size() > 0) {
|
||
|
// This is a leaf; check the points
|
||
|
debugAssertM(node->child[0] == NULL, "Malformed Point tree");
|
||
|
debugAssertM(node->child[1] == NULL, "Malformed Point tree");
|
||
|
|
||
|
// Test values at this node against remaining planes
|
||
|
for (int p = 0; p < plane.size(); ++p) {
|
||
|
if (((parentMask >> p) & 1) != 0) {
|
||
|
// Test against this plane
|
||
|
const Plane& curPlane = plane[p];
|
||
|
for (int v = node->valueArray.size() - 1; v >= 0; --v) {
|
||
|
if (curPlane.halfSpaceContains(node->valueArray[v].position())) {
|
||
|
members.append(node->valueArray[v].value);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
uint32 childMask = 0xFFFFFF;
|
||
|
|
||
|
// Iterate through child nodes
|
||
|
for (int c = 0; c < 2; ++c) {
|
||
|
if (node->child[c] &&
|
||
|
! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) {
|
||
|
// This node was not culled
|
||
|
getIntersectingMembers(plane, members, node->child[c], childMask);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
/**
|
||
|
Returns all members inside the set of planes.
|
||
|
@param members The results are appended to this array.
|
||
|
*/
|
||
|
void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const {
|
||
|
if (root == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
getIntersectingMembers(plane, members, root, 0xFFFFFF);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Typically used to find all visible
|
||
|
objects inside the view frustum (see also Camera::getClipPlanes)... i.e. all objects
|
||
|
<B>not</B> culled by frustum.
|
||
|
|
||
|
Example:
|
||
|
<PRE>
|
||
|
Array<Object*> visible;
|
||
|
tree.getIntersectingMembers(camera.frustum(), visible);
|
||
|
// ... Draw all objects in the visible array.
|
||
|
</PRE>
|
||
|
@param members The results are appended to this array.
|
||
|
*/
|
||
|
void getIntersectingMembers(const Frustum& frustum, Array<T>& members) const {
|
||
|
Array<Plane> plane;
|
||
|
|
||
|
for (int i = 0; i < frustum.faceArray.size(); ++i) {
|
||
|
plane.append(frustum.faceArray[i].plane);
|
||
|
}
|
||
|
|
||
|
getIntersectingMembers(plane, members);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
C++ STL style iterator variable. See beginBoxIntersection().
|
||
|
The iterator overloads the -> (dereference) operator, so this
|
||
|
acts like a pointer to the current member.
|
||
|
*/
|
||
|
// This iterator turns Node::getIntersectingMembers into a
|
||
|
// coroutine. It first translates that method from recursive to
|
||
|
// stack based, then captures the system state (analogous to a Scheme
|
||
|
// continuation) after each element is appended to the member array,
|
||
|
// and allowing the computation to be restarted.
|
||
|
class BoxIntersectionIterator {
|
||
|
private:
|
||
|
friend class TreeType;
|
||
|
|
||
|
/** True if this is the "end" iterator instance */
|
||
|
bool isEnd;
|
||
|
|
||
|
/** The box that we're testing against. */
|
||
|
AABox box;
|
||
|
|
||
|
/** Node that we're currently looking at. Undefined if isEnd
|
||
|
is true. */
|
||
|
Node* node;
|
||
|
|
||
|
/** Nodes waiting to be processed */
|
||
|
// We could use backpointers within the tree and careful
|
||
|
// state management to avoid ever storing the stack-- but
|
||
|
// it is much easier this way and only inefficient if the
|
||
|
// caller uses post increment (which they shouldn't!).
|
||
|
Array<Node*> stack;
|
||
|
|
||
|
/** The next index of current->valueArray to return.
|
||
|
Undefined when isEnd is true.*/
|
||
|
int nextValueArrayIndex;
|
||
|
|
||
|
BoxIntersectionIterator() : isEnd(true) {}
|
||
|
|
||
|
BoxIntersectionIterator(const AABox& b, const Node* root) :
|
||
|
isEnd(root == NULL), box(b),
|
||
|
node(const_cast<Node*>(root)), nextValueArrayIndex(-1) {
|
||
|
|
||
|
// We intentionally start at the "-1" index of the current
|
||
|
// node so we can use the preincrement operator to move
|
||
|
// ourselves to element 0 instead of repeating all of the
|
||
|
// code from the preincrement method. Note that this might
|
||
|
// cause us to become the "end" instance.
|
||
|
++(*this);
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
inline bool operator!=(const BoxIntersectionIterator& other) const {
|
||
|
return ! (*this == other);
|
||
|
}
|
||
|
|
||
|
bool operator==(const BoxIntersectionIterator& other) const {
|
||
|
if (isEnd) {
|
||
|
return other.isEnd;
|
||
|
} else if (other.isEnd) {
|
||
|
return false;
|
||
|
} else {
|
||
|
// Two non-end iterators; see if they match. This is kind of
|
||
|
// silly; users shouldn't call == on iterators in general unless
|
||
|
// one of them is the end iterator.
|
||
|
if ((box != other.box) || (node != other.node) ||
|
||
|
(nextValueArrayIndex != other.nextValueArrayIndex) ||
|
||
|
(stack.length() != other.stack.length())) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// See if the stacks are the same
|
||
|
for (int i = 0; i < stack.length(); ++i) {
|
||
|
if (stack[i] != other.stack[i]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We failed to find a difference; they must be the same
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Pre increment.
|
||
|
*/
|
||
|
BoxIntersectionIterator& operator++() {
|
||
|
++nextValueArrayIndex;
|
||
|
|
||
|
bool foundIntersection = false;
|
||
|
while (! isEnd && ! foundIntersection) {
|
||
|
|
||
|
// Search for the next node if we've exhausted this one
|
||
|
while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) {
|
||
|
// If we entered this loop, then the iterator has exhausted the elements at
|
||
|
// node (possibly because it just switched to a child node with no members).
|
||
|
// This loop continues until it finds a node with members or reaches
|
||
|
// the end of the whole intersection search.
|
||
|
|
||
|
// If the right child overlaps the box, push it onto the stack for
|
||
|
// processing.
|
||
|
if ((node->child[1] != NULL) &&
|
||
|
(box.high()[node->splitAxis] > node->splitLocation)) {
|
||
|
stack.push(node->child[1]);
|
||
|
}
|
||
|
|
||
|
// If the left child overlaps the box, push it onto the stack for
|
||
|
// processing.
|
||
|
if ((node->child[0] != NULL) &&
|
||
|
(box.low()[node->splitAxis] < node->splitLocation)) {
|
||
|
stack.push(node->child[0]);
|
||
|
}
|
||
|
|
||
|
if (stack.length() > 0) {
|
||
|
// Go on to the next node (which may be either one of the ones we
|
||
|
// just pushed, or one from farther back the tree).
|
||
|
node = stack.pop();
|
||
|
nextValueArrayIndex = 0;
|
||
|
} else {
|
||
|
// That was the last node; we're done iterating
|
||
|
isEnd = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Search for the next intersection at this node until we run out of children
|
||
|
while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) {
|
||
|
if (box.intersects(node->valueArray[nextValueArrayIndex].bounds)) {
|
||
|
foundIntersection = true;
|
||
|
} else {
|
||
|
++nextValueArrayIndex;
|
||
|
// If we exhaust this node, we'll loop around the master loop
|
||
|
// to find a new node.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Post increment (much slower than preincrement!).
|
||
|
*/
|
||
|
BoxIntersectionIterator operator++(int) {
|
||
|
BoxIntersectionIterator old = *this;
|
||
|
++this;
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
/** Overloaded dereference operator so the iterator can masquerade as a pointer
|
||
|
to a member */
|
||
|
const T& operator*() const {
|
||
|
alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
|
||
|
return node->valueArray[nextValueArrayIndex].value;
|
||
|
}
|
||
|
|
||
|
/** Overloaded dereference operator so the iterator can masquerade as a pointer
|
||
|
to a member */
|
||
|
T const * operator->() const {
|
||
|
alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
|
||
|
return &(stack.last()->valueArray[nextValueArrayIndex].value);
|
||
|
}
|
||
|
|
||
|
/** Overloaded cast operator so the iterator can masquerade as a pointer
|
||
|
to a member */
|
||
|
operator T*() const {
|
||
|
alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
|
||
|
return &(stack.last()->valueArray[nextValueArrayIndex].value);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
Iterates through the members that intersect the box
|
||
|
*/
|
||
|
BoxIntersectionIterator beginBoxIntersection(const AABox& box) const {
|
||
|
return BoxIntersectionIterator(box, root);
|
||
|
}
|
||
|
|
||
|
BoxIntersectionIterator endBoxIntersection() const {
|
||
|
// The "end" iterator instance
|
||
|
return BoxIntersectionIterator();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Appends all members whose bounds intersect the box.
|
||
|
See also PointKDTree::beginBoxIntersection.
|
||
|
*/
|
||
|
void getIntersectingMembers(const AABox& box, Array<T>& members) const {
|
||
|
if (root == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
@param members The results are appended to this array.
|
||
|
*/
|
||
|
void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const {
|
||
|
if (root == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
AABox box;
|
||
|
sphere.getBounds(box);
|
||
|
root->getIntersectingMembers(box, sphere, members);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Stores the locations of the splitting planes (the structure but not the content)
|
||
|
so that the tree can be quickly rebuilt from a previous configuration without
|
||
|
calling balance.
|
||
|
*/
|
||
|
void serializeStructure(BinaryOutput& bo) const {
|
||
|
Node::serializeStructure(root, bo);
|
||
|
}
|
||
|
|
||
|
/** Clears the member table */
|
||
|
void deserializeStructure(BinaryInput& bi) {
|
||
|
clear();
|
||
|
root = Node::deserializeStructure(bi);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Returns an array of all members of the set. See also PointKDTree::begin.
|
||
|
*/
|
||
|
void getMembers(Array<T>& members) const {
|
||
|
memberTable.getKeys(members);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
C++ STL style iterator variable. See begin().
|
||
|
Overloads the -> (dereference) operator, so this acts like a pointer
|
||
|
to the current member.
|
||
|
*/
|
||
|
class Iterator {
|
||
|
private:
|
||
|
friend class TreeType;
|
||
|
|
||
|
// Note: this is a Table iterator, we are currently defining
|
||
|
// Set iterator
|
||
|
typename MemberTable::Iterator it;
|
||
|
|
||
|
Iterator(const typename MemberTable::Iterator& it) : it(it) {}
|
||
|
|
||
|
public:
|
||
|
inline bool operator!=(const Iterator& other) const {
|
||
|
return !(*this == other);
|
||
|
}
|
||
|
|
||
|
bool operator==(const Iterator& other) const {
|
||
|
return it == other.it;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Pre increment.
|
||
|
*/
|
||
|
Iterator& operator++() {
|
||
|
++it;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Post increment (slower than preincrement).
|
||
|
*/
|
||
|
Iterator operator++(int) {
|
||
|
Iterator old = *this;
|
||
|
++(*this);
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
const T& operator*() const {
|
||
|
return it->key;
|
||
|
}
|
||
|
|
||
|
T* operator->() const {
|
||
|
return &(it->key);
|
||
|
}
|
||
|
|
||
|
operator T*() const {
|
||
|
return &(it->key);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
C++ STL style iterator method. Returns the first member.
|
||
|
Use preincrement (++entry) to get to the next element (iteration
|
||
|
order is arbitrary).
|
||
|
Do not modify the set while iterating.
|
||
|
*/
|
||
|
Iterator begin() const {
|
||
|
return Iterator(memberTable.begin());
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
C++ STL style iterator method. Returns one after the last iterator
|
||
|
element.
|
||
|
*/
|
||
|
Iterator end() const {
|
||
|
return Iterator(memberTable.end());
|
||
|
}
|
||
|
#undef TreeType
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
#endif
|