Lagrange
split_long_edges.h
1/*
2 * Copyright 2016 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 <vector>
15
16#include <lagrange/Edge.h>
17#include <lagrange/Mesh.h>
18#include <lagrange/attributes/attribute_utils.h>
19#include <lagrange/attributes/map_corner_attributes.h>
20#include <lagrange/attributes/rename_attribute.h>
21#include <lagrange/common.h>
22#include <lagrange/create_mesh.h>
23#include <lagrange/legacy/inline.h>
24#include <lagrange/mesh_cleanup/split_triangle.h>
25#include <lagrange/utils/safe_cast.h>
26
27namespace lagrange {
28LAGRANGE_LEGACY_INLINE
29namespace legacy {
30
31// TODO: Should accept const ref, needs refactor (non-moving export facet attribute)
32template <typename MeshType>
33std::unique_ptr<MeshType>
34split_long_edges(MeshType& mesh, typename MeshType::Scalar sq_tol, bool recursive = false)
35{
36 using Index = typename MeshType::Index;
37 using Scalar = typename MeshType::Scalar;
38 using Edge = typename MeshType::Edge;
39 if (mesh.get_vertex_per_facet() != 3) {
40 throw "Only triangle is supported";
41 }
42 const Index dim = mesh.get_dim();
43 const Index num_vertices = mesh.get_num_vertices();
44 const Index num_facets = mesh.get_num_facets();
45 const auto& vertices = mesh.get_vertices();
46 const auto& facets = mesh.get_facets();
47 std::vector<typename MeshType::VertexType> additional_vertices;
48 EdgeMap<Index, std::vector<Index>> splitting_pts;
49 EdgeSet<Index> visited;
50 std::vector<std::tuple<Index, Index, Scalar>> vertex_mapping;
51
52 typename MeshType::AttributeArray active_facets;
53 const bool has_active_region = mesh.has_facet_attribute("__is_active");
54 if (has_active_region) {
55 mesh.export_facet_attribute("__is_active", active_facets);
56 } else {
57 active_facets.resize(num_facets, 1);
58 active_facets.setZero();
59 }
60 auto is_active = [&active_facets](Index fid) { return active_facets(fid, 0) != 0; };
61
62 auto split_edge = [&vertices,
63 &additional_vertices,
64 &splitting_pts,
65 &visited,
66 sq_tol,
67 num_vertices,
68 &vertex_mapping](const Edge& edge) {
69 if (visited.find(edge) != visited.end()) return;
70 visited.insert(edge);
71 if (splitting_pts.find(edge) != splitting_pts.end()) return;
72 Scalar sq_length = (vertices.row(edge[0]) - vertices.row(edge[1])).squaredNorm();
73 if (sq_length <= sq_tol) return;
74
75 const Scalar num_segments = std::ceil(sqrt(sq_length / sq_tol));
76 const auto v0 = vertices.row(edge[0]).eval();
77 const auto v1 = vertices.row(edge[1]).eval();
78 std::vector<Index> split_pts_indices;
79 split_pts_indices.push_back(edge[0]);
80 const Index base = safe_cast<Index>(additional_vertices.size()) + num_vertices;
81 for (Index i = 1; i < num_segments; i++) {
82 Scalar ratio = i / num_segments;
83 additional_vertices.push_back(v0 * (1.0f - ratio) + v1 * ratio);
84 vertex_mapping.emplace_back(edge[0], edge[1], 1.0f - ratio);
85 split_pts_indices.push_back(base + i - 1);
86 }
87 split_pts_indices.push_back(edge[1]);
88 splitting_pts.insert({edge, split_pts_indices});
89 };
90
91 for (Index i = 0; i < num_facets; i++) {
92 if (has_active_region && !is_active(i)) continue;
93 split_edge({{facets(i, 0), facets(i, 1)}});
94 split_edge({{facets(i, 1), facets(i, 2)}});
95 split_edge({{facets(i, 2), facets(i, 0)}});
96 }
97
98 // Concatenate original vertices and additional vertices.
99 const Index total_num_vertices = num_vertices + safe_cast<Index>(additional_vertices.size());
100 la_runtime_assert(vertex_mapping.size() == additional_vertices.size());
101 typename MeshType::VertexArray all_vertices(total_num_vertices, dim);
102 all_vertices.block(0, 0, num_vertices, dim) = vertices;
103 for (Index i = num_vertices; i < total_num_vertices; i++) {
104 all_vertices.row(i) = additional_vertices[i - num_vertices];
105 }
106
107 // Re-triangulate facets.
108 std::vector<Eigen::Matrix<Index, 3, 1>> out_facets;
109 std::vector<Index> facet_map;
110 for (Index i = 0; i < num_facets; i++) {
111 Index corners[3];
112 std::vector<Index> chain;
113
114 // Copy over inactive facets.
115 if (has_active_region && !is_active(i)) {
116 la_runtime_assert(facets.row(i).maxCoeff() < total_num_vertices);
117 la_runtime_assert(facets.row(i).minCoeff() >= 0);
118 out_facets.push_back(facets.row(i));
119 facet_map.push_back(i);
120 continue;
121 }
122
123 for (Index j = 0; j < 3; j++) {
124 Edge e{{facets(i, j), facets(i, (j + 1) % 3)}};
125 corners[j] = safe_cast<Index>(chain.size());
126 chain.push_back(e[0]);
127 auto itr = splitting_pts.find(e);
128 if (itr != splitting_pts.end()) {
129 const auto& pts = itr->second;
130 la_runtime_assert(pts.size() >= 3);
131 if (pts[0] == e[0]) {
132 auto start = std::next(pts.cbegin());
133 auto end = std::prev(pts.cend());
134 chain.insert(chain.end(), start, end);
135 } else {
136 la_runtime_assert(pts[0] == e[1]);
137 auto start = std::next(pts.crbegin());
138 auto end = std::prev(pts.crend());
139 chain.insert(chain.end(), start, end);
140 }
141 }
142 }
143 if (chain.size() == 3) {
144 // No split.
145 la_runtime_assert(facets.row(i).maxCoeff() < total_num_vertices);
146 la_runtime_assert(facets.row(i).minCoeff() >= 0);
147 out_facets.push_back(facets.row(i));
148 facet_map.push_back(i);
149 active_facets(i, 0) = 0;
150 } else {
151 auto additional_facets =
152 split_triangle(all_vertices, chain, corners[0], corners[1], corners[2]);
153 std::copy(
154 additional_facets.begin(),
155 additional_facets.end(),
156 std::back_inserter(out_facets));
157 facet_map.insert(facet_map.end(), additional_facets.size(), i);
158 active_facets(i, 0) = 1;
159 }
160 }
161
162 const Index num_out_facets = safe_cast<Index>(out_facets.size());
163 typename MeshType::FacetArray all_facets(num_out_facets, 3);
164 for (Index i = 0; i < num_out_facets; i++) {
165 la_runtime_assert(out_facets[i].minCoeff() >= 0);
166 la_runtime_assert(out_facets[i].maxCoeff() < total_num_vertices);
167 all_facets.row(i) = out_facets[i];
168 }
169
170 // Mark active facets (i.e. facets that are split).
171 if (has_active_region) {
172 mesh.import_facet_attribute("__is_active", active_facets);
173 } else {
174 mesh.add_facet_attribute("__is_active");
175 mesh.import_facet_attribute("__is_active", active_facets);
176 }
177
178 auto out_mesh = create_mesh(all_vertices, all_facets);
179
180 // Port vertex attributes
181 auto vertex_attributes = mesh.get_vertex_attribute_names();
182 auto map_vertex_fn = [&](Eigen::Index i,
183 std::vector<std::pair<Eigen::Index, double>>& weights) {
184 weights.clear();
185 if (i < num_vertices) {
186 weights.emplace_back(i, 1.0);
187 } else {
188 const auto& record = vertex_mapping[i - num_vertices];
189 Index v0 = std::get<0>(record);
190 Index v1 = std::get<1>(record);
191 Scalar ratio = std::get<2>(record);
192 weights.emplace_back(v0, ratio);
193 weights.emplace_back(v1, 1.0 - ratio);
194 }
195 };
196
197 for (const auto& name : vertex_attributes) {
198 auto attr = mesh.get_vertex_attribute_array(name);
199 auto attr2 = to_shared_ptr(attr->row_slice(total_num_vertices, map_vertex_fn));
200 out_mesh->add_vertex_attribute(name);
201 out_mesh->set_vertex_attribute_array(name, std::move(attr2));
202 }
203
204 // Port facet attributes
205 auto facet_attributes = mesh.get_facet_attribute_names();
206 for (const auto& name : facet_attributes) {
207 auto attr = mesh.get_facet_attribute_array(name);
208 auto attr2 = to_shared_ptr(attr->row_slice(facet_map));
209 out_mesh->add_facet_attribute(name);
210 out_mesh->set_facet_attribute_array(name, std::move(attr2));
211 }
212
213 // Port corner attributes
214 map_corner_attributes(mesh, *out_mesh, facet_map);
215
216 // Port indexed attributes.
217 const auto& indexed_attr_names = mesh.get_indexed_attribute_names();
218 typename MeshType::AttributeArray attr;
219 typename MeshType::IndexArray indices;
220 for (const auto& attr_name : indexed_attr_names) {
221 std::tie(attr, indices) = mesh.get_indexed_attribute(attr_name);
222 assert(indices.rows() == facets.rows());
223
224 auto get_vertex_index_in_facet = [&facets](Index fid, Index vid) -> Index {
225 if (vid == facets(fid, 0))
226 return 0;
227 else if (vid == facets(fid, 1))
228 return 1;
229 else {
230 assert(vid == facets(fid, 2));
231 return 2;
232 }
233 };
234
235 typename MeshType::AttributeArray out_attr(num_out_facets * 3, attr.cols());
236 for (auto i : range(num_out_facets)) {
237 const auto old_fid = facet_map[i];
238 assert(old_fid < facets.rows());
239
240 for (Index j = 0; j < 3; j++) {
241 const auto vid = all_facets(i, j);
242 if (vid < num_vertices) {
243 const auto old_j = get_vertex_index_in_facet(old_fid, vid);
244 // Vertex is part of the input vertices.
245 out_attr.row(i * 3 + j) = attr.row(indices(old_fid, old_j));
246 } else {
247 const auto& record = vertex_mapping[vid - num_vertices];
248 Index v0 = std::get<0>(record);
249 Index v1 = std::get<1>(record);
250 Scalar ratio = std::get<2>(record);
251 Index old_j0 = get_vertex_index_in_facet(old_fid, v0);
252 Index old_j1 = get_vertex_index_in_facet(old_fid, v1);
253
254 out_attr.row(i * 3 + j) = attr.row(indices(old_fid, old_j0)) * ratio +
255 attr.row(indices(old_fid, old_j1)) * (1.0f - ratio);
256 }
257 }
258 }
259
260 if (attr_name == "normal") {
261 for (auto i : range(num_out_facets * 3)) {
262 out_attr.row(i).stableNormalize();
263 }
264 }
265
266 const std::string tmp_name = "__" + attr_name;
267 out_mesh->add_corner_attribute(tmp_name);
268 out_mesh->import_corner_attribute(tmp_name, out_attr);
269 map_corner_attribute_to_indexed_attribute(*out_mesh, tmp_name);
270 rename_indexed_attribute(*out_mesh, tmp_name, attr_name);
271 out_mesh->remove_corner_attribute(tmp_name);
272 }
273
274 if (recursive && total_num_vertices > num_vertices) {
275 return split_long_edges(*out_mesh, sq_tol, recursive);
276 } else
277 return out_mesh;
278}
279} // namespace legacy
280} // namespace lagrange
Definition: Edge.h:29
Definition: Mesh.h:48
@ Edge
Per-edge mesh attributes.
Definition: AttributeFwd.h:34
#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
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
std::shared_ptr< T > to_shared_ptr(std::unique_ptr< T > &&ptr)
Helper for automatic type deduction for unique_ptr to shared_ptr conversion.
Definition: common.h:88
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 split_long_edges(SurfaceMesh< Scalar, Index > &mesh, SplitLongEdgesOptions options={})
Split edges that are longer than options.max_edge_length.
Definition: split_long_edges.cpp:66