PolyFEM
Loading...
Searching...
No Matches
MeshUtils.cpp
Go to the documentation of this file.
1
2#include "MeshUtils.hpp"
3
12
13#include <unordered_set>
14#include <set>
15#include <tuple>
16
17#include <igl/PI.h>
18#include <igl/read_triangle_mesh.h>
19
20#include <geogram/mesh/mesh_io.h>
21#include <geogram/mesh/mesh_geometry.h>
22#include <geogram/basic/geometry.h>
23#include <geogram/mesh/mesh_preprocessing.h>
24#include <geogram/mesh/mesh_topology.h>
25#include <geogram/mesh/mesh_geometry.h>
26#include <geogram/mesh/mesh_repair.h>
27#include <geogram/mesh/mesh_AABB.h>
28#include <geogram/voronoi/CVT.h>
29#include <geogram/basic/logger.h>
31
32using namespace polyfem::io;
33using namespace polyfem::utils;
34
35bool polyfem::mesh::is_planar(const GEO::Mesh &M, const double tol)
36{
37 if (M.vertices.dimension() == 2)
38 return true;
39
40 assert(M.vertices.dimension() == 3);
41 GEO::vec3 min_corner, max_corner;
42 GEO::get_bbox(M, &min_corner[0], &max_corner[0]);
43 const double diff = (max_corner[2] - min_corner[2]);
44
45 return fabs(diff) < tol;
46}
47
48GEO::vec3 polyfem::mesh::mesh_vertex(const GEO::Mesh &M, GEO::index_t v)
49{
50 using GEO::index_t;
51 GEO::vec3 p(0, 0, 0);
52 for (index_t d = 0; d < std::min(3u, (index_t)M.vertices.dimension()); ++d)
53 {
54 if (M.vertices.double_precision())
55 {
56 p[d] = M.vertices.point_ptr(v)[d];
57 }
58 else
59 {
60 p[d] = M.vertices.single_precision_point_ptr(v)[d];
61 }
62 }
63 return p;
64}
65
66// -----------------------------------------------------------------------------
67
68GEO::vec3 polyfem::mesh::facet_barycenter(const GEO::Mesh &M, GEO::index_t f)
69{
70 using GEO::index_t;
71 GEO::vec3 p(0, 0, 0);
72 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
73 {
74 p += polyfem::mesh::mesh_vertex(M, M.facets.vertex(f, lv));
75 }
76 return p / M.facets.nb_vertices(f);
77}
78
79// -----------------------------------------------------------------------------
80
81GEO::index_t polyfem::mesh::mesh_create_vertex(GEO::Mesh &M, const GEO::vec3 &p)
82{
83 using GEO::index_t;
84 auto v = M.vertices.create_vertex();
85 for (index_t d = 0; d < std::min(3u, (index_t)M.vertices.dimension()); ++d)
86 {
87 if (M.vertices.double_precision())
88 {
89 M.vertices.point_ptr(v)[d] = p[d];
90 }
91 else
92 {
93 M.vertices.single_precision_point_ptr(v)[d] = (float)p[d];
94 }
95 }
96 return v;
97}
98
100
101void polyfem::mesh::compute_element_tags(const GEO::Mesh &M, std::vector<ElementType> &element_tags)
102{
103 using GEO::index_t;
104
105 std::vector<ElementType> old_tags = element_tags;
106
107 element_tags.resize(M.facets.nb());
108
109 // Step 0: Compute boundary vertices as true boundary + vertices incident to a polygon
110 GEO::Attribute<bool> is_boundary_vertex(M.vertices.attributes(), "boundary_vertex");
111 std::vector<bool> is_interface_vertex(M.vertices.nb(), false);
112 {
113 for (index_t f = 0; f < M.facets.nb(); ++f)
114 {
115 if (M.facets.nb_vertices(f) != 4 || (!old_tags.empty() && old_tags[f] == ElementType::INTERIOR_POLYTOPE))
116 {
117 // Vertices incident to polygonal facets (triangles or > 4 vertices) are marked as interface
118 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
119 {
120 is_interface_vertex[M.facets.vertex(f, lv)] = true;
121 }
122 }
123 }
124 }
125
126 // Step 1: Determine which vertices are regular or not
127 //
128 // Interior vertices are regular if they are incident to exactly 4 quads
129 // Boundary vertices are regular if they are incident to at most 2 quads, and no other facets
130 std::vector<int> degree(M.vertices.nb(), 0);
131 std::vector<bool> is_regular_vertex(M.vertices.nb());
132 for (index_t f = 0; f < M.facets.nb(); ++f)
133 {
134 if (M.facets.nb_vertices(f) == 4)
135 {
136 // Only count incident quads for the degree
137 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
138 {
139 index_t v = M.facets.vertex(f, lv);
140 degree[v]++;
141 }
142 }
143 }
144 for (index_t v = 0; v < M.vertices.nb(); ++v)
145 {
146 // assert(degree[v] > 0); // We assume there are no isolated vertices here
147 if (is_boundary_vertex[v] || is_interface_vertex[v])
148 {
149 is_regular_vertex[v] = (degree[v] <= 2);
150 }
151 else
152 {
153 is_regular_vertex[v] = (degree[v] == 4);
154 }
155 }
156
157 // Step 2: Iterate over the facets and determine the type
158 for (index_t f = 0; f < M.facets.nb(); ++f)
159 {
160 assert(M.facets.nb_vertices(f) > 2);
161 if (!old_tags.empty() && old_tags[f] == ElementType::INTERIOR_POLYTOPE)
162 continue;
163
164 if (M.facets.nb_vertices(f) == 4)
165 {
166 // Quad facet
167
168 // a) Determine if it is on the mesh boundary
169 bool is_boundary_facet = false;
170 bool is_interface_facet = false;
171 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
172 {
173 if (is_boundary_vertex[M.facets.vertex(f, lv)])
174 {
175 is_boundary_facet = true;
176 }
177 if (is_interface_vertex[M.facets.vertex(f, lv)])
178 {
179 is_interface_facet = true;
180 }
181 }
182
183 // b) Determine if it is regular or not
184 if (is_boundary_facet || is_interface_facet)
185 {
186 // A boundary quad is regular iff all its vertices are incident to at most 2 other quads
187 // We assume that non-boundary vertices of a boundary quads are always regular
188 bool is_singular = false;
189 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
190 {
191 index_t v = M.facets.vertex(f, lv);
192 if (is_boundary_vertex[v] || is_interface_vertex[v])
193 {
194 if (!is_regular_vertex[v])
195 {
196 is_singular = true;
197 break;
198 }
199 }
200 else
201 {
202 if (!is_regular_vertex[v])
203 {
204 element_tags[f] = ElementType::UNDEFINED;
205 break;
206 }
207 }
208 }
209
210 if (is_interface_facet)
211 {
212 element_tags[f] = ElementType::INTERFACE_CUBE;
213 }
214 else if (is_singular)
215 {
216 element_tags[f] = ElementType::SIMPLE_SINGULAR_BOUNDARY_CUBE;
217 }
218 else
219 {
220 element_tags[f] = ElementType::REGULAR_BOUNDARY_CUBE;
221 }
222 }
223 else
224 {
225 // An interior quad is regular if all its vertices are singular
226 int nb_singulars = 0;
227 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
228 {
229 if (!is_regular_vertex[M.facets.vertex(f, lv)])
230 {
231 ++nb_singulars;
232 }
233 }
234
235 if (nb_singulars == 0)
236 {
237 element_tags[f] = ElementType::REGULAR_INTERIOR_CUBE;
238 }
239 else if (nb_singulars == 1)
240 {
241 element_tags[f] = ElementType::SIMPLE_SINGULAR_INTERIOR_CUBE;
242 }
243 else
244 {
245 element_tags[f] = ElementType::MULTI_SINGULAR_INTERIOR_CUBE;
246 }
247 }
248 }
249 else
250 {
251 // Polygonal facet
252
253 // Note: In this function, we consider triangles as polygonal facets
254 ElementType tag = ElementType::INTERIOR_POLYTOPE;
255 GEO::Attribute<bool> boundary_vertices(M.vertices.attributes(), "boundary_vertex");
256 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
257 {
258 if (boundary_vertices[M.facets.vertex(f, lv)])
259 {
260 tag = ElementType::BOUNDARY_POLYTOPE;
261 break;
262 }
263 }
264
265 element_tags[f] = tag;
266 }
267 }
268
269 // TODO what happens at the neighs?
270 // Override for simplices
271 for (index_t f = 0; f < M.facets.nb(); ++f)
272 {
273 if (M.facets.nb_vertices(f) == 3)
274 {
275 element_tags[f] = ElementType::SIMPLEX;
276 }
277 }
278}
279
281
282namespace
283{
284
285 // Signed area of a polygonal facet
286 double signed_area(const GEO::Mesh &M, GEO::index_t f)
287 {
288 using namespace GEO;
289 double result = 0;
290 index_t v0 = M.facet_corners.vertex(M.facets.corners_begin(f));
291 const vec3 &p0 = Geom::mesh_vertex(M, v0);
292 for (index_t c =
293 M.facets.corners_begin(f) + 1;
294 c + 1 < M.facets.corners_end(f); c++)
295 {
296 index_t v1 = M.facet_corners.vertex(c);
297 const vec3 &p1 = polyfem::mesh::mesh_vertex(M, v1);
298 index_t v2 = M.facet_corners.vertex(c + 1);
299 const vec3 &p2 = polyfem::mesh::mesh_vertex(M, v2);
300 result += Geom::triangle_signed_area(vec2(&p0[0]), vec2(&p1[0]), vec2(&p2[0]));
301 }
302 return result;
303 }
304
305} // anonymous namespace
306
308{
309 using namespace GEO;
310 vector<index_t> component;
311 index_t nb_components = get_connected_components(M, component);
312 vector<double> comp_signed_volume(nb_components, 0.0);
313 for (index_t f = 0; f < M.facets.nb(); ++f)
314 {
315 comp_signed_volume[component[f]] += signed_area(M, f);
316 }
317 for (index_t f = 0; f < M.facets.nb(); ++f)
318 {
319 if (comp_signed_volume[component[f]] < 0.0)
320 {
321 M.facets.flip(f);
322 }
323 }
324}
325
327
328void polyfem::mesh::reorder_mesh(Eigen::MatrixXd &V, Eigen::MatrixXi &F, const Eigen::VectorXi &C, Eigen::VectorXi &R)
329{
330 assert(V.rows() == C.size());
331 int num_colors = C.maxCoeff() + 1;
332 Eigen::VectorXi count(num_colors);
333 count.setZero();
334 for (int i = 0; i < C.size(); ++i)
335 {
336 ++count[C(i)];
337 }
338 R.resize(num_colors + 1);
339 R(0) = 0;
340 for (int c = 0; c < num_colors; ++c)
341 {
342 R(c + 1) = R(c) + count(c);
343 }
344 count.setZero();
345 Eigen::VectorXi remap(C.size());
346 for (int i = 0; i < C.size(); ++i)
347 {
348 remap[i] = R(C(i)) + count[C(i)];
349 ++count[C(i)];
350 }
351 // Remap vertices
352 Eigen::MatrixXd NV(V.rows(), V.cols());
353 for (int v = 0; v < V.rows(); ++v)
354 {
355 NV.row(remap(v)) = V.row(v);
356 }
357 V = NV;
358 // Remap face indices
359 for (int f = 0; f < F.rows(); ++f)
360 {
361 for (int lv = 0; lv < F.cols(); ++lv)
362 {
363 F(f, lv) = remap(F(f, lv));
364 }
365 }
366}
367
369
370namespace
371{
372
373 void compute_unsigned_distance_field(const GEO::Mesh &M,
374 const GEO::MeshFacetsAABB &aabb_tree, const Eigen::MatrixXd &P, Eigen::VectorXd &D)
375 {
376 assert(P.cols() == 3);
377 D.resize(P.rows());
378#pragma omp parallel for
379 for (int i = 0; i < P.rows(); ++i)
380 {
381 GEO::vec3 pos(P(i, 0), P(i, 1), P(i, 2));
382 double sq_dist = aabb_tree.squared_distance(pos);
383 D(i) = sq_dist;
384 }
385 }
386
387 // calculate twice signed area of triangle (0,0)-(x1,y1)-(x2,y2)
388 // return an SOS-determined sign (-1, +1, or 0 only if it's a truly degenerate triangle)
389 int orientation(
390 double x1, double y1, double x2, double y2, double &twice_signed_area)
391 {
392 twice_signed_area = y1 * x2 - x1 * y2;
393 if (twice_signed_area > 0)
394 return 1;
395 else if (twice_signed_area < 0)
396 return -1;
397 else if (y2 > y1)
398 return 1;
399 else if (y2 < y1)
400 return -1;
401 else if (x1 > x2)
402 return 1;
403 else if (x1 < x2)
404 return -1;
405 else
406 return 0; // only true when x1==x2 and y1==y2
407 }
408
409 // robust test of (x0,y0) in the triangle (x1,y1)-(x2,y2)-(x3,y3)
410 // if true is returned, the barycentric coordinates are set in a,b,c.
411 //
412 // Note: This function comes from SDFGen by Christopher Batty.
413 // https://github.com/christopherbatty/SDFGen/blob/master/makelevelset3.cpp
414 bool point_in_triangle_2d(
415 double x0, double y0, double x1, double y1,
416 double x2, double y2, double x3, double y3,
417 double &a, double &b, double &c)
418 {
419 x1 -= x0;
420 x2 -= x0;
421 x3 -= x0;
422 y1 -= y0;
423 y2 -= y0;
424 y3 -= y0;
425 int signa = orientation(x2, y2, x3, y3, a);
426 if (signa == 0)
427 return false;
428 int signb = orientation(x3, y3, x1, y1, b);
429 if (signb != signa)
430 return false;
431 int signc = orientation(x1, y1, x2, y2, c);
432 if (signc != signa)
433 return false;
434 double sum = a + b + c;
435 geo_assert(sum != 0); // if the SOS signs match and are nonzero, there's no way all of a, b, and c are zero.
436 a /= sum;
437 b /= sum;
438 c /= sum;
439 return true;
440 }
441
442 // -----------------------------------------------------------------------------
443
444 // \brief Computes the (approximate) orientation predicate in 2d.
445 // \details Computes the sign of the (approximate) signed volume of
446 // the triangle p0, p1, p2
447 // \param[in] p0 first vertex of the triangle
448 // \param[in] p1 second vertex of the triangle
449 // \param[in] p2 third vertex of the triangle
450 // \retval POSITIVE if the triangle is oriented positively
451 // \retval ZERO if the triangle is flat
452 // \retval NEGATIVE if the triangle is oriented negatively
453 // \todo check whether orientation is inverted as compared to
454 // Shewchuk's version.
455 // Taken from geogram/src/lib/geogram/delaunay/delaunay_2d.cpp
456 inline GEO::Sign orient_2d_inexact(GEO::vec2 p0, GEO::vec2 p1, GEO::vec2 p2)
457 {
458 double a11 = p1[0] - p0[0];
459 double a12 = p1[1] - p0[1];
460
461 double a21 = p2[0] - p0[0];
462 double a22 = p2[1] - p0[1];
463
464 double Delta = GEO::det2x2(
465 a11, a12,
466 a21, a22);
467
468 return GEO::geo_sgn(Delta);
469 }
470
471 // -----------------------------------------------------------------------------
472
483 template <int X = 0, int Y = 1, int Z = 2>
484 int intersect_ray_z(const GEO::Mesh &M, GEO::index_t f, const GEO::vec3 &q, double &z)
485 {
486 using namespace GEO;
487
488 index_t c = M.facets.corners_begin(f);
489 const vec3 &p1 = Geom::mesh_vertex(M, M.facet_corners.vertex(c++));
490 const vec3 &p2 = Geom::mesh_vertex(M, M.facet_corners.vertex(c++));
491 const vec3 &p3 = Geom::mesh_vertex(M, M.facet_corners.vertex(c));
492
493 double u, v, w;
494 if (point_in_triangle_2d(
495 q[X], q[Y], p1[X], p1[Y], p2[X], p2[Y], p3[X], p3[Y], u, v, w))
496 {
497 z = u * p1[Z] + v * p2[Z] + w * p3[Z];
498 auto sign = orient_2d_inexact(vec2(p1[X], p1[Y]), vec2(p2[X], p2[Y]), vec2(p3[X], p3[Y]));
499 switch (sign)
500 {
501 case GEO::POSITIVE:
502 return 1;
503 case GEO::NEGATIVE:
504 return -1;
505 case GEO::ZERO:
506 default:
507 return 0;
508 }
509 }
510
511 return 0;
512 }
513
514 // -----------------------------------------------------------------------------
515
516 void compute_sign(const GEO::Mesh &M, const GEO::MeshFacetsAABB &aabb_tree,
517 const Eigen::MatrixXd &P, Eigen::VectorXd &D)
518 {
519 assert(P.cols() == 3);
520 assert(D.size() == P.rows());
521
522 GEO::vec3 min_corner, max_corner;
523 GEO::get_bbox(M, &min_corner[0], &max_corner[0]);
524
525#pragma omp parallel for
526 for (int k = 0; k < P.rows(); ++k)
527 {
528 GEO::vec3 center(P(k, 0), P(k, 1), P(k, 2));
529
530 GEO::Box box;
531 box.xyz_min[0] = box.xyz_max[0] = center[0];
532 box.xyz_min[1] = box.xyz_max[1] = center[1];
533 box.xyz_min[2] = min_corner[2];
534 box.xyz_max[2] = max_corner[2];
535
536 std::vector<std::pair<double, int>> inter;
537 auto action = [&M, &inter, &center](GEO::index_t f) {
538 double z;
539 if (int s = intersect_ray_z(M, f, center, z))
540 {
541 inter.emplace_back(z, s);
542 }
543 };
544 aabb_tree.compute_bbox_facet_bbox_intersections(box, action);
545 std::sort(inter.begin(), inter.end());
546
547 std::vector<double> reduced;
548 for (int i = 0, s = 0; i < (int)inter.size(); ++i)
549 {
550 const int ds = inter[i].second;
551 s += ds;
552 if ((s == -1 && ds < 0) || (s == 0 && ds > 0))
553 {
554 reduced.push_back(inter[i].first);
555 }
556 }
557
558 int num_before = 0;
559 for (double z : reduced)
560 {
561 if (z < center[2])
562 {
563 ++num_before;
564 }
565 }
566 if (num_before % 2 == 1)
567 {
568 // Point is inside
569 D(k) *= -1.0;
570 }
571 }
572 }
573
574} // anonymous namespace
575
577
578void polyfem::mesh::to_geogram_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, GEO::Mesh &M)
579{
580 M.clear();
581 // Setup vertices
582 M.vertices.create_vertices((int)V.rows());
583 for (int i = 0; i < (int)M.vertices.nb(); ++i)
584 {
585 GEO::vec3 &p = M.vertices.point(i);
586 p[0] = V(i, 0);
587 p[1] = V(i, 1);
588 p[2] = V.cols() >= 3 ? V(i, 2) : 0;
589 }
590 // Setup faces
591 if (F.cols() == 3)
592 {
593 M.facets.create_triangles((int)F.rows());
594 }
595 else if (F.cols() == 4)
596 {
597 M.facets.create_quads((int)F.rows());
598 }
599 else
600 {
601 throw std::runtime_error("Mesh format not supported");
602 }
603 for (int c = 0; c < (int)M.facets.nb(); ++c)
604 {
605 for (int lv = 0; lv < F.cols(); ++lv)
606 {
607 M.facets.set_vertex(c, lv, F(c, lv));
608 }
609 }
610}
611
612// void polyfem::mesh::to_geogram_mesh_3d(const Eigen::MatrixXd &V, const Eigen::MatrixXi &C, GEO::Mesh &M) {
613// M.clear();
614// // Setup vertices
615// M.vertices.create_vertices((int) V.rows());
616// assert(V.cols() == 3);
617// for (int i = 0; i < (int) M.vertices.nb(); ++i) {
618// GEO::vec3 &p = M.vertices.point(i);
619// p[0] = V(i, 0);
620// p[1] = V(i, 1);
621// p[2] = V(i, 2);
622// }
623
624// if(C.cols() == 4)
625// M.cells.create_tets((int) C.rows());
626// else if(C.cols() == 8)
627// M.cells.create_hexes((int) C.rows());
628// else
629// assert(false);
630
631// for (int c = 0; c < (int) M.cells.nb(); ++c) {
632// for (int lv = 0; lv < C.cols(); ++lv) {
633// M.cells.set_vertex(c, lv, C(c, lv));
634// }
635// }
636// M.cells.connect();
637// // GEO::mesh_reorient(M);
638// }
639
640// -----------------------------------------------------------------------------
641
642void polyfem::mesh::from_geogram_mesh(const GEO::Mesh &M, Eigen::MatrixXd &V, Eigen::MatrixXi &F, Eigen::MatrixXi &T)
643{
644 V.resize(M.vertices.nb(), 3);
645 for (int i = 0; i < (int)M.vertices.nb(); ++i)
646 {
647 GEO::vec3 p = M.vertices.point(i);
648 V.row(i) << p[0], p[1], p[2];
649 }
650 assert(M.facets.are_simplices());
651 F.resize(M.facets.nb(), 3);
652 for (int c = 0; c < (int)M.facets.nb(); ++c)
653 {
654 for (int lv = 0; lv < 3; ++lv)
655 {
656 F(c, lv) = M.facets.vertex(c, lv);
657 }
658 }
659 assert(M.cells.are_simplices());
660 T.resize(M.cells.nb(), 4);
661 for (int c = 0; c < (int)M.cells.nb(); ++c)
662 {
663 for (int lv = 0; lv < 4; ++lv)
664 {
665 T(c, lv) = M.cells.vertex(c, lv);
666 }
667 }
668}
669
670// -----------------------------------------------------------------------------
671
672void polyfem::mesh::signed_squared_distances(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F,
673 const Eigen::MatrixXd &P, Eigen::VectorXd &D)
674{
675 GEO::Mesh M;
676 to_geogram_mesh(V, F, M);
677 GEO::MeshFacetsAABB aabb_tree(M);
678 compute_unsigned_distance_field(M, aabb_tree, P, D);
679 compute_sign(M, aabb_tree, P, D);
680}
681
682// -----------------------------------------------------------------------------
683
684double polyfem::mesh::signed_volume(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
685{
686 assert(F.cols() == 3);
687 assert(V.cols() == 3);
688 std::array<Eigen::RowVector3d, 4> t;
689 t[3] = Eigen::RowVector3d::Zero(V.cols());
690 double volume_total = 0;
691 for (int f = 0; f < F.rows(); ++f)
692 {
693 for (int lv = 0; lv < F.cols(); ++lv)
694 {
695 t[lv] = V.row(F(f, lv));
696 }
697 double vol = GEO::Geom::tetra_signed_volume(t[0].data(), t[1].data(), t[2].data(), t[3].data());
698 volume_total += vol;
699 }
700 return -volume_total;
701}
702
703// -----------------------------------------------------------------------------
704
705void polyfem::mesh::orient_closed_surface(const Eigen::MatrixXd &V, Eigen::MatrixXi &F, bool positive)
706{
707 if ((positive ? 1 : -1) * signed_volume(V, F) < 0)
708 {
709 for (int f = 0; f < F.rows(); ++f)
710 {
711 F.row(f) = F.row(f).reverse().eval();
712 }
713 }
714}
715
716// -----------------------------------------------------------------------------
717
718namespace
719{
720 struct EdgeInterfacePrimitive
721 {
723 Eigen::Vector2d from;
724 Eigen::Vector2d to;
725 };
726
727 struct FaceInterfacePrimitive
728 {
730 std::vector<Eigen::Vector3d> vertices;
731 };
732
733 bool same_point(const polyfem::RowVectorNd &a, const polyfem::RowVectorNd &b)
734 {
735 return a.size() == b.size() && (a - b).squaredNorm() <= 1e-24;
736 }
737
738 bool overlapping_segments(const EdgeInterfacePrimitive &first, const EdgeInterfacePrimitive &second)
739 {
740 const Eigen::Vector2d direction = first.to - first.from;
741 const double length = direction.norm();
742 const double second_length = (second.to - second.from).norm();
743 const double scale = std::max({1.0, length, second_length});
744 const double tolerance = 1e-12 * scale;
745 if (length <= tolerance || second_length <= tolerance)
746 return false;
747
748 auto cross = [](const Eigen::Vector2d &a, const Eigen::Vector2d &b) {
749 return a.x() * b.y() - a.y() * b.x();
750 };
751 if (std::abs(cross(direction, second.from - first.from)) > tolerance * length
752 || std::abs(cross(direction, second.to - first.from)) > tolerance * length)
753 return false;
754
755 const Eigen::Vector2d tangent = direction / length;
756 const double second_from = (second.from - first.from).dot(tangent);
757 const double second_to = (second.to - first.from).dot(tangent);
758 const double overlap_begin = std::max(0.0, std::min(second_from, second_to));
759 const double overlap_end = std::min(length, std::max(second_from, second_to));
760 return overlap_end - overlap_begin > tolerance;
761 }
762
763 Eigen::Vector3d face_normal(const FaceInterfacePrimitive &face)
764 {
765 Eigen::Vector3d normal = Eigen::Vector3d::Zero();
766 for (int i = 0; i < face.vertices.size(); ++i)
767 normal += face.vertices[i].cross(face.vertices[(i + 1) % face.vertices.size()]);
768 return normal;
769 }
770
771 Eigen::Vector2d project_point(const Eigen::Vector3d &point, const int dropped_axis)
772 {
773 if (dropped_axis == 0)
774 return {point.y(), point.z()};
775 if (dropped_axis == 1)
776 return {point.x(), point.z()};
777 return {point.x(), point.y()};
778 }
779
780 bool point_in_convex_polygon(
781 const Eigen::Vector2d &point,
782 const std::vector<Eigen::Vector2d> &polygon,
783 const double tolerance)
784 {
785 double sign = 0;
786 for (int i = 0; i < polygon.size(); ++i)
787 {
788 const Eigen::Vector2d edge = polygon[(i + 1) % polygon.size()] - polygon[i];
789 const Eigen::Vector2d offset = point - polygon[i];
790 const double cross = edge.x() * offset.y() - edge.y() * offset.x();
791 if (std::abs(cross) <= tolerance)
792 continue;
793 if (sign == 0)
794 sign = cross;
795 else if (sign * cross < 0)
796 return false;
797 }
798 return true;
799 }
800
801 bool overlapping_faces(const FaceInterfacePrimitive &first, const FaceInterfacePrimitive &second)
802 {
803 const Eigen::Vector3d first_normal = face_normal(first);
804 const Eigen::Vector3d second_normal = face_normal(second);
805 const double first_area_scale = first_normal.norm();
806 const double second_area_scale = second_normal.norm();
807 const double coordinate_scale = std::max({1.0,
808 first.vertices.front().norm(),
809 second.vertices.front().norm()});
810 const double tolerance = 1e-12 * coordinate_scale;
811 if (first_area_scale <= tolerance * tolerance || second_area_scale <= tolerance * tolerance)
812 return false;
813
814 const Eigen::Vector3d unit_normal = first_normal / first_area_scale;
815 if (unit_normal.cross(second_normal / second_area_scale).norm() > 1e-10)
816 return false;
817 for (const auto &point : second.vertices)
818 if (std::abs(unit_normal.dot(point - first.vertices.front())) > tolerance)
819 return false;
820
821 Eigen::Index dropped_axis;
822 unit_normal.cwiseAbs().maxCoeff(&dropped_axis);
823 std::vector<Eigen::Vector2d> first_polygon, second_polygon;
824 first_polygon.reserve(first.vertices.size());
825 second_polygon.reserve(second.vertices.size());
826 for (const auto &point : first.vertices)
827 first_polygon.push_back(project_point(point, dropped_axis));
828 for (const auto &point : second.vertices)
829 second_polygon.push_back(project_point(point, dropped_axis));
830
831 auto contains = [tolerance](const auto &container, const auto &contained) {
832 return std::all_of(contained.begin(), contained.end(), [&](const Eigen::Vector2d &point) {
833 return point_in_convex_polygon(point, container, tolerance);
834 });
835 };
836 return contains(first_polygon, second_polygon) || contains(second_polygon, first_polygon);
837 }
838} // namespace
839
840std::vector<std::pair<polyfem::mesh::Navigation::Index, polyfem::mesh::Navigation::Index>>
842{
843 using Index = Navigation::Index;
844 std::vector<EdgeInterfacePrimitive> first_edges, second_edges;
845 auto collect = [](const Mesh2D &mesh, auto &edges) {
846 for (int f = 0; f < mesh.n_faces(); ++f)
847 {
848 auto index = mesh.get_index_from_face(f);
849 for (int le = 0; le < mesh.n_face_vertices(f); ++le)
850 {
851 if (mesh.is_boundary_edge(index.edge))
852 {
853 const auto opposite = mesh.switch_vertex(index);
854 edges.push_back({index,
855 mesh.point(index.vertex).head<2>(),
856 mesh.point(opposite.vertex).head<2>()});
857 }
858 index = mesh.next_around_face(index);
859 }
860 }
861 };
862 collect(first, first_edges);
863 collect(second, second_edges);
864
865 std::vector<std::pair<Index, Index>> result;
866 std::set<std::tuple<int, int, int, int, int, int>> visited_pairs;
867 for (const auto &lhs : first_edges)
868 {
869 for (const auto &rhs : second_edges)
870 {
871 if (!overlapping_segments(lhs, rhs))
872 continue;
873 Index second_index = rhs.index;
874 if ((lhs.to - lhs.from).dot(rhs.to - rhs.from) > 0)
875 second_index = second.switch_vertex(second_index);
876 const auto key = std::make_tuple(
877 lhs.index.face, lhs.index.edge, lhs.index.vertex,
878 second_index.face, second_index.edge, second_index.vertex);
879 if (visited_pairs.insert(key).second)
880 result.emplace_back(lhs.index, second_index);
881 }
882 }
883 return result;
884}
885
886std::vector<std::pair<polyfem::mesh::Navigation3D::Index, polyfem::mesh::Navigation3D::Index>>
888{
889 using Index = Navigation3D::Index;
890 std::vector<FaceInterfacePrimitive> first_faces, second_faces;
891 auto collect = [](const Mesh3D &mesh, auto &faces) {
892 std::unordered_set<int> visited_faces;
893 for (int c = 0; c < mesh.n_cells(); ++c)
894 {
895 for (int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
896 {
897 const int face = mesh.cell_face(c, lf);
898 if (!mesh.is_boundary_face(face) || !visited_faces.insert(face).second)
899 continue;
900 auto index = mesh.get_index_from_element(c, lf, 0);
901 std::vector<Eigen::Vector3d> vertices(mesh.n_face_vertices(face));
902 for (int lv = 0; lv < vertices.size(); ++lv)
903 vertices[lv] = mesh.point(mesh.face_vertex(face, lv)).head<3>();
904 faces.push_back({index, std::move(vertices)});
905 }
906 }
907 };
908 collect(first, first_faces);
909 collect(second, second_faces);
910
911 std::vector<std::pair<Index, Index>> result;
912 for (const auto &lhs : first_faces)
913 {
914 for (const auto &rhs : second_faces)
915 {
916 if (!overlapping_faces(lhs, rhs))
917 continue;
918 Index second_index = rhs.index;
919 for (int lv = 0; lv < second.n_face_vertices(second_index.face); ++lv)
920 {
921 if (same_point(first.point(lhs.index.vertex), second.point(second_index.vertex)))
922 break;
923 second_index = second.next_around_face(second_index);
924 }
925 result.emplace_back(lhs.index, second_index);
926 }
927 }
928 return result;
929}
930
931void polyfem::mesh::extract_polyhedra(const Mesh3D &mesh, std::vector<std::unique_ptr<GEO::Mesh>> &polys, bool triangulated)
932{
933 std::vector<int> vertex_g2l(mesh.n_vertices() + mesh.n_faces(), -1);
934 std::vector<int> vertex_l2g;
935 for (int c = 0; c < mesh.n_cells(); ++c)
936 {
937 if (!mesh.is_polytope(c))
938 {
939 continue;
940 }
941 auto poly = std::make_unique<GEO::Mesh>();
942 int nv = mesh.n_cell_vertices(c);
943 int nf = mesh.n_cell_faces(c);
944 poly->vertices.create_vertices((triangulated ? nv + nf : nv) + 1);
945 vertex_l2g.clear();
946 vertex_l2g.reserve(nv);
947 for (int lf = 0; lf < nf; ++lf)
948 {
949 GEO::vector<GEO::index_t> facet_vertices;
950 auto index = mesh.get_index_from_element(c, lf, 0);
951 for (int lv = 0; lv < mesh.n_face_vertices(index.face); ++lv)
952 {
953 Eigen::RowVector3d p = mesh.point(index.vertex);
954 if (vertex_g2l[index.vertex] < 0)
955 {
956 vertex_g2l[index.vertex] = vertex_l2g.size();
957 vertex_l2g.push_back(index.vertex);
958 }
959 int v1 = vertex_g2l[index.vertex];
960 facet_vertices.push_back(v1);
961 poly->vertices.point(v1) = GEO::vec3(p.data());
962 index = mesh.next_around_face(index);
963 }
964 if (triangulated)
965 {
966 GEO::vec3 p(0, 0, 0);
967 for (GEO::index_t lv = 0; lv < facet_vertices.size(); ++lv)
968 {
969 p += poly->vertices.point(facet_vertices[lv]);
970 }
971 p /= facet_vertices.size();
972 int v0 = vertex_l2g.size();
973 vertex_l2g.push_back(0);
974 poly->vertices.point(v0) = p;
975 for (GEO::index_t lv = 0; lv < facet_vertices.size(); ++lv)
976 {
977 int v1 = facet_vertices[lv];
978 int v2 = facet_vertices[(lv + 1) % facet_vertices.size()];
979 poly->facets.create_triangle(v0, v1, v2);
980 }
981 }
982 else
983 {
984 poly->facets.create_polygon(facet_vertices);
985 }
986 }
987 {
988 Eigen::RowVector3d p = mesh.kernel(c);
989 poly->vertices.point(nv) = GEO::vec3(p.data());
990 }
991 assert(vertex_l2g.size() == size_t(triangulated ? nv + nf : nv));
992
993 for (int v : vertex_l2g)
994 {
995 vertex_g2l[v] = -1;
996 }
997
998 poly->facets.compute_borders();
999 poly->facets.connect();
1000
1001 polys.emplace_back(std::move(poly));
1002 }
1003}
1004
1005// -----------------------------------------------------------------------------
1006
1007// In Geogram, local vertices of a hex are numbered as follows:
1008//
1009// v5────v7
1010// ╱┆ ╱│
1011// v1─┼──v3 │
1012// │v4┄┄┄┼v6
1013// │╱ │╱
1014// v0────v2
1015//
1016// However, `get_ordered_vertices_from_hex()` retrieves the local vertices in
1017// this order:
1018//
1019// v7────v6
1020// ╱┆ ╱│
1021// v4─┼──v5 │
1022// │v3┄┄┄┼v2
1023// │╱ │╱
1024// v0────v1
1025//
1026
1027void polyfem::mesh::to_geogram_mesh(const Mesh3D &mesh, GEO::Mesh &M)
1028{
1029 M.clear();
1030 // Convert vertices
1031 M.vertices.create_vertices((int)mesh.n_vertices());
1032 for (int i = 0; i < (int)M.vertices.nb(); ++i)
1033 {
1034 auto pt = mesh.point(i);
1035 GEO::vec3 &p = M.vertices.point(i);
1036 p[0] = pt[0];
1037 p[1] = pt[1];
1038 p[2] = pt[2];
1039 }
1040 // Convert faces
1041 for (int f = 0, lf = 0; f < mesh.n_faces(); ++f)
1042 {
1043 if (mesh.is_boundary_face(f))
1044 {
1045 int nv = mesh.n_face_vertices(f);
1046 M.facets.create_polygon(nv);
1047 for (int lv = 0; lv < nv; ++lv)
1048 {
1049 M.facets.set_vertex(lf, lv, mesh.face_vertex(f, lv));
1050 }
1051 ++lf;
1052 }
1053 }
1054 // Convert cells
1055 typedef std::array<int, 8> Vector8i;
1056 Vector8i g2p = {{0, 4, 1, 5, 3, 7, 2, 6}};
1057 for (int c = 0; c < mesh.n_cells(); ++c)
1058 {
1059 if (mesh.is_cube(c))
1060 {
1061 Vector8i lvp = mesh.get_ordered_vertices_from_hex(c);
1062 Vector8i lvg;
1063 for (size_t k = 0; k < 8; ++k)
1064 {
1065 lvg[k] = lvp[g2p[k]];
1066 }
1067 std::reverse(lvg.begin(), lvg.end());
1068 M.cells.create_hex(
1069 lvg[0], lvg[1], lvg[2], lvg[3],
1070 lvg[4], lvg[5], lvg[6], lvg[7]);
1071 }
1072 else
1073 {
1074 // TODO: Support conversion of tets as well!
1075 }
1076 }
1077 M.facets.connect();
1078 M.cells.connect();
1079 // M.cells.compute_borders();
1080 GEO::mesh_reorient(M);
1081}
1082
1084
1085void polyfem::mesh::sample_surface(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, int num_samples,
1086 Eigen::MatrixXd &P, Eigen::MatrixXd *N, int num_lloyd, int num_newton)
1087{
1088 assert(num_samples > 3);
1089 GEO::Mesh M;
1090 to_geogram_mesh(V, F, M);
1091 GEO::CentroidalVoronoiTesselation CVT(&M);
1092 // GEO::mesh_save(M, "foo.obj");
1093 bool was_quiet = GEO::Logger::instance()->is_quiet();
1094 GEO::Logger::instance()->set_quiet(true);
1095 CVT.compute_initial_sampling(num_samples);
1096 GEO::Logger::instance()->set_quiet(was_quiet);
1097
1098 if (num_lloyd > 0)
1099 {
1100 CVT.Lloyd_iterations(num_lloyd);
1101 }
1102
1103 if (num_newton > 0)
1104 {
1105 CVT.Newton_iterations(num_newton);
1106 }
1107
1108 P.resize(3, num_samples);
1109 std::copy_n(CVT.embedding(0), 3 * num_samples, P.data());
1110 P.transposeInPlace();
1111
1112 if (N)
1113 {
1114 GEO::MeshFacetsAABB aabb(M);
1115 N->resizeLike(P);
1116 for (int i = 0; i < num_samples; ++i)
1117 {
1118 GEO::vec3 p(P(i, 0), P(i, 1), P(i, 2));
1119 GEO::vec3 nearest_point;
1120 double sq_dist;
1121 auto f = aabb.nearest_facet(p, nearest_point, sq_dist);
1122 GEO::vec3 n = normalize(GEO::Geom::mesh_facet_normal(M, f));
1123 N->row(i) << n[0], n[1], n[2];
1124 }
1125 }
1126}
1127
1129
1130namespace
1131{
1132
1133 bool approx_aligned(const double *a_, const double *b_, const double *p_, const double *q_, double tol = 1e-6)
1134 {
1135 using namespace GEO;
1136 vec3 a(a_), b(b_), p(p_), q(q_);
1137 double da = std::sqrt(Geom::point_segment_squared_distance(a, p, q));
1138 double db = std::sqrt(Geom::point_segment_squared_distance(b, p, q));
1139 double cos_theta = Geom::cos_angle(b - a, p - q);
1140 return (da < tol && db < tol && std::abs(std::abs(cos_theta) - 1.0) < tol);
1141 }
1142
1143} // anonymous namespace
1144
1145// -----------------------------------------------------------------------------
1146
1147void polyfem::mesh::extract_parent_edges(const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IE,
1148 const Eigen::MatrixXd &BV, const Eigen::MatrixXi &BE, Eigen::MatrixXi &OE)
1149{
1150 assert(IV.cols() == 2 || IV.cols() == 3);
1151 assert(BV.cols() == 2 || BV.cols() == 3);
1152 typedef std::pair<int, int> Edge;
1153 std::vector<Edge> selected;
1154 for (int e1 = 0; e1 < IE.rows(); ++e1)
1155 {
1156 Eigen::RowVector3d a;
1157 a.setZero();
1158 a.head(IV.cols()) = IV.row(IE(e1, 0));
1159 Eigen::RowVector3d b;
1160 b.setZero();
1161 b.head(IV.cols()) = IV.row(IE(e1, 1));
1162 for (int e2 = 0; e2 < BE.rows(); ++e2)
1163 {
1164 Eigen::RowVector3d p;
1165 p.setZero();
1166 p.head(BV.cols()) = BV.row(BE(e2, 0));
1167 Eigen::RowVector3d q;
1168 q.setZero();
1169 q.head(BV.cols()) = BV.row(BE(e2, 1));
1170 if (approx_aligned(a.data(), b.data(), p.data(), q.data()))
1171 {
1172 selected.emplace_back(IE(e1, 0), IE(e1, 1));
1173 break;
1174 }
1175 }
1176 }
1177
1178 OE.resize(selected.size(), 2);
1179 for (int e = 0; e < OE.rows(); ++e)
1180 {
1181 OE.row(e) << selected[e].first, selected[e].second;
1182 }
1183}
1184
1186
1188 const Eigen::MatrixXd &vertices,
1189 const Eigen::MatrixXi &codim_edges,
1190 const Eigen::MatrixXi &faces,
1191 Eigen::VectorXi &codim_vertices)
1192{
1193 std::vector<bool> is_vertex_codim(vertices.rows(), true);
1194 for (int i = 0; i < codim_edges.rows(); i++)
1195 {
1196 for (int j = 0; j < codim_edges.cols(); j++)
1197 {
1198 is_vertex_codim[codim_edges(i, j)] = false;
1199 }
1200 }
1201 for (int i = 0; i < faces.rows(); i++)
1202 {
1203 for (int j = 0; j < faces.cols(); j++)
1204 {
1205 is_vertex_codim[faces(i, j)] = false;
1206 }
1207 }
1208 const auto n_codim_vertices = std::count(is_vertex_codim.begin(), is_vertex_codim.end(), true);
1209 codim_vertices.resize(n_codim_vertices);
1210 for (int i = 0, ci = 0; i < vertices.rows(); i++)
1211 {
1212 if (is_vertex_codim[i])
1213 {
1214 codim_vertices[ci++] = i;
1215 }
1216 }
1217}
1218
1220 const Eigen::MatrixXi &tets,
1221 Eigen::MatrixXi &faces)
1222{
1223 std::unordered_set<Eigen::Vector3i, HashMatrix> tri_to_tet(4 * tets.rows());
1224 for (int i = 0; i < tets.rows(); i++)
1225 {
1226 tri_to_tet.emplace(tets(i, 0), tets(i, 2), tets(i, 1));
1227 tri_to_tet.emplace(tets(i, 0), tets(i, 3), tets(i, 2));
1228 tri_to_tet.emplace(tets(i, 0), tets(i, 1), tets(i, 3));
1229 tri_to_tet.emplace(tets(i, 1), tets(i, 2), tets(i, 3));
1230 }
1231
1232 std::vector<Eigen::RowVector3i> faces_vector;
1233 for (const auto &tri : tri_to_tet)
1234 {
1235 // find dual triangle with reversed indices:
1236 bool is_surface_triangle =
1237 tri_to_tet.find(Eigen::Vector3i(tri[2], tri[1], tri[0])) == tri_to_tet.end()
1238 && tri_to_tet.find(Eigen::Vector3i(tri[1], tri[0], tri[2])) == tri_to_tet.end()
1239 && tri_to_tet.find(Eigen::Vector3i(tri[0], tri[2], tri[1])) == tri_to_tet.end();
1240 if (is_surface_triangle)
1241 {
1242 faces_vector.emplace_back(tri[0], tri[1], tri[2]);
1243 }
1244 }
1245
1246 faces.resize(faces_vector.size(), 3);
1247 for (int i = 0; i < faces.rows(); i++)
1248 {
1249 faces.row(i) = faces_vector[i];
1250 }
1251}
1252
1254 const Eigen::MatrixXd &vertices,
1255 const Eigen::MatrixXi &tets,
1256 Eigen::MatrixXd &surface_vertices,
1257 Eigen::MatrixXi &tris)
1258{
1259 Eigen::MatrixXi full_tris;
1260 find_triangle_surface_from_tets(tets, full_tris);
1261
1262 std::unordered_map<int, int> full_to_surface;
1263 std::vector<size_t> surface_to_full;
1264 for (int i = 0; i < full_tris.rows(); i++)
1265 {
1266 for (int j = 0; j < full_tris.cols(); j++)
1267 {
1268 if (full_to_surface.find(full_tris(i, j)) == full_to_surface.end())
1269 {
1270 full_to_surface[full_tris(i, j)] = surface_to_full.size();
1271 surface_to_full.push_back(full_tris(i, j));
1272 }
1273 }
1274 }
1275
1276 surface_vertices.resize(surface_to_full.size(), 3);
1277 for (int i = 0; i < surface_to_full.size(); i++)
1278 {
1279 surface_vertices.row(i) = vertices.row(surface_to_full[i]);
1280 }
1281
1282 tris.resize(full_tris.rows(), full_tris.cols());
1283 for (int i = 0; i < tris.rows(); i++)
1284 {
1285 for (int j = 0; j < tris.cols(); j++)
1286 {
1287 tris(i, j) = full_to_surface[full_tris(i, j)];
1288 }
1289 }
1290}
1291
1293 const std::string &mesh_path,
1294 Eigen::MatrixXd &vertices,
1295 Eigen::VectorXi &codim_vertices,
1296 Eigen::MatrixXi &codim_edges,
1297 Eigen::MatrixXi &faces)
1298{
1299 vertices.resize(0, 0);
1300 codim_vertices.resize(0);
1301 codim_edges.resize(0, 0);
1302 faces.resize(0, 0);
1303
1304 std::string lowername = mesh_path;
1305 std::transform(
1306 lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
1307
1308 if (StringUtils::endswith(lowername, ".msh"))
1309 {
1310 Eigen::MatrixXi cells;
1311 std::vector<std::vector<int>> elements;
1312 std::vector<std::vector<double>> weights;
1313 std::vector<int> body_ids;
1314 if (!MshReader::load(mesh_path, vertices, cells, elements, weights, body_ids))
1315 {
1316 logger().error("Unable to load mesh: {}", mesh_path);
1317 return false;
1318 }
1319
1320 if (cells.cols() == 1)
1321 codim_vertices = cells;
1322 else if (cells.cols() == 2)
1323 codim_edges = cells;
1324 else if (cells.cols() == 3)
1325 faces = cells;
1326 else if (cells.cols() == 4)
1327 {
1328 if (vertices.cols() == 2)
1329 {
1330 logger().error("read_surface_mesh not implemented for 2D quad meshes");
1331 return false;
1332 }
1333 else
1334 {
1335 // TODO: how to distinguish between 3D tet mesh and 3D surface quad mesh?
1336 assert(vertices.cols() == 3);
1337 Eigen::MatrixXd surface_vertices;
1338 extract_triangle_surface_from_tets(vertices, cells, surface_vertices, faces);
1339 vertices = surface_vertices;
1340 }
1341 }
1342 else
1343 {
1344 logger().error("read_surface_mesh not implemented for hexahedral and polygonal/polyhedral meshes");
1345 return false;
1346 }
1347 }
1348 else if (StringUtils::endswith(lowername, ".obj")) // Use specialized OBJ reader function with polyline support
1349 {
1350 if (!OBJReader::read(mesh_path, vertices, codim_edges, faces))
1351 {
1352 logger().error("Unable to load mesh: {}", mesh_path);
1353 return false;
1354 }
1355 }
1356 else if (!igl::read_triangle_mesh(mesh_path, vertices, faces))
1357 {
1358 GEO::Mesh mesh;
1359 if (!GEO::mesh_load(mesh_path, mesh))
1360 {
1361 logger().error("Unable to load mesh: {}", mesh_path);
1362 return false;
1363 }
1364
1365 int dim = is_planar(mesh) ? 2 : 3;
1366 vertices.resize(mesh.vertices.nb(), dim);
1367 for (int vi = 0; vi < mesh.vertices.nb(); vi++)
1368 {
1369 const auto &v = mesh.vertices.point(vi);
1370 for (int vj = 0; vj < dim; vj++)
1371 {
1372 vertices(vi, vj) = v[vj];
1373 }
1374 }
1375
1376 // TODO: Check that this works even for a volumetric mesh
1377 assert(mesh.facets.nb());
1378 int face_cols = mesh.facets.nb_vertices(0);
1379 faces.resize(mesh.facets.nb(), face_cols);
1380 for (int fi = 0; fi < mesh.facets.nb(); fi++)
1381 {
1382 assert(face_cols == mesh.facets.nb_vertices(fi));
1383 for (int fj = 0; fj < mesh.facets.nb_vertices(fi); fj++)
1384 {
1385 faces(fi, fj) = mesh.facets.vertex(fi, fj);
1386 }
1387 }
1388 }
1389
1390 find_codim_vertices(vertices, codim_edges, faces, codim_vertices);
1391
1392 return true;
1393}
1394
1395int polyfem::mesh::count_faces(const int dim, const Eigen::MatrixXi &cells)
1396{
1397 std::unordered_set<std::vector<int>, HashVector> boundaries;
1398
1399 auto insert = [&](std::vector<int> v) {
1400 std::sort(v.begin(), v.end());
1401 boundaries.insert(v);
1402 };
1403
1404 for (int i = 0; i < cells.rows(); i++)
1405 {
1406 const auto &cell = cells.row(i);
1407 if (cells.cols() == 3) // triangle
1408 {
1409 insert({{cell(0), cell(1)}});
1410 insert({{cell(1), cell(2)}});
1411 insert({{cell(2), cell(0)}});
1412 }
1413 else if (cells.cols() == 4 && dim == 2) // quadralateral
1414 {
1415 insert({{cell(0), cell(1)}});
1416 insert({{cell(1), cell(2)}});
1417 insert({{cell(2), cell(3)}});
1418 insert({{cell(3), cell(0)}});
1419 }
1420 else if (cells.cols() == 4 && dim == 3) // tetrahedron
1421 {
1422 insert({{cell(0), cell(2), cell(1)}});
1423 insert({{cell(0), cell(3), cell(2)}});
1424 insert({{cell(0), cell(1), cell(3)}});
1425 insert({{cell(1), cell(2), cell(3)}});
1426 }
1427 else if (cells.cols() == 8) // hexahedron
1428 {
1429 insert({{cell(0), cell(1), cell(2), cell(3)}});
1430 insert({{cell(1), cell(5), cell(6), cell(3)}});
1431 insert({{cell(5), cell(4), cell(7), cell(6)}});
1432 insert({{cell(0), cell(4), cell(7), cell(3)}});
1433 insert({{cell(0), cell(4), cell(5), cell(1)}});
1434 insert({{cell(2), cell(6), cell(7), cell(3)}});
1435 }
1436 else
1437 {
1438 throw std::runtime_error("count_boundary_elements not implemented for polygons");
1439 }
1440 }
1441
1442 return boundaries.size();
1443}
1444
1446{
1447 using namespace GEO;
1448 typedef std::pair<index_t, index_t> Edge;
1449
1450 if (M.edges.nb() > 0)
1451 return;
1452
1453 M.facets.connect();
1454 M.cells.connect();
1455 if (M.cells.nb() != 0 && M.facets.nb() == 0)
1456 {
1457 M.cells.compute_borders();
1458 }
1459
1460 // Compute a list of all the edges, and store edge index as a corner attribute
1461 std::vector<std::pair<Edge, index_t>> e2c; // edge to corner id
1462 for (index_t f = 0; f < M.facets.nb(); ++f)
1463 {
1464 for (index_t c = M.facets.corners_begin(f); c < M.facets.corners_end(f); ++c)
1465 {
1466 index_t v = M.facet_corners.vertex(c);
1467 index_t c2 = M.facets.next_corner_around_facet(f, c);
1468 index_t v2 = M.facet_corners.vertex(c2);
1469 e2c.emplace_back(std::make_pair(std::min(v, v2), std::max(v, v2)), c);
1470 }
1471 }
1472 std::sort(e2c.begin(), e2c.end());
1473
1474 // Assign unique id to edges
1475 M.edges.clear();
1476 Edge prev_e(-1, -1);
1477 for (const auto &kv : e2c)
1478 {
1479 Edge e = kv.first;
1480 if (e != prev_e)
1481 {
1482 M.edges.create_edge(e.first, e.second);
1483 prev_e = e;
1484 }
1485 }
1486}
int V
QuadratureVector da
Definition Assembler.cpp:26
void find_codim_vertices(const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &codim_edges, const Eigen::MatrixXi &faces, Eigen::VectorXi &codim_vertices)
void find_triangle_surface_from_tets(const Eigen::MatrixXi &tets, Eigen::MatrixXi &faces)
Eigen::RowVectorXd point
Eigen::MatrixXd F
std::vector< std::pair< int, double > > weights
std::vector< Eigen::VectorXi > faces
int z
static bool load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector< std::vector< int > > &elements, std::vector< std::vector< double > > &weights, std::vector< int > &body_ids)
Definition MshReader.cpp:43
static bool read(const std::string obj_file_name, std::vector< std::vector< double > > &V, std::vector< std::vector< double > > &TC, std::vector< std::vector< double > > &N, std::vector< std::vector< int > > &F, std::vector< std::vector< int > > &FTC, std::vector< std::vector< int > > &FN, std::vector< std::vector< int > > &L)
Read a mesh from an ascii obj file.
Definition OBJReader.cpp:32
Navigation::Index next_around_face(Navigation::Index idx) const
Definition Mesh2D.hpp:61
virtual Navigation::Index switch_vertex(Navigation::Index idx) const =0
virtual Navigation::Index get_index_from_face(int f, int lv=0) const =0
virtual Navigation3D::Index get_index_from_element(int hi, int lf, int lv) const =0
virtual RowVectorNd kernel(const int cell_id) const =0
std::array< int, 8 > get_ordered_vertices_from_hex(const int element_index) const
Definition Mesh3D.cpp:504
virtual int n_cell_faces(const int c_id) const =0
virtual int cell_face(const int c_id, const int lf_id) const =0
virtual Navigation3D::Index next_around_face(Navigation3D::Index idx) const =0
virtual int n_vertices() const =0
number of vertices
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Definition Mesh.cpp:450
bool is_cube(const int el_id) const
checks if element is cube compatible
Definition Mesh.cpp:437
virtual RowVectorNd point(const int global_index) const =0
point coordinates
virtual bool is_boundary_face(const int face_global_id) const =0
is face boundary
virtual bool is_boundary_edge(const int edge_global_id) const =0
is edge boundary
virtual int n_cells() const =0
number of cells
virtual int n_faces() const =0
number of faces
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
virtual int n_cell_vertices(const int c_id) const =0
number of vertices of a cell
virtual int face_vertex(const int f_id, const int lv_id) const =0
id of the face vertex
Definition State.hpp:17
M
Definition eigs.py:94
list vertices
Definition p_bases.py:238
int norm
Definition p_bases.py:265
Eigen::Matrix< double, dim, 1 > cross(const Eigen::Matrix< double, dim, 1 > &x, const Eigen::Matrix< double, dim, 1 > &y)
Eigen::ArrayXd P(const int m, const int p, const Eigen::ArrayXd &z)
Definition p_n_bases.cpp:42
std::vector< std::pair< Navigation::Index, Navigation::Index > > compute_mesh_interface(const Mesh2D &first, const Mesh2D &second)
Pair coincident boundary edges, including nonconforming leader/follower edges.
bool is_planar(const GEO::Mesh &M, const double tol=1e-5)
Determine if the given mesh is planar (2D or tiny z-range).
Definition MeshUtils.cpp:35
void sample_surface(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, int num_samples, Eigen::MatrixXd &P, Eigen::MatrixXd *N=nullptr, int num_lloyd=10, int num_newton=10)
Samples points on a surface.
void orient_closed_surface(const Eigen::MatrixXd &V, Eigen::MatrixXi &F, bool positive=true)
Orient a triangulated surface to have positive volume.
double signed_volume(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
Compute the signed volume of a surface mesh.
void reorder_mesh(Eigen::MatrixXd &V, Eigen::MatrixXi &F, const Eigen::VectorXi &C, Eigen::VectorXi &R)
Reorder vertices of a mesh using color tags, so that vertices are ordered by increasing colors.
void orient_normals_2d(GEO::Mesh &M)
Orient facets of a 2D mesh so that each connected component has positive volume.
void extract_parent_edges(const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IE, const Eigen::MatrixXd &BV, const Eigen::MatrixXi &BE, Eigen::MatrixXi &OE)
Extract a set of edges that are overlap with a set given set of parent edges, using vertices position...
ElementType
Type of Element, check [Poly-Spline Finite Element Method] for a complete description.
Definition Mesh.hpp:31
void extract_triangle_surface_from_tets(const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &tets, Eigen::MatrixXd &surface_vertices, Eigen::MatrixXi &tris)
Extract triangular surface from a tetmesh.
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:48
GEO::index_t mesh_create_vertex(GEO::Mesh &M, const GEO::vec3 &p)
Definition MeshUtils.cpp:81
void to_geogram_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, GEO::Mesh &M)
Converts a triangle mesh to a Geogram mesh.
void signed_squared_distances(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::MatrixXd &P, Eigen::VectorXd &D)
Computes the signed squared distance from a list of points to a triangle mesh.
GEO::vec3 facet_barycenter(const GEO::Mesh &M, GEO::index_t f)
Definition MeshUtils.cpp:68
void from_geogram_mesh(const GEO::Mesh &M, Eigen::MatrixXd &V, Eigen::MatrixXi &F, Eigen::MatrixXi &T)
Extract simplices from a Geogram mesh.
void generate_edges(GEO::Mesh &M)
assing edges to M
bool read_surface_mesh(const std::string &mesh_path, Eigen::MatrixXd &vertices, Eigen::VectorXi &codim_vertices, Eigen::MatrixXi &codim_edges, Eigen::MatrixXi &faces)
read a surface mesh
void extract_polyhedra(const Mesh3D &mesh, std::vector< std::unique_ptr< GEO::Mesh > > &polys, bool triangulated=false)
Extract polyhedra from a 3D volumetric mesh.
int count_faces(const int dim, const Eigen::MatrixXi &cells)
Count the number of boundary elements (triangles for tetmesh and edges for triangle mesh)
void compute_element_tags(const GEO::Mesh &M, std::vector< ElementType > &element_tags)
Compute the type of each facet in a surface mesh.
bool endswith(const std::string &str, const std::string &suffix)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13