11#include <Eigen/Geometry>
13#include <igl/boundary_facets.h>
14#include <igl/oriented_facets.h>
17#include <unordered_set>
32 std::vector<MeshWithID> result;
33 result.reserve(ids.size());
34 for (
const int id : ids)
40 child->remove_elements(keep);
41 result.push_back({id, std::move(child)});
48 const int kept = std::count(keep.begin(), keep.end(),
true);
50 auto filter_vector = [&keep, kept](
auto &values) {
53 assert(values.size() == keep.size());
54 using Value =
typename std::decay_t<
decltype(values)>::value_type;
55 std::vector<Value> filtered;
56 filtered.reserve(kept);
57 for (
int i = 0; i < keep.size(); ++i)
59 filtered.push_back(values[i]);
60 values = std::move(filtered);
71 assert(
orders_.rows() == keep.size());
72 Eigen::MatrixXi filtered(kept,
orders_.cols());
73 for (
int i = 0, j = 0; i < keep.size(); ++i)
75 filtered.row(j++) =
orders_.row(i);
84 assert(dim == 2 || dim == 3);
86 std::unique_ptr<Mesh> mesh;
87 if (dim == 2 && non_conforming)
88 mesh = std::make_unique<NCMesh2D>();
89 else if (dim == 2 && !non_conforming)
90 mesh = std::make_unique<CMesh2D>();
91 else if (dim == 3 && non_conforming)
92 mesh = std::make_unique<NCMesh3D>();
93 else if (dim == 3 && !non_conforming)
94 mesh = std::make_unique<CMesh3D>();
95 if (!mesh || !mesh->build_from_data(data))
107 vertices.row(i) =
point(i);
109 int element_width = 0;
112 Eigen::MatrixXi elements = Eigen::MatrixXi::Constant(
n_elements(), element_width, -1);
125 const Eigen::Vector2d a = vertices.row(elements(e, 1)) - vertices.row(elements(e, 0));
126 const Eigen::Vector2d b = vertices.row(elements(e, 2)) - vertices.row(elements(e, 0));
127 if (a.x() * b.y() - a.y() * b.x() < 0)
128 std::swap(elements(e, 1), elements(e, 2));
132 Eigen::Matrix3d jacobian;
133 jacobian.col(0) = vertices.row(elements(e, 1)) - vertices.row(elements(e, 0));
134 jacobian.col(1) = vertices.row(elements(e, 2)) - vertices.row(elements(e, 0));
135 jacobian.col(2) = vertices.row(elements(e, 3)) - vertices.row(elements(e, 0));
136 if (jacobian.determinant() < 0)
137 std::swap(elements(e, 1), elements(e, 2));
142 MeshData data(std::move(vertices), std::move(elements));
176 const auto *mesh3d =
dynamic_cast<const Mesh3D *
>(
this);
177 if (mesh3d ==
nullptr)
180 for (
int f = 0; f <
n_faces(); ++f)
189 for (
int j = 0; j < mesh3d->n_cell_faces(c); ++j)
191 data.
cell_faces[c].push_back(mesh3d->cell_face(c, j));
210 std::sort(tmp.begin(), tmp.end());
211 tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
212 if (!tmp.empty() && tmp.front() == -1)
213 tmp.erase(tmp.begin());
218 for (
int e = 0; e <
n_edges(); ++e)
225 for (
int f = 0; f <
n_faces(); ++f)
228 for (
int f = 0; f <
n_faces(); ++f)
244 [](
const auto &
weights) { return !weights.empty(); }));
255 std::unordered_map<std::vector<int>, int,
HashVector> labels;
259 std::sort(side.begin(), side.end());
260 const auto [it, inserted] = labels.emplace(std::move(side), data.
boundary_ids[i]);
262 logger().warn(
"Mesh side has multiple labels; using {}.", it->second);
265 std::vector<int> side = vertices;
266 std::sort(side.begin(), side.end());
267 const auto it = labels.find(side);
280 for (
int e = 0; e <
n_edges(); ++e)
289 for (
int f = 0; f <
n_faces(); ++f)
298 for (
int c = 0; c <
n_cells(); ++c)
348 if (in_node_to_node.size() <= 0 ||
node_ids_.empty())
369 const auto p =
point(n);
370 node_ids_[n] = marker(n, p, is_boundary);
391 std::vector<std::pair<int, int>> res;
394 for (
int e_id = 0; e_id <
n_edges(); ++e_id)
399 res.emplace_back(std::min(e0, e1), std::max(e0, e1));
407 std::vector<std::vector<int>> res(
n_faces());
409 for (
int f_id = 0; f_id <
n_faces(); ++f_id)
411 auto &tmp = res[f_id];
415 std::sort(tmp.begin(), tmp.end());
423 std::unordered_map<std::pair<int, int>, size_t,
HashPair> res;
426 for (
int e_id = 0; e_id <
n_edges(); ++e_id)
431 res[std::pair<int, int>(std::min(e0, e1), std::max(e0, e1))] = e_id;
439 std::unordered_map<std::vector<int>, size_t,
HashVector> res;
442 for (
int f_id = 0; f_id <
n_faces(); ++f_id)
448 std::sort(f.begin(), f.end());
464 auto complete_higher_order = [](
const Mesh &
source) {
465 std::pair<Eigen::MatrixXd, std::vector<std::vector<int>>> result;
466 if (!
source.higher_order_connectivity_.empty())
468 result.first =
source.higher_order_nodes_;
469 result.second =
source.higher_order_connectivity_;
473 result.first.resize(
source.n_vertices(),
source.dimension());
474 result.second.resize(
source.n_elements());
475 for (
int v = 0; v <
source.n_vertices(); ++v)
476 result.first.row(v) =
source.point(v);
477 for (
int e = 0; e <
source.n_elements(); ++e)
478 for (
int j = 0; j <
source.n_cell_vertices(e); ++j)
479 result.second[e].push_back(
source.element_vertex(e, j));
481 if (result.first.rows() <
source.n_vertices()
482 || result.second.size() != size_t(
source.n_elements()))
484 for (
int v = 0; v <
source.n_vertices(); ++v)
485 result.first.row(v) =
source.point(v);
489 auto left = complete_higher_order(*
this);
490 auto right = complete_higher_order(mesh);
491 const int left_extra = left.first.rows() -
n_vertices;
492 const int right_extra = right.first.rows() - other_vertices;
493 Eigen::MatrixXd nodes(
n_vertices + other_vertices + left_extra + right_extra,
dimension());
495 nodes.middleRows(
n_vertices, other_vertices) = right.first.topRows(other_vertices);
497 nodes.middleRows(
n_vertices + other_vertices, left_extra) = left.first.bottomRows(left_extra);
499 nodes.bottomRows(right_extra) = right.first.bottomRows(right_extra);
501 for (
auto &element : left.second)
502 for (
int &node : element)
504 node += other_vertices;
505 for (
auto &element : right.second)
506 for (
int &node : element)
507 node = node < other_vertices
524 for (
int i = 0; i <
node_ids_.size(); ++i)
587 Eigen::MatrixXi mesh_orders = mesh.
orders_;
588 if (mesh_orders.size() == 0)
590 assert(
orders_.cols() == mesh_orders.cols());
592 orders_.bottomRows(mesh_orders.rows()) = mesh_orders;
641 for (
int i = 0; i < incoming_faces.size(); ++i)
642 if (incoming_faces(i) >= 0)
647 Eigen::MatrixXi combined = Eigen::MatrixXi::Constant(
648 old_rows + incoming_faces.rows(), width, -1);
650 combined.bottomLeftCorner(incoming_faces.rows(), incoming_faces.cols()) = incoming_faces;
657 template <
typename T>
658 void transform_high_order_nodes(std::vector<T> &nodes,
const MatrixNd &A,
const VectorNd &b)
664 n.nodes = (n.nodes * A.transpose()).rowwise() + b.transpose();
698 nodes = (nodes * A.transpose()).rowwise() + b.transpose();
Format-independent input used to construct a runtime Mesh.
bool has_polyhedral_topology() const
Eigen::MatrixXd higher_order_nodes
std::vector< std::vector< int > > higher_order_connectivity
std::vector< int > node_ids
std::vector< int > geometry_ids
std::vector< std::vector< int > > boundary_elements
std::vector< std::vector< int > > faces
Optional topology for arbitrary polyhedral cells.
std::vector< int > body_ids
std::vector< int > boundary_ids
Eigen::MatrixXd cell_kernel_points
std::vector< std::vector< int > > cell_faces
std::vector< std::vector< int > > cell_face_orientations
std::vector< std::vector< double > > higher_order_weights
std::vector< bool > cell_is_hex
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
void set_cell_weights(const std::vector< std::vector< double > > &in_cell_weights)
Set the cell weights for rational polynomial meshes.
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
int element_vertex(const int el_id, const int lv_id) const
id of the vertex of a element
bool is_rational_
stores if the mesh is rational
bool has_boundary_ids() const
checks if surface selections are available
virtual void compute_boundary_ids(const std::function< int(const size_t, const std::vector< int > &, const RowVectorNd &, bool)> &marker)=0
computes boundary selections based on a function
void cell_barycenters(Eigen::MatrixXd &barycenters) const
all cells barycenters
std::vector< std::vector< int > > higher_order_connectivity_
virtual RowVectorNd face_barycenter(const int f) const =0
face barycenter
int get_geometry_id(const int element) const
Get the geometry ID of an element. The default geometry is 0.
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 void set_body_ids(const std::vector< int > &body_ids)
Set the volume sections.
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 simplex
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
std::vector< int > geometry_ids_
list of geometry labels, one per top-dimensional element
void set_is_rational(const bool in_is_rational)
Set the is rational object.
std::vector< int > boundary_ids_
list of surface labels
bool is_prism(const int el_id) const
checks if element is a prism
void clear_higher_order_data()
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
MeshData to_mesh_data() const
Capture the complete runtime mesh in the canonical, format-independent representation.
virtual bool build_topology(const MeshData &data)=0
Construct only the dimension-specific topology.
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
virtual std::unique_ptr< Mesh > copy() const =0
Create a copy of the mesh.
void set_geometry_ids(const std::vector< int > &geometry_ids)
Set the geometry selection, one ID per element.
std::vector< MeshWithID > split() const
Split the mesh according to its per-element geometry IDs.
bool has_poly() const
checks if the mesh has polytopes
Eigen::MatrixXd higher_order_nodes_
Canonical connectivity is retained because the runtime edge/face caches do not preserve the original ...
std::vector< int > body_ids_
list of volume labels
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
void transform_higher_order_data(const MatrixNd &A, const VectorNd &b)
static std::unique_ptr< Mesh > create(MeshData data, bool non_conforming=false)
Construct a runtime mesh from format-independent decoded data.
std::vector< FaceNodes > face_nodes_
high-order nodes associates to faces
virtual int n_faces() const =0
number of faces
bool build_from_data(const MeshData &data)
Import all topology and metadata from MeshData.
std::vector< EdgeNodes > edge_nodes_
high-order nodes associates to edges
std::vector< std::vector< int > > faces() const
list of sorted faces.
virtual void attach_higher_order_nodes(const Eigen::MatrixXd &V, const std::vector< std::vector< int > > &nodes)=0
attach high order nodes
int n_boundary_elements() const
utitlity to return the number of boundary elements, faces or edges in 3d and 2d
void filter_element_data(const std::vector< bool > &keep)
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
bool is_pyramid(const int el_id) const
checks if element is a pyramid
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
bool has_explicit_polyhedral_topology_
Whether the source MeshData supplied explicit face/cell incidence.
Eigen::VectorXi in_ordered_vertices_
Order of the input vertices.
bool has_geometry_ids() const
virtual int n_cell_vertices(const int c_id) const =0
number of vertices of a cell
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
int boundary_element_vertex(const int primitive_id, const int lv_id) const
@ 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,...
@ PRISM
Boundary polytope.
@ 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 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
void log_and_throw_error(const std::string &msg)