https://mooseframework.inl.gov
Loading...
Searching...
No Matches
Public Member Functions | Private Types | Private Member Functions | Private Attributes | List of all members
TriangleManifold Class Reference

Utility for querying point containment against a closed triangulated surface mesh. More...

#include <TriangleManifold.h>

Public Member Functions

 TriangleManifold (MeshBase &mesh, const Real surface_tolerance)
 Build a manifold classifier from a prepared surface mesh.
 
bool contains (const Point &point) const
 
SurfaceGeometry::SurfaceSide sideness (const Point &point) const
 Classify a query point relative to the manifold surface.
 
const libMesh::BoundingBoxboundingBox () const
 
std::size_t numTriangles () const
 

Private Types

enum class  RayIntersection { Miss , Hit , Ambiguous }
 Result of intersecting the positive x-direction ray with a triangle. More...
 

Private Member Functions

void finalize ()
 Complete post-parse validation and acceleration-structure setup.
 
void buildCandidateGrid ()
 Build the yz-plane lookup grid used to accelerate +x ray queries.
 
bool pointInsideBoundingBox (const Point &point) const
 Cheap global bounding-box rejection for containment queries.
 
bool pointOnSurface (const Point &point) const
 Detect whether a query point lies on or extremely near the manifold surface.
 
RayIntersection rayIntersectsTriangle (const Point &point, const libMesh::Elem &tri) const
 Intersect a positive x-direction ray with a single triangle.
 
bool containsBySolidAngle (const Point &point) const
 Robust fallback containment query based on accumulated solid angle.
 
SurfaceGeometry::SurfaceSide classifyByParity (const Point &point) const
 Resolve INSIDE vs OUTSIDE via the fixed +x ray parity test (with solid-angle fallback for ambiguous grazing hits).
 
std::vector< dof_id_type > rayCandidates (const Point &point) const
 Get the subset of triangles whose yz extents may intersect the query ray.
 

Private Attributes

std::size_t _num_y_cells = 1
 Number of yz-grid bins in the y direction.
 
std::size_t _num_z_cells = 1
 Number of yz-grid bins in the z direction.
 
Real _y_min = 0.0
 Minimum global y coordinate used to map query points into yz-grid bins.
 
Real _z_min = 0.0
 Minimum global z coordinate used to map query points into yz-grid bins.
 
Real _y_cell_size = 1.0
 Width of one yz-grid cell in the y direction.
 
Real _z_cell_size = 1.0
 Width of one yz-grid cell in the z direction.
 
std::unordered_map< std::uint64_t, std::vector< dof_id_type > > _ray_grid
 Lookup from packed yz-grid cell index to triangles that could intersect the +x query ray.
 
MeshBase & _mesh
 
const Real _surface_tolerance
 Absolute tolerance used throughout validation and geometric classification.
 
const libMesh::BoundingBox _bounding_box
 Global bounding box of the transformed manifold.
 
const std::unique_ptr< libMesh::PointLocatorBase_point_locator
 Pre-built point locator for fast proximity-to-surface detection.
 

Detailed Description

Utility for querying point containment against a closed triangulated surface mesh.

The class takes ownership of a reference to a prepared 2D surface mesh, validates that it forms a closed 2-manifold (every triangle has exactly three neighbors, all elements are Tri3, and the mesh is consistently oriented), builds a lightweight yz-plane acceleration grid, and exposes contains() for point-in-solid queries.

Containment is resolved in three stages:

  1. Cheap global bounding-box rejection.
  2. Near-surface detection via a pre-built point locator (points within surface_tolerance of the mesh surface are treated as inside).
  3. Odd/even parity counting on a fixed +x ray, with automatic fallback to a solid-angle accumulation test when the ray grazes a triangle edge or vertex.

The referenced mesh must outlive this object. Any geometric transforms (scale, rotation, translation) should be applied to the mesh before constructing a TriangleManifold.

Definition at line 45 of file TriangleManifold.h.

Member Enumeration Documentation

◆ RayIntersection

enum class TriangleManifold::RayIntersection
strongprivate

Result of intersecting the positive x-direction ray with a triangle.

Ambiguous is returned when the hit is too close to an edge, vertex, or the ray origin. In that case we abandon parity counting and fall back to the more expensive but robust solid-angle test.

Enumerator
Miss 
Hit 
Ambiguous 

Definition at line 93 of file TriangleManifold.h.

Constructor & Destructor Documentation

◆ TriangleManifold()

TriangleManifold::TriangleManifold ( MeshBase &  mesh,
const Real  surface_tolerance 
)

Build a manifold classifier from a prepared surface mesh.

Parameters
meshSerialized 2D surface mesh that defines the closed manifold. Must outlive this object. Any desired transforms should already be applied to the mesh.
surface_toleranceAbsolute tolerance used for manifold validation and near-surface classification. Choose this relative to the mesh length scale and expected coordinate noise from the export pipeline.

Definition at line 57 of file TriangleManifold.C.

58 : _mesh(mesh),
59 _surface_tolerance(surface_tolerance),
61 _point_locator(_mesh.sub_point_locator())
62{
63 mooseAssert(_surface_tolerance > 0.0, "surface_tolerance must be strictly positive.");
64 mooseAssert(mesh.is_serial(), "Input manifold mesh must be serialized.");
65 mooseAssert(mesh.mesh_dimension() == 2, "Manifold mesh must be a surface.");
66
67 // Finish topology validation and acceleration-structure setup before queries are allowed.
68 finalize();
69
70 _point_locator->set_close_to_point_tol(_surface_tolerance);
71}
const Real _surface_tolerance
Absolute tolerance used throughout validation and geometric classification.
const libMesh::BoundingBox _bounding_box
Global bounding box of the transformed manifold.
void finalize()
Complete post-parse validation and acceleration-structure setup.
const std::unique_ptr< libMesh::PointLocatorBase > _point_locator
Pre-built point locator for fast proximity-to-surface detection.
MeshBase & mesh
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)

Member Function Documentation

◆ boundingBox()

const libMesh::BoundingBox & TriangleManifold::boundingBox ( ) const
inline
Returns
The manifold bounding box.

Definition at line 78 of file TriangleManifold.h.

78{ return _bounding_box; };

Referenced by buildCandidateGrid(), pointInsideBoundingBox(), and rayCandidates().

◆ buildCandidateGrid()

void TriangleManifold::buildCandidateGrid ( )
private

Build the yz-plane lookup grid used to accelerate +x ray queries.

Definition at line 146 of file TriangleManifold.C.

147{
148 // Since every containment ray travels in +x, only y and z are needed to choose candidate
149 // triangles for the parity test.
150 const auto extent_y =
151 std::max(boundingBox().max()(1) - boundingBox().min()(1), _surface_tolerance);
152 const auto extent_z =
153 std::max(boundingBox().max()(2) - boundingBox().min()(2), _surface_tolerance);
154
155 // Choose a roughly square yz grid scaled by aspect ratio so candidate lists stay short.
156 const auto target_cells =
157 std::max<dof_id_type>(1, static_cast<dof_id_type>(std::sqrt(numTriangles())));
158 const auto aspect = std::sqrt(extent_y / extent_z);
159
160 _num_y_cells = std::max<dof_id_type>(
161 1, static_cast<dof_id_type>(std::lround(std::sqrt(target_cells) * aspect)));
162 _num_z_cells = std::max<std::size_t>(
163 1, static_cast<std::size_t>(std::ceil(static_cast<Real>(target_cells) / _num_y_cells)));
164
165 _y_min = boundingBox().min()(1);
166 _z_min = boundingBox().min()(2);
167 _y_cell_size = extent_y / _num_y_cells;
168 _z_cell_size = extent_z / _num_z_cells;
169
170 for (const auto elem : _mesh.active_element_ptr_range())
171 {
172 const auto triangle_index = elem->id();
173 const auto bbox = elem->loose_bounding_box();
174
175 const auto y_start =
177 const auto y_stop =
179 const auto z_start =
181 const auto z_stop =
183
184 // Insert the triangle into every grid cell touched by its yz projection.
185 for (const auto iy : make_range(y_start, y_stop + 1))
186 for (const auto iz : make_range(z_start, z_stop + 1))
187 _ray_grid[TriangleManifoldUtils::packCell(iy, iz)].push_back(triangle_index);
188 }
189}
for(PetscInt i=0;i< nvars;++i)
std::size_t _num_y_cells
Number of yz-grid bins in the y direction.
std::size_t _num_z_cells
Number of yz-grid bins in the z direction.
std::size_t numTriangles() const
Real _y_cell_size
Width of one yz-grid cell in the y direction.
Real _z_min
Minimum global z coordinate used to map query points into yz-grid bins.
Real _z_cell_size
Width of one yz-grid cell in the z direction.
std::unordered_map< std::uint64_t, std::vector< dof_id_type > > _ray_grid
Lookup from packed yz-grid cell index to triangles that could intersect the +x query ray.
Real _y_min
Minimum global y coordinate used to map query points into yz-grid bins.
const libMesh::BoundingBox & boundingBox() const
const Point & min() const
auto max(const L &left, const R &right)
auto min(const L &left, const R &right)
std::uint64_t packCell(const std::size_t y_index, const std::size_t z_index)
std::size_t cellIndex(const Real value, const Real min_value, const Real cell_size, const std::size_t num_cells)
uint8_t dof_id_type
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)

Referenced by finalize().

◆ classifyByParity()

SurfaceGeometry::SurfaceSide TriangleManifold::classifyByParity ( const Point &  point) const
private

Resolve INSIDE vs OUTSIDE via the fixed +x ray parity test (with solid-angle fallback for ambiguous grazing hits).

Assumes the point is neither outside the bounding box nor on the surface; those cases are handled by sideness().

Definition at line 95 of file TriangleManifold.C.

96{
97 // Candidate filtering keeps the parity test from touching every triangle in large surfaces.
98 const auto candidates = rayCandidates(point);
99 unsigned int num_hits = 0;
100
101 for (const auto triangle_index : candidates)
102 {
103 const auto tri = _mesh.elem_ptr(triangle_index);
104 // Triangles wholly behind the ray origin cannot contribute to the parity count.
105 if (tri->loose_bounding_box().max()(0) < point(0) - _surface_tolerance)
106 continue;
107
108 switch (rayIntersectsTriangle(point, *tri))
109 {
111 break;
113 // Standard odd/even counting on a closed surface.
114 ++num_hits;
115 break;
117 // Edge and vertex grazing hits are exactly where parity counting becomes brittle.
120 }
121 }
122
123 return (num_hits % 2) ? SurfaceGeometry::SurfaceSide::INSIDE
125}
RayIntersection rayIntersectsTriangle(const Point &point, const libMesh::Elem &tri) const
Intersect a positive x-direction ray with a single triangle.
bool containsBySolidAngle(const Point &point) const
Robust fallback containment query based on accumulated solid angle.
std::vector< dof_id_type > rayCandidates(const Point &point) const
Get the subset of triangles whose yz extents may intersect the query ray.
@ INSIDE
The point lies strictly in the interior of the closed surface.
@ OUTSIDE
The point lies strictly in the exterior of the closed surface.

Referenced by sideness().

◆ contains()

bool TriangleManifold::contains ( const Point &  point) const
Returns
True if the point is inside the manifold or lies on the surface.

Definition at line 74 of file TriangleManifold.C.

75{
76 // ON and INSIDE both count as contained; only OUTSIDE is excluded.
78}
SurfaceGeometry::SurfaceSide sideness(const Point &point) const
Classify a query point relative to the manifold surface.

Referenced by ManifoldSubdomainGenerator::generate().

◆ containsBySolidAngle()

bool TriangleManifold::containsBySolidAngle ( const Point &  point) const
private

Robust fallback containment query based on accumulated solid angle.

Definition at line 259 of file TriangleManifold.C.

260{
261 // For a closed oriented mesh, the total solid angle is approximately +-4\pi inside and 0
262 // outside. Taking parity over components handles nested shells cleanly.
263 Real total_angle = 0.0;
264 for (const auto elem : _mesh.active_element_ptr_range())
265 total_angle +=
266 geom_utils::solidAngle(point, elem->node_ref(0), elem->node_ref(1), elem->node_ref(2));
267
268 return std::abs(total_angle) > 2.0 * libMesh::pi;
269}
Real solidAngle(const Point &point, const Point &v0, const Point &v1, const Point &v2)
Compute the signed solid angle subtended by one oriented triangle at the query point.
const Real pi

Referenced by classifyByParity().

◆ finalize()

void TriangleManifold::finalize ( )
private

Complete post-parse validation and acceleration-structure setup.

Definition at line 128 of file TriangleManifold.C.

129{
130 // Validate that the mesh can be used as a manifold
131 // We use the same logic as determining if the mesh can the tetrahedralized.
132 auto umesh = dynamic_cast<UnstructuredMesh *>(&_mesh);
133 if (!umesh)
134 mooseError("TriangleManifold requires an UnstructuredMesh to validate the surface manifold.");
136 auto msg = checker.improveAndValidate();
137 if (!msg.empty())
139 "The inputted surface mesh cannot be treated as manifold for the following reasons:\n",
140 msg);
141
143}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
void buildCandidateGrid()
Build the yz-plane lookup grid used to accelerate +x ray queries.

Referenced by TriangleManifold().

◆ numTriangles()

std::size_t TriangleManifold::numTriangles ( ) const
inline
Returns
The number of triangles in the loaded manifold.

Definition at line 83 of file TriangleManifold.h.

83{ return _mesh.n_active_elem(); }

Referenced by buildCandidateGrid().

◆ pointInsideBoundingBox()

bool TriangleManifold::pointInsideBoundingBox ( const Point &  point) const
private

Cheap global bounding-box rejection for containment queries.

Definition at line 192 of file TriangleManifold.C.

193{
194 // Inflate the global box by the tolerance so near-surface points are not rejected too early.
195 return point(0) >= boundingBox().min()(0) - _surface_tolerance &&
196 point(0) <= boundingBox().max()(0) + _surface_tolerance &&
197 point(1) >= boundingBox().min()(1) - _surface_tolerance &&
198 point(1) <= boundingBox().max()(1) + _surface_tolerance &&
199 point(2) >= boundingBox().min()(2) - _surface_tolerance &&
200 point(2) <= boundingBox().max()(2) + _surface_tolerance;
201}
const Point & max() const

Referenced by sideness().

◆ pointOnSurface()

bool TriangleManifold::pointOnSurface ( const Point &  point) const
private

Detect whether a query point lies on or extremely near the manifold surface.

Definition at line 204 of file TriangleManifold.C.

205{
206 return (*_point_locator)(point) != nullptr;
207}

Referenced by sideness().

◆ rayCandidates()

std::vector< dof_id_type > TriangleManifold::rayCandidates ( const Point &  point) const
private

Get the subset of triangles whose yz extents may intersect the query ray.

Definition at line 272 of file TriangleManifold.C.

273{
274 // If the query is outside the manifold's yz extent, the fixed +x ray cannot hit anything.
275 if (point(1) < boundingBox().min()(1) - _surface_tolerance ||
276 point(1) > boundingBox().max()(1) + _surface_tolerance ||
277 point(2) < boundingBox().min()(2) - _surface_tolerance ||
278 point(2) > boundingBox().max()(2) + _surface_tolerance)
279 return {};
280
281 const auto y_index =
283 const auto z_index =
285 const auto it = _ray_grid.find(TriangleManifoldUtils::packCell(y_index, z_index));
286 if (it == _ray_grid.end())
287 return {};
288
289 // Return by value to keep the helper simple and avoid exposing internal storage.
290 return it->second;
291}

Referenced by classifyByParity().

◆ rayIntersectsTriangle()

TriangleManifold::RayIntersection TriangleManifold::rayIntersectsTriangle ( const Point &  point,
const libMesh::Elem tri 
) const
private

Intersect a positive x-direction ray with a single triangle.

Definition at line 210 of file TriangleManifold.C.

211{
212 // This is a fixed-direction Moller-Trumbore style ray/triangle intersection test.
213 static const Point direction(1.0, 0.0, 0.0);
214
215 const Point edge1 = tri.node_ref(1) - tri.node_ref(0);
216 const Point edge2 = tri.node_ref(2) - tri.node_ref(0);
217 const Point h = direction.cross(edge2);
218 const Real determinant = edge1 * h;
219 const Real characteristic_length = std::max(edge1.norm(), edge2.norm());
220
221 if (std::abs(determinant) <= _surface_tolerance * characteristic_length)
222 // Nearly parallel triangles are ignored because they do not provide a stable parity event.
224
225 const Real inv_determinant = 1.0 / determinant;
226 const Point s = point - tri.node_ref(0);
227 const Real u = inv_determinant * (s * h);
228 if (u < 0.0 || u > 1.0)
230
231 const Point q = s.cross(edge1);
232 const Real v = inv_determinant * (direction * q);
233 if (v < 0.0 || u + v > 1.0)
235
236 const Real t = inv_determinant * (edge2 * q);
237 if (t < 0.0)
238 // Intersections behind the ray origin do not contribute to +x parity counting.
240 if (t <= _surface_tolerance)
241 // Hits too close to the ray origin are treated as ambiguous boundary situations.
243
244 const Point intersection = point + t * direction;
245 const auto tolerance_sq = _surface_tolerance * _surface_tolerance;
246 if (geom_utils::pointSegmentDistanceSq(intersection, tri.node_ref(0), tri.node_ref(1)) <=
247 tolerance_sq ||
248 geom_utils::pointSegmentDistanceSq(intersection, tri.node_ref(1), tri.node_ref(2)) <=
249 tolerance_sq ||
250 geom_utils::pointSegmentDistanceSq(intersection, tri.node_ref(2), tri.node_ref(0)) <=
251 tolerance_sq)
252 // Edge and vertex hits are where odd/even parity counting is the least reliable.
254
256}
const Node & node_ref(const unsigned int i) const
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
Real pointSegmentDistanceSq(const Point &point, const Point &a, const Point &b)
Compute the squared distance from a point to a 3-D line segment.

Referenced by classifyByParity().

◆ sideness()

SurfaceGeometry::SurfaceSide TriangleManifold::sideness ( const Point &  point) const

Classify a query point relative to the manifold surface.

On-surface points (within surface_tolerance of the surface) are reported as SurfaceGeometry::SurfaceSide::ON; this is checked before parity counting. Otherwise the fixed +x ray parity test (with solid-angle fallback) resolves INSIDE vs OUTSIDE.

Returns
SurfaceGeometry::SurfaceSide::INSIDE, ::OUTSIDE, or ::ON.

Definition at line 81 of file TriangleManifold.C.

82{
83 // Most points are rejected here before we perform any per-triangle work.
84 if (!pointInsideBoundingBox(point))
86
87 // Near-surface points are resolved before parity counting.
88 if (pointOnSurface(point))
90
91 return classifyByParity(point);
92}
bool pointInsideBoundingBox(const Point &point) const
Cheap global bounding-box rejection for containment queries.
SurfaceGeometry::SurfaceSide classifyByParity(const Point &point) const
Resolve INSIDE vs OUTSIDE via the fixed +x ray parity test (with solid-angle fallback for ambiguous g...
bool pointOnSurface(const Point &point) const
Detect whether a query point lies on or extremely near the manifold surface.
@ ON
The point lies on the surface itself, within tolerance.

Referenced by contains().

Member Data Documentation

◆ _bounding_box

const libMesh::BoundingBox TriangleManifold::_bounding_box
private

Global bounding box of the transformed manifold.

Definition at line 153 of file TriangleManifold.h.

Referenced by boundingBox().

◆ _mesh

MeshBase& TriangleManifold::_mesh
private

◆ _num_y_cells

std::size_t TriangleManifold::_num_y_cells = 1
private

Number of yz-grid bins in the y direction.

Definition at line 127 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), and rayCandidates().

◆ _num_z_cells

std::size_t TriangleManifold::_num_z_cells = 1
private

Number of yz-grid bins in the z direction.

Definition at line 130 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), and rayCandidates().

◆ _point_locator

const std::unique_ptr<libMesh::PointLocatorBase> TriangleManifold::_point_locator
private

Pre-built point locator for fast proximity-to-surface detection.

Definition at line 156 of file TriangleManifold.h.

Referenced by pointOnSurface(), and TriangleManifold().

◆ _ray_grid

std::unordered_map<std::uint64_t, std::vector<dof_id_type> > TriangleManifold::_ray_grid
private

Lookup from packed yz-grid cell index to triangles that could intersect the +x query ray.

Definition at line 145 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), and rayCandidates().

◆ _surface_tolerance

const Real TriangleManifold::_surface_tolerance
private

Absolute tolerance used throughout validation and geometric classification.

Definition at line 150 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), classifyByParity(), pointInsideBoundingBox(), rayCandidates(), rayIntersectsTriangle(), and TriangleManifold().

◆ _y_cell_size

Real TriangleManifold::_y_cell_size = 1.0
private

Width of one yz-grid cell in the y direction.

Definition at line 139 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), and rayCandidates().

◆ _y_min

Real TriangleManifold::_y_min = 0.0
private

Minimum global y coordinate used to map query points into yz-grid bins.

Definition at line 133 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), and rayCandidates().

◆ _z_cell_size

Real TriangleManifold::_z_cell_size = 1.0
private

Width of one yz-grid cell in the z direction.

Definition at line 142 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), and rayCandidates().

◆ _z_min

Real TriangleManifold::_z_min = 0.0
private

Minimum global z coordinate used to map query points into yz-grid bins.

Definition at line 136 of file TriangleManifold.h.

Referenced by buildCandidateGrid(), and rayCandidates().


The documentation for this class was generated from the following files: