SpatialOctreeΒΆ

Axom provides an implementation of the octree spatial index. The SpatialOctree recursively divides a bounding box into a hierarchy of non-intersecting bounding boxes. Each level of subdivision divides the bounding box of interest along each of its dimensions, so 2D SpatialOctree objects contain four child bounding boxes at each level, while 3D objects contain eight children at each level.

The Octree class hierarchy is useful for building custom spatial acceleration data structures, such as quest::InOutOctree.

The figure below shows the construction of several levels of a 2D octree.

Diagram showing points in a bounding box

In contrast to a BVHTree, which computes a bounding box at each step, the octree structure begins with a user-specified bounding box.

Diagram showing first division of a SpatialOctree

The octree divides all dimensions in half at each step.

Diagram showing second division of a SpatialOctree
Diagram showing third division of a SpatialOctree

Similar to the BVHTree, the Octree divides a bounding box only if an object intersects that bounding box. In contrast to the BVHTree, the bounding box bins are non-intersecting, and division does not depend on the data in the bounding box. An \(N\)-dimensional octree divides into \(2^N\) bounding boxes at each step.

The following code example shows use of the SpatialOctree. Include headers and define types:

#include "axom/spin/SpatialOctree.hpp"

using LeafNodeType = axom::spin::BlockData;

using OctreeType = axom::spin::SpatialOctree<in3D, LeafNodeType>;
using OctBlockIndex = OctreeType::BlockIndex;
using OctSpacePt = OctreeType::SpacePt;
using OctBBox = OctreeType::GeometricBoundingBox;

Then, instantiate the SpatialOctree and locate or refine blocks that contain query points.

  OctBBox bb(OctSpacePt(10), OctSpacePt(20));

  // Generate a point within the bounding box
  double alpha = 2. / 3.;
  OctSpacePt queryPt = OctSpacePt::lerp(bb.getMin(), bb.getMax(), alpha);

  // Instantiate the Octree
  OctreeType octree(bb);

  // Find the block containing the query point
  OctBlockIndex leafBlock = octree.findLeafBlock(queryPt);
  // and the bounding box of the block.
  OctBBox leafBB = octree.blockBoundingBox(leafBlock);

  for(int i = 0; i < octree.maxInternalLevel(); ++i)
  {
    // SpatialOctree allows a code to refine (subdivide) a block
    octree.refineLeaf(leafBlock);
    // and locate the (new) child block containing the query point.
    leafBlock = octree.findLeafBlock(queryPt);
  }

Unlike the BVHTree class, the SpatialOctree is intended as a building block for further specialization. Please see the quest::InOutOctree as an example of this.

Some ancillary classes used in the implementation of SpatialOctree include BlockData, which ties data to a block; Brood, used to construct and organize sibling blocks; OctreeBase, implementing non-geometric operations such as refinement and identification of parent or child nodes; and SparseOctreeLevel and DenseOctreeLevel, which hold the blocks at any one level of the SpatialOctree. Of these, library users will probably be most interested in providing a custom implementation of BlockData to hold algorithm data associated with a box within an octree. See the quest::InOutOctree class for an example of this.