PolyFEM
Loading...
Searching...
No Matches
Mesh.cpp
Go to the documentation of this file.
1
7
11
14
15#include <geogram/mesh/mesh_io.h>
16#include <geogram/mesh/mesh_geometry.h>
17
18#include <Eigen/Geometry>
19
20#include <igl/boundary_facets.h>
21#include <igl/oriented_facets.h>
22#include <igl/edges.h>
23
24#include <filesystem>
25#include <unordered_set>
26#include <set>
27#include <type_traits>
28
30namespace polyfem::mesh
31{
32 using namespace polyfem::io;
33 using namespace polyfem::utils;
34
35 std::vector<MeshWithID> Mesh::split() const
36 {
37 std::set<int> ids;
38 for (int e = 0; e < n_elements(); ++e)
39 ids.insert(get_geometry_id(e));
40
41 std::vector<MeshWithID> result;
42 result.reserve(ids.size());
43 for (const int id : ids)
44 {
45 auto child = copy();
46 std::vector<bool> keep(n_elements(), false);
47 for (int e = 0; e < n_elements(); ++e)
48 keep[e] = get_geometry_id(e) == id;
49 child->remove_elements(keep);
50 result.push_back({id, std::move(child)});
51 }
52 return result;
53 }
54
55 void Mesh::filter_element_data(const std::vector<bool> &keep)
56 {
57 const int kept = std::count(keep.begin(), keep.end(), true);
58
59 auto filter_vector = [&keep, kept](auto &values) {
60 if (values.empty())
61 return;
62 assert(values.size() == keep.size());
63 using Value = typename std::decay_t<decltype(values)>::value_type;
64 std::vector<Value> filtered;
65 filtered.reserve(kept);
66 for (int i = 0; i < keep.size(); ++i)
67 if (keep[i])
68 filtered.push_back(values[i]);
69 values = std::move(filtered);
70 };
71
72 filter_vector(elements_tag_);
73 filter_vector(body_ids_);
74 filter_vector(geometry_ids_);
75 filter_vector(cell_weights_);
76
77 if (orders_.size() > 0)
78 {
79 assert(orders_.rows() == keep.size());
80 Eigen::MatrixXi filtered(kept, orders_.cols());
81 for (int i = 0, j = 0; i < keep.size(); ++i)
82 if (keep[i])
83 filtered.row(j++) = orders_.row(i);
84 orders_ = std::move(filtered);
85 }
86 }
87
88 namespace
89 {
90 std::vector<int> sort_face(const Eigen::RowVectorXi f)
91 {
92 std::vector<int> sorted_face(f.data(), f.data() + f.size());
93 std::sort(sorted_face.begin(), sorted_face.end());
94 return sorted_face;
95 }
96
97 // Constructs a list of unique faces represented in a given mesh (V,T)
98 //
99 // Inputs:
100 // T: #T × 4 matrix of indices of tet corners
101 // Outputs:
102 // F: #F × 3 list of faces in no particular order
103 template <typename DerivedT, typename DerivedF>
104 void get_faces(
105 const Eigen::MatrixBase<DerivedT> &T,
106 Eigen::PlainObjectBase<DerivedF> &F)
107 {
108 assert(T.cols() == 4);
109 assert(T.rows() >= 1);
110
111 Eigen::MatrixXi BF, OF;
112 igl::boundary_facets(T, BF);
113 igl::oriented_facets(T, OF); // boundary facets + duplicated interior faces
114 assert((OF.rows() + BF.rows()) % 2 == 0);
115 const int num_faces = (OF.rows() + BF.rows()) / 2;
116 F.resize(num_faces, 3);
117 F.topRows(BF.rows()) = BF;
118 std::unordered_set<std::vector<int>, HashVector> processed_faces;
119 for (int fi = 0; fi < BF.rows(); fi++)
120 {
121 processed_faces.insert(sort_face(BF.row(fi)));
122 }
123
124 for (int fi = 0; fi < OF.rows(); fi++)
125 {
126 std::vector<int> sorted_face = sort_face(OF.row(fi));
127 const auto iter = processed_faces.find(sorted_face);
128 if (iter == processed_faces.end())
129 {
130 F.row(processed_faces.size()) = OF.row(fi);
131 processed_faces.insert(sorted_face);
132 }
133 }
134
135 assert(F.rows() == processed_faces.size());
136 }
137 } // namespace
138
139 std::unique_ptr<Mesh> Mesh::create(const int dim, const bool non_conforming)
140 {
141 assert(dim == 2 || dim == 3);
142 if (dim == 2 && non_conforming)
143 return std::make_unique<NCMesh2D>();
144 else if (dim == 2 && !non_conforming)
145 return std::make_unique<CMesh2D>();
146 else if (dim == 3 && non_conforming)
147 return std::make_unique<NCMesh3D>();
148 else if (dim == 3 && !non_conforming)
149 return std::make_unique<CMesh3D>();
150 throw std::runtime_error("Invalid dimension");
151 }
152
153 std::unique_ptr<Mesh> Mesh::create(GEO::Mesh &meshin, const bool non_conforming)
154 {
155 if (is_planar(meshin))
156 {
157 generate_edges(meshin);
158 std::unique_ptr<Mesh> mesh = create(2, non_conforming);
159 if (mesh->load(meshin))
160 {
161 mesh->in_ordered_vertices_ = Eigen::VectorXi::LinSpaced(meshin.vertices.nb(), 0, meshin.vertices.nb() - 1);
162 assert(mesh->in_ordered_vertices_[0] == 0);
163 assert(mesh->in_ordered_vertices_[1] == 1);
164 assert(mesh->in_ordered_vertices_[2] == 2);
165 assert(mesh->in_ordered_vertices_[mesh->in_ordered_vertices_.size() - 1] == meshin.vertices.nb() - 1);
166
167 mesh->in_ordered_edges_.resize(meshin.edges.nb(), 2);
168
169 for (int e = 0; e < (int)meshin.edges.nb(); ++e)
170 {
171 for (int lv = 0; lv < 2; ++lv)
172 {
173 mesh->in_ordered_edges_(e, lv) = meshin.edges.vertex(e, lv);
174 }
175 assert(mesh->in_ordered_edges_(e, 0) != mesh->in_ordered_edges_(e, 1));
176 }
177 assert(mesh->in_ordered_edges_.size() > 0);
178
179 mesh->in_ordered_faces_.resize(0, 0);
180
181 return mesh;
182 }
183 }
184 else
185 {
186 std::unique_ptr<Mesh> mesh = create(3, non_conforming);
187 meshin.cells.connect();
188 if (mesh->load(meshin))
189 {
190 mesh->in_ordered_vertices_ = Eigen::VectorXi::LinSpaced(meshin.vertices.nb(), 0, meshin.vertices.nb() - 1);
191 assert(mesh->in_ordered_vertices_[0] == 0);
192 assert(mesh->in_ordered_vertices_[1] == 1);
193 assert(mesh->in_ordered_vertices_[2] == 2);
194 assert(mesh->in_ordered_vertices_[mesh->in_ordered_vertices_.size() - 1] == meshin.vertices.nb() - 1);
195
196 mesh->in_ordered_edges_.resize(meshin.edges.nb(), 2);
197
198 for (int e = 0; e < (int)meshin.edges.nb(); ++e)
199 {
200 for (int lv = 0; lv < 2; ++lv)
201 {
202 mesh->in_ordered_edges_(e, lv) = meshin.edges.vertex(e, lv);
203 }
204 }
205 assert(mesh->in_ordered_edges_.size() > 0);
206
207 mesh->in_ordered_faces_.resize(meshin.facets.nb(), meshin.facets.nb_vertices(0));
208
209 for (int f = 0; f < (int)meshin.edges.nb(); ++f)
210 {
211 assert(mesh->in_ordered_faces_.cols() == meshin.facets.nb_vertices(f));
212
213 for (int lv = 0; lv < mesh->in_ordered_faces_.cols(); ++lv)
214 {
215 mesh->in_ordered_faces_(f, lv) = meshin.facets.vertex(f, lv);
216 }
217 }
218 assert(mesh->in_ordered_faces_.size() > 0);
219
220 return mesh;
221 }
222 }
223
224 logger().error("Failed to load mesh");
225 return nullptr;
226 }
227
228 std::unique_ptr<Mesh> Mesh::create(const std::string &path, const bool non_conforming)
229 {
230 if (!std::filesystem::exists(path))
231 {
232 logger().error(path.empty() ? "No mesh provided!" : "Mesh file does not exist: {}", path);
233 return nullptr;
234 }
235
236 std::string lowername = path;
237 std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
238
239 if (StringUtils::endswith(lowername, ".hybrid"))
240 {
241 std::unique_ptr<Mesh> mesh = create(3, non_conforming);
242 if (mesh->load(path))
243 {
244 // TODO add in_ordered_vertices_, in_ordered_edges_, in_ordered_faces_
245 return mesh;
246 }
247 }
248 else if (StringUtils::endswith(lowername, ".msh"))
249 {
250 Eigen::MatrixXd vertices;
251 Eigen::MatrixXi cells;
252 std::vector<std::vector<int>> elements;
253 std::vector<std::vector<double>> weights;
254 std::vector<int> body_ids;
255 std::vector<std::vector<int>> boundary_elements;
256 std::vector<int> boundary_ids;
257
258 if (!MshReader::load(path, vertices, cells, elements, weights, body_ids, boundary_elements, boundary_ids))
259 {
260 logger().error("Failed to load MSH mesh: {}", path);
261 return nullptr;
262 }
263
264 const int dim = vertices.cols();
265 std::unique_ptr<Mesh> mesh = create(vertices, cells, non_conforming);
266
267 // Only tris and tets
268 if ((dim == 2 && cells.cols() == 3) || (dim == 3 && cells.cols() == 4))
269 {
270 mesh->attach_higher_order_nodes(vertices, elements);
271 mesh->set_cell_weights(weights);
272 // TODO: not clear?
273 }
274
275 for (const auto &w : weights)
276 {
277 if (!w.empty())
278 {
279 mesh->set_is_rational(true);
280 break;
281 }
282 }
283
284 mesh->set_body_ids(body_ids);
285
286 if (!boundary_ids.empty())
287 {
288 std::unordered_map<std::vector<int>, int, HashVector> boundary_element_to_id;
289 for (int i = 0; i < boundary_elements.size(); ++i)
290 {
291 std::sort(boundary_elements[i].begin(), boundary_elements[i].end());
292 const auto [it, inserted] = boundary_element_to_id.emplace(boundary_elements[i], boundary_ids[i]);
293 if (!inserted && it->second != boundary_ids[i])
294 logger().warn("Gmsh side has multiple physical tags; using tag {}.", it->second);
295 }
296
297 int matched_boundaries = 0;
298 mesh->compute_boundary_ids([&](const size_t primitive_id, const std::vector<int> &vertices, const RowVectorNd &, const bool) {
299 std::vector<int> sorted_vertices = vertices;
300 std::sort(sorted_vertices.begin(), sorted_vertices.end());
301 const auto it = boundary_element_to_id.find(sorted_vertices);
302 if (it == boundary_element_to_id.end())
303 return mesh->get_default_boundary_id(primitive_id);
304 ++matched_boundaries;
305 return it->second;
306 });
307
308 if (matched_boundaries != boundary_element_to_id.size())
309 logger().warn(
310 "Unable to match {} of {} tagged Gmsh sides to mesh primitives.",
311 boundary_element_to_id.size() - matched_boundaries, boundary_element_to_id.size());
312 }
313
314 return mesh;
315 }
316 else
317 {
318 GEO::Mesh tmp;
319 if (GEO::mesh_load(path, tmp))
320 {
321 return create(tmp, non_conforming);
322 }
323 }
324 logger().error("Failed to load mesh: {}", path);
325 return nullptr;
326 }
327
328 std::unique_ptr<Mesh> Mesh::create(
329 const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &cells, const bool non_conforming)
330 {
331 const int dim = vertices.cols();
332
333 std::unique_ptr<Mesh> mesh = create(dim, non_conforming);
334
335 mesh->build_from_matrices(vertices, cells);
336
337 std::vector<int> tmp(cells.data(), cells.data() + cells.size());
338 std::sort(tmp.begin(), tmp.end());
339 tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
340
341 mesh->in_ordered_vertices_ = Eigen::Map<Eigen::VectorXi, Eigen::Unaligned>(tmp.data(), tmp.size());
342 // assert(mesh->in_ordered_vertices_[0] == 0);
343 // assert(mesh->in_ordered_vertices_[1] == 1);
344 // assert(mesh->in_ordered_vertices_[2] == 2);
345 // assert(mesh->in_ordered_vertices_[mesh->in_ordered_vertices_.size() - 1] == vertices.rows() - 1);
346
347 if (dim == 2)
348 {
349 std::unordered_set<std::pair<int, int>, HashPair> edges;
350 for (int f = 0; f < cells.rows(); ++f)
351 {
352 for (int lv = 0; lv < cells.cols(); ++lv)
353 {
354 const int v0 = cells(f, lv);
355 const int v1 = cells(f, (lv + 1) % cells.cols());
356 edges.emplace(std::pair<int, int>(std::min(v0, v1), std::max(v0, v1)));
357 }
358 }
359 mesh->in_ordered_edges_.resize(edges.size(), 2);
360 int index = 0;
361 for (auto it = edges.begin(); it != edges.end(); ++it)
362 {
363 mesh->in_ordered_edges_(index, 0) = it->first;
364 mesh->in_ordered_edges_(index, 1) = it->second;
365 ++index;
366 }
367
368 assert(mesh->in_ordered_edges_.size() > 0);
369
370 mesh->in_ordered_faces_.resize(0, 0);
371 }
372 else
373 {
374 if (cells.cols() == 4)
375 {
376 get_faces(cells, mesh->in_ordered_faces_);
377 igl::edges(mesh->in_ordered_faces_, mesh->in_ordered_edges_);
378 }
379 // else TODO
380 }
381
382 return mesh;
383 }
384
386
387 void Mesh::edge_barycenters(Eigen::MatrixXd &barycenters) const
388 {
389 barycenters.resize(n_edges(), dimension());
390 for (int e = 0; e < n_edges(); ++e)
391 {
392 barycenters.row(e) = edge_barycenter(e);
393 }
394 }
395
396 void Mesh::face_barycenters(Eigen::MatrixXd &barycenters) const
397 {
398 barycenters.resize(n_faces(), dimension());
399 for (int f = 0; f < n_faces(); ++f)
400 {
401 barycenters.row(f) = face_barycenter(f);
402 }
403 }
404
405 void Mesh::cell_barycenters(Eigen::MatrixXd &barycenters) const
406 {
407 barycenters.resize(n_cells(), dimension());
408 for (int c = 0; c < n_cells(); ++c)
409 {
410 barycenters.row(c) = cell_barycenter(c);
411 }
412 }
413
415
416 // Queries on the tags
417 bool Mesh::is_spline_compatible(const int el_id) const
418 {
419 if (is_volume())
420 {
423 // || elements_tag_[el_id] == ElementType::SIMPLE_SINGULAR_INTERIOR_CUBE
424 // || elements_tag_[el_id] == ElementType::SIMPLE_SINGULAR_BOUNDARY_CUBE;
425 }
426 else
427 {
430 // || elements_tag_[el_id] == ElementType::INTERFACE_CUBE
431 // || elements_tag_[el_id] == ElementType::SIMPLE_SINGULAR_INTERIOR_CUBE;
432 }
433 }
434
435 // -----------------------------------------------------------------------------
436
447
448 // -----------------------------------------------------------------------------
449
450 bool Mesh::is_polytope(const int el_id) const
451 {
454 }
455
456 void Mesh::update_nodes(const Eigen::VectorXi &in_node_to_node)
457 {
458 if (in_node_to_node.size() <= 0 || node_ids_.empty())
459 {
460 node_ids_.clear();
461 return;
462 }
463
464 const auto tmp = node_ids_;
465
466 for (int n = 0; n < n_vertices(); ++n)
467 {
468 node_ids_[in_node_to_node[n]] = tmp[n];
469 }
470 }
471
472 void Mesh::compute_node_ids(const std::function<int(const size_t, const RowVectorNd &, bool)> &marker)
473 {
474 node_ids_.resize(n_vertices());
475
476 for (int n = 0; n < n_vertices(); ++n)
477 {
478 bool is_boundary = is_boundary_vertex(n);
479 const auto p = point(n);
480 node_ids_[n] = marker(n, p, is_boundary);
481 }
482 }
483
484 void Mesh::load_boundary_ids(const std::string &path)
485 {
487
488 std::ifstream file(path);
489
490 std::string line;
491 int bindex = 0;
492 while (std::getline(file, line))
493 {
494 std::istringstream iss(line);
495 int v;
496 iss >> v;
497 boundary_ids_[bindex] = v;
498
499 ++bindex;
500 }
501
502 assert(boundary_ids_.size() == size_t(bindex));
503
504 file.close();
505 }
506
507 bool Mesh::is_simplex(const int el_id) const
508 {
509 return elements_tag_[el_id] == ElementType::SIMPLEX;
510 }
511
512 bool Mesh::is_prism(const int el_id) const
513 {
514 return elements_tag_[el_id] == ElementType::PRISM;
515 }
516
517 bool Mesh::is_pyramid(const int el_id) const
518 {
519 return elements_tag_[el_id] == ElementType::PYRAMID;
520 }
521
522 std::vector<std::pair<int, int>> Mesh::edges() const
523 {
524 std::vector<std::pair<int, int>> res;
525 res.reserve(n_edges());
526
527 for (int e_id = 0; e_id < n_edges(); ++e_id)
528 {
529 const int e0 = edge_vertex(e_id, 0);
530 const int e1 = edge_vertex(e_id, 1);
531
532 res.emplace_back(std::min(e0, e1), std::max(e0, e1));
533 }
534
535 return res;
536 }
537
538 std::vector<std::vector<int>> Mesh::faces() const
539 {
540 std::vector<std::vector<int>> res(n_faces());
541
542 for (int f_id = 0; f_id < n_faces(); ++f_id)
543 {
544 auto &tmp = res[f_id];
545 for (int lv_id = 0; lv_id < n_face_vertices(f_id); ++lv_id)
546 tmp.push_back(face_vertex(f_id, lv_id));
547
548 std::sort(tmp.begin(), tmp.end());
549 }
550
551 return res;
552 }
553
554 std::unordered_map<std::pair<int, int>, size_t, HashPair> Mesh::edges_to_ids() const
555 {
556 std::unordered_map<std::pair<int, int>, size_t, HashPair> res;
557 res.reserve(n_edges());
558
559 for (int e_id = 0; e_id < n_edges(); ++e_id)
560 {
561 const int e0 = edge_vertex(e_id, 0);
562 const int e1 = edge_vertex(e_id, 1);
563
564 res[std::pair<int, int>(std::min(e0, e1), std::max(e0, e1))] = e_id;
565 }
566
567 return res;
568 }
569
570 std::unordered_map<std::vector<int>, size_t, HashVector> Mesh::faces_to_ids() const
571 {
572 std::unordered_map<std::vector<int>, size_t, HashVector> res;
573 res.reserve(n_faces());
574
575 for (int f_id = 0; f_id < n_faces(); ++f_id)
576 {
577 std::vector<int> f;
578 f.reserve(n_face_vertices(f_id));
579 for (int lv_id = 0; lv_id < n_face_vertices(f_id); ++lv_id)
580 f.push_back(face_vertex(f_id, lv_id));
581 std::sort(f.begin(), f.end());
582
583 res[f] = f_id;
584 }
585
586 return res;
587 }
588
589 void Mesh::append(const Mesh &mesh)
590 {
591 const int n_vertices = this->n_vertices();
592
593 elements_tag_.insert(elements_tag_.end(), mesh.elements_tag_.begin(), mesh.elements_tag_.end());
594
595 // --------------------------------------------------------------------
596
597 // Initialize node_ids_ if it is not initialized yet.
598 if (!has_node_ids() && mesh.has_node_ids())
599 {
600 node_ids_.resize(n_vertices);
601 for (int i = 0; i < node_ids_.size(); ++i)
602 node_ids_[i] = get_node_id(i); // results in default if node_ids_ is empty
603 }
604
605 if (mesh.has_node_ids())
606 {
607 node_ids_.insert(node_ids_.end(), mesh.node_ids_.begin(), mesh.node_ids_.end());
608 }
609 else if (has_node_ids()) // && !mesh.has_node_ids()
610 {
611 node_ids_.resize(n_vertices + mesh.n_vertices());
612 for (int i = 0; i < mesh.n_vertices(); ++i)
613 node_ids_[n_vertices + i] = mesh.get_node_id(i); // results in default if node_ids_ is empty
614 }
615
616 assert(node_ids_.empty() || node_ids_.size() == n_vertices + mesh.n_vertices());
617
618 // --------------------------------------------------------------------
619
620 // Initialize boundary_ids_ if it is not initialized yet.
621 if (!has_boundary_ids() && mesh.has_boundary_ids())
622 {
624 for (int i = 0; i < boundary_ids_.size(); ++i)
626 }
627
628 if (mesh.has_boundary_ids())
629 {
630 boundary_ids_.insert(boundary_ids_.end(), mesh.boundary_ids_.begin(), mesh.boundary_ids_.end());
631 }
632 else if (has_boundary_ids()) // && !mesh.has_boundary_ids()
633 {
635 for (int i = 0; i < mesh.n_boundary_elements(); ++i)
636 boundary_ids_[n_boundary_elements() + i] = mesh.get_boundary_id(i); // results in default if mesh.boundary_ids_ is empty
637 }
638
639 // --------------------------------------------------------------------
640
641 // Initialize body_ids_ if it is not initialized yet.
642 if (!has_body_ids() && mesh.has_body_ids())
643 body_ids_ = std::vector<int>(n_elements(), 0); // 0 is the default body_id
644
645 if (mesh.has_body_ids())
646 body_ids_.insert(body_ids_.end(), mesh.body_ids_.begin(), mesh.body_ids_.end());
647 else if (has_body_ids()) // && !mesh.has_body_ids()
648 body_ids_.resize(n_elements() + mesh.n_elements(), 0); // 0 is the default body_id
649
650 // --------------------------------------------------------------------
651 // Initialize geometry_ids_ if it is not initialized yet.
652 if (!has_geometry_ids() && mesh.has_geometry_ids())
653 geometry_ids_ = std::vector<int>(n_elements(), 0);
654
655 if (mesh.has_geometry_ids())
656 geometry_ids_.insert(geometry_ids_.end(), mesh.geometry_ids_.begin(), mesh.geometry_ids_.end());
657 else if (has_geometry_ids())
658 geometry_ids_.resize(n_elements() + mesh.n_elements(), 0);
659
660 // --------------------------------------------------------------------
661
662 if (orders_.size() == 0)
663 orders_.setOnes(n_elements(), 1);
664 Eigen::MatrixXi mesh_orders = mesh.orders_;
665 if (mesh_orders.size() == 0)
666 mesh_orders.setOnes(mesh.n_elements(), 1);
667 assert(orders_.cols() == mesh_orders.cols());
668 orders_.conservativeResize(orders_.rows() + mesh_orders.rows(), orders_.cols());
669 orders_.bottomRows(mesh_orders.rows()) = mesh_orders;
670
672
673 // --------------------------------------------------------------------
674 for (const auto &n : mesh.edge_nodes_)
675 {
676 auto tmp = n;
677 tmp.v1 += n_vertices;
678 tmp.v2 += n_vertices;
679 edge_nodes_.push_back(tmp);
680 }
681 for (const auto &n : mesh.face_nodes_)
682 {
683 auto tmp = n;
684 tmp.v1 += n_vertices;
685 tmp.v2 += n_vertices;
686 tmp.v3 += n_vertices;
687 face_nodes_.push_back(tmp);
688 }
689 for (const auto &n : mesh.cell_nodes_)
690 {
691 auto tmp = n;
692 tmp.v1 += n_vertices;
693 tmp.v2 += n_vertices;
694 tmp.v3 += n_vertices;
695 tmp.v4 += n_vertices;
696 cell_nodes_.push_back(tmp);
697 }
698 cell_weights_.insert(cell_weights_.end(), mesh.cell_weights_.begin(), mesh.cell_weights_.end());
699 // --------------------------------------------------------------------
700
701 assert(in_ordered_vertices_.cols() == mesh.in_ordered_vertices_.cols());
702 in_ordered_vertices_.conservativeResize(in_ordered_vertices_.rows() + mesh.in_ordered_vertices_.rows(), in_ordered_vertices_.cols());
703 in_ordered_vertices_.bottomRows(mesh.in_ordered_vertices_.rows()) = mesh.in_ordered_vertices_.array() + n_vertices;
704
705 if (in_ordered_edges_.size() == 0 || mesh.in_ordered_edges_.size() == 0)
706 in_ordered_edges_.resize(0, 0);
707 else
708 {
709 assert(in_ordered_edges_.cols() == mesh.in_ordered_edges_.cols());
711 }
712
713 if (in_ordered_faces_.size() == 0 || mesh.in_ordered_faces_.size() == 0)
714 in_ordered_faces_.resize(0, 0);
715 else
716 {
717 assert(in_ordered_faces_.cols() == mesh.in_ordered_faces_.cols());
719 }
720 }
721
722 namespace
723 {
724 template <typename T>
725 void transform_high_order_nodes(std::vector<T> &nodes, const MatrixNd &A, const VectorNd &b)
726 {
727 for (T &n : nodes)
728 {
729 if (n.nodes.size())
730 {
731 n.nodes = (n.nodes * A.transpose()).rowwise() + b.transpose();
732 }
733 }
734 }
735 } // namespace
736
738 {
739 for (int i = 0; i < n_vertices(); ++i)
740 {
741 VectorNd p = point(i).transpose();
742 p = A * p + b;
743 set_point(i, p.transpose());
744 }
745
746 transform_high_order_nodes(edge_nodes_, A, b);
747 transform_high_order_nodes(face_nodes_, A, b);
748 transform_high_order_nodes(cell_nodes_, A, b);
749 }
750} // namespace polyfem::mesh
Eigen::RowVectorXd point
std::vector< std::pair< int, double > > weights
static bool load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector< std::vector< int > > &elements, std::vector< std::vector< double > > &weights, std::vector< int > &body_ids)
Definition MshReader.cpp:43
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:49
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:174
virtual int n_vertices() const =0
number of vertices
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Definition Mesh.cpp:450
Eigen::MatrixXi orders_
list of geometry orders, one per cell
Definition Mesh.hpp:738
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
Definition Mesh.cpp:554
std::vector< ElementType > elements_tag_
list of element types
Definition Mesh.hpp:728
virtual RowVectorNd edge_barycenter(const int e) const =0
edge barycenter
bool is_rational_
stores if the mesh is rational
Definition Mesh.hpp:740
bool has_boundary_ids() const
checks if surface selections are available
Definition Mesh.hpp:561
void cell_barycenters(Eigen::MatrixXd &barycenters) const
all cells barycenters
Definition Mesh.cpp:405
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.
Definition Mesh.hpp:547
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
Definition Mesh.cpp:437
bool has_node_ids() const
checks if points selections are available
Definition Mesh.hpp:557
Eigen::MatrixXi in_ordered_faces_
Order of the input faces, TODO: change to std::vector of Eigen::Vector.
Definition Mesh.hpp:756
void compute_node_ids(const std::function< int(const size_t, const RowVectorNd &, bool)> &marker)
computes boundary selections based on a function
Definition Mesh.cpp:472
virtual int get_boundary_id(const int primitive) const
Get the boundary selection of an element (face in 3d, edge in 2d)
Definition Mesh.hpp:499
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
Definition Mesh.cpp:507
void face_barycenters(Eigen::MatrixXd &barycenters) const
all face barycenters
Definition Mesh.cpp:396
virtual bool has_body_ids() const
checks if volumes selections are available
Definition Mesh.hpp:565
bool is_spline_compatible(const int el_id) const
checks if element is spline compatible
Definition Mesh.cpp:417
virtual void load_boundary_ids(const std::string &path)
loads the boundary selections for a file
Definition Mesh.cpp:484
std::vector< int > geometry_ids_
list of geometry labels, one per top-dimensional element
Definition Mesh.hpp:736
std::vector< int > boundary_ids_
list of surface labels
Definition Mesh.hpp:732
bool is_prism(const int el_id) const
checks if element is a prism
Definition Mesh.cpp:512
void apply_affine_transformation(const MatrixNd &A, const VectorNd &b)
Apply an affine transformation to the vertex positions .
Definition Mesh.cpp:737
std::vector< int > node_ids_
list of node labels
Definition Mesh.hpp:730
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
Definition Mesh.cpp:570
std::vector< CellNodes > cell_nodes_
high-order nodes associates to cells
Definition Mesh.hpp:747
std::vector< std::vector< double > > cell_weights_
weights associates to cells for rational polynomail meshes
Definition Mesh.hpp:749
virtual bool is_volume() const =0
checks if mesh is volume
std::vector< std::pair< int, int > > edges() const
list of sorted edges.
Definition Mesh.cpp:522
void update_nodes(const Eigen::VectorXi &in_node_to_node)
Update the node ids to reorder them.
Definition Mesh.cpp:456
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.
std::vector< MeshWithID > split() const
Split the mesh according to its per-element geometry IDs.
Definition Mesh.cpp:35
std::vector< int > body_ids_
list of volume labels
Definition Mesh.hpp:734
static std::unique_ptr< Mesh > create(const std::string &path, const bool non_conforming=false)
factory to build the proper mesh
Definition Mesh.cpp:228
virtual int get_default_boundary_id(const int primitive) const
Get the default boundary selection of an element (face in 3d, edge in 2d)
Definition Mesh.hpp:487
int dimension() const
utily for dimension
Definition Mesh.hpp:164
virtual int n_cells() const =0
number of cells
std::vector< FaceNodes > face_nodes_
high-order nodes associates to faces
Definition Mesh.hpp:745
virtual int n_faces() const =0
number of faces
std::vector< EdgeNodes > edge_nodes_
high-order nodes associates to edges
Definition Mesh.hpp:743
std::vector< std::vector< int > > faces() const
list of sorted faces.
Definition Mesh.cpp:538
int n_boundary_elements() const
utitlity to return the number of boundary elements, faces or edges in 3d and 2d
Definition Mesh.hpp:179
void filter_element_data(const std::vector< bool > &keep)
Definition Mesh.cpp:55
Eigen::MatrixXi in_ordered_edges_
Order of the input edges.
Definition Mesh.hpp:754
virtual void append(const Mesh &mesh)
appends a new mesh to the end of this
Definition Mesh.cpp:589
bool is_pyramid(const int el_id) const
checks if element is a pyramid
Definition Mesh.cpp:517
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
Definition Mesh.cpp:387
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.
Definition Mesh.hpp:752
bool has_geometry_ids() const
Definition Mesh.hpp:553
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:508
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).
Definition MeshUtils.cpp:35
@ 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 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.
Definition Logger.cpp:44
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Definition Types.hpp:11
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor, 3, 3 > MatrixNd
Definition Types.hpp:14
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13