15#include <geogram/mesh/mesh_io.h>
16#include <geogram/mesh/mesh_geometry.h>
18#include <Eigen/Geometry>
20#include <igl/boundary_facets.h>
21#include <igl/oriented_facets.h>
25#include <unordered_set>
35 std::vector<int> sort_face(
const Eigen::RowVectorXi f)
37 std::vector<int> sorted_face(
f.data(),
f.data() +
f.size());
38 std::sort(sorted_face.begin(), sorted_face.end());
48 template <
typename DerivedT,
typename DerivedF>
50 const Eigen::MatrixBase<DerivedT> &T,
51 Eigen::PlainObjectBase<DerivedF> &
F)
53 assert(
T.cols() == 4);
54 assert(
T.rows() >= 1);
56 Eigen::MatrixXi BF, OF;
57 igl::boundary_facets(T, BF);
58 igl::oriented_facets(T, OF);
59 assert((OF.rows() + BF.rows()) % 2 == 0);
60 const int num_faces = (OF.rows() + BF.rows()) / 2;
61 F.resize(num_faces, 3);
62 F.topRows(BF.rows()) = BF;
63 std::unordered_set<std::vector<int>,
HashVector> processed_faces;
64 for (
int fi = 0; fi < BF.rows(); fi++)
66 processed_faces.insert(sort_face(BF.row(fi)));
69 for (
int fi = 0; fi < OF.rows(); fi++)
71 std::vector<int> sorted_face = sort_face(OF.row(fi));
72 const auto iter = processed_faces.find(sorted_face);
73 if (iter == processed_faces.end())
75 F.row(processed_faces.size()) = OF.row(fi);
76 processed_faces.insert(sorted_face);
80 assert(
F.rows() == processed_faces.size());
84 std::unique_ptr<Mesh>
Mesh::create(
const int dim,
const bool non_conforming)
86 assert(dim == 2 || dim == 3);
87 if (dim == 2 && non_conforming)
88 return std::make_unique<NCMesh2D>();
89 else if (dim == 2 && !non_conforming)
90 return std::make_unique<CMesh2D>();
91 else if (dim == 3 && non_conforming)
92 return std::make_unique<NCMesh3D>();
93 else if (dim == 3 && !non_conforming)
94 return std::make_unique<CMesh3D>();
95 throw std::runtime_error(
"Invalid dimension");
98 std::unique_ptr<Mesh>
Mesh::create(GEO::Mesh &meshin,
const bool non_conforming)
103 std::unique_ptr<Mesh> mesh =
create(2, non_conforming);
104 if (mesh->load(meshin))
106 mesh->in_ordered_vertices_ = Eigen::VectorXi::LinSpaced(meshin.vertices.nb(), 0, meshin.vertices.nb() - 1);
107 assert(mesh->in_ordered_vertices_[0] == 0);
108 assert(mesh->in_ordered_vertices_[1] == 1);
109 assert(mesh->in_ordered_vertices_[2] == 2);
110 assert(mesh->in_ordered_vertices_[mesh->in_ordered_vertices_.size() - 1] == meshin.vertices.nb() - 1);
112 mesh->in_ordered_edges_.resize(meshin.edges.nb(), 2);
114 for (
int e = 0; e < (int)meshin.edges.nb(); ++e)
116 for (
int lv = 0; lv < 2; ++lv)
118 mesh->in_ordered_edges_(e, lv) = meshin.edges.vertex(e, lv);
120 assert(mesh->in_ordered_edges_(e, 0) != mesh->in_ordered_edges_(e, 1));
122 assert(mesh->in_ordered_edges_.size() > 0);
124 mesh->in_ordered_faces_.resize(0, 0);
131 std::unique_ptr<Mesh> mesh =
create(3, non_conforming);
132 meshin.cells.connect();
133 if (mesh->load(meshin))
135 mesh->in_ordered_vertices_ = Eigen::VectorXi::LinSpaced(meshin.vertices.nb(), 0, meshin.vertices.nb() - 1);
136 assert(mesh->in_ordered_vertices_[0] == 0);
137 assert(mesh->in_ordered_vertices_[1] == 1);
138 assert(mesh->in_ordered_vertices_[2] == 2);
139 assert(mesh->in_ordered_vertices_[mesh->in_ordered_vertices_.size() - 1] == meshin.vertices.nb() - 1);
141 mesh->in_ordered_edges_.resize(meshin.edges.nb(), 2);
143 for (
int e = 0; e < (int)meshin.edges.nb(); ++e)
145 for (
int lv = 0; lv < 2; ++lv)
147 mesh->in_ordered_edges_(e, lv) = meshin.edges.vertex(e, lv);
150 assert(mesh->in_ordered_edges_.size() > 0);
152 mesh->in_ordered_faces_.resize(meshin.facets.nb(), meshin.facets.nb_vertices(0));
154 for (
int f = 0; f < (int)meshin.edges.nb(); ++f)
156 assert(mesh->in_ordered_faces_.cols() == meshin.facets.nb_vertices(f));
158 for (
int lv = 0; lv < mesh->in_ordered_faces_.cols(); ++lv)
160 mesh->in_ordered_faces_(f, lv) = meshin.facets.vertex(f, lv);
163 assert(mesh->in_ordered_faces_.size() > 0);
169 logger().error(
"Failed to load mesh");
173 std::unique_ptr<Mesh>
Mesh::create(
const std::string &path,
const bool non_conforming)
175 if (!std::filesystem::exists(path))
177 logger().error(path.empty() ?
"No mesh provided!" :
"Mesh file does not exist: {}", path);
181 std::string lowername = path;
182 std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
186 std::unique_ptr<Mesh> mesh =
create(3, non_conforming);
187 if (mesh->load(path))
195 Eigen::MatrixXd vertices;
196 Eigen::MatrixXi cells;
197 std::vector<std::vector<int>> elements;
198 std::vector<std::vector<double>> weights;
199 std::vector<int> body_ids;
201 if (!
MshReader::load(path, vertices, cells, elements, weights, body_ids))
203 logger().error(
"Failed to load MSH mesh: {}", path);
207 const int dim = vertices.cols();
208 std::unique_ptr<Mesh> mesh =
create(vertices, cells, non_conforming);
211 if ((dim == 2 && cells.cols() == 3) || (dim == 3 && cells.cols() == 4))
213 mesh->attach_higher_order_nodes(vertices, elements);
214 mesh->set_cell_weights(weights);
218 for (
const auto &w : weights)
222 mesh->set_is_rational(
true);
227 mesh->set_body_ids(body_ids);
234 if (GEO::mesh_load(path, tmp))
236 return create(tmp, non_conforming);
239 logger().error(
"Failed to load mesh: {}", path);
244 const Eigen::MatrixXd &vertices,
const Eigen::MatrixXi &cells,
const bool non_conforming)
246 const int dim = vertices.cols();
248 std::unique_ptr<Mesh> mesh =
create(dim, non_conforming);
250 mesh->build_from_matrices(vertices, cells);
252 std::vector<int> tmp(cells.data(), cells.data() + cells.size());
253 std::sort(tmp.begin(), tmp.end());
254 tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
256 mesh->in_ordered_vertices_ = Eigen::Map<Eigen::VectorXi, Eigen::Unaligned>(tmp.data(), tmp.size());
265 for (
int f = 0; f < cells.rows(); ++f)
267 for (
int lv = 0; lv < cells.cols(); ++lv)
269 const int v0 = cells(f, lv);
270 const int v1 = cells(f, (lv + 1) % cells.cols());
271 edges.emplace(std::pair<int, int>(std::min(v0, v1), std::max(v0, v1)));
274 mesh->in_ordered_edges_.resize(
edges.size(), 2);
276 for (
auto it =
edges.begin(); it !=
edges.end(); ++it)
278 mesh->in_ordered_edges_(index, 0) = it->first;
279 mesh->in_ordered_edges_(index, 1) = it->second;
283 assert(mesh->in_ordered_edges_.size() > 0);
285 mesh->in_ordered_faces_.resize(0, 0);
289 if (cells.cols() == 4)
291 get_faces(cells, mesh->in_ordered_faces_);
292 igl::edges(mesh->in_ordered_faces_, mesh->in_ordered_edges_);
305 for (
int e = 0; e <
n_edges(); ++e)
314 for (
int f = 0; f <
n_faces(); ++f)
323 for (
int c = 0; c <
n_cells(); ++c)
373 if (in_node_to_node.size() <= 0 ||
node_ids_.empty())
394 const auto p =
point(n);
395 node_ids_[n] = marker(n, p, is_boundary);
403 std::ifstream file(path);
407 while (std::getline(file, line))
409 std::istringstream iss(line);
429 std::vector<std::pair<int, int>> res;
432 for (
int e_id = 0; e_id <
n_edges(); ++e_id)
437 res.emplace_back(std::min(e0, e1), std::max(e0, e1));
445 std::vector<std::vector<int>> res(
n_faces());
447 for (
int f_id = 0; f_id <
n_faces(); ++f_id)
449 auto &tmp = res[f_id];
453 std::sort(tmp.begin(), tmp.end());
461 std::unordered_map<std::pair<int, int>, size_t,
HashPair> res;
464 for (
int e_id = 0; e_id <
n_edges(); ++e_id)
469 res[std::pair<int, int>(std::min(e0, e1), std::max(e0, e1))] = e_id;
477 std::unordered_map<std::vector<int>, size_t,
HashVector> res;
480 for (
int f_id = 0; f_id <
n_faces(); ++f_id)
486 std::sort(f.begin(), f.end());
506 for (
int i = 0; i <
node_ids_.size(); ++i)
559 Eigen::MatrixXi mesh_orders = mesh.
orders_;
560 if (mesh_orders.size() == 0)
562 assert(
orders_.cols() == mesh_orders.cols());
564 orders_.bottomRows(mesh_orders.rows()) = mesh_orders;
619 template <
typename T>
620 void transform_high_order_nodes(std::vector<T> &nodes,
const MatrixNd &A,
const VectorNd &b)
626 n.nodes = (n.nodes * A.transpose()).rowwise() + b.transpose();
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)
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
virtual int n_vertices() const =0
number of vertices
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Eigen::MatrixXi orders_
list of geometry orders, one per cell
std::unordered_map< std::pair< int, int >, size_t, polyfem::utils::HashPair > edges_to_ids() const
map from edge (pair of v id) to the id of the edge
std::vector< ElementType > elements_tag_
list of element types
virtual RowVectorNd edge_barycenter(const int e) const =0
edge barycenter
bool is_rational_
stores if the mesh is rational
bool has_boundary_ids() const
checks if surface selections are available
void cell_barycenters(Eigen::MatrixXd &barycenters) const
all cells barycenters
virtual RowVectorNd face_barycenter(const int f) const =0
face barycenter
virtual void set_point(const int global_index, const RowVectorNd &p)=0
Set the point.
bool is_cube(const int el_id) const
checks if element is cube compatible
bool has_node_ids() const
checks if points selections are available
virtual RowVectorNd point(const int global_index) const =0
point coordinates
Eigen::MatrixXi in_ordered_faces_
Order of the input faces, TODO: change to std::vector of Eigen::Vector.
void compute_node_ids(const std::function< int(const size_t, const RowVectorNd &, bool)> &marker)
computes boundary selections based on a function
virtual int get_boundary_id(const int primitive) const
Get the boundary selection of an element (face in 3d, edge in 2d)
virtual bool is_boundary_vertex(const int vertex_global_id) const =0
is vertex boundary
bool is_simplex(const int el_id) const
checks if element is simples compatible
void face_barycenters(Eigen::MatrixXd &barycenters) const
all face barycenters
virtual bool has_body_ids() const
checks if volumes selections are available
bool is_spline_compatible(const int el_id) const
checks if element is spline compatible
virtual void load_boundary_ids(const std::string &path)
loads the boundary selections for a file
std::vector< int > boundary_ids_
list of surface labels
void apply_affine_transformation(const MatrixNd &A, const VectorNd &b)
Apply an affine transformation to the vertex positions .
std::vector< int > node_ids_
list of node labels
std::unordered_map< std::vector< int >, size_t, polyfem::utils::HashVector > faces_to_ids() const
map from face (tuple of v id) to the id of the face
std::vector< CellNodes > cell_nodes_
high-order nodes associates to cells
std::vector< std::vector< double > > cell_weights_
weights associates to cells for rational polynomail meshes
virtual bool is_volume() const =0
checks if mesh is volume
std::vector< std::pair< int, int > > edges() const
list of sorted edges.
void update_nodes(const Eigen::VectorXi &in_node_to_node)
Update the node ids to reorder them.
virtual int edge_vertex(const int e_id, const int lv_id) const =0
id of the edge vertex
std::vector< int > body_ids_
list of volume labels
static std::unique_ptr< Mesh > create(const std::string &path, const bool non_conforming=false)
factory to build the proper mesh
virtual int get_default_boundary_id(const int primitive) const
Get the default boundary selection of an element (face in 3d, edge in 2d)
int dimension() const
utily for dimension
virtual int n_cells() const =0
number of cells
std::vector< FaceNodes > face_nodes_
high-order nodes associates to faces
virtual int n_faces() const =0
number of faces
std::vector< EdgeNodes > edge_nodes_
high-order nodes associates to edges
std::vector< std::vector< int > > faces() const
list of sorted faces.
int n_boundary_elements() const
utitlity to return the number of boundary elements, faces or edges in 3d and 2d
Eigen::MatrixXi in_ordered_edges_
Order of the input edges.
virtual void append(const Mesh &mesh)
appends a new mesh to the end of this
virtual RowVectorNd cell_barycenter(const int c) const =0
cell barycenter
virtual int n_edges() const =0
number of edges
void edge_barycenters(Eigen::MatrixXd &barycenters) const
all edges barycenters
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
Eigen::VectorXi in_ordered_vertices_
Order of the input vertices.
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
virtual int face_vertex(const int f_id, const int lv_id) const =0
id of the face vertex
bool is_planar(const GEO::Mesh &M, const double tol=1e-5)
Determine if the given mesh is planar (2D or tiny z-range).
@ REGULAR_INTERIOR_CUBE
Triangle/tet element.
@ REGULAR_BOUNDARY_CUBE
Quad/Hex incident to more than 1 singular vertices (should not happen in 2D)
@ MULTI_SINGULAR_BOUNDARY_CUBE
Quad incident to exactly 1 singular vertex (in 2D); hex incident to exactly 1 singular interior edge,...
@ MULTI_SINGULAR_INTERIOR_CUBE
Quad/hex incident to exactly 1 singular vertex (in 2D) or edge (in 3D)
@ SIMPLE_SINGULAR_INTERIOR_CUBE
Regular quad/hex inside a 3^n patch.
@ INTERFACE_CUBE
Boundary hex that is not regular nor SimpleSingularBoundaryCube.
@ INTERIOR_POLYTOPE
Quad/hex that is at the interface with a polytope (if a cube has both external boundary and and inter...
@ SIMPLE_SINGULAR_BOUNDARY_CUBE
Boundary quad/hex, where all boundary vertices/edges are incident to at most 2 quads/hexes.
@ BOUNDARY_POLYTOPE
Interior polytope.
void generate_edges(GEO::Mesh &M)
assing edges to M
bool endswith(const std::string &str, const std::string &suffix)
void append_rows(DstMat &dst, const SrcMat &src)
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor, 3, 3 > MatrixNd
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd