PolyFEM
Loading...
Searching...
No Matches
Singularities.cpp
Go to the documentation of this file.
1
2#include "Singularities.hpp"
3#include "Navigation.hpp"
5#include <algorithm>
7
9 const GEO::Mesh &M, Eigen::VectorXi &V, int regular_degree, bool ignore_border)
10{
11 using GEO::index_t;
12 std::vector<int> degree(M.vertices.nb(), 0);
13 for (index_t f = 0; f < M.facets.nb(); ++f)
14 {
15 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
16 {
17 index_t v = M.facets.vertex(f, lv);
18 degree[v]++;
19 }
20 }
21
22 // Ignore border vertices if requested
23 if (ignore_border)
24 {
25 GEO::Attribute<bool> boundary_vertices(M.vertices.attributes(), "boundary_vertex");
26 for (index_t v = 0; v < M.vertices.nb(); ++v)
27 {
28 if (boundary_vertices[v])
29 {
30 degree[v] = regular_degree;
31 }
32 }
33 }
34
35 // Count and create list of irregular vertices
36 long nb_reg = std::count(degree.begin(), degree.end(), regular_degree);
37 V.resize(M.vertices.nb() - nb_reg);
38 for (index_t v = 0, i = 0; v < M.vertices.nb(); ++v)
39 {
40 if (degree[v] != regular_degree)
41 {
42 assert(i < V.size());
43 V(i++) = v;
44 }
45 }
46}
47
48void polyfem::mesh::singular_edges(const GEO::Mesh &M, const Eigen::VectorXi &V, Eigen::MatrixX2i &E)
49{
50 using GEO::index_t;
51 std::vector<bool> is_singular(M.vertices.nb(), false);
52 for (int i = 0; i < V.size(); ++i)
53 {
54 is_singular[V(i)] = true;
55 }
56 std::vector<std::pair<int, int>> edges;
57 for (index_t e = 0; e < M.edges.nb(); ++e)
58 {
59 index_t v1 = M.edges.vertex(e, 0);
60 index_t v2 = M.edges.vertex(e, 1);
61 if (is_singular[v1] && is_singular[v2])
62 {
63 edges.emplace_back(v1, v2);
64 }
65 }
66 E.resize(edges.size(), 2);
67 int cnt = 0;
68 for (const auto &kv : edges)
69 {
70 assert(cnt < E.rows());
71 E.row(cnt++) << kv.first, kv.second;
72 }
73}
74
76 const GEO::Mesh &M, Eigen::VectorXi &V, Eigen::MatrixX2i &E, int regular_degree, bool ignore_border)
77{
78 singular_vertices(M, V, regular_degree, ignore_border);
79 singular_edges(M, V, E);
80}
81
83
85 GEO::Mesh &M, const Eigen::VectorXi &V, const Eigen::MatrixX2i &E, double t)
86{
87 using GEO::index_t;
89
90 assert(M.vertices.dimension() == 2 || M.vertices.dimension() == 3);
91
92 // Step 0: Make sure all endpoints of E are in V
93 std::vector<int> singulars;
94 singulars.reserve(V.size() + E.size());
95 for (int i = 0; i < V.size(); ++i)
96 {
97 singulars.push_back(V(i));
98 }
99 for (int i = 0; i < E.size(); ++i)
100 {
101 singulars.push_back(E.data()[i]);
102 }
103 std::sort(singulars.begin(), singulars.end());
104 auto it = std::unique(singulars.begin(), singulars.end());
105 singulars.resize(std::distance(singulars.begin(), it));
106
107 std::vector<bool> is_singular(M.vertices.nb(), false);
108 for (int v : singulars)
109 {
110 is_singular[v] = true;
111 }
112
113 // Step 1: Find all edges around singularities and create new vertices on those edge
114 std::vector<int> edge_to_midpoint(M.edges.nb(), -1);
115 for (index_t e = 0; e < M.edges.nb(); ++e)
116 {
117 for (index_t lv = 0; lv < 2; ++lv)
118 {
119 int v1 = M.edges.vertex(e, lv);
120 int v2 = M.edges.vertex(e, (lv + 1) % 2);
121 if (is_singular[v1] && !is_singular[v2])
122 {
123 GEO::vec3 coords = t * mesh_vertex(M, v1) + (1.0 - t) * mesh_vertex(M, v2);
124 edge_to_midpoint[e] = mesh_create_vertex(M, coords);
125 }
126 }
127 }
128
129 GEO::Attribute<GEO::index_t> c2e(M.facet_corners.attributes(), "edge_id");
130
131 // Step 2: Iterate over all facets, and subdivide accordingly
132 std::vector<index_t> facets_to_delete;
133 std::vector<int> next_vertex_around_hole(M.vertices.nb(), -1);
134 for (index_t f = 0, old_num_facets = M.facets.nb(); f < old_num_facets; ++f)
135 {
136 // a. Iterate over edges around the facet, until one marked edge is found
137 // b. Place the navigator index so that its vertex points to the unmarked vertex
138 // c. Navigate around until another marked edge is found (assert it is different from the previous one)
139 // d. Possibly invert the list of vertices / edge midpoints to respect initial facet orientation
140 // e. Create new facets given the list + 2 edge midpoints. The rules are:
141 // i. If #v == 1, don't create the midfacet vertex iff the original facet was a triangle
142 // ii. If #v == 2, then don't create the midfacet vertex
143 // iii. If #v >= 3, then create midfacet vertex iff orignal facet was a quad or a tri
144 assert(M.facets.nb_vertices(f) > 2);
145
146 // a. Iterate over edges around the facet, until one marked edge is found
147 Index idx1 = Navigation::get_index_from_face(M, c2e, f, 0);
148
149 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
150 {
151 if (edge_to_midpoint[idx1.edge] != -1)
152 {
153 break;
154 }
155 idx1 = Navigation::next_around_face(M, c2e, idx1);
156 }
157 if (edge_to_midpoint[idx1.edge] == -1)
158 {
159 continue;
160 }
161 facets_to_delete.push_back(f);
162
163 // b. Place the navigator index so that its vertex points to the unmarked vertex
164 if (is_singular[idx1.vertex])
165 {
166 idx1 = Navigation::switch_vertex(M, idx1);
167 assert(!is_singular[idx1.vertex]);
168 }
169
170 // c. Navigate around until another marked edge is found (assert it is different from the previous one)
171 Index idx2 = Navigation::switch_edge(M, c2e, idx1);
172 GEO::vector<index_t> unmarked_vertices;
173 // std::cout << f << ' ' << std::endl;
174 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
175 {
176 assert(!is_singular[idx2.vertex]);
177 unmarked_vertices.push_back(idx2.vertex);
178 // std::cout << idx2.edge << ':' << idx2.vertex << ' ';
179 if (edge_to_midpoint[idx2.edge] != -1)
180 {
181 break;
182 }
183 idx2 = Navigation::next_around_face(M, c2e, idx2);
184 }
185 // std::cout << std::endl;
186 assert(edge_to_midpoint[idx2.edge] != -1);
187 assert(idx1.edge != idx2.edge);
188 assert(unmarked_vertices.front() == idx1.vertex);
189 assert(unmarked_vertices.back() == idx2.vertex);
190
191 // d. Possibly invert the list of vertices / edge midpoints to respect initial facet orientation
192 {
193 int v1 = idx2.vertex;
194 int v0 = Navigation::switch_vertex(M, idx2).vertex;
195 int lv1 = M.facets.find_vertex(f, v1);
196 int v2 = M.facets.vertex(f, (lv1 + 1) % M.facets.nb_vertices(f));
197 if (v0 != v2)
198 {
199 // Need to reverse the sequence in `unmarked_vertices`, and swap(idx1, idx2)
200 std::reverse(unmarked_vertices.begin(), unmarked_vertices.end());
201 std::swap(idx1, idx2);
202 }
203 }
204
205 // e. Create new facets given the list + 2 edge midpoints. The rules are:
206 // i. If #v == 1, don't create the midfacet vertex iff the original facet was a triangle
207 // ii. If #v == 2, then don't create the midfacet vertex
208 // iii. If #v >= 3, then create midfacet vertex iff orignal facet was a quad or a tri
209 int sf = M.facets.nb_vertices(f);
210 int nv = (int)unmarked_vertices.size();
211 bool create_midfacet_point = false;
212 if (nv == 1)
213 {
214 if (sf == 3)
215 {
216 create_midfacet_point = false;
217 }
218 else
219 {
220 create_midfacet_point = true;
221 }
222 }
223 else if (nv == 2)
224 {
225 create_midfacet_point = false;
226 }
227 else
228 {
229 if (sf == 3 || sf == 4)
230 {
231 create_midfacet_point = true;
232 }
233 else
234 {
235 create_midfacet_point = false;
236 }
237 }
238
239 int ve1 = edge_to_midpoint[idx1.edge];
240 int ve2 = edge_to_midpoint[idx2.edge];
241 if (create_midfacet_point)
242 {
243 GEO::vec3 coords = facet_barycenter(M, f);
244 index_t vf = mesh_create_vertex(M, coords);
245 next_vertex_around_hole.resize(vf + 1);
246 assert(next_vertex_around_hole[ve1] == -1);
247 next_vertex_around_hole[ve1] = vf;
248 next_vertex_around_hole[vf] = ve2;
249 if (nv == 1)
250 {
251 M.facets.create_quad(unmarked_vertices.front(), ve2, vf, ve1);
252 }
253 else
254 {
255 M.facets.create_quad(unmarked_vertices[0], unmarked_vertices[1], vf, ve1);
256 M.facets.create_quad(*(unmarked_vertices.rbegin() + 1), unmarked_vertices.back(), ve2, vf);
257 for (int i = 1; i < int(unmarked_vertices.size()) - 2; ++i)
258 {
259 M.facets.create_triangle(unmarked_vertices[i], unmarked_vertices[i + 1], vf);
260 }
261 }
262 }
263 else
264 {
265 assert(next_vertex_around_hole[ve1] == -1);
266 next_vertex_around_hole[ve1] = ve2;
267 unmarked_vertices.push_back(ve2);
268 unmarked_vertices.push_back(ve1);
269 M.facets.create_polygon(unmarked_vertices);
270 }
271 }
272
273 // Step 3: Iterate over new vertices, loop around holes and create polygonal facets
274 std::vector<bool> marked(M.vertices.nb(), false);
275 for (index_t v = 0; v < M.vertices.nb(); ++v)
276 {
277 if (next_vertex_around_hole[v] == -1 || marked[v])
278 {
279 continue;
280 }
281 GEO::vector<index_t> hole;
282 int vi = v;
283 do
284 {
285 vi = next_vertex_around_hole[vi];
286 assert(vi != -1);
287 hole.push_back(vi);
288 marked[vi] = true;
289 // std::cout << vi << ';';
290 } while (vi != (int)v);
291 M.facets.create_polygon(hole);
292 }
293
294 // Step 4: Remove facets that have been split, and delete isolated vertices
295 GEO::vector<index_t> to_delete_mask(M.facets.nb(), 0u);
296 for (index_t f : facets_to_delete)
297 {
298 to_delete_mask[f] = 1;
299 }
300 M.facets.delete_elements(to_delete_mask);
301 M.edges.clear();
302 M.vertices.remove_isolated();
303
304#if 0
305 // Step 2: Find all faces adjacent to a marked edge and place a new vertex at the center of the facet if needed
306 std::vector<int> facet_to_midpoint(M.facets.nb(), -1);
307 GEO::vector<index_t> facets_to_delete;
308 for (index_t f = 0; f < M.facets.nb(); ++f) {
309 Index idx = Navigation::get_index_from_face(M, f, 0);
310 std::vector<int> marked_edges;
311 for (index_t le = 0; le < M.facets.nb_vertices(f); ++le) {
312 if (edge_to_midpoint[idx.edge] != -1) {
313 marked_edges.push_back(le);
314 }
315 idx = Navigation::next_around_face(M, idx);
316 }
317 // There should be exactly 2 split edges per face, otherwise I don't know what to do!
318 assert(marked_edges.empty() || marked_edges.size() == 2);
319 if (!marked_edges.empty()) {
320 int e1 = marked_edges.front();
321 int e2 = marked_edges.back();
322 if (e1 + 1 == e2 || (e2 + 1) % (int) M.facets.nb_vertices(f) == e1) {
323 GEO::vec3 coords = facet_barycenter(f);
324 facet_to_midpoint[f] = M.vertices.create_vertex(&coords[0]);
325 }
326 facets_to_delete.push_back(f);
327 }
328 }
329
330 // Step 3: Create new facets for each marked facet, based on its configuration
331 std::vector<index_t> next_around_hole(M.vertices.nb(), -1);
332 std::vector<index_t> prev_around_hole(M.vertices.nb(), -1);
333
334 // Step 4: Remove facets that have been split, and delete isolated vertices
335 M.facets.delete_elements(facets_to_delete);
336 M.vertices.remove_isolated();
337 // + orient facets
338#endif
339}
int V
void create_patch_around_singularities(GEO::Mesh &M, const Eigen::VectorXi &V, const Eigen::MatrixX2i &E, double t=0.5)
GEO::vec3 mesh_vertex(const GEO::Mesh &M, GEO::index_t v)
Retrieve a 3D vector with the position of a given vertex.
Definition MeshUtils.cpp:44
GEO::index_t mesh_create_vertex(GEO::Mesh &M, const GEO::vec3 &p)
Definition MeshUtils.cpp:77
void singularity_graph(const GEO::Mesh &M, Eigen::VectorXi &V, Eigen::MatrixX2i &E, int regular_degree=4, bool ignore_border=true)
GEO::vec3 facet_barycenter(const GEO::Mesh &M, GEO::index_t f)
Definition MeshUtils.cpp:64
void singular_edges(const GEO::Mesh &M, const Eigen::VectorXi &V, Eigen::MatrixX2i &E)
void singular_vertices(const GEO::Mesh &M, Eigen::VectorXi &V, int regular_degree=4, bool ignore_border=true)