Lagrange
remove_degenerate_triangles.h
1/*
2 * Copyright 2017 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 <algorithm>
15#include <set>
16#include <vector>
17
18#include <lagrange/Edge.h>
19#include <lagrange/Mesh.h>
20#include <lagrange/MeshTrait.h>
21#include <lagrange/attributes/map_corner_attributes.h>
22#include <lagrange/common.h>
23#include <lagrange/legacy/inline.h>
24#include <lagrange/mesh_cleanup/detect_degenerate_triangles.h>
25#include <lagrange/mesh_cleanup/remove_short_edges.h>
26#include <lagrange/mesh_cleanup/split_triangle.h>
27#include <lagrange/utils/safe_cast.h>
28
29namespace lagrange {
30LAGRANGE_LEGACY_INLINE
31namespace legacy {
32
45template <typename MeshType>
46std::unique_ptr<MeshType> remove_degenerate_triangles(const MeshType& mesh)
47{
48 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
49
50 using Index = typename MeshType::Index;
51 using Facet = typename Eigen::Matrix<Index, 3, 1>;
52 using Edge = typename MeshType::Edge;
53
54 lagrange::logger().trace("[remove_degenerate_triangles]");
55 auto out_mesh = remove_short_edges(mesh);
56
57 detect_degenerate_triangles(*out_mesh);
58 la_runtime_assert(out_mesh->has_facet_attribute("is_degenerate"));
59 const auto& is_degenerate = out_mesh->get_facet_attribute("is_degenerate");
60 const Index num_degenerate_faces = safe_cast<Index>(is_degenerate.sum());
61 if (num_degenerate_faces == 0) {
62 return out_mesh;
63 }
64
65 const Index dim = out_mesh->get_dim();
66 const auto& vertices = out_mesh->get_vertices();
67 const Index num_vertices = out_mesh->get_num_vertices();
68 const Index num_facets = out_mesh->get_num_facets();
69 const Index vertex_per_facet = out_mesh->get_vertex_per_facet();
70 la_runtime_assert(vertex_per_facet == 3);
71 const auto& facets = out_mesh->get_facets();
72 la_runtime_assert(facets.minCoeff() >= 0);
73 la_runtime_assert(facets.maxCoeff() < num_vertices);
74
75 auto on_edge = [&vertices, dim](const Edge& edge, const Index pid) {
76 for (Index i = 0; i < dim; i++) {
77 double ep1 = vertices(edge[0], i);
78 double ep2 = vertices(edge[1], i);
79 double p = vertices(pid, i);
80 if (ep1 > p && ep2 < p)
81 return true;
82 else if (ep1 < p && ep2 > p)
83 return true;
84 else if (ep1 == p && ep2 == p)
85 continue;
86 else
87 return false;
88 }
89 // edge is degenrated with both end points equals to pid.
90 throw std::runtime_error("Edge must be degenerated.");
91 };
92
93 // Compute edge facet adjacency for triangles wihtin 1-ring
94 // neighborhood of degenerate triangles.
95 std::unordered_set<Index> active_vertices;
96 std::unordered_set<Index> active_facets;
97 for (Index i = 0; i < num_facets; ++i) {
98 if (is_degenerate(i, 0)) {
99 active_facets.insert(i);
100 active_vertices.insert(facets(i, 0));
101 active_vertices.insert(facets(i, 1));
102 active_vertices.insert(facets(i, 2));
103 }
104 }
105 // consolidate region. activate all facets adjacent to an active vertex.
106 // note: might want to move this inside compute_edge_facet_map_in_active_facets
107 for (Index i = 0; i < num_facets; ++i) {
108 if (active_facets.find(i) == active_facets.end()) { // was it active already?
109 for (Index j = 0; j < vertex_per_facet; ++j) {
110 if (active_vertices.find(facets(i, j)) != active_vertices.end()) {
111 active_facets.insert(i);
112 }
113 }
114 }
115 }
116 auto edge_facet_map = compute_edge_facet_map_in_active_facets(*out_mesh, active_facets);
117
118 std::vector<bool> visited(num_facets, false);
119 std::function<void(std::set<Index>&, EdgeSet<Index>&, const Index)> get_collinear_pts;
120 get_collinear_pts =
121 [&edge_facet_map, &facets, &visited, vertex_per_facet, &get_collinear_pts, &is_degenerate](
122 std::set<Index>& collinear_pts,
123 EdgeSet<Index>& involved_edges,
124 const Index fid) {
125 if (visited[fid]) return;
126 visited[fid] = true;
127 if (!is_degenerate(fid, 0)) return;
128
129 // Base case.
130 for (Index i = 0; i < vertex_per_facet; i++) {
131 Index v = facets(fid, i);
132 collinear_pts.insert(v);
133 Edge e{{facets(fid, i), facets(fid, (i + 1) % vertex_per_facet)}};
134 involved_edges.insert(e);
135 }
136
137 // Recurse.
138 for (Index i = 0; i < vertex_per_facet; i++) {
139 Edge edge{{facets(fid, i), facets(fid, (i + 1) % vertex_per_facet)}};
140 const auto itr = edge_facet_map.find(edge);
141 la_runtime_assert(itr != edge_facet_map.end());
142 for (const auto next_fid : itr->second) {
143 get_collinear_pts(collinear_pts, involved_edges, next_fid);
144 }
145 }
146 };
147
148 using SplittingPts = std::vector<Index>;
149 EdgeMap<Index, SplittingPts> splitting_points;
150 for (const auto& item : edge_facet_map) {
151 // const auto& edge = item.first;
152 const auto& adj_facets = item.second;
153 std::set<Index> collinear_pts;
154 EdgeSet<Index> involved_edges;
155 for (const auto& fid : adj_facets) {
156 if (!is_degenerate(fid, 0)) continue;
157 get_collinear_pts(collinear_pts, involved_edges, fid);
158 break;
159 }
160
161 for (const auto& edge : involved_edges) {
162 SplittingPts pts;
163 for (const auto vid : collinear_pts) {
164 if (on_edge(edge, vid)) {
165 pts.push_back(vid);
166 }
167 }
168 splitting_points[edge] = pts;
169 }
170 }
171
172 const auto index_comp = [&vertices, dim](const Index i, const Index j) {
173 for (Index d = 0; d < dim; d++) {
174 if (vertices(i, d) == vertices(j, d))
175 continue;
176 else
177 return vertices(i, d) < vertices(j, d);
178 }
179 return false;
180 };
181
182 for (auto& item : splitting_points) {
183 std::sort(item.second.begin(), item.second.end(), index_comp);
184 }
185
186 std::vector<Facet> splitted_facets;
187 std::vector<Index> facet_map;
188 // const auto& active_facets = edge_facet_map.active_facets();
189 for (Index i = 0; i < num_facets; i++) {
190 if (active_facets.find(i) == active_facets.end()) { // !active_facets[i]) {
191 splitted_facets.emplace_back(facets.row(i));
192 facet_map.push_back(i);
193 continue;
194 } else if (is_degenerate(i)) {
195 continue;
196 }
197
198 std::vector<Index> chain;
199 chain.reserve(8);
200 Index v[3];
201 const Facet f = facets.row(i);
202 for (Index j = 0; j < 3; j++) {
203 Edge e{{f[j], f[(j + 1) % 3]}};
204 v[j] = safe_cast<Index>(chain.size());
205 chain.push_back(f[j]);
206 const auto itr = splitting_points.find(e);
207 if (itr != splitting_points.end()) {
208 const auto& pts = itr->second;
209 if (index_comp(e[0], e[1])) {
210 std::copy(pts.begin(), pts.end(), std::back_inserter(chain));
211 } else {
212 std::copy(pts.rbegin(), pts.rend(), std::back_inserter(chain));
213 }
214 }
215 }
216
217 if (chain.size() == 3) {
218 splitted_facets.emplace_back(facets.row(i));
219 facet_map.push_back(i);
220 } else {
221 auto new_facets = split_triangle(vertices, chain, v[0], v[1], v[2]);
222 std::move(new_facets.begin(), new_facets.end(), std::back_inserter(splitted_facets));
223 facet_map.insert(facet_map.end(), new_facets.size(), i);
224 }
225 }
226
227 const auto num_out_facets = splitted_facets.size();
228 typename MeshType::FacetArray out_facets(num_out_facets, 3);
229 for (auto i : range(num_out_facets)) {
230 out_facets.row(i) = splitted_facets[i];
231 }
232 la_runtime_assert(num_out_facets == 0 || out_facets.minCoeff() >= 0);
233 la_runtime_assert(num_out_facets == 0 || out_facets.maxCoeff() < num_vertices);
234
235 auto out_mesh2 = create_mesh(vertices, out_facets);
236
237 // Port attributes
238 map_attributes(*out_mesh, *out_mesh2, {}, facet_map);
239
240 return remove_short_edges(*out_mesh2);
241}
242
243} // namespace legacy
244} // namespace lagrange
Definition: Edge.h:29
Definition: Mesh.h:48
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:40
@ Edge
Per-edge mesh attributes.
Definition: AttributeFwd.h:34
@ Facet
Per-facet mesh attributes.
Definition: AttributeFwd.h:31
#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
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 split_triangle(size_t num_points, span< const Scalar > points, span< const Index > chain, span< Index > visited_buffer, std::vector< Index > &queue_buffer, const Index v0, const Index v1, const Index v2, span< Index > triangulation)
Split a triangle into smaller triangles based on the chain of splitting points on the edges.
Definition: split_triangle.cpp:27
void remove_short_edges(SurfaceMesh< Scalar, Index > &mesh, Scalar threshold=0)
Collapse all edges shorter than a given tolerance.
Definition: remove_short_edges.cpp:29