PolyFEM
Loading...
Searching...
No Matches
Refinement.cpp
Go to the documentation of this file.
1
2#include "Refinement.hpp"
3#include "Navigation.hpp"
5#include "PolygonUtils.hpp"
7
8#include <iostream>
9#include <vector>
10#include <array>
11#include <numeric>
13
14using namespace polyfem;
15
17 const Eigen::MatrixXi &Q, Eigen::MatrixXi &edge_index,
18 std::vector<std::vector<int>> &adj,
19 std::vector<std::pair<int, int>> *pairs_of_edges,
20 std::vector<std::pair<int, int>> *pairs_of_quads,
21 Eigen::MatrixXi *quad_index)
22{
23 assert(Q.cols() == 4); // Assumes quad mesh
24 typedef std::pair<int, int> Edge;
25
26 std::vector<std::pair<int, int>> edges;
27
28 edge_index.resizeLike(Q); // (f, lv) -> edge id
29 adj.clear();
30 edges.clear();
31 if (pairs_of_quads)
32 {
33 pairs_of_quads->clear();
34 }
35 if (quad_index)
36 {
37 quad_index->setConstant(Q.rows(), Q.cols(), -1);
38 }
39
40 // Number mesh edges & build dual edge-graph
41 std::vector<std::pair<Edge, std::pair<int, int>>> accu;
42 accu.reserve((size_t)Q.size());
43 for (int f = 0; f < Q.rows(); ++f)
44 {
45 for (int lv = 0; lv < 4; ++lv)
46 {
47 int x = Q(f, lv);
48 int y = Q(f, (lv + 1) % 4);
49 accu.emplace_back(Edge(std::min(x, y), std::max(x, y)), std::make_pair(f, lv));
50 }
51 }
52 std::sort(accu.begin(), accu.end());
53 edges.reserve(accu.size() / 2);
54 edge_index.setConstant(-1);
55 int id = -1;
56 int prev_quad = -1;
57 int prev_lv = -1;
58 Edge prev_side(-1, -1);
59 for (const auto &kv : accu)
60 {
61 // kv = (v1, v2), (f, lv)
62 Edge e = kv.first;
63 int f = kv.second.first;
64 int lv = kv.second.second;
65 // printf("(%d,%d) -> %d, %d\n", e.first, e.second, f, lv);
66 if (e != prev_side)
67 {
68 ++id;
69 edges.emplace_back(e);
70 }
71 else
72 {
73 if (pairs_of_quads)
74 {
75 if (quad_index)
76 {
77 (*quad_index)(f, lv) = prev_quad;
78 (*quad_index)(prev_quad, prev_lv) = f;
79 }
80 pairs_of_quads->emplace_back(prev_quad, f);
81 }
82 }
83 edge_index(f, lv) = id;
84 prev_side = e;
85 prev_quad = f;
86 prev_lv = lv;
87 }
88 adj.resize(edges.size());
89 for (int f = 0; f < Q.rows(); ++f)
90 {
91 for (int lv = 0; lv < 4; ++lv)
92 {
93 int e1 = edge_index(f, lv);
94 int e2 = edge_index(f, (lv + 2) % 4);
95 assert(e1 != -1);
96 if (e1 < e2)
97 {
98 adj[e1].push_back(e2);
99 adj[e2].push_back(e1);
100 }
101 }
102 }
103
104 if (pairs_of_edges)
105 {
106 std::swap(*pairs_of_edges, edges);
107 }
108}
109
111
112namespace
113{
114
115 enum class PatternType
116 {
117 NOT_PERIODIC, // Opposite borders do not match
118 NOT_SYMMETRIC, // At least one border is not symmetric along its bisector
119 SIMPLE_PERIODIC, // Opposite borders match individually
120 DOUBLE_PERIODIC, // All four borders match between each others
121 };
122
123 // -----------------------------------------------------------------------------
124
125 // Determine the type of periodicity of the given pattern. This function rely
126 // only on the input vertex positions, but does not check if the underlying
127 // topology is consistent.
128 //
129 // @param[in] V #V x 3 matrix of vertex positions
130 // @param[in] F #F x 3 matrix of triangle indexes
131 // @param[out] border_vertices List of vertices along each side
132 //
133 // @return { Type of periodicity of the pattern. }
134 //
135 PatternType compute_pattern_type(
136 const Eigen::MatrixXd &V, const Eigen::MatrixXi &F,
137 std::array<Eigen::VectorXi, 4> &border_vertices)
138 {
139 Eigen::Vector2d lower = V.colwise().minCoeff().head<2>();
140 Eigen::Vector2d upper = V.colwise().maxCoeff().head<2>();
141
142 std::array<Eigen::MatrixXd, 2> border;
143
144 // Compute vertices along the border. Sides are numbered as follows:
145 //
146 // 2
147 // v3 ----- v2
148 // | |
149 // 3 | | 1
150 // | |
151 // v0 ----- v1
152 // 0
153 // ↑y
154 // └─→x
155 for (int d = 0; d < 2; ++d)
156 {
157 std::vector<std::pair<double, int>> low, upp;
158 for (int i = 0; i < V.rows(); ++i)
159 {
160 if (V(i, 1 - d) == lower[1 - d])
161 {
162 low.emplace_back(V(i, d), i);
163 }
164 if (V(i, 1 - d) == upper[1 - d])
165 {
166 upp.emplace_back(V(i, d), i);
167 }
168 }
169 std::sort(low.begin(), low.end());
170 std::sort(upp.begin(), upp.end());
171 border_vertices[d ? 3 : 0].resize(low.size());
172 for (size_t i = 0; i < low.size(); ++i)
173 {
174 border_vertices[d ? 3 : 0][i] = low[i].second;
175 }
176 border_vertices[d ? 1 : 2].resize(upp.size());
177 for (size_t i = 0; i < upp.size(); ++i)
178 {
179 border_vertices[d ? 1 : 2][i] = upp[i].second;
180 }
181 }
182 for (int i : {2, 3})
183 {
184 border_vertices[i].reverseInPlace();
185 }
186
187 // Check if borders have the same topology and geometry
188 for (int d = 0; d < 2; ++d)
189 {
190 std::vector<double> low, upp;
191 for (int i = 0; i < V.rows(); ++i)
192 {
193 if (V(i, d) == lower[d])
194 {
195 low.push_back(V(i, 1 - d));
196 }
197 if (V(i, d) == upper[d])
198 {
199 upp.push_back(V(i, 1 - d));
200 }
201 }
202 std::sort(low.begin(), low.end());
203 std::sort(upp.begin(), upp.end());
204 if (low.size() != upp.size())
205 {
206 return PatternType::NOT_PERIODIC;
207 }
208 size_t n = low.size();
209 border[d].resize(n, 2);
210 for (size_t i = 0; i < n; ++i)
211 {
212 border[d](i, 0) = low[i];
213 border[d](i, 1) = upp[i];
214 }
215 if (!border[d].col(0).isApprox(border[d].col(1)))
216 {
217 return PatternType::NOT_PERIODIC;
218 }
219 }
220
221 // Check if borders are symmetric
222 for (int d = 0; d < 2; ++d)
223 {
224 if (!(border[d].col(0).array() - lower[d]).isApprox(upper[d] - border[d].col(0).array().reverse()))
225 {
226 return PatternType::NOT_SYMMETRIC;
227 }
228 }
229
230 // Check if horizontal and vertical borders are matching
231 if (border[0].size() == border[1].size())
232 {
233 if (!border[0].col(0).isApprox(border[1].col(0)))
234 {
235 logger().warn("Pattern boundaries have the same number of vertices, but their position differ slighly.");
236 Eigen::MatrixXd X(border[0].size(), 2);
237 X.col(0) = border[0].col(0);
238 X.col(1) = border[1].col(0);
239 //TODO
240 // std::cout << X << std::endl << std::endl;;
241 }
242 return PatternType::DOUBLE_PERIODIC;
243 }
244 else
245 {
246 return PatternType::SIMPLE_PERIODIC;
247 }
248
249 return PatternType::SIMPLE_PERIODIC;
250 }
251
252 // -----------------------------------------------------------------------------
253
254 Eigen::VectorXi vertex_degree(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
255 {
256 Eigen::VectorXi count = Eigen::VectorXi::Zero(V.rows());
257
258 for (unsigned i = 0; i < F.rows(); ++i)
259 {
260 for (unsigned j = 0; j < F.cols(); ++j)
261 {
262 // Avoid duplicate edges
263 if (F(i, j) < F(i, (j + 1) % F.cols()))
264 {
265 count(F(i, j)) += 1;
266 count(F(i, (j + 1) % F.cols())) += 1;
267 }
268 }
269 }
270
271 return count;
272 }
273
274} // anonymous namespace
275
277
279 const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IQ,
280 const Eigen::MatrixXd &PV, const Eigen::MatrixXi &PF,
281 Eigen::MatrixXd &OV, Eigen::MatrixXi &OF, Eigen::VectorXi *SF,
282 EvalParametersFunc evalFunc, GetAdjacentLocalEdge getAdjLocalEdge)
283{
284 // List of vertices along each border (from lv to (lv+1)%4)
285 std::array<Eigen::VectorXi, 4> border_vertices;
286
287 auto pattern = compute_pattern_type(PV, PF, border_vertices);
288 auto valence = vertex_degree(IV, IQ);
289 // auto border = igl::is_border_vertex(IV, IQ);
290
291 // Check input validity
292 switch (pattern)
293 {
294 case PatternType::NOT_PERIODIC:
295 logger().error("Pattern not periodic");
296 return false;
297 case PatternType::NOT_SYMMETRIC:
298 logger().error("Pattern not symmetric");
299 return false;
300 case PatternType::SIMPLE_PERIODIC:
301 logger().error("Pattern simple periodic");
302 return false;
303 case PatternType::DOUBLE_PERIODIC:
304 // std::cout << "Pattern double periodic" << std::endl;
305 // Not implemented
306 break;
307 default:
308 logger().error("Unknown patter type");
309 return false;
310 }
311
312 // Normalized coordinates (between 0 and 1 for barycentric coordinates)
313 auto lower = PV.colwise().minCoeff();
314 auto upper = PV.colwise().maxCoeff();
315 Eigen::MatrixXd PVN = (PV.rowwise() - lower).array().rowwise() / (upper - lower).cwiseMax(1e-5).array();
316
317 // If the eval function is undefined, don't do any remapping
318 if (!evalFunc)
319 {
320 evalFunc = [&](const Eigen::MatrixXd &uv, Eigen::MatrixXd &mapped, int q) {
321 const auto &u = uv.col(0).array();
322 const auto &v = uv.col(1).array();
323 Eigen::RowVector3d a = IV.row(IQ(q, 0));
324 Eigen::RowVector3d b = IV.row(IQ(q, 1));
325 Eigen::RowVector3d c = IV.row(IQ(q, 2));
326 Eigen::RowVector3d d = IV.row(IQ(q, 3));
327 mapped = ((1 - u) * (1 - v)).matrix() * a + (u * (1 - v)).matrix() * b + (u * v).matrix() * c + ((1 - u) * v).matrix() * d;
328 };
329 }
330
331 // Mapped uv samples
332 Eigen::MatrixXd mapped;
333
334 // Instantiate (duplicating vertices)
335 Eigen::MatrixXd P0 = PVN.row(0);
336 evalFunc(P0, mapped, 0);
337 Eigen::MatrixXd V(IQ.rows() * PV.rows(), mapped.cols());
338 Eigen::MatrixXi F(IQ.rows() * PF.rows(), PF.cols());
339 if (SF)
340 {
341 SF->resize(V.rows());
342 }
343
344 // Remapped vertex id (after duplicate removal)
345 Eigen::VectorXi remap(V.rows());
346 remap.setConstant(-1);
347
348 for (int q = 0; q < IQ.rows(); ++q)
349 {
350 evalFunc(PVN, mapped, q);
351 int v0 = (int)PVN.rows() * q;
352 V.middleRows(v0, mapped.rows()) = mapped;
353 int f0 = (int)PF.rows() * q;
354 F.middleRows(f0, PF.rows()) = PF.array() + v0;
355 if (SF)
356 {
357 SF->segment(v0, mapped.rows()).setConstant(q);
358 }
359 }
360
361 auto min_max = [](int x, int y) {
362 return std::make_pair(std::min(x, y), std::max(x, y));
363 };
364
365 if (getAdjLocalEdge)
366 {
367 // Adjacency function has already been provided
368 int cnt = 0;
369 for (int q1 = 0; q1 < IQ.rows(); ++q1)
370 {
371 const int v1 = (int)PVN.rows() * q1;
372 for (int lv1 = 0; lv1 < 4; ++lv1)
373 {
374 const auto res = getAdjLocalEdge(q1, lv1);
375 const int q2 = std::get<0>(res);
376 const int v2 = (int)PVN.rows() * q2;
377 const int lv2 = std::get<1>(res);
378 const bool rev = std::get<2>(res);
379 if (q2 > q1)
380 {
381 Eigen::VectorXi side1 = border_vertices[lv1].array() + v1;
382 Eigen::VectorXi side2 = border_vertices[lv2].array() + v2;
383 if (rev)
384 {
385 side2.reverseInPlace();
386 }
387 // std::cout << side1.transpose() << std::endl;
388 // std::cout << side2.transpose() << std::endl << std::endl;
389 for (int ii = 0; ii < (int)side1.size(); ++ii)
390 {
391 const int x1 = side1[ii];
392 const int x2 = side2[ii];
393 if (remap(x1) < 0)
394 {
395 remap(x1) = cnt++;
396 }
397 remap(x2) = remap(x1);
398 }
399 }
400 }
401 }
402 for (int v = 0; v < V.rows(); ++v)
403 {
404 if (remap(v) < 0)
405 {
406 remap(v) = cnt++;
407 }
408 }
409 }
410 else
411 {
412 // Compute adjacency info on the quad mesh
413 Eigen::MatrixXi edge_index;
414 std::vector<std::vector<int>> adj;
415 std::vector<std::pair<int, int>> pairs_of_quads;
416 edge_adjacency_graph(IQ, edge_index, adj, nullptr, &pairs_of_quads);
417 // Stitch vertices from adjacent quads
418 for (const auto &qq : pairs_of_quads)
419 {
420 int q1 = qq.first;
421 int q2 = qq.second;
422 if (q2 > q1)
423 {
424 std::swap(q1, q2);
425 }
426 const int v1 = (int)PVN.rows() * q1;
427 const int v2 = (int)PVN.rows() * q2;
428 int lv1 = 0;
429 int lv2 = 0;
430 for (; lv1 < 4; ++lv1)
431 {
432 int x1 = IQ(q1, lv1);
433 int y1 = IQ(q1, (lv1 + 1) % 4);
434 for (lv2 = 0; lv2 < 4; ++lv2)
435 {
436 int x2 = IQ(q2, lv2);
437 int y2 = IQ(q2, (lv2 + 1) % 4);
438 if (min_max(x1, y1) == min_max(x2, y2))
439 {
440 int e = edge_index(q1, lv1);
441 // tfm::printf("quads: (%s, %s) and (%s, %s)\n", q1, lv1, q2, lv2);
442 // tfm::printf("└ edge: %s-%s (id: %e)\n", x1, y1, e);
443 assert(edge_index(q2, lv2) == e);
444 Eigen::VectorXi side1 = border_vertices[lv1].array() + v1;
445 Eigen::VectorXi side2 = border_vertices[lv2].array() + v2;
446 if (x1 > y1)
447 {
448 side1.reverseInPlace();
449 }
450 if (x2 > y2)
451 {
452 side2.reverseInPlace();
453 }
454 for (int ii = 0; ii < (int)side1.size(); ++ii)
455 {
456 remap(side2[ii]) = side1[ii];
457 }
458 lv1 = 4;
459 lv2 = 4;
460 }
461 }
462 }
463 }
464 }
465
466 // OV = V;
467 // OF = F;
468 // return true;
469
470 // Remap vertices according to 'remap'
471 int num_remapped = remap.maxCoeff() + 1;
472 OV.resize(num_remapped, V.cols());
473 for (int v = 0; v < V.rows(); ++v)
474 {
475 OV.row(remap(v)) = V.row(v);
476 }
477 for (int f = 0; f < F.rows(); ++f)
478 {
479 for (int lv = 0; lv < F.cols(); ++lv)
480 {
481 int ov = F(f, lv);
482 int nv = remap(ov);
483 F(f, lv) = nv;
484 }
485 }
486 OF = F;
487 // Eigen::VectorXi I;
488 // igl::remove_unreferenced(V, F, OV, OF, I);
489
490 // Remap tags on vertices
491 if (SF)
492 {
493 Eigen::VectorXi tmp = *SF;
494 SF->resize(OV.rows());
495 SF->setZero();
496 for (int v = 0; v < V.rows(); ++v)
497 {
498 (*SF)(remap(v)) = tmp(v);
499 }
500 }
501
502 return true;
503}
504
506
508 const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IF,
509 Eigen::MatrixXd &OV, Eigen::MatrixXi &OF)
510{
511 Eigen::MatrixXd PV(9, 3);
512 Eigen::MatrixXi PF(4, 4);
513 PV << -1, 0, 0,
514 0, 0, 0,
515 0, 1, 0,
516 -1, 1, 0,
517 1, 1, 0,
518 1, 0, 0,
519 1, -1, 0,
520 0, -1, 0,
521 -1, -1, 0;
522 PF << 1, 2, 3, 4,
523 3, 2, 6, 5,
524 6, 2, 8, 7,
525 2, 1, 9, 8;
526 PF = PF.array() - 1;
527 // std::cout << PF << std::endl;
528 bool res = instantiate_pattern(IV, IF, PV, PF, OV, OF);
529 assert(res);
530}
531
533
534void polyfem::mesh::Polygons::polar_split(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, std::vector<std::vector<int>> &OF, double t)
535{
536 assert(IV.cols() == 2 || IV.cols() == 3);
537 Eigen::RowVector3d bary;
538 if (is_star_shaped(IV, bary))
539 {
540 if (t == 0.0)
541 {
542 // Special case 1: collapse the remaining polygon into a single vertex
543 OV.resize(IV.rows() + 1, IV.cols());
544 OV.topRows(IV.rows()) = IV;
545 OV.bottomRows(1) = bary.head(IV.cols());
546 int n = (int)IV.rows();
547 for (int i = 0; i < IV.rows(); ++i)
548 {
549 OF.push_back({i, (i + 1) % n, n});
550 }
551 }
552 else if (t < 0.0)
553 {
554 throw std::invalid_argument("Value t should be >= 0.0.");
555 }
556 else if (t >= 1.0)
557 {
558 throw std::invalid_argument("Value t >= 1.0 would create degenerate elements.");
559 }
560 else
561 {
562 // Create 1-ring around the central point
563 OV.resize(2 * IV.rows(), IV.cols());
564 OV.topRows(IV.rows()) = IV;
565 OV.bottomRows(IV.rows()) = (t * IV).rowwise() + (1.0 - t) * bary.head(IV.cols());
566 int n = (int)IV.rows();
567 OF.push_back({});
568 for (int i = 0; i < IV.rows(); ++i)
569 {
570 OF.front().push_back(i + n);
571 OF.push_back({i, (i + 1) % n, (i + 1) % n + n, i + n});
572 }
573 }
574 }
575 else
576 {
577 throw std::invalid_argument("Non star-shaped input polygon.");
578 }
579}
580
581// -----------------------------------------------------------------------------
582
583void polyfem::mesh::Polygons::catmul_clark_split(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, std::vector<std::vector<int>> &OF)
584{
585 assert(IV.cols() == 2 || IV.cols() == 3);
586 // std::cout << IV.rows() << std::endl;
587 assert(IV.rows() % 2 == 0);
588 Eigen::RowVector3d bary;
589 if (is_star_shaped(IV, bary))
590 {
591 // Create 1-ring around the central point
592 OV.resize(IV.rows() + 1, IV.cols());
593 OV.topRows(IV.rows()) = IV;
594 OV.bottomRows(1) = bary.head(IV.cols());
595 int n = (int)IV.rows();
596 for (int i = 1; i < IV.rows(); i += 2)
597 {
598 OF.push_back({i, (i + 1) % n, (i + 2) % n, n});
599 }
600 }
601 else
602 {
603 throw std::invalid_argument("Non star-shaped input polygon.");
604 }
605}
606
607// -----------------------------------------------------------------------------
608
609void polyfem::mesh::Polygons::no_split(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, std::vector<std::vector<int>> &OF)
610{
611 OV = IV;
612 OF.clear();
613 OF.push_back({});
614 OF.front().resize(IV.rows());
615 std::iota(OF.front().begin(), OF.front().end(), 0);
616}
617
619
620void polyfem::mesh::refine_polygonal_mesh(const GEO::Mesh &M_in, GEO::Mesh &M_out, Polygons::SplitFunction split_func)
621{
622 using GEO::index_t;
623 using Navigation::Index;
624
625 // Step 0: Clear output mesh, and fill it with M_in's vertices
626 assert(&M_in != &M_out);
627 M_out.copy(M_in);
628 M_out.edges.clear();
629 M_out.facets.clear();
630 GEO::Attribute<GEO::index_t> c2e(M_in.facet_corners.attributes(), "edge_id");
631
632 // Step 1: Iterate over facets and refine triangles and quads
633 std::vector<int> edge_to_midpoint(M_in.edges.nb(), -1);
634 for (index_t f = 0; f < M_in.facets.nb(); ++f)
635 {
636 index_t nv = M_in.facets.nb_vertices(f);
637 assert(nv > 2);
638 Index idx = Navigation::get_index_from_face(M_in, c2e, f, 0);
639 assert(Navigation::switch_vertex(M_in, idx).vertex == (int)M_in.facets.vertex(f, 1));
640
641 // Create mid-edge vertices
642 for (index_t lv = 0; lv < M_in.facets.nb_vertices(f); ++lv, idx = Navigation::next_around_face(M_in, c2e, idx))
643 {
644 assert(idx.vertex == (int)M_in.facets.vertex(f, lv));
645 if (edge_to_midpoint[idx.edge] == -1)
646 {
647 GEO::vec3 coords = 0.5 * (mesh_vertex(M_in, idx.vertex) + mesh_vertex(M_in, Navigation::switch_vertex(M_in, idx).vertex));
648 edge_to_midpoint[idx.edge] = mesh_create_vertex(M_out, coords);
649 assert(edge_to_midpoint[idx.edge] + 1 == (int)M_out.vertices.nb());
650 }
651 }
652
653 if (/*nv == 3 ||*/ nv == 4)
654 {
655 // Create mid-face vertex
656 index_t vf = mesh_create_vertex(M_out, facet_barycenter(M_in, f));
657 assert(vf + 1 == M_out.vertices.nb());
658
659 // Create quads
660 for (index_t lv = 0; lv < M_in.facets.nb_vertices(f); ++lv)
661 {
662 idx = Navigation::get_index_from_face(M_in, c2e, f, lv);
663 assert(Navigation::switch_vertex(M_in, idx).vertex == (int)M_in.facets.vertex(f, (lv + 1) % M_in.facets.nb_vertices(f)));
664 int v1 = idx.vertex;
665 int v12 = edge_to_midpoint[idx.edge];
666 int v01 = edge_to_midpoint[Navigation::switch_edge(M_in, c2e, idx).edge];
667 assert(v12 != -1 && v01 != -1);
668 M_out.facets.create_quad(v1, v12, vf, v01);
669 }
670 }
671 }
672
673 // Step 2: Create polygonal faces following vertices around holes
674 for (index_t f = 0; f < M_in.facets.nb(); ++f)
675 {
676 if (M_in.facets.nb_vertices(f) <= 4)
677 {
678 continue;
679 }
680 GEO::vector<index_t> hole;
681 {
682 auto idx = Navigation::get_index_from_face(M_in, c2e, f, 0);
683 for (index_t lv = 0; lv < M_in.facets.nb_vertices(f); ++lv)
684 {
685 hole.push_back(idx.vertex);
686 hole.push_back(edge_to_midpoint[idx.edge]);
687 idx = Navigation::next_around_face(M_in, c2e, idx);
688 }
689 }
690
691 if (M_in.vertices.dimension() != 2)
692 {
693 // std::cerr << "WARNING: Input mesh has dimension > 2, but polygonal facets will be split considering their XY coordinates only." << std::endl;
694 }
695
696 // Subdivide the hole using polar refinement
697 index_t n = hole.size();
698 Eigen::MatrixXd P(n, 2), V;
699 std::vector<std::vector<int>> F;
700 for (index_t k = 0; k < n; ++k)
701 {
702 GEO::vec3 pk = polyfem::mesh::mesh_vertex(M_out, hole[k]);
703 P.row(k) << pk[0], pk[1];
704 }
705 split_func(P, V, F);
706 assert(V.rows() >= n);
707 std::vector<int> remap(V.rows() - n);
708 for (index_t k = n, lk = 0; k < V.rows(); ++k, ++lk)
709 {
710 GEO::vec3 qk = GEO::vec3(V(k, 0), V(k, 1), 0);
711 remap[lk] = mesh_create_vertex(M_out, qk);
712 }
713 for (const auto &poly : F)
714 {
715 GEO::vector<index_t> vertices;
716 for (int vk : poly)
717 {
718 if (vk < (int)n)
719 {
720 vertices.push_back(hole[vk]);
721 }
722 else
723 {
724 vertices.push_back(remap[vk - n]);
725 }
726 // std::cout << vertices.back() << ',';
727 }
728 // std::cout << std::endl;
729 M_out.facets.create_polygon(vertices);
730 }
731 }
732}
733
735
736void polyfem::mesh::refine_triangle_mesh(const GEO::Mesh &M_in, GEO::Mesh &M_out)
737{
738 using GEO::index_t;
739 using Navigation::Index;
740
741 // Step 1: Clear output mesh, and fill it with M_in's vertices
742 assert(&M_in != &M_out);
743 M_out.copy(M_in);
744 M_out.edges.clear();
745 M_out.facets.clear();
746 GEO::Attribute<GEO::index_t> c2e(M_in.facet_corners.attributes(), "edge_id");
747
748 // Step 2: Iterate over facets and refine triangles
749 std::vector<int> edge_to_midpoint(M_in.edges.nb(), -1);
750 for (index_t f = 0; f < M_in.facets.nb(); ++f)
751 {
752 index_t nv = M_in.facets.nb_vertices(f);
753 assert(nv == 3);
754 Index idx = Navigation::get_index_from_face(M_in, c2e, f, 0);
755 assert(Navigation::switch_vertex(M_in, idx).vertex == (int)M_in.facets.vertex(f, 1));
756
757 // Create mid-edge vertices
758 for (index_t lv = 0; lv < M_in.facets.nb_vertices(f); ++lv, idx = Navigation::next_around_face(M_in, c2e, idx))
759 {
760 assert(idx.vertex == (int)M_in.facets.vertex(f, lv));
761 if (edge_to_midpoint[idx.edge] == -1)
762 {
763 GEO::vec3 coords = 0.5 * (mesh_vertex(M_in, idx.vertex) + mesh_vertex(M_in, Navigation::switch_vertex(M_in, idx).vertex));
764 edge_to_midpoint[idx.edge] = mesh_create_vertex(M_out, coords);
765 assert(edge_to_midpoint[idx.edge] + 1 == (int)M_out.vertices.nb());
766 }
767 }
768
769 // Create triangles
770 std::array<index_t, 3> e2v;
771 for (index_t lv = 0; lv < M_in.facets.nb_vertices(f); ++lv)
772 {
773 idx = Navigation::get_index_from_face(M_in, c2e, f, lv);
774 assert(Navigation::switch_vertex(M_in, idx).vertex == (int)M_in.facets.vertex(f, (lv + 1) % M_in.facets.nb_vertices(f)));
775 int v1 = idx.vertex;
776 int v12 = edge_to_midpoint[idx.edge];
777 int v01 = edge_to_midpoint[Navigation::switch_edge(M_in, c2e, idx).edge];
778 e2v[lv] = v12;
779 assert(v12 != -1 && v01 != -1);
780 M_out.facets.create_triangle(v1, v12, v01);
781 }
782 M_out.facets.create_triangle(e2v[0], e2v[1], e2v[2]);
783 }
784}
vector< list< int > > adj
int V
int y
int x
Index get_index_from_face(const GEO::Mesh &M, const GEO::Attribute< GEO::index_t > &c2e, int f, int lv)
Index switch_edge(const GEO::Mesh &M, const GEO::Attribute< GEO::index_t > &c2e, Index idx)
Index next_around_face(const GEO::Mesh &M, const GEO::Attribute< GEO::index_t > &c2e, Index idx)
Index switch_vertex(const GEO::Mesh &M, Index idx)
void polar_split(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, std::vector< std::vector< int > > &OF, double t=0.5)
Split a polygon using polar refinement.
void catmul_clark_split(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, std::vector< std::vector< int > > &OF)
Split a polygon using polar refinement.
std::function< void(Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, std::vector< std::vector< int > > &OF)> SplitFunction
void no_split(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, std::vector< std::vector< int > > &OF)
Don't split polygons.
void refine_triangle_mesh(const GEO::Mesh &M_in, GEO::Mesh &M_out)
Refine a triangle mesh.
void refine_quad_mesh(const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IF, Eigen::MatrixXd &OV, Eigen::MatrixXi &OF)
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 edge_adjacency_graph(const Eigen::MatrixXi &Q, Eigen::MatrixXi &edge_index, std::vector< std::vector< int > > &adj, std::vector< std::pair< int, int > > *pairs_of_edges=nullptr, std::vector< std::pair< int, int > > *pairs_of_quads=nullptr, Eigen::MatrixXi *quad_index=nullptr)
GEO::vec3 facet_barycenter(const GEO::Mesh &M, GEO::index_t f)
Definition MeshUtils.cpp:64
bool instantiate_pattern(const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IF, const Eigen::MatrixXd &PV, const Eigen::MatrixXi &PF, Eigen::MatrixXd &OV, Eigen::MatrixXi &OF, Eigen::VectorXi *SF=nullptr, EvalParametersFunc evalFunc=nullptr, GetAdjacentLocalEdge getAdjLocalEdge=nullptr)
std::function< void(const Eigen::MatrixXd &, Eigen::MatrixXd &, int)> EvalParametersFunc
void refine_polygonal_mesh(const GEO::Mesh &M_in, GEO::Mesh &M_out, Polygons::SplitFunction split_func)
Refine a polygonal mesh.
std::function< std::tuple< int, int, bool >(int, int)> GetAdjacentLocalEdge
bool is_star_shaped(const Eigen::MatrixXd &IV, Eigen::RowVector3d &bary)
Determine whether a polygon is star-shaped or not.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42