Surface mesh point queries: C++ API

Codes written in C++ may use the object-oriented C++ APIs to perform in/out and signed distance queries. In addition to language choice, the C++ API lets a code work with more than one mesh at the same time. Unlike the C API, the C++ API for in/out and signed distance queries has no initializer taking a file name: readying the mesh is a separate, prior step.

In/out Octree

The C++ in/out query is provided by the quest::InOutOctree class, from the following header. See <axom>/src/axom/quest/tests/quest_inout_octree.cpp.

#include "axom/quest/InOutOctree.hpp"

Some type aliases are useful for the sake of brevity. The class is templated on the dimensionality of the mesh. Currently, only meshes in 3D are supported; here DIM equals 3.

using Octree3D = axom::quest::InOutOctree<DIM>;

using GeometricBoundingBox = Octree3D::GeometricBoundingBox;
using SpacePt = Octree3D::SpacePt;

Instantiate the object using GeometricBoundingBox bbox and a mesh, and generate the index.

  Octree3D octree(bbox, mesh);
  octree.generateIndex();

Test a query point.

SpacePt pt = SpacePt::make_point(2., 3., 1.);
bool inside = octree.within(pt);

All cleanup happens when the index object’s destructor is called (in this case, when the variable octree goes out of scope).

Signed Distance

The C++ signed distance query is provided by the quest::SignedDistance class, which wraps an instance of primal::BVHTree. Examples from <axom>/src/axom/quest/tests/quest_signed_distance.cpp.

Class header:

#include "axom/quest/SignedDistance.hpp"

The constructor takes several arguments:

  • const mint::Mesh* surfaceMesh: A pointer to a surface mesh with triangles and/or quadrilaterals.

  • bool isWatertight: Indicates the mesh is a watertight mesh, a manifold. The signed distance from a point to a manifold is mathematically well-defined. When the input is not a closed surface mesh, the mesh must span the entire computational mesh domain, dividing it into two regions.

  • bool computeSign (default true): Optional. Enables or disables the computation of signs in distance queries.

  • int allocatorID: Optional. Sets a custom Umpire allocator to use in constructing the underlying BVH; by default, this is set to a default allocator for the execution space the SignedDistance class is instantiated in (host-side memory for CPU and OpenMP, unified memory for GPUs).

Note that the second and subsequent arguments to the constructor correspond to quest::signed_distance_set functions in the C API.

As with the InOutOctree, the class is templated on the dimensionality of the mesh, with only 3D meshes being supported. The class also accepts a template parameter for execution space, for running signed distance queries with OpenMP or on a GPU.

  // Set execution space
  constexpr int BlockSize = 256;
  #if defined(__CUDACC__)
  using ExecSpace = axom::CUDA_EXEC<BlockSize>;
  #elif defined(__HIPCC__)
  using ExecSpace = axom::HIP_EXEC<BlockSize>;
  #else
  using ExecSpace = axom::SEQ_EXEC;
  #endif

  // Create a custom allocator
  constexpr size_t PoolSize = 1024 * 1024 * 1024;
  umpire::ResourceManager& rm = umpire::ResourceManager::getInstance();
  umpire::Allocator device_allocator =
    rm.makeAllocator<umpire::strategy::QuickPool>(
      "DEVICE_POOL",
      rm.getAllocator(umpire::resource::Device),
      PoolSize);
  int device_pool_id = device_allocator.getId();

  // Set SignedDistance options
  constexpr bool is_watertight = true;
  constexpr bool compute_signs = true;
  quest::SignedDistance<3, ExecSpace> signed_distance(surface_mesh,
                                                      is_watertight,
                                                      compute_signs,
                                                      device_pool_id);

Test a query point.

axom::primal::Point< double,3 > pt =
  axom::primal::Point< double,3 >::make_point(2., 3., 1.);
double signedDistance = signed_distance.computeDistance(pt);

Test a batch of query points.

const int numPoints = 20;
axom::primal::Point<double, 3>* pts =
  axom::allocate<axom::primal::Point<double, 3>>(numPoints);
for (int ipt = 0; ipt < numPoints; ipt++)
{
  // fill pts array
  pts[ipt] = ...;
}
double signedDists = axom::allocate<double>(20);
signed_distance.computeDistances(numPts, pts, signedDists);

The object destructor takes care of all cleanup.