7 #include "imstkLooseOctree.h" 8 #include "imstkLogger.h" 9 #include "imstkSurfaceMesh.h" 14 const double halfWidth,
const uint32_t depth) :
18 m_LowerBound(nodeCenter - Vec3d(halfWidth, halfWidth, halfWidth)),
19 m_UpperBound(nodeCenter + Vec3d(halfWidth, halfWidth, halfWidth)),
20 m_LowerExtendedBound(nodeCenter - 2.0 * Vec3d(halfWidth, halfWidth, halfWidth)),
21 m_UpperExtendedBound(nodeCenter + 2.0 * Vec3d(halfWidth, halfWidth, halfWidth)),
22 m_HalfWidth(halfWidth),
24 m_MaxDepth(tree->m_MaxDepth),
28 for (
int type = 0; type < OctreePrimitiveType::NumPrimitiveTypes; ++type)
37 #if defined(DEBUG) || defined(_DEBUG) || !defined(NDEBUG) 38 LOG_IF(FATAL, (!
m_pChildren)) <<
"Children node block is nullptr";
51 for (uint32_t childIdx = 0; childIdx < 8u; ++childIdx)
92 const auto childHalfWidth =
m_HalfWidth *
static_cast<double>(0.5);
93 for (uint32_t childIdx = 0; childIdx < 8u; ++childIdx)
96 newCenter[0] += (childIdx & 1) ? childHalfWidth : -childHalfWidth;
97 newCenter[1] += (childIdx & 2) ? childHalfWidth : -childHalfWidth;
98 newCenter[2] += (childIdx & 4) ? childHalfWidth : -childHalfWidth;
123 for (uint32_t childIdx = 0; childIdx < 8u; ++childIdx)
139 bool bAllEmpty =
true;
140 bool bAllLeaves =
true;
141 for (uint32_t childIdx = 0; childIdx < 8u; ++childIdx)
145 bAllLeaves &= pChildNode.isLeaf();
147 for (
int i = 0; i < OctreePrimitiveType::NumPrimitiveTypes; ++i)
149 bAllEmpty &= (pChildNode.m_PrimitiveCounts[i] == 0);
154 if (bAllEmpty && bAllLeaves)
178 static const auto type = OctreePrimitiveType::Point;
190 uint32_t childIdx = 0;
191 for (uint32_t dim = 0; dim < 3; ++dim)
195 childIdx |= (1 << dim);
207 const Vec3d priCenter(
208 (lowerCorner[0] + upperCorner[0]) * 0.5,
209 (lowerCorner[1] + upperCorner[1]) * 0.5,
210 (lowerCorner[2] + upperCorner[2]) * 0.5);
212 #if defined(DEBUG) || defined(_DEBUG) || !defined(NDEBUG) 214 <<
"Invalid primitive data (a non-root node must loosely contain primitives)";
223 uint32_t childIdx = 0;
224 bool bStraddle =
false;
226 for (uint32_t dim = 0; dim < 3; ++dim)
238 childIdx |= (1 << dim);
267 const double minWidthRatio ,
const std::string name ) :
271 m_MinWidthRatio(minWidthRatio),
272 m_MinWidth(minWidth),
273 m_pRootNode(new
OctreeNode(this, nullptr, center, width * static_cast<double>(0.5), 1u)),
274 m_NumAllocatedNodes(1u)
296 for (
int type = 0; type < OctreePrimitiveType::NumPrimitiveTypes; ++type)
326 delete[] pPrimitiveBlock;
338 for (
auto it = r.begin(), iEnd = r.end(); it != iEnd; ++it)
340 const auto pNodeBlock = *it;
341 for (uint32_t childIdx = 0; childIdx < 8u; ++childIdx)
343 const auto& pNode = pNodeBlock->m_Nodes[childIdx];
344 for (
int type = 0; type < OctreePrimitiveType::NumPrimitiveTypes; ++type)
346 if (prevResult < pNode.m_PrimitiveCounts[type])
348 prevResult = pNode.m_PrimitiveCounts[type];
355 [](
const uint32_t x,
const uint32_t y) -> uint32_t
357 return x > y ? x : y;
365 static const auto type =
static_cast<int>(OctreePrimitiveType::Point);
367 const auto pGeometry =
static_cast<Geometry*
>(pointset.get());
368 const auto geomIdx =
static_cast<uint32_t
>(pGeometry->getGlobalId());
371 const auto numNewPrimitives =
static_cast<uint32_t
>(pointset->getNumVertices());
376 vPrimitivePtrs.reserve(vPrimitivePtrs.size() + numNewPrimitives);
377 for (uint32_t idx = 0; idx < numNewPrimitives; ++idx)
379 const auto pPrimitive = &pPrimitiveBlock[idx];
381 vPrimitivePtrs.push_back(pPrimitive);
384 LOG(INFO) <<
"Added " << numNewPrimitives <<
" points to " <<
m_Name;
385 return numNewPrimitives;
392 static const auto type =
static_cast<int>(OctreePrimitiveType::Triangle);
394 const auto pGeometry =
static_cast<Geometry*
>(surfMesh.get());
395 const auto geomIdx =
static_cast<uint32_t
>(pGeometry->getGlobalId());
398 const auto numNewPrimitives =
static_cast<uint32_t
>(surfMesh->getNumCells());
403 vPrimitivePtrs.reserve(vPrimitivePtrs.size() + numNewPrimitives);
404 for (uint32_t triIdx = 0; triIdx < numNewPrimitives; ++triIdx)
406 const auto pPrimitive = &pPrimitiveBlock[triIdx];
408 vPrimitivePtrs.push_back(pPrimitive);
411 LOG(INFO) <<
"Added " << numNewPrimitives <<
" triangles to " <<
m_Name;
412 return numNewPrimitives;
419 static const auto type =
static_cast<int>(OctreePrimitiveType::Analytical);
421 const auto pGeometry = geometry.get();
422 const auto geomIdx =
static_cast<uint32_t
>(pGeometry->getGlobalId());
428 const auto pPrimitive = &pPrimitiveBlock[0];
432 LOG(INFO) <<
"Added a new analytical geometry to " <<
m_Name;
439 LOG_IF(FATAL, (
hasGeometry(geomIdx))) <<
"Geometry has previously been added";
458 LOG(WARNING) <<
"There was not any geometry added in the tree named '" <<
m_Name <<
"'";
467 double minWidth = IMSTK_DOUBLE_MAX;
468 for (
int type = OctreePrimitiveType::Triangle; type <= OctreePrimitiveType::Analytical; ++type)
471 if (vPrimitivePtrs.size() == 0)
475 const auto primitiveMinWidth = tbb::parallel_reduce(tbb::blocked_range<size_t>(0, vPrimitivePtrs.size()),
477 [&](
const tbb::blocked_range<size_t>& r,
double prevResult) ->
double {
478 for (
auto i = r.begin(), iEnd = r.end(); i != iEnd; ++i)
480 const auto pPrimitive = vPrimitivePtrs[i];
484 for (uint32_t dim = 0; dim < 3; ++dim)
486 widths[dim] = pPrimitive->m_UpperCorner[dim] - pPrimitive->m_LowerCorner[dim];
488 auto maxBoxWidth = widths[0];
489 maxBoxWidth = maxBoxWidth < widths[1] ? widths[1] : maxBoxWidth;
490 maxBoxWidth = maxBoxWidth < widths[2] ? widths[2] : maxBoxWidth;
491 prevResult = prevResult > maxBoxWidth ? maxBoxWidth : prevResult;
495 [](
const double x,
const double y) ->
double {
496 return x < y ? x : y;
499 minWidth = minWidth < primitiveMinWidth ? minWidth : primitiveMinWidth;
504 LOG(WARNING) <<
"Object/triangles have too small size";
514 uint32_t numLevelNodes = 1u;
515 uint32_t maxNumTreeNodes = 1u;
521 maxNumTreeNodes += numLevelNodes;
530 <<
", max depth = " <<
m_MaxDepth <<
", max num. nodes = " << maxNumTreeNodes;
550 for (
int type = 0; type < OctreePrimitiveType::NumPrimitiveTypes; ++type)
565 if (vPrimitivePtrs.size() == 0)
569 ParallelUtils::parallelFor(vPrimitivePtrs.size(),
570 [&](
const size_t idx) {
571 const auto pPrimitive = vPrimitivePtrs[idx];
572 const auto pointset =
static_cast<PointSet*
>(pPrimitive->m_pGeometry);
574 pPrimitive->m_Position = { point[0], point[1], point[2] };
583 if (vPrimitivePtrs.size() == 0)
587 ParallelUtils::parallelFor(vPrimitivePtrs.size(),
588 [&](
const size_t idx) {
589 const auto pPrimitive = vPrimitivePtrs[idx];
620 if (vPrimitivePtrs.size() == 0)
624 ParallelUtils::parallelFor(vPrimitivePtrs.size(),
625 [&](
const size_t idx) {
626 const auto pPrimitive = vPrimitivePtrs[idx];
627 const auto pointset =
static_cast<PointSet*
>(pPrimitive->m_pGeometry);
631 pPrimitive->m_Position = { point[0], point[1], point[2] };
633 auto pNode = pPrimitive->m_pNode;
634 if (!pNode->looselyContains(point) && pNode !=
m_pRootNode)
638 pNode = pNode->m_pParent;
639 if (pNode->contains(point) || pNode ==
m_pRootNode)
641 pPrimitive->m_bValid =
false;
642 pPrimitive->m_pNode = pNode;
649 pPrimitive->m_bValid = (pNode !=
m_pRootNode) ?
true :
false;
658 if (vPrimitivePtrs.size() == 0)
662 ParallelUtils::parallelFor(vPrimitivePtrs.size(),
663 [&](
const size_t idx) {
664 const auto pPrimitive = vPrimitivePtrs[idx];
666 const auto lowerCorner = pPrimitive->m_LowerCorner;
667 const auto upperCorner = pPrimitive->m_UpperCorner;
669 (lowerCorner[0] + upperCorner[0]) * 0.5,
670 (lowerCorner[1] + upperCorner[1]) * 0.5,
671 (lowerCorner[2] + upperCorner[2]) * 0.5);
673 auto pNode = pPrimitive->m_pNode;
674 if (!pNode->looselyContains(lowerCorner, upperCorner) && pNode !=
m_pRootNode)
678 pNode = pNode->m_pParent;
680 if (pNode->contains(lowerCorner, upperCorner) || pNode ==
m_pRootNode)
682 pPrimitive->m_bValid =
false;
683 pPrimitive->m_pNode = pNode;
691 pPrimitive->m_bValid =
true;
697 bool bStraddle =
false;
699 for (uint32_t dim = 0; dim < 3; ++dim)
703 if (pNode->m_Center[dim] - (pNode->m_HalfWidth * 0.5) > lowerCorner[dim]
704 || pNode->m_Center[dim] + (pNode->m_HalfWidth * 1.5) < upperCorner[dim])
712 if (pNode->m_Center[dim] + (pNode->m_HalfWidth * 0.5) < upperCorner[dim]
713 || pNode->m_Center[dim] - (pNode->m_HalfWidth * 1.5) > lowerCorner[dim])
724 pPrimitive->m_bValid =
true;
729 pPrimitive->m_bValid =
false;
730 pPrimitive->m_pNode = pNode;
745 for (
auto it = r.begin(), iEnd = r.end(); it != iEnd; ++it)
747 const auto pNodeBlock = *it;
748 for (uint32_t childIdx = 0; childIdx < 8u; ++childIdx)
750 auto& pNode = pNodeBlock->m_Nodes[childIdx];
751 for (
int type = 0; type < OctreePrimitiveType::NumPrimitiveTypes; ++type)
753 const auto pOldHead = pNode.m_pPrimitiveListHeads[type];
763 const auto pNext = pIter->
m_pNext;
772 pNode.m_pPrimitiveListHeads[type] = pNewHead;
773 pNode.m_PrimitiveCounts[type] = count;
784 if (vPrimitivePtrs.size() == 0)
788 ParallelUtils::parallelFor(vPrimitivePtrs.size(),
789 [&](
const size_t idx) {
790 const auto pPrimitive = vPrimitivePtrs[idx];
791 if (pPrimitive->m_bValid)
796 const auto pNode = pPrimitive->m_pNode;
797 (type == OctreePrimitiveType::Point) ? pNode->insertPoint(pPrimitive) : pNode->insertNonPointPrimitive(pPrimitive, type);
804 #if defined(DEBUG) || defined(_DEBUG) || !defined(NDEBUG) 805 LOG_IF(FATAL, (type == OctreePrimitiveType::Point))
806 <<
"Cannot compute bounding box for point primitive";
812 if (type == OctreePrimitiveType::Triangle)
815 const auto face = (*surfMesh->getCells())[pPrimitive->
m_Idx];
818 upperCorner = lowerCorner;
820 const Vec3d verts12[2] = {
821 surfMesh->getVertexPosition(face[1]),
822 surfMesh->getVertexPosition(face[2])
825 for (uint32_t dim = 0; dim < 3; ++dim)
827 lowerCorner[dim] = lowerCorner[dim] < verts12[0][dim] ? lowerCorner[dim] : verts12[0][dim];
828 lowerCorner[dim] = lowerCorner[dim] < verts12[1][dim] ? lowerCorner[dim] : verts12[1][dim];
830 upperCorner[dim] = upperCorner[dim] > verts12[0][dim] ? upperCorner[dim] : verts12[0][dim];
831 upperCorner[dim] = upperCorner[dim] > verts12[1][dim] ? upperCorner[dim] : verts12[1][dim];
839 pPrimitive->
m_LowerCorner = { lowerCorner[0], lowerCorner[1], lowerCorner[2] };
840 pPrimitive->
m_UpperCorner = { upperCorner[0], upperCorner[1], upperCorner[2] };
879 for (uint32_t i = 0; i < numBlocks; ++i)
881 const auto pBlock = &pBigBlock[i];
893 <<
"Internal data corrupted, may be all nodes were not returned from tree";
899 m_pNodeBigBlocks.resize(0);
void updatePositionAndCheckValidity()
For each point primitive, update its position from its parent geometry and check if it is still loose...
OctreeNodeBlock * m_NextBlock
Pointer to the next block in the memory pool.
void unlock()
End a thread-safe region.
void clearPrimitive(const OctreePrimitiveType type)
Completely remove all data of the given primitive type in the tree.
std::array< double, 3 > m_Position
For a point primitive, store its position.
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
void removeInvalidPrimitivesFromNodes()
Remove all invalid primitives from the tree nodes previously contained them.
const uint32_t m_Idx
Index of the primitive in the parent geometry (such as index of the triangle in a mesh) ...
OctreePrimitive * m_pPrimitiveListHeads[OctreePrimitiveType::NumPrimitiveTypes]
Heads of the link lists storing (Classified) primitives.
uint32_t m_NumAvaiableBlocksInPool
Count the number of nodes available in memory pool.
OctreeNode *const m_pRootNode
Root node, should not be reassigned throughout the existence of the tree.
LooseOctree(const Vec3d ¢er, const double width, const double minWidth, const double minWidthRatio=1.0, const std::string name="LooseOctree")
Octree constructor.
void populatePointPrimitives()
Populate point primitive to tree nodes, from top (root node) down to leaf nodes.
LooseOctree * m_pTree
Pointer to the octree, used to request children from memory pool during splitting node...
tbb::concurrent_unordered_set< OctreeNodeBlock * > m_sActiveTreeNodeBlocks
Set of node blocks that are in use (node blocks that have been taken from memory pool) ...
const double m_Width
Width of the tree bounding box.
OctreeNode * getChildNode(const uint32_t childIdx) const
Get a child node.
bool m_bAlwaysRebuild
If true, the octree is always be rebuit from scratch every time calling to update() ...
virtual void clear()
Clear all primitive and geometry data, but still keep allocated nodes in memory pool to recycle...
void computePrimitiveBoundingBox(OctreePrimitive *const pPrimitive, const OctreePrimitiveType type)
Compute the AABB bounding box of a non-point primitive.
OctreePrimitiveType
The OctreePrimitiveType enum Type of primitive stored in the octree.
void addGeometry(const uint32_t geomIdx)
Add geometry to the internal geometry list to check for duplication.
void update()
Update tree (the tree is rebuilt from scratch if m_bAlwaysRebuild is true, otherwise it is incrementa...
virtual void computeBoundingBox(Vec3d &lowerCorner, Vec3d &upperCorner, const double paddingPercent=0.0)
Compute the bounding box for the geometry.
const uint32_t m_Depth
Depth of this node (depth > 0, depth = 1 starting at the root node)
void updateBoundingBoxAndCheckValidity(const OctreePrimitiveType type)
For each non-point primitive, update its bounding box from its parent geometry and check if it is sti...
std::array< double, 3 > m_LowerCorner
For a non-point primitive, store its AABB's lower corner.
OctreeNodeBlock * m_pChildren
Pointer to a memory block containing 8 children nodes.
std::array< double, 3 > m_UpperCorner
For a non-point primitive, store its AABB's upper corner.
ParallelUtils::SpinLock m_PoolLock
Atomic lock for multi-threading modification of the memory pool.
Geometry *const m_pGeometry
Pointer to the parent geometry that the primitive belong to.
void incrementalUpdate()
Incrementally update octree from current state.
ParallelUtils::SpinLock m_NodeSplitingLock
Mutex lock for thread-safe splitting node.
OctreeNode()
Dummy constructor, called only during memory allocation in memory pool.
void reinsertInvalidPrimitives(const OctreePrimitiveType type)
For each invalid primitive, insert it back to the tree in a top-down manner starting from the lowest ...
void rebuild()
Rebuild the tree from scratch.
uint32_t m_MaxDepth
Cache the max depth of the tree (maximum depth level possible)
void insertPoint(OctreePrimitive *const pPrimitive)
Insert a point primitive into the subtree in a top-down manner.
std::vector< OctreePrimitive * > m_pPrimitiveBlocks[OctreePrimitiveType::NumPrimitiveTypes]
const Vec3d m_Center
Center of the tree.
const Vec3d m_Center
Center of this node.
uint32_t addAnalyticalGeometry(const std::shared_ptr< Geometry > &geometry)
Add an analytical geometry (such as plane/sphere/cube etc) into the tree (it will not be populated to...
OctreeNodeBlock * requestChildrenFromPool()
Request a block of 8 tree nodes from memory pool (this is called only during splitting node) If the m...
void removeEmptyDescendants()
Recursively remove all descendant nodes that do not contain primitives (all 8 children of a node are ...
Base class for any geometrical representation.
uint32_t getMaxNumPrimitivesInNodes() const
Count the maximum number of primitives stored in a tree node.
ParallelUtils::SpinLock m_PrimitiveLock[OctreePrimitiveType::NumPrimitiveTypes]
Mutex lock for thread-safe primitive list modification.
double m_MinWidth
Minimum width allowed for the tree nodes.
const Vec3d & getVertexPosition(const size_t vertNum, DataType type=DataType::PostTransform) const
Returns the position of a vertex given its index.
uint32_t m_MaxDepth
Max depth of the tree, which is computed based on m_MinWidth.
void lock()
Start a thread-safe region, where only one thread can execute at a time until a call to the unlock fu...
const double m_HalfWidth
Half width of the node AABB.
The OctreePrimitive struct For each octree primitive (point/triangle/analytical geometry), store its relevant data.
const double m_MinWidthRatio
If there is no point primitive, minWidth will be recomputed as minWidth = min(width of all non-point ...
std::vector< OctreePrimitive * > m_vPrimitivePtrs[OctreePrimitiveType::NumPrimitiveTypes]
Store pointers of primitives created from geometry elements, such as points, triangles, analytical geometries.
void removeGeometry(const uint32_t geomIdx)
Remove geometry from the internal geometry list (does nothing if the geometry does not exist...
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
uint32_t m_NumAllocatedNodes
Count the total number of allocated nodes so far.
const std::string m_Name
Name of the tree.
uint32_t addPointSet(const std::shared_ptr< PointSet > &pointset)
Add a PointSet geometry into the tree (the points will not be populated to tree nodes until calling t...
std::vector< OctreeNodeBlock * > m_pNodeBigBlocks
Class LooseOctree, where each tree node has a loose boundary which is exactly twice big as its exact...
OctreeNodeBlock * m_pNodeBlockPoolHead
The pool of tree nodes, storing pre-allocated nodes as a linked list.
bool looselyContains(const Vec3d &point)
Check if the given point is contained in the node loose boundary (which is 2X bigger than the boundin...
void clearPrimitiveData(const OctreePrimitiveType type)
Recursively clear primitive data (linked lists and counters) Note that the primitives are still exist...
bool m_bCompleteBuild
This is set to true after tree has been built, otherwise false.
void returnChildrenToPool(OctreeNodeBlock *const pNodeBlock)
Return 8 children nodes to memory pool (this is called only during destroying descendant nodes) ...
void deallocateMemoryPool()
Deallocate all node block in memory pool, called only during octree destructor.
bool isLeaf() const
Check if this node is a leaf node.
void allocateMoreNodeBlock(const uint32_t numBlocks)
Pre-allocate a given number of node blocks (each block contains 8 nodes) and add them to the memory p...
The OctreeNodeBlock struct This is a data structure to store a memory block of 8 tree node at a time ...
void removeAllDescendants()
Recursively remove all descendant nodes (return them back to memory pool) As a result, after calling to this function, the current node will become a leaf node.
void populateNonPointPrimitives(const OctreePrimitiveType type)
Populate non-point primitive (triangle/analytical geometry) to tree nodes, from top (root node) down ...
bool m_bIsLeaf
True if this node does not have any child node (a node should have either 0 or 8 children) ...
OctreeNode * m_pNode
Pointer to the octree node containing the primitive.
bool hasGeometry(uint32_t geomIdx) const
Check if a geometry with the given geometry index has been added to the octree before.
std::unordered_set< uint32_t > m_sGeometryIndices
List of all indices of the added geometries, to check for duplication such that one geometry cannot b...
void build()
Build octree from the provided geometries.
uint32_t m_PrimitiveCounts[OctreePrimitiveType::NumPrimitiveTypes]
Count the number of (classified) primitives stored in this node.
uint32_t addTriangleMesh(const std::shared_ptr< SurfaceMesh > &surfMesh)
Add a triangle mesh into the tree (the triangles of the mesh will not be populated to tree nodes unti...
void split()
Split node (requesting 8 children nodes from memory pool)
OctreePrimitive * m_pNext
Pointer to the next node in the primitive list of the octree node.
void insertNonPointPrimitive(OctreePrimitive *const pPrimitive, const OctreePrimitiveType type)
Insert a non-point primitive into the subtree in a top-down manner.
void keepPrimitive(OctreePrimitive *const pPrimitive, const OctreePrimitiveType type)
Keep the primitive at this node as cannot pass it down further to any child node. ...