Lagrange
MeshTopology.h
1/*
2 * Copyright 2018 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <memory>
15#include <stdexcept>
16#include <vector>
17
18// clang-format off
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>
24// clang-format on
25
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>
30
31namespace lagrange {
32LAGRANGE_LEGACY_INLINE
33namespace legacy {
34
35template <typename MeshType>
37{
38public:
39 using IndexList = typename MeshType::IndexList;
40 using Edge = typename MeshType::Edge;
41 using Index = typename MeshType::Index;
42
43public:
45 : m_euler(0)
46 , m_is_vertex_manifold(false)
47 , m_is_edge_manifold(false)
48 , m_initialized(false)
49 {}
50
51 void initialize(MeshType& mesh)
52 {
53 mesh.initialize_edge_data();
54 initialize_euler(mesh);
55 initialize_manifoldness(mesh);
56 initialize_boundary(mesh);
57 m_initialized = true;
58 }
59
60 bool is_initialized() const { return m_initialized; }
61
62 const std::vector<std::vector<Index>>& get_boundaries() const { return m_boundary_loops; }
63
64 int get_euler() const { return m_euler; }
65
66 bool is_cylinder() const
67 {
68 return is_manifold() && m_euler == 0 && m_boundary_loops.size() == 2;
69 }
70
71 bool is_disc() const { return is_manifold() && m_euler == 1 && m_boundary_loops.size() == 1; }
72
73 bool is_manifold() const { return m_is_vertex_manifold && m_is_edge_manifold; }
74
75 bool is_vertex_manifold() const { return m_is_vertex_manifold; }
76
77 bool is_edge_manifold() const { return m_is_edge_manifold; }
78
79 const std::vector<std::vector<Index>>& get_boundary_loops() const { return m_boundary_loops; }
80
81protected:
82 void initialize_euler(const MeshType& mesh)
83 {
84 m_euler =
85 (int)mesh.get_num_vertices() + (int)mesh.get_num_facets() - (int)mesh.get_num_edges();
86 }
87
88 void initialize_manifoldness(const MeshType& mesh)
89 {
90 m_is_edge_manifold = check_edge_manifold(mesh);
91 if (m_is_edge_manifold) {
92 m_is_vertex_manifold = check_vertex_manifold(mesh);
93 } else {
94 m_is_vertex_manifold = false;
95 }
96 }
97
98 void initialize_boundary(MeshType& mesh)
99 {
100 if (is_manifold()) {
101 m_boundary_loops = extract_boundary_loops(mesh);
102 } else {
103 // Only simple boundary loop is supported.
104 m_boundary_loops.clear();
105 }
106 }
107
108 bool check_edge_manifold(const MeshType& mesh) const
109 {
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;
113 }
114 return true;
115 }
116
117 bool check_vertex_manifold(const MeshType& mesh) const
118 {
120 mesh.get_vertex_per_facet() == 3,
121 "Vertex manifold check only supports triangle mesh for now.");
122
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;
126
127 auto is_vertex_manifold = [&](Index vid) {
128 auto& rim_edges = thread_rim_edges.local();
129 rim_edges.clear();
130 rim_edges.reserve(mesh.get_num_facets_around_vertex(vid));
131
132 mesh.foreach_corners_around_vertex(vid, [&](Index ci) {
133 Index fid = ci / 3;
134 Index lv = ci % 3;
135 rim_edges.push_back({facets(fid, (lv + 1) % 3), facets(fid, (lv + 2) % 3)});
136 });
137
138 const auto loops = legacy::chain_edges<Index>(rim_edges);
139 return loops.size() == 1;
140 };
141
142 return tbb::parallel_reduce(
143 tbb::blocked_range<Index>(0, num_vertices),
144 true,
145 [&](const tbb::blocked_range<Index>& r, bool manifold) -> bool {
146 if (!manifold) return false;
147
148 for (Index vi = r.begin(); vi != r.end(); vi++) {
149 if (!is_vertex_manifold(vi)) {
150 return false;
151 }
152 }
153 return true;
154 },
155 [](bool r1, bool r2) -> bool { return r1 && r2; });
156 }
157
158private:
159 std::vector<std::vector<typename MeshType::Index>> m_boundary_loops;
160 int m_euler;
161 bool m_is_vertex_manifold;
162 bool m_is_edge_manifold;
163 bool m_initialized;
164};
165
166} // namespace legacy
167} // namespace lagrange
Definition: Edge.h:29
Definition: Mesh.h:48
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