Lagrange
resolve_vertex_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 <exception>
15#include <list>
16#include <numeric>
17#include <unordered_map>
18
19#include <lagrange/Edge.h>
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/utils/Error.h>
26#include <lagrange/utils/chain_edges.h>
27
28namespace lagrange {
29LAGRANGE_LEGACY_INLINE
30namespace legacy {
31
40template <typename MeshType>
41std::unique_ptr<MeshType> resolve_vertex_nonmanifoldness(MeshType& mesh)
42{
43 if (!mesh.is_connectivity_initialized()) {
44 mesh.initialize_connectivity();
45 }
46 if (mesh.get_vertex_per_facet() != 3) {
47 throw std::runtime_error(
48 "Resolve vertex nonmanifoldness is only implemented for triangle mesh");
49 }
50
51 using Index = typename MeshType::Index;
52 using Edge = typename MeshType::Edge;
53 using VertexArray = typename MeshType::VertexArray;
54 using FacetArray = typename MeshType::FacetArray;
55
56 const auto dim = mesh.get_dim();
57 const Index num_vertices = mesh.get_num_vertices();
58 const auto& vertices = mesh.get_vertices();
59 const auto& facets = mesh.get_facets();
60
61 auto get_opposite_edge = [&](Index fid, Index vid) {
62 const auto& f = facets.row(fid);
63 if (f[0] == vid) {
64 return Edge(f[1], f[2]);
65 } else if (f[1] == vid) {
66 return Edge(f[2], f[0]);
67 } else if (f[2] == vid) {
68 return Edge(f[0], f[1]);
69 } else {
70 throw Error(
71 "Facet " + std::to_string(fid) + " does not contain vertex " + std::to_string(vid));
72 }
73 };
74
75 FacetArray out_facets(facets);
76 Index vertex_count = mesh.get_num_vertices();
77
78 // this is a backward vertex map, we initialize with iota as {0, 1, 2, 3, ...}
79 // later we add elements gradually, since we don't know the final size in advance
80 // This sounds really bad, but in practice there are not many non-manifold vertices,
81 // and because of std::vector's memory allocation strategy, copying will typically
82 // never happen, or at most once.
83 std::vector<Index> backward_vertex_map(vertex_count);
84 std::iota(backward_vertex_map.begin(), backward_vertex_map.end(), 0);
85
86 for (Index i = 0; i < num_vertices; i++) {
87 const auto& adj_facets = mesh.get_facets_adjacent_to_vertex(i);
88 std::vector<Index> rim_edges;
89 rim_edges.reserve(adj_facets.size() * 2);
90 for (Index fid : adj_facets) {
91 const auto e = get_opposite_edge(fid, i);
92 rim_edges.push_back(e[0]);
93 rim_edges.push_back(e[1]);
94 }
95 auto [loops, chains] = chain_directed_edges<Index>({rim_edges.data(), rim_edges.size()});
96 if (loops.size() + chains.size() > 1) {
97 std::unordered_map<Index, Index> comp_map;
98 Index comp_count = 0;
99 for (const auto& loop : loops) {
100 for (const auto vid : loop) {
101 comp_map[vid] = comp_count;
102 }
103 comp_count++;
104 }
105 for (const auto& chain : chains) {
106 for (const auto vid : chain) {
107 comp_map[vid] = comp_count;
108 }
109 comp_count++;
110 }
111
112 // Assign a new vertex for each additional rim loops.
113 for (Index fid : adj_facets) {
114 auto& f = facets.row(fid);
115 for (Index j = 0; j < 3; j++) {
116 if (f[j] == i) {
117 const Index next = (j + 1) % 3;
118 const Index prev = (j + 2) % 3;
119 assert(comp_map.find(f[next]) != comp_map.end());
120 assert(comp_map.find(f[prev]) != comp_map.end());
121 if (comp_map[f[next]] != comp_map[f[prev]]) {
122 throw std::runtime_error(
123 "Complex edge loop detected. Vertex " + std::to_string(i) +
124 "'s one ring neighborhood must contain nonmanifold_edges!");
125 }
126 assert(comp_map[f[next]] == comp_map[f[prev]]);
127 const Index comp_id = comp_map[f[next]];
128 if (comp_id > 0) {
129 const Index new_vertex_index = vertex_count + comp_id - 1;
130 out_facets(fid, j) = new_vertex_index;
131 if (safe_cast<Index>(backward_vertex_map.size()) <= new_vertex_index) {
132 backward_vertex_map.resize(new_vertex_index + 1, invalid<Index>());
133 }
134 backward_vertex_map[new_vertex_index] = i;
135 }
136 break;
137 }
138 }
139 }
140 vertex_count += safe_cast<Index>(loops.size() + chains.size()) - 1;
141 }
142 }
143 assert(out_facets.rows() == 0 || out_facets.maxCoeff() == vertex_count - 1);
144
145 // all vertices between 0 and num_vertices are the same, so we can block-copy
146 VertexArray out_vertices(vertex_count, dim);
147 out_vertices.block(0, 0, num_vertices, dim) = vertices;
148 for (Index i = num_vertices; i < vertex_count; i++) {
149 out_vertices.row(i) = vertices.row(backward_vertex_map[i]);
150 }
151
152 auto out_mesh = create_mesh(std::move(out_vertices), std::move(out_facets));
153
154 map_attributes(mesh, *out_mesh, backward_vertex_map);
155
156 return out_mesh;
157}
158} // namespace legacy
159} // namespace lagrange
Definition: Edge.h:29
Definition: Mesh.h:48
@ Edge
Per-edge mesh attributes.
Definition: AttributeFwd.h:34
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 resolve_vertex_nonmanifoldness(SurfaceMesh< Scalar, Index > &mesh)
Resolve nonmanifold vertices by pulling disconnected 1-ring neighborhood apart.
Definition: resolve_vertex_nonmanifoldness.cpp:35
@ Error
Throw an error if collision is detected.