19#include <lagrange/utils/warnoff.h>
20#include <tbb/blocked_range.h>
21#include <tbb/enumerable_thread_specific.h>
22#include <tbb/parallel_reduce.h>
23#include <lagrange/utils/warnon.h>
26#include <lagrange/extract_boundary_loops.h>
27#include <lagrange/legacy/inline.h>
28#include <lagrange/legacy/chain_edges.h>
29#include <lagrange/utils/safe_cast.h>
35template <
typename MeshType>
39 using IndexList =
typename MeshType::IndexList;
41 using Index =
typename MeshType::Index;
46 , m_is_vertex_manifold(
false)
47 , m_is_edge_manifold(
false)
48 , m_initialized(
false)
53 mesh.initialize_edge_data();
54 initialize_euler(mesh);
55 initialize_manifoldness(mesh);
56 initialize_boundary(mesh);
60 bool is_initialized()
const {
return m_initialized; }
62 const std::vector<std::vector<Index>>& get_boundaries()
const {
return m_boundary_loops; }
64 int get_euler()
const {
return m_euler; }
66 bool is_cylinder()
const
68 return is_manifold() && m_euler == 0 && m_boundary_loops.size() == 2;
71 bool is_disc()
const {
return is_manifold() && m_euler == 1 && m_boundary_loops.size() == 1; }
73 bool is_manifold()
const {
return m_is_vertex_manifold && m_is_edge_manifold; }
75 bool is_vertex_manifold()
const {
return m_is_vertex_manifold; }
77 bool is_edge_manifold()
const {
return m_is_edge_manifold; }
79 const std::vector<std::vector<Index>>& get_boundary_loops()
const {
return m_boundary_loops; }
82 void initialize_euler(
const MeshType& mesh)
85 (int)mesh.get_num_vertices() + (int)mesh.get_num_facets() - (int)mesh.get_num_edges();
88 void initialize_manifoldness(
const MeshType& mesh)
90 m_is_edge_manifold = check_edge_manifold(mesh);
91 if (m_is_edge_manifold) {
94 m_is_vertex_manifold =
false;
98 void initialize_boundary(
MeshType& mesh)
104 m_boundary_loops.clear();
108 bool check_edge_manifold(
const MeshType& mesh)
const
110 const Index num_edges = mesh.get_num_edges();
111 for (
auto ei :
range(num_edges)) {
112 if (mesh.get_num_facets_around_edge(ei) > 2)
return false;
120 mesh.get_vertex_per_facet() == 3,
121 "Vertex manifold check only supports triangle mesh for now.");
123 const auto num_vertices = mesh.get_num_vertices();
124 const auto& facets = mesh.get_facets();
125 tbb::enumerable_thread_specific<std::vector<Edge>> thread_rim_edges;
127 auto is_vertex_manifold = [&](Index vid) {
128 auto& rim_edges = thread_rim_edges.local();
130 rim_edges.reserve(mesh.get_num_facets_around_vertex(vid));
132 mesh.foreach_corners_around_vertex(vid, [&](Index ci) {
135 rim_edges.push_back({facets(fid, (lv + 1) % 3), facets(fid, (lv + 2) % 3)});
138 const auto loops = legacy::chain_edges<Index>(rim_edges);
139 return loops.size() == 1;
142 return tbb::parallel_reduce(
143 tbb::blocked_range<Index>(0, num_vertices),
145 [&](
const tbb::blocked_range<Index>& r,
bool manifold) ->
bool {
146 if (!manifold)
return false;
148 for (Index vi = r.begin(); vi != r.end(); vi++) {
149 if (!is_vertex_manifold(vi)) {
155 [](
bool r1,
bool r2) ->
bool {
return r1 && r2; });
159 std::vector<std::vector<typename MeshType::Index>> m_boundary_loops;
161 bool m_is_vertex_manifold;
162 bool m_is_edge_manifold;
Definition: MeshTopology.h:37
bool check_vertex_manifold(const MeshType &mesh) const
Definition: MeshTopology.h:117
std::vector< std::vector< Index > > extract_boundary_loops(const SurfaceMesh< Scalar, Index > &mesh)
Extract boundary loops from a surface mesh.
Definition: extract_boundary_loops.cpp:24
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
Main namespace for Lagrange.
Definition: AABBIGL.h:30