Lagrange
resolve_nonmanifoldness.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 <numeric>
16#include <set>
17#include <unordered_map>
18#include <vector>
19
20#include <lagrange/Mesh.h>
21#include <lagrange/attributes/map_attributes.h>
22#include <lagrange/common.h>
23#include <lagrange/create_mesh.h>
24#include <lagrange/legacy/inline.h>
25#include <lagrange/mesh_cleanup/remove_duplicate_facets.h>
26#include <lagrange/mesh_cleanup/remove_isolated_vertices.h>
27#include <lagrange/mesh_cleanup/remove_topologically_degenerate_triangles.h>
28#include <lagrange/mesh_cleanup/resolve_vertex_nonmanifoldness.h>
29
30
31namespace lagrange {
32LAGRANGE_LEGACY_INLINE
33namespace legacy {
34
45template <typename MeshType>
46std::unique_ptr<MeshType> resolve_nonmanifoldness(MeshType& mesh)
47{
48 if (!mesh.is_connectivity_initialized()) {
49 mesh.initialize_connectivity();
50 }
51
52 mesh.initialize_edge_data();
53
54 using Index = typename MeshType::Index;
55 using VertexArray = typename MeshType::VertexArray;
56 using FacetArray = typename MeshType::FacetArray;
57
58 const Index num_vertices = mesh.get_num_vertices();
59 const Index num_facets = mesh.get_num_facets();
60 const Index vertex_per_facet = mesh.get_vertex_per_facet();
61 const auto& vertices = mesh.get_vertices();
62 const auto& facets = mesh.get_facets();
63
64
71 auto get_orientation = [&facets](const Index v0, const Index v1, const Index fid) -> bool {
72 const auto& f = facets.row(fid);
73 if (f[0] == v0 && f[1] == v1)
74 return true;
75 else if (f[1] == v0 && f[2] == v1)
76 return true;
77 else if (f[2] == v0 && f[0] == v1)
78 return true;
79 else
80 return false;
81 };
82
91 auto is_inconsistently_oriented = [&mesh, &get_orientation](const Index ei) -> bool {
92 const auto e = mesh.get_edge_vertices(ei);
93 std::array<bool, 2> orientations{false, false};
94 size_t count = 0;
95 mesh.foreach_facets_around_edge(ei, [&](Index fid) {
96 orientations[count] = get_orientation(e[0], e[1], fid);
97 count++;
98 });
99 return orientations[0] == orientations[1];
100 };
101
108 auto is_inconsistently_oriented_wrt_facets =
109 [&mesh, &get_orientation](const Index ei, const Index f0, const Index f1) {
110 const auto e = mesh.get_edge_vertices(ei);
111 return get_orientation(e[0], e[1], f0) == get_orientation(e[0], e[1], f1);
112 };
113
118 auto is_nonmanifold_edge = [&mesh, &is_inconsistently_oriented](const Index ei) {
119 auto edge_valence = mesh.get_num_facets_around_edge(ei);
120 if (edge_valence > 2) return true;
121 if (edge_valence <= 1) return false;
122 return is_inconsistently_oriented(ei);
123 };
124
125 // Flood fill color across manifold edges. The color field split the
126 // facets into locally manifold components. Edges and vertices adjacent
127 // to multiple colors will be split.
128 constexpr Index BLANK = 0;
129 std::vector<Index> colors(num_facets, BLANK);
130 Index curr_color = 1;
131 for (Index i = 0; i < num_facets; i++) {
132 if (colors[i] != BLANK) continue;
133 std::queue<Index> Q;
134 Q.push(i);
135 colors[i] = curr_color;
136 while (!Q.empty()) {
137 Index curr_fid = Q.front();
138 Q.pop();
139 for (Index j = 0; j < vertex_per_facet; j++) {
140 Index ei = mesh.get_edge(curr_fid, j);
141 if (is_nonmanifold_edge(ei)) continue;
142
143 mesh.foreach_facets_around_edge(ei, [&](Index adj_fid) {
144 if (colors[adj_fid] == BLANK) {
145 colors[adj_fid] = curr_color;
146 Q.push(adj_fid);
147 }
148 });
149 }
150 }
151 curr_color++;
152 }
153
154 Index vertex_count = num_vertices;
155 // Note:
156 // The goal of vertex_map is to split the 1-ring neighborhood of a
157 // non-manifold vertex based on the colors of its adjacent facets. All
158 // adjacent facets sharing the sanme color will share the same copy of
159 // this vertex.
160 //
161 // To achieve this, I store a color map for each non-manifold vertex,
162 // where vertex_map maps a non-manifold vertex to its color map.
163 // A color map maps specific color to the vertex index after the split.
164 std::unordered_map<Index, std::unordered_map<Index, Index>> vertex_map;
165 // Split non-manifold edges.
166 for (Index i = 0; i < mesh.get_num_edges(); ++i) {
167 const auto e = mesh.get_edge_vertices(i);
168
169 if (!is_nonmanifold_edge(i)) continue;
170 auto itr0 = vertex_map.find(e[0]);
171 auto itr1 = vertex_map.find(e[1]);
172
173 if (itr0 == vertex_map.end()) {
174 itr0 = vertex_map.insert({e[0], std::unordered_map<Index, Index>()}).first;
175 }
176 if (itr1 == vertex_map.end()) {
177 itr1 = vertex_map.insert({e[1], std::unordered_map<Index, Index>()}).first;
178 }
179
180 auto& color_map_0 = itr0->second;
181 auto& color_map_1 = itr1->second;
182
183 std::unordered_map<Index, std::list<Index>> color_count;
184 mesh.foreach_facets_around_edge(i, [&](Index fid) {
185 const auto c = colors[fid];
186
187 auto c_itr = color_count.find(c);
188 if (c_itr == color_count.end()) {
189 color_count.insert({c, {fid}});
190 } else {
191 c_itr->second.push_back(fid);
192 }
193
194 auto c_itr0 = color_map_0.find(c);
195 if (c_itr0 == color_map_0.end()) {
196 if (color_map_0.size() == 0) {
197 color_map_0.insert({c, e[0]});
198 } else {
199 color_map_0.insert({c, vertex_count});
200 vertex_count++;
201 }
202 }
203
204 auto c_itr1 = color_map_1.find(c);
205 if (c_itr1 == color_map_1.end()) {
206 if (color_map_1.size() == 0) {
207 color_map_1.insert({c, e[1]});
208 } else {
209 color_map_1.insert({c, vertex_count});
210 vertex_count++;
211 }
212 }
213 });
214
215 for (const auto& entry : color_count) {
216 // Corner case 1:
217 // Exact two facets share the same color around this edge, but
218 // they are inconsistently oriented. Thus, they needs to be
219 // detacted.
220 const bool inconsistent_edge =
221 (entry.second.size() == 2) &&
222 is_inconsistently_oriented_wrt_facets(i, entry.second.front(), entry.second.back());
223
224 // Corner case 2:
225 // Some facets around this non-manifold edge are connected via
226 // a chain of manifold edges. Thus, they have the same color.
227 // To resolve this, I am detacching all facets of this color
228 // adjacent to this edge.
229 const bool single_comp_nonmanifoldness = entry.second.size() > 2;
230
231 if (single_comp_nonmanifoldness || inconsistent_edge) {
232 // Each facet will be reconnect to a newly created edge.
233 // This solution is not ideal, but works.
234 for (const auto fid : entry.second) {
235 colors[fid] = curr_color;
236 color_map_0.insert({curr_color, vertex_count});
237 vertex_count++;
238 color_map_1.insert({curr_color, vertex_count});
239 vertex_count++;
240 curr_color++;
241 }
242 }
243 }
244 }
245
246 // Split non-manifold vertices
247 for (Index i = 0; i < num_vertices; i++) {
248 const auto& adj_facets = mesh.get_facets_adjacent_to_vertex(i);
249 std::set<Index> adj_colors;
250 for (auto adj_f : adj_facets) {
251 adj_colors.insert(colors[adj_f]);
252 }
253 if (adj_colors.size() <= 1) continue;
254
255 auto itr = vertex_map.find(i);
256 if (itr == vertex_map.end()) {
257 vertex_map.insert({i, {}});
258 }
259
260 auto& vi_map = vertex_map[i];
261 for (auto c : adj_colors) {
262 auto c_itr = vi_map.find(c);
263 if (c_itr == vi_map.end()) {
264 if (vi_map.size() == 0) {
265 vi_map.insert({c, i});
266 } else {
267 vi_map.insert({c, vertex_count});
268 vertex_count++;
269 }
270 }
271 }
272 }
273
274 VertexArray manifold_vertices(vertex_count, vertices.cols());
275 manifold_vertices.topRows(num_vertices) = vertices;
276
277 std::vector<Index> backward_vertex_map(vertex_count, 0);
278 std::iota(backward_vertex_map.begin(), backward_vertex_map.begin() + num_vertices, 0);
279
280 for (const auto& itr : vertex_map) {
281 Index vid = itr.first;
282 for (const auto& itr2 : itr.second) {
283 manifold_vertices.row(itr2.second) = vertices.row(vid);
284 backward_vertex_map[itr2.second] = vid;
285 }
286 }
287
288 FacetArray manifold_facets = facets;
289 for (Index i = 0; i < num_facets; i++) {
290 const Index c = colors[i];
291 for (Index j = 0; j < vertex_per_facet; j++) {
292 const auto& itr = vertex_map.find(manifold_facets(i, j));
293 if (itr != vertex_map.end()) {
294 manifold_facets(i, j) = itr->second[c];
295 }
296 }
297 }
298
299 auto out_mesh = create_mesh(std::move(manifold_vertices), std::move(manifold_facets));
300
301 map_attributes(mesh, *out_mesh, backward_vertex_map);
302
303 out_mesh->initialize_connectivity();
304 out_mesh->initialize_edge_data();
305
306 out_mesh = resolve_vertex_nonmanifoldness(*out_mesh);
307 out_mesh = remove_topologically_degenerate_triangles(*out_mesh);
308 out_mesh = remove_duplicate_facets(*out_mesh);
309 out_mesh = remove_isolated_vertices(*out_mesh);
310 return out_mesh;
311}
312} // namespace legacy
313} // namespace lagrange
Definition: Mesh.h:48
void map_attributes(const SurfaceMesh< Scalar, Index > &source_mesh, SurfaceMesh< Scalar, Index > &target_mesh, span< const Index > mapping_data, span< const Index > mapping_offsets={}, const MapAttributesOptions &options={})
Map attributes from the source mesh to the target mesh.
Definition: map_attributes.cpp:26
Main namespace for Lagrange.
Definition: AABBIGL.h:30
auto create_mesh(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets)
This function create a new mesh given the vertex and facet arrays by copying data into the Mesh objec...
Definition: create_mesh.h:39
void remove_duplicate_facets(SurfaceMesh< Scalar, Index > &mesh, const RemoveDuplicateFacetOptions &opts={})
Remove duplicate facets in the mesh.
Definition: remove_duplicate_facets.cpp:235
void resolve_vertex_nonmanifoldness(SurfaceMesh< Scalar, Index > &mesh)
Resolve nonmanifold vertices by pulling disconnected 1-ring neighborhood apart.
Definition: resolve_vertex_nonmanifoldness.cpp:35
void remove_isolated_vertices(SurfaceMesh< Scalar, Index > &mesh)
Removes isolated vertices of a mesh.
Definition: remove_isolated_vertices.cpp:20
void resolve_nonmanifoldness(SurfaceMesh< Scalar, Index > &mesh)
Resolve both non-manifold vertices and non-manifold edges in the input mesh.
Definition: resolve_nonmanifoldness.cpp:35