/** @file PointHashGrid.h @maintainer Morgan McGuire, http://graphics.cs.williams.edu @created 2008-07-01 @edited 2009-05-28 Copyright 2000-2010, Morgan McGuire. All rights reserved. */ #ifndef G3D_PointHashGrid_h #define G3D_PointHashGrid_h #include "G3D/platform.h" #include "G3D/EqualsTrait.h" #include "G3D/HashTrait.h" #include "G3D/Vector3.h" #include "G3D/Vector3int32.h" #include "G3D/Array.h" #include "G3D/Table.h" #include "G3D/AABox.h" #include "G3D/Sphere.h" #include "G3D/SmallArray.h" namespace G3D { /** Storage of data in a sparse 3D grid of point-based data. The space cost for n elements is O(n). For data with approximately uniform density (with respect to the radius hint), the time cost of searching for neighbors is O(1). Value must be supported by a G3D::PositionTrait and G3D::EqualsTrait. Overloads are provided for common G3D classes like G3D::Vector3. For example:
class EqualsFunc { public: static bool equals(const Data& p, const Data& q) { return p == q; } }; class PosFunc { public: static void getPosition(const Data& d, Vector3& pos) { pos = d.location; } }; PointHashGrid grid;If the Value class defines operator==, the Equalsfunc is optional:
PointHashGrid grid;*/ template
position * m_cellWidth
*/
CellTable m_data;
MemoryManager::Ref m_memoryManager;
/** Intentionally unimplemented: prevent copy construction. */
PointHashGrid(const ThisType&);
/** Intentionally unimplemented: prevent assignment. */
PointHashGrid& operator=(const ThisType&);
/** Locate the cell and index within that cell containing v. Called by
remove() and contains(). */
bool find(const Value& v,
Vector3int32& foundCellCoord,
Cell*& foundCell,
int& index) {
Vector3 pos;
PosFunc::getPosition(v, pos);
Vector3int32 cellCoord;
getCellCoord(pos, cellCoord);
for (int i = 0; i < 27; ++i) {
Vector3int32 c = cellCoord + m_offsetArray[i];
Cell* cell = m_data.getPointer(c);
if (cell != NULL) {
// The cell exists
for (int j = 0; j < cell->size(); ++j) {
if (EqualsFunc::equals((*cell)[j].value, v)) {
foundCell = cell;
index = j;
foundCellCoord = c;
return true;
}
}
}
}
// Not found
return false;
}
/** Given a real-space position, returns the cell coord
containing it.*/
inline void getCellCoord(const Vector3& pos, Vector3int32& cellCoord) const {
for (int a = 0; a < 3; ++a) {
cellCoord[a] = iFloor(pos[a] * m_invCellWidth);
}
}
/** Initializes m_offsetArray. */
void initOffsetArray() {
int i = 0;
Vector3int32 d;
for (d.x = -1; d.x <= +1; ++d.x) {
for (d.y = -1; d.y <= +1; ++d.y) {
for (d.z = -1; d.z <= +1; ++d.z) {
m_offsetArray[i] = d;
++i;
}
}
}
// Put (0, 0, 0) first, so that contains() is most likely to find
// the value quickly.
i = (1 * 3 + 1) * 3 + 1;
debugAssert(m_offsetArray[i] == Vector3int32(0,0,0));
Vector3int32 temp = m_offsetArray[0];
m_offsetArray[0] = m_offsetArray[i];
m_offsetArray[i] = temp;
}
public:
/**
@param radiusHint the radius that will typically be used with
beginSphereIntersection and beginBoxIntersection. If two Values are equal,
their positions must be within this radius as well.
*/
PointHashGrid(float radiusHint, const MemoryManager::Ref& m = MemoryManager::create()) : m_size(0), m_memoryManager(m) {
initOffsetArray();
m_data.clearAndSetMemoryManager(m_memoryManager);
debugAssertM(radiusHint > 0, "Cell radius must be positive");
m_cellWidth = radiusHint;
m_invCellWidth = 1.0f / m_cellWidth;
}
/**
If radiusHint is negative, it is automatically chosen to put
about 5 values in each grid cell (which means about 27 * 5
values for each beginIntersection call).
*/
PointHashGrid(const ArraygetPosition(v, p)
.
Multiple elements that are equal may be inserted; all copies will be
in the data structure. */
void insert(const Value& v) {
Vector3 pos;
PosFunc::getPosition(v, pos);
Vector3int32 cellCoord;
getCellCoord(pos, cellCoord);
// See if the cell already exists
Cell& cell = m_data.getCreate(cellCoord);
if (cell.size() == 0) {
// Use the same memory manager as for the whole class
cell.clearAndSetMemoryManager(m_memoryManager);
}
Entry& entry = cell.next();
entry.value = v;
entry.position = pos;
// Update the bounds
if (size() == 0) {
m_bounds = AABox(pos);
} else {
m_bounds.merge(pos);
}
++m_size;
++m_epoch;
}
/** Inserts all elements of the array. */
void insert(const Arrayfor (Grid::Iterator i = grid.begin(); i != grid.end(), ++i) { const Value& = *i; ... }*/ Iterator begin() const { return Iterator(this); } const Iterator& end() const { static const Iterator it; return it; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // Forward declaration required by older gcc versions for friend declaration in BoxIterator class SphereIterator; class BoxIterator { private: friend class ThisType; friend class SphereIterator; bool m_isEnd; const ThisType* m_grid; /** Lower bound on the boxes covered, inclusive. */ Vector3int32 m_lo; /** Upper bound on the boxes covered, inclusive.*/ Vector3int32 m_hi; /** If true, test values against m_box before returning them.*/ bool m_exact; /** The underlying box in 3D space */ AABox m_box; /** The iterator winds through the 3D grid between m_lo and (m_lo + m_extent) in Z,Y,X-major order. This is the index keeping track of how far it has come */ Vector3int32 m_current; /** The current cell. */ Cell* m_cell; /** Index within m_cell of the current value */ int m_arrayIndex; const int m_epoch; /** Called from advance() */ void advanceCell() { do { ++m_current.x; if (m_current.x > m_hi.x) { m_current.x = m_lo.x; ++m_current.y; if (m_current.y > m_hi.y) { m_current.y = m_lo.y; ++m_current.z; if (m_current.z > m_hi.z) { m_isEnd = true; return; } } } // Pick up the new cell m_cell = m_grid->m_data.getPointer(m_current); // Keep advancing if the cell does not exist } while ((m_cell == NULL) || (m_cell->size() == 0)); } /** Advance to the next value */ void advance() { debugAssert(! m_isEnd); do { ++m_arrayIndex; bool inConstructor = (m_cell == NULL); if (inConstructor || m_arrayIndex >= m_cell->size()) { advanceCell(); m_arrayIndex = 0; if (m_isEnd) { // Ran out of values return; } debugAssert(m_cell != NULL); } // Advance until we have a value that can be returned, either // because we don't care about exactness or because it is // guaranteed to be within the box. } while (m_exact && ! m_box.contains(position())); } /** End iterator */ BoxIterator() : m_isEnd(true), m_grid(NULL), m_exact(true), m_current(0,0,0), m_cell(NULL), m_arrayIndex(0), m_epoch(0) {} /** Begin iterator */ BoxIterator(const ThisType* grid, bool exact, const AABox& box) : m_isEnd(false), m_grid(grid), m_exact(exact), m_box(box), m_current(-1, 0 ,0), m_cell(NULL), m_arrayIndex(0), m_epoch(grid->m_epoch) { m_grid->getCellCoord(box.low(), m_lo); m_grid->getCellCoord(box.high(), m_hi); // Get to the first value m_current = m_lo; // Back up one so that advancing takes us to the first --m_current.x; advance(); } const Value& value() const { debugAssert(! m_isEnd); return (*m_cell)[m_arrayIndex].value; } /** Used by SphereIterator::advance() */ const Vector3& position() const { debugAssert(! m_isEnd); return (*m_cell)[m_arrayIndex].position; } // Intentionally unimplemented BoxIterator& operator=(const BoxIterator&); public: inline bool operator!=(const BoxIterator& other) const { if (other.m_isEnd && m_isEnd) { return false; } else { return (m_isEnd != other.m_isEnd) || (m_cell != other.m_cell) || (m_arrayIndex != other.m_arrayIndex); } } bool operator==(const BoxIterator& other) const { return !(*this != other); } /** Preincrement */ BoxIterator& operator++() { debugAssert(! m_isEnd); debugAssertM(m_epoch == m_grid->m_epoch, "It is illegal to mutate the HashGrid " "while iterating through it."); advance(); return *this; } /** Post increment (slower) */ BoxIterator operator++(int) { Iterator old = *this; ++(*this); return old; } const Value& operator*() const { return value(); } const Value* operator->() const { return &value(); } operator Value*() const { return &value(); } bool hasMore() const { return ! m_isEnd; } }; // BoxIterator /** Finds all values whose positions are within @a box. It is an error to mutate the PointHashGrid while iterating through it. @param exact If false, the iterator will execute more quickly but will likely return some values that lie outside the box. Set exact = false if you are going to test the results against the yourself box anyway. */ BoxIterator beginBoxIntersection(const AABox& box, bool exact = true) const { return BoxIterator(this, exact, box); } const BoxIterator& endBoxIntersection() const { static const BoxIterator it; return it; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// class SphereIterator { private: friend class ThisType; bool m_isEnd; Sphere m_sphere; BoxIterator m_boxIterator; SphereIterator() : m_isEnd(true) {} void advance() { if (! m_boxIterator.hasMore()) { m_isEnd = true; return; } while (! m_sphere.contains(m_boxIterator.position())) { ++m_boxIterator; if (! m_boxIterator.hasMore()) { m_isEnd = true; return; } } } static AABox getBoundingBox(const Sphere& s) { AABox box; s.getBounds(box); return box; } SphereIterator(const ThisType* grid, const Sphere& sphere) : m_isEnd(false), m_sphere(sphere), m_boxIterator(grid, false, getBoundingBox(sphere)) { // Find the first element that is actually in the sphere, // not just the box. advance(); } const Value& value() const { return *m_boxIterator; } // TODO: if the sphere is very big compared to radius, check each // cell's box to see if the cell itself is actually inside the sphere // before iterating through it, since there may be many boxes outside the sphere. // Intentionally unimplemented SphereIterator& operator=(const SphereIterator&); public: inline bool operator!=(const SphereIterator& other) const { if (other.m_isEnd && m_isEnd) { return false; } else { return (m_isEnd != other.m_isEnd) || (m_sphere != other.m_sphere) || (m_boxIterator != other.m_boxIterator); } } bool operator==(const SphereIterator& other) const { return !(*this != other); } /** Preincrement */ SphereIterator& operator++() { debugAssert(! m_isEnd); ++m_boxIterator; advance(); return *this; } /** Post increment (slower) */ SphereIterator operator++(int) { Iterator old = *this; ++(*this); return old; } const Value& operator*() const { return value(); } const Value* operator->() const { return &value(); } operator Value*() const { return &value(); } bool hasMore() const { return ! m_isEnd; } }; // SphereIterator /** Finds all values whose positions are within @a sphere. It is an error to mutate the HashGrid while iterating through it. */ SphereIterator beginSphereIntersection(const Sphere& sphere) const { return SphereIterator(this, sphere); } const SphereIterator& endSphereIntersection() const { static const SphereIterator it; return it; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /** Dereference to access the bounds() and size() [element count] of the underlying cell objet. Example:
for(PointHashGrid*/ class CellIterator { private: friend class ThisType; bool m_isEnd; const ThisType* m_grid; typename CellTable::Iterator m_tableIterator; const int m_epoch; Cell& cell() { return m_tableIterator->value; } public: class CellObject { friend class CellIterator; private: const CellIterator* m_parent; CellObject() : m_parent(NULL) {} public: /** Returns the bounds on this cell */ AABox bounds() const { const Vector3int32& k = m_parent->m_tableIterator->key; return AABox(Vector3(k) * m_parent->m_cellWidth, Vector3(k + Vector3int32(1, 1, 1)) * m_parent->m_cellWidth); } /** Number of elements inside this cell */ int size() const { debugAssert(! m_parent->m_isEnd); return m_parent->m_tableIterator->value.size(); } }; private: /** Used to make the indirection work.*/ CellObject m_indirection; /** End iterator. Note that the m_tableIterator is initialized to the end iterator of a temporary value! This is ok because we'll never look at the value of the m_tableIterator, since we're initializing the "end" Iterator.*/ CellIterator() : m_isEnd(true), m_grid(NULL), m_tableIterator( CellTable().end() ), m_epoch(0) {} CellIterator(const ThisType* grid) : m_isEnd(false), m_grid(grid), m_tableIterator( grid->m_data.begin()), m_epoch(grid->m_epoch) { m_indirection.m_parent = this; m_isEnd = ! m_tableIterator.hasMore(); } // Intentionally unimplemented CellIterator& operator=(const CellIterator&); public: const CellObject& operator*() const { return m_indirection; } const CellObject* operator->() const { return &m_indirection; } operator CellObject*() const { return &m_indirection; } inline bool operator!=(const CellIterator& other) const { // != is called more often than == during iteration return !( (m_isEnd && other.m_isEnd) || ((m_isEnd == other.m_isEnd) && (m_tableIterator != other.m_tableIterator))); } bool operator==(const CellIterator& other) const { return !(*this != other); } /** Preincrement */ CellIterator& operator++() { debugAssertM(m_epoch == m_grid->m_epoch, "It is illegal to mutate the HashGrid while " "iterating through it."); ++m_tableIterator; m_isEnd = ! m_tableIterator.hasMore(); return *this; } /** Post increment (slower) */ CellIterator operator++(int) { Iterator old = *this; ++(*this); return old; } bool hasMore() const { return ! m_isEnd; } }; // CellIterator /** Iterates through the non-empty cells. This is intended primarily for debugging and visualizing the data structure.*/ CellIterator beginCells() const { return CellIterator(this); } const CellIterator& endCells() const { static const CellIterator it; return it; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /** Returns true if there is a value that is exactly equal to @a v. This will check all neighboring cells to avoid roundoff error at cell boundaries. */ bool contains(const Value& v) const { Cell* cell = NULL; int index = 0; Vector3int32 cellCoord; return const_cast::CellIterator iter = grid.beginCells(); iter != grid.endCells(); ++iter) { entriesFound += iter->size(); }