11#include <unordered_set>
14#include <igl/read_triangle_mesh.h>
16#include <geogram/mesh/mesh_io.h>
17#include <geogram/mesh/mesh_geometry.h>
18#include <geogram/basic/geometry.h>
19#include <geogram/mesh/mesh_preprocessing.h>
20#include <geogram/mesh/mesh_topology.h>
21#include <geogram/mesh/mesh_geometry.h>
22#include <geogram/mesh/mesh_repair.h>
23#include <geogram/mesh/mesh_AABB.h>
24#include <geogram/voronoi/CVT.h>
25#include <geogram/basic/logger.h>
33 if (M.vertices.dimension() == 2)
36 assert(M.vertices.dimension() == 3);
37 GEO::vec3 min_corner, max_corner;
38 GEO::get_bbox(M, &min_corner[0], &max_corner[0]);
39 const double diff = (max_corner[2] - min_corner[2]);
41 return fabs(diff) < tol;
48 for (index_t d = 0; d < std::min(3u, (index_t)M.vertices.dimension()); ++d)
50 if (M.vertices.double_precision())
52 p[d] = M.vertices.point_ptr(v)[d];
56 p[d] = M.vertices.single_precision_point_ptr(v)[d];
68 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
72 return p / M.facets.nb_vertices(f);
80 auto v = M.vertices.create_vertex();
81 for (index_t d = 0; d < std::min(3u, (index_t)M.vertices.dimension()); ++d)
83 if (M.vertices.double_precision())
85 M.vertices.point_ptr(v)[d] = p[d];
89 M.vertices.single_precision_point_ptr(v)[d] = (float)p[d];
101 std::vector<ElementType> old_tags = element_tags;
103 element_tags.resize(M.facets.nb());
106 GEO::Attribute<bool> is_boundary_vertex(M.vertices.attributes(),
"boundary_vertex");
107 std::vector<bool> is_interface_vertex(M.vertices.nb(),
false);
109 for (index_t f = 0; f < M.facets.nb(); ++f)
111 if (M.facets.nb_vertices(f) != 4 || (!old_tags.empty() && old_tags[f] == ElementType::INTERIOR_POLYTOPE))
114 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
116 is_interface_vertex[M.facets.vertex(f, lv)] =
true;
126 std::vector<int> degree(M.vertices.nb(), 0);
127 std::vector<bool> is_regular_vertex(M.vertices.nb());
128 for (index_t f = 0; f < M.facets.nb(); ++f)
130 if (M.facets.nb_vertices(f) == 4)
133 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
135 index_t v = M.facets.vertex(f, lv);
140 for (index_t v = 0; v < M.vertices.nb(); ++v)
143 if (is_boundary_vertex[v] || is_interface_vertex[v])
145 is_regular_vertex[v] = (degree[v] <= 2);
149 is_regular_vertex[v] = (degree[v] == 4);
154 for (index_t f = 0; f < M.facets.nb(); ++f)
156 assert(M.facets.nb_vertices(f) > 2);
157 if (!old_tags.empty() && old_tags[f] == ElementType::INTERIOR_POLYTOPE)
160 if (M.facets.nb_vertices(f) == 4)
165 bool is_boundary_facet =
false;
166 bool is_interface_facet =
false;
167 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
169 if (is_boundary_vertex[M.facets.vertex(f, lv)])
171 is_boundary_facet =
true;
173 if (is_interface_vertex[M.facets.vertex(f, lv)])
175 is_interface_facet =
true;
180 if (is_boundary_facet || is_interface_facet)
184 bool is_singular =
false;
185 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
187 index_t v = M.facets.vertex(f, lv);
188 if (is_boundary_vertex[v] || is_interface_vertex[v])
190 if (!is_regular_vertex[v])
198 if (!is_regular_vertex[v])
200 element_tags[f] = ElementType::UNDEFINED;
206 if (is_interface_facet)
208 element_tags[f] = ElementType::INTERFACE_CUBE;
210 else if (is_singular)
212 element_tags[f] = ElementType::SIMPLE_SINGULAR_BOUNDARY_CUBE;
216 element_tags[f] = ElementType::REGULAR_BOUNDARY_CUBE;
222 int nb_singulars = 0;
223 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
225 if (!is_regular_vertex[M.facets.vertex(f, lv)])
231 if (nb_singulars == 0)
233 element_tags[f] = ElementType::REGULAR_INTERIOR_CUBE;
235 else if (nb_singulars == 1)
237 element_tags[f] = ElementType::SIMPLE_SINGULAR_INTERIOR_CUBE;
241 element_tags[f] = ElementType::MULTI_SINGULAR_INTERIOR_CUBE;
251 GEO::Attribute<bool> boundary_vertices(M.vertices.attributes(),
"boundary_vertex");
252 for (index_t lv = 0; lv < M.facets.nb_vertices(f); ++lv)
254 if (boundary_vertices[M.facets.vertex(f, lv)])
256 tag = ElementType::BOUNDARY_POLYTOPE;
262 element_tags[f] = tag;
268 for (index_t f = 0; f < M.facets.nb(); ++f)
270 if (M.facets.nb_vertices(f) == 3)
272 element_tags[f] = ElementType::SIMPLEX;
283 double signed_area(
const GEO::Mesh &M, GEO::index_t f)
287 index_t v0 = M.facet_corners.vertex(M.facets.corners_begin(f));
288 const vec3 &p0 = Geom::mesh_vertex(M, v0);
290 M.facets.corners_begin(f) + 1;
291 c + 1 < M.facets.corners_end(f); c++)
293 index_t v1 = M.facet_corners.vertex(c);
295 index_t v2 = M.facet_corners.vertex(c + 1);
297 result += Geom::triangle_signed_area(vec2(&p0[0]), vec2(&p1[0]), vec2(&p2[0]));
307 vector<index_t> component;
308 index_t nb_components = get_connected_components(M, component);
309 vector<double> comp_signed_volume(nb_components, 0.0);
310 for (index_t f = 0; f < M.facets.nb(); ++f)
312 comp_signed_volume[component[f]] += signed_area(M, f);
314 for (index_t f = 0; f < M.facets.nb(); ++f)
316 if (comp_signed_volume[component[f]] < 0.0)
327 assert(
V.rows() == C.size());
328 int num_colors = C.maxCoeff() + 1;
329 Eigen::VectorXi count(num_colors);
331 for (
int i = 0; i < C.size(); ++i)
335 R.resize(num_colors + 1);
337 for (
int c = 0; c < num_colors; ++c)
339 R(c + 1) = R(c) + count(c);
342 Eigen::VectorXi remap(C.size());
343 for (
int i = 0; i < C.size(); ++i)
345 remap[i] = R(C(i)) + count[C(i)];
349 Eigen::MatrixXd NV(
V.rows(),
V.cols());
350 for (
int v = 0; v <
V.rows(); ++v)
352 NV.row(remap(v)) =
V.row(v);
356 for (
int f = 0; f < F.rows(); ++f)
358 for (
int lv = 0; lv < F.cols(); ++lv)
360 F(f, lv) = remap(F(f, lv));
370 void compute_unsigned_distance_field(
const GEO::Mesh &M,
371 const GEO::MeshFacetsAABB &aabb_tree,
const Eigen::MatrixXd &P, Eigen::VectorXd &D)
373 assert(P.cols() == 3);
375#pragma omp parallel for
376 for (
int i = 0; i < P.rows(); ++i)
378 GEO::vec3 pos(P(i, 0), P(i, 1), P(i, 2));
379 double sq_dist = aabb_tree.squared_distance(pos);
387 double x1,
double y1,
double x2,
double y2,
double &twice_signed_area)
389 twice_signed_area = y1 * x2 - x1 * y2;
390 if (twice_signed_area > 0)
392 else if (twice_signed_area < 0)
411 bool point_in_triangle_2d(
412 double x0,
double y0,
double x1,
double y1,
413 double x2,
double y2,
double x3,
double y3,
414 double &a,
double &b,
double &c)
422 int signa = orientation(x2, y2, x3, y3, a);
425 int signb = orientation(x3, y3, x1, y1, b);
428 int signc = orientation(x1, y1, x2, y2, c);
431 double sum = a + b + c;
432 geo_assert(sum != 0);
453 inline GEO::Sign orient_2d_inexact(GEO::vec2 p0, GEO::vec2 p1, GEO::vec2 p2)
455 double a11 = p1[0] - p0[0];
456 double a12 = p1[1] - p0[1];
458 double a21 = p2[0] - p0[0];
459 double a22 = p2[1] - p0[1];
461 double Delta = GEO::det2x2(
465 return GEO::geo_sgn(Delta);
480 template <
int X = 0,
int Y = 1,
int Z = 2>
481 int intersect_ray_z(
const GEO::Mesh &M, GEO::index_t f,
const GEO::vec3 &q,
double &
z)
485 index_t c =
M.facets.corners_begin(f);
486 const vec3 &p1 = Geom::mesh_vertex(M,
M.facet_corners.vertex(c++));
487 const vec3 &p2 = Geom::mesh_vertex(M,
M.facet_corners.vertex(c++));
488 const vec3 &p3 = Geom::mesh_vertex(M,
M.facet_corners.vertex(c));
491 if (point_in_triangle_2d(
492 q[X], q[Y], p1[X], p1[Y], p2[X], p2[Y], p3[X], p3[Y], u, v, w))
494 z = u * p1[Z] + v * p2[Z] + w * p3[Z];
495 auto sign = orient_2d_inexact(vec2(p1[X], p1[Y]), vec2(p2[X], p2[Y]), vec2(p3[X], p3[Y]));
513 void compute_sign(
const GEO::Mesh &M,
const GEO::MeshFacetsAABB &aabb_tree,
514 const Eigen::MatrixXd &P, Eigen::VectorXd &D)
516 assert(
P.cols() == 3);
517 assert(D.size() ==
P.rows());
519 GEO::vec3 min_corner, max_corner;
520 GEO::get_bbox(M, &min_corner[0], &max_corner[0]);
522#pragma omp parallel for
523 for (
int k = 0; k <
P.rows(); ++k)
525 GEO::vec3 center(
P(k, 0),
P(k, 1),
P(k, 2));
528 box.xyz_min[0] = box.xyz_max[0] = center[0];
529 box.xyz_min[1] = box.xyz_max[1] = center[1];
530 box.xyz_min[2] = min_corner[2];
531 box.xyz_max[2] = max_corner[2];
533 std::vector<std::pair<double, int>> inter;
534 auto action = [&
M, &inter, ¢er](GEO::index_t
f) {
536 if (
int s = intersect_ray_z(M, f, center,
z))
538 inter.emplace_back(
z, s);
541 aabb_tree.compute_bbox_facet_bbox_intersections(box, action);
542 std::sort(inter.begin(), inter.end());
544 std::vector<double> reduced;
545 for (
int i = 0, s = 0; i < (int)inter.size(); ++i)
547 const int ds = inter[i].second;
549 if ((s == -1 && ds < 0) || (s == 0 && ds > 0))
551 reduced.push_back(inter[i].first);
556 for (
double z : reduced)
563 if (num_before % 2 == 1)
579 M.vertices.create_vertices((
int)
V.rows());
580 for (
int i = 0; i < (int)M.vertices.nb(); ++i)
582 GEO::vec3 &p = M.vertices.point(i);
585 p[2] =
V.cols() >= 3 ?
V(i, 2) : 0;
590 M.facets.create_triangles((
int)F.rows());
592 else if (F.cols() == 4)
594 M.facets.create_quads((
int)F.rows());
598 throw std::runtime_error(
"Mesh format not supported");
600 for (
int c = 0; c < (int)M.facets.nb(); ++c)
602 for (
int lv = 0; lv < F.cols(); ++lv)
604 M.facets.set_vertex(c, lv, F(c, lv));
641 V.resize(M.vertices.nb(), 3);
642 for (
int i = 0; i < (int)M.vertices.nb(); ++i)
644 GEO::vec3 p = M.vertices.point(i);
645 V.row(i) << p[0], p[1], p[2];
647 assert(M.facets.are_simplices());
648 F.resize(M.facets.nb(), 3);
649 for (
int c = 0; c < (int)M.facets.nb(); ++c)
651 for (
int lv = 0; lv < 3; ++lv)
653 F(c, lv) = M.facets.vertex(c, lv);
656 assert(M.cells.are_simplices());
657 T.resize(M.cells.nb(), 4);
658 for (
int c = 0; c < (int)M.cells.nb(); ++c)
660 for (
int lv = 0; lv < 4; ++lv)
662 T(c, lv) = M.cells.vertex(c, lv);
670 const Eigen::MatrixXd &P, Eigen::VectorXd &D)
674 GEO::MeshFacetsAABB aabb_tree(M);
675 compute_unsigned_distance_field(M, aabb_tree, P, D);
676 compute_sign(M, aabb_tree, P, D);
683 assert(F.cols() == 3);
684 assert(
V.cols() == 3);
685 std::array<Eigen::RowVector3d, 4> t;
686 t[3] = Eigen::RowVector3d::Zero(
V.cols());
687 double volume_total = 0;
688 for (
int f = 0; f < F.rows(); ++f)
690 for (
int lv = 0; lv < F.cols(); ++lv)
692 t[lv] =
V.row(F(f, lv));
694 double vol = GEO::Geom::tetra_signed_volume(t[0].data(), t[1].data(), t[2].data(), t[3].data());
697 return -volume_total;
706 for (
int f = 0; f < F.rows(); ++f)
708 F.row(f) = F.row(f).reverse().eval();
718 std::vector<int> vertex_l2g;
719 for (
int c = 0; c < mesh.
n_cells(); ++c)
725 auto poly = std::make_unique<GEO::Mesh>();
728 poly->vertices.create_vertices((triangulated ? nv + nf : nv) + 1);
730 vertex_l2g.reserve(nv);
731 for (
int lf = 0; lf < nf; ++lf)
733 GEO::vector<GEO::index_t> facet_vertices;
737 Eigen::RowVector3d p = mesh.
point(index.vertex);
738 if (vertex_g2l[index.vertex] < 0)
740 vertex_g2l[index.vertex] = vertex_l2g.size();
741 vertex_l2g.push_back(index.vertex);
743 int v1 = vertex_g2l[index.vertex];
744 facet_vertices.push_back(v1);
745 poly->vertices.point(v1) = GEO::vec3(p.data());
750 GEO::vec3 p(0, 0, 0);
751 for (GEO::index_t lv = 0; lv < facet_vertices.size(); ++lv)
753 p += poly->vertices.point(facet_vertices[lv]);
755 p /= facet_vertices.size();
756 int v0 = vertex_l2g.size();
757 vertex_l2g.push_back(0);
758 poly->vertices.point(v0) = p;
759 for (GEO::index_t lv = 0; lv < facet_vertices.size(); ++lv)
761 int v1 = facet_vertices[lv];
762 int v2 = facet_vertices[(lv + 1) % facet_vertices.size()];
763 poly->facets.create_triangle(v0, v1, v2);
768 poly->facets.create_polygon(facet_vertices);
772 Eigen::RowVector3d p = mesh.
kernel(c);
773 poly->vertices.point(nv) = GEO::vec3(p.data());
775 assert(vertex_l2g.size() ==
size_t(triangulated ? nv + nf : nv));
777 for (
int v : vertex_l2g)
782 poly->facets.compute_borders();
783 poly->facets.connect();
785 polys.emplace_back(std::move(poly));
815 M.vertices.create_vertices((
int)mesh.
n_vertices());
816 for (
int i = 0; i < (int)M.vertices.nb(); ++i)
818 auto pt = mesh.
point(i);
819 GEO::vec3 &p = M.vertices.point(i);
825 for (
int f = 0, lf = 0; f < mesh.
n_faces(); ++f)
830 M.facets.create_polygon(nv);
831 for (
int lv = 0; lv < nv; ++lv)
833 M.facets.set_vertex(lf, lv, mesh.
face_vertex(f, lv));
839 typedef std::array<int, 8> Vector8i;
840 Vector8i g2p = {{0, 4, 1, 5, 3, 7, 2, 6}};
841 for (
int c = 0; c < mesh.
n_cells(); ++c)
847 for (
size_t k = 0; k < 8; ++k)
849 lvg[k] = lvp[g2p[k]];
851 std::reverse(lvg.begin(), lvg.end());
853 lvg[0], lvg[1], lvg[2], lvg[3],
854 lvg[4], lvg[5], lvg[6], lvg[7]);
864 GEO::mesh_reorient(M);
870 const Eigen::RowVector3d &kernel, Eigen::MatrixXd &OV, Eigen::MatrixXi &OF, Eigen::MatrixXi &OT)
872 assert(
V.cols() == 3);
873 OV.resize(
V.rows() + 1,
V.cols());
874 OV.topRows(
V.rows()) =
V;
875 OV.bottomRows(1) = kernel;
877 OT.resize(OF.rows(), 4);
878 OT.col(0).setConstant(
V.rows());
885 Eigen::MatrixXd &P, Eigen::MatrixXd *N,
int num_lloyd,
int num_newton)
887 assert(num_samples > 3);
890 GEO::CentroidalVoronoiTesselation CVT(&M);
892 bool was_quiet = GEO::Logger::instance()->is_quiet();
893 GEO::Logger::instance()->set_quiet(
true);
894 CVT.compute_initial_sampling(num_samples);
895 GEO::Logger::instance()->set_quiet(was_quiet);
899 CVT.Lloyd_iterations(num_lloyd);
904 CVT.Newton_iterations(num_newton);
907 P.resize(3, num_samples);
908 std::copy_n(CVT.embedding(0), 3 * num_samples, P.data());
909 P.transposeInPlace();
913 GEO::MeshFacetsAABB aabb(M);
915 for (
int i = 0; i < num_samples; ++i)
917 GEO::vec3 p(P(i, 0), P(i, 1), P(i, 2));
918 GEO::vec3 nearest_point;
920 auto f = aabb.nearest_facet(p, nearest_point, sq_dist);
921 GEO::vec3 n = normalize(GEO::Geom::mesh_facet_normal(M, f));
922 N->row(i) << n[0], n[1], n[2];
932 bool approx_aligned(
const double *a_,
const double *b_,
const double *p_,
const double *q_,
double tol = 1e-6)
935 vec3 a(a_), b(b_), p(p_), q(q_);
936 double da = std::sqrt(Geom::point_segment_squared_distance(a, p, q));
937 double db = std::sqrt(Geom::point_segment_squared_distance(b, p, q));
938 double cos_theta = Geom::cos_angle(b - a, p - q);
939 return (
da < tol && db < tol && std::abs(std::abs(cos_theta) - 1.0) < tol);
947 const Eigen::MatrixXd &BV,
const Eigen::MatrixXi &BE, Eigen::MatrixXi &OE)
949 assert(IV.cols() == 2 || IV.cols() == 3);
950 assert(BV.cols() == 2 || BV.cols() == 3);
951 typedef std::pair<int, int>
Edge;
952 std::vector<Edge> selected;
953 for (
int e1 = 0; e1 < IE.rows(); ++e1)
955 Eigen::RowVector3d a;
957 a.head(IV.cols()) = IV.row(IE(e1, 0));
958 Eigen::RowVector3d b;
960 b.head(IV.cols()) = IV.row(IE(e1, 1));
961 for (
int e2 = 0; e2 < BE.rows(); ++e2)
963 Eigen::RowVector3d p;
965 p.head(BV.cols()) = BV.row(BE(e2, 0));
966 Eigen::RowVector3d q;
968 q.head(BV.cols()) = BV.row(BE(e2, 1));
969 if (approx_aligned(a.data(), b.data(), p.data(), q.data()))
971 selected.emplace_back(IE(e1, 0), IE(e1, 1));
977 OE.resize(selected.size(), 2);
978 for (
int e = 0; e < OE.rows(); ++e)
980 OE.row(e) << selected[e].first, selected[e].second;
987 const Eigen::MatrixXd &vertices,
988 const Eigen::MatrixXi &codim_edges,
989 const Eigen::MatrixXi &
faces,
990 Eigen::VectorXi &codim_vertices)
992 std::vector<bool> is_vertex_codim(vertices.rows(),
true);
993 for (
int i = 0; i < codim_edges.rows(); i++)
995 for (
int j = 0; j < codim_edges.cols(); j++)
997 is_vertex_codim[codim_edges(i, j)] =
false;
1000 for (
int i = 0; i <
faces.rows(); i++)
1002 for (
int j = 0; j <
faces.cols(); j++)
1004 is_vertex_codim[
faces(i, j)] =
false;
1007 const auto n_codim_vertices = std::count(is_vertex_codim.begin(), is_vertex_codim.end(),
true);
1008 codim_vertices.resize(n_codim_vertices);
1009 for (
int i = 0, ci = 0; i < vertices.rows(); i++)
1011 if (is_vertex_codim[i])
1013 codim_vertices[ci++] = i;
1019 const Eigen::MatrixXi &tets,
1020 Eigen::MatrixXi &
faces)
1022 std::unordered_set<Eigen::Vector3i, HashMatrix> tri_to_tet(4 * tets.rows());
1023 for (
int i = 0; i < tets.rows(); i++)
1025 tri_to_tet.emplace(tets(i, 0), tets(i, 2), tets(i, 1));
1026 tri_to_tet.emplace(tets(i, 0), tets(i, 3), tets(i, 2));
1027 tri_to_tet.emplace(tets(i, 0), tets(i, 1), tets(i, 3));
1028 tri_to_tet.emplace(tets(i, 1), tets(i, 2), tets(i, 3));
1031 std::vector<Eigen::RowVector3i> faces_vector;
1032 for (
const auto &tri : tri_to_tet)
1035 bool is_surface_triangle =
1036 tri_to_tet.find(Eigen::Vector3i(tri[2], tri[1], tri[0])) == tri_to_tet.end()
1037 && tri_to_tet.find(Eigen::Vector3i(tri[1], tri[0], tri[2])) == tri_to_tet.end()
1038 && tri_to_tet.find(Eigen::Vector3i(tri[0], tri[2], tri[1])) == tri_to_tet.end();
1039 if (is_surface_triangle)
1041 faces_vector.emplace_back(tri[0], tri[1], tri[2]);
1045 faces.resize(faces_vector.size(), 3);
1046 for (
int i = 0; i <
faces.rows(); i++)
1048 faces.row(i) = faces_vector[i];
1053 const Eigen::MatrixXd &vertices,
1054 const Eigen::MatrixXi &tets,
1055 Eigen::MatrixXd &surface_vertices,
1056 Eigen::MatrixXi &tris)
1058 Eigen::MatrixXi full_tris;
1061 std::unordered_map<int, int> full_to_surface;
1062 std::vector<size_t> surface_to_full;
1063 for (
int i = 0; i < full_tris.rows(); i++)
1065 for (
int j = 0; j < full_tris.cols(); j++)
1067 if (full_to_surface.find(full_tris(i, j)) == full_to_surface.end())
1069 full_to_surface[full_tris(i, j)] = surface_to_full.size();
1070 surface_to_full.push_back(full_tris(i, j));
1075 surface_vertices.resize(surface_to_full.size(), 3);
1076 for (
int i = 0; i < surface_to_full.size(); i++)
1078 surface_vertices.row(i) = vertices.row(surface_to_full[i]);
1081 tris.resize(full_tris.rows(), full_tris.cols());
1082 for (
int i = 0; i < tris.rows(); i++)
1084 for (
int j = 0; j < tris.cols(); j++)
1086 tris(i, j) = full_to_surface[full_tris(i, j)];
1092 const std::string &mesh_path,
1093 Eigen::MatrixXd &vertices,
1094 Eigen::VectorXi &codim_vertices,
1095 Eigen::MatrixXi &codim_edges,
1096 Eigen::MatrixXi &
faces)
1098 vertices.resize(0, 0);
1099 codim_vertices.resize(0);
1100 codim_edges.resize(0, 0);
1103 std::string lowername = mesh_path;
1105 lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
1109 Eigen::MatrixXi cells;
1110 std::vector<std::vector<int>> elements;
1111 std::vector<std::vector<double>> weights;
1112 std::vector<int> body_ids;
1113 if (!
MshReader::load(mesh_path, vertices, cells, elements, weights, body_ids))
1115 logger().error(
"Unable to load mesh: {}", mesh_path);
1119 if (cells.cols() == 1)
1120 codim_vertices = cells;
1121 else if (cells.cols() == 2)
1122 codim_edges = cells;
1123 else if (cells.cols() == 3)
1125 else if (cells.cols() == 4)
1127 if (vertices.cols() == 2)
1129 logger().error(
"read_surface_mesh not implemented for 2D quad meshes");
1135 assert(vertices.cols() == 3);
1136 Eigen::MatrixXd surface_vertices;
1138 vertices = surface_vertices;
1143 logger().error(
"read_surface_mesh not implemented for hexahedral and polygonal/polyhedral meshes");
1151 logger().error(
"Unable to load mesh: {}", mesh_path);
1155 else if (!igl::read_triangle_mesh(mesh_path, vertices,
faces))
1158 if (!GEO::mesh_load(mesh_path, mesh))
1160 logger().error(
"Unable to load mesh: {}", mesh_path);
1165 vertices.resize(mesh.vertices.nb(), dim);
1166 for (
int vi = 0; vi < mesh.vertices.nb(); vi++)
1168 const auto &v = mesh.vertices.point(vi);
1169 for (
int vj = 0; vj < dim; vj++)
1171 vertices(vi, vj) = v[vj];
1176 assert(mesh.facets.nb());
1177 int face_cols = mesh.facets.nb_vertices(0);
1178 faces.resize(mesh.facets.nb(), face_cols);
1179 for (
int fi = 0; fi < mesh.facets.nb(); fi++)
1181 assert(face_cols == mesh.facets.nb_vertices(fi));
1182 for (
int fj = 0; fj < mesh.facets.nb_vertices(fi); fj++)
1184 faces(fi, fj) = mesh.facets.vertex(fi, fj);
1196 std::unordered_set<std::vector<int>,
HashVector> boundaries;
1198 auto insert = [&](std::vector<int> v) {
1199 std::sort(v.begin(), v.end());
1200 boundaries.insert(v);
1203 for (
int i = 0; i < cells.rows(); i++)
1205 const auto &cell = cells.row(i);
1206 if (cells.cols() == 3)
1208 insert({{cell(0), cell(1)}});
1209 insert({{cell(1), cell(2)}});
1210 insert({{cell(2), cell(0)}});
1212 else if (cells.cols() == 4 && dim == 2)
1214 insert({{cell(0), cell(1)}});
1215 insert({{cell(1), cell(2)}});
1216 insert({{cell(2), cell(3)}});
1217 insert({{cell(3), cell(0)}});
1219 else if (cells.cols() == 4 && dim == 3)
1221 insert({{cell(0), cell(2), cell(1)}});
1222 insert({{cell(0), cell(3), cell(2)}});
1223 insert({{cell(0), cell(1), cell(3)}});
1224 insert({{cell(1), cell(2), cell(3)}});
1226 else if (cells.cols() == 8)
1228 insert({{cell(0), cell(1), cell(2), cell(3)}});
1229 insert({{cell(1), cell(5), cell(6), cell(3)}});
1230 insert({{cell(5), cell(4), cell(7), cell(6)}});
1231 insert({{cell(0), cell(4), cell(7), cell(3)}});
1232 insert({{cell(0), cell(4), cell(5), cell(1)}});
1233 insert({{cell(2), cell(6), cell(7), cell(3)}});
1237 throw std::runtime_error(
"count_boundary_elements not implemented for polygons");
1241 return boundaries.size();
1246 using namespace GEO;
1247 typedef std::pair<index_t, index_t>
Edge;
1249 if (M.edges.nb() > 0)
1254 if (M.cells.nb() != 0 && M.facets.nb() == 0)
1256 M.cells.compute_borders();
1260 std::vector<std::pair<Edge, index_t>> e2c;
1261 for (index_t f = 0; f < M.facets.nb(); ++f)
1263 for (index_t c = M.facets.corners_begin(f); c < M.facets.corners_end(f); ++c)
1265 index_t v = M.facet_corners.vertex(c);
1266 index_t c2 = M.facets.next_corner_around_facet(f, c);
1267 index_t v2 = M.facet_corners.vertex(c2);
1268 e2c.emplace_back(std::make_pair(std::min(v, v2), std::max(v, v2)), c);
1271 std::sort(e2c.begin(), e2c.end());
1275 Edge prev_e(-1, -1);
1276 for (
const auto &kv : e2c)
1281 M.edges.create_edge(e.first, e.second);
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)
std::vector< Eigen::VectorXi > faces
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)
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.
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
virtual int n_cell_faces(const int c_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
bool is_cube(const int el_id) const
checks if element is cube compatible
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 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
Eigen::ArrayXd P(const int m, const int p, const Eigen::ArrayXd &z)
bool is_planar(const GEO::Mesh &M, const double tol=1e-5)
Determine if the given mesh is planar (2D or tiny z-range).
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.
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.
GEO::index_t mesh_create_vertex(GEO::Mesh &M, const GEO::vec3 &p)
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)
void from_geogram_mesh(const GEO::Mesh &M, Eigen::MatrixXd &V, Eigen::MatrixXi &F, Eigen::MatrixXi &T)
Extract simplices from a Geogram mesh.
void tertrahedralize_star_shaped_surface(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::RowVector3d &kernel, Eigen::MatrixXd &OV, Eigen::MatrixXi &OF, Eigen::MatrixXi &OT)
Tetrahedralize a star-shaped mesh, with a given point in its kernel.
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.