Check and repair a mesh

The STL file format specifies triangles without de-duplicating their vertices. Vertex welding is needed for several mesh algorithms that require a watertight manifold. Additionally, mesh files often contain errors and require some kind of cleanup. The following code examples are excerpts from the file <axom>/src/tools/mesh_tester.cpp.

Quest provides a function to weld vertices within a distance of some specified epsilon. This function takes arguments mint::UnstructuredMesh< mint::SINGLE_SHAPE > **surface_mesh and double epsilon, and modifies surface_mesh. In addition to the mint Mesh and UnstructuredMesh headers (see previous page), we include the headers declaring the functions for checking and repairing surface meshes.

#include "axom/quest/MeshTester.hpp"

The function call itself:

    quest::weldTriMeshVertices(&surface_mesh, params.weldThreshold);

One problem that can occur in a surface mesh is self-intersection. A well-formed mesh will have each triangle touching the edge of each of its neighbors. Intersecting or degenerate triangles can cause problems for some spatial algorithms. To detect such problems using Quest, we first make containers to record defects that might be found.

    std::vector<std::pair<int, int>> collisions;
    std::vector<int> degenerate;

Then, we call the function to detect self-intersections and degenerate triangles.

        // Use a uniform grid spatial index

After calling findTriMeshIntersections, collisions will hold the indexes of each pair of intersecting triangles and degenerate will contain the index of each degenerate triangle. The user code can then address or report any triangles found. Mesh repair beyond welding close vertices is beyond the scope of the Quest component.

Check for watertightness

Before using Quest’s surface point queries, a mesh must be watertight, with no cracks or holes. Quest provides a function to test for watertightness, declared in the same header file as the tests self-intersection and an enum indicating watertightness of a mesh. If the code is working with a mesh read in from an STL file, weld the vertices (see above) before checking for watertightness!

    quest::WatertightStatus wtstat = quest::isSurfaceMeshWatertight(surface_mesh);

This routine builds the face relation of the supplied triangle surface mesh. The face of a triangle is a one-dimensional edge. If the mesh is big, building the face relation may take some time. Once built, the routine queries face relation: each edge of every triangle must be incident in two triangles. If the mesh has a defect where more than two triangles share an edge, the routine returns CHECK_FAILED. If the mesh has a hole, at least one triangle edge is incident in only one triangle and the routine returns NOT_WATERTIGHT. Otherwise, each edge is incident in two triangles, and the routine returns WATERTIGHT.

After testing for watertightness, report the result.

    case quest::WatertightStatus::WATERTIGHT:
      std::cout << "The mesh is watertight." << std::endl;
    case quest::WatertightStatus::NOT_WATERTIGHT:
      std::cout << "The mesh is not watertight: at least one "
                << "boundary edge was detected." << std::endl;
      std::cout << "An error was encountered while checking." << std::endl
                << "This may be due to a non-manifold mesh." << std::endl;

After an STL mesh has

  • been read in with STLReader,
  • had vertices welded using weldTriMeshVertices(),
  • contains no self-intersections as reported by findTriMeshIntersections(),
  • and is watertight as reported by isSurfaceMeshWatertight(),

the in-out and distance field queries will work as designed.