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