PolyFEM
Loading...
Searching...
No Matches
LocalMesh.cpp
Go to the documentation of this file.
1#include "LocalMesh.hpp"
2
7
8#include <paraviewo/VTUWriter.hpp>
9
10#include <wmtk/utils/TupleUtils.hpp>
11
12#include <igl/boundary_facets.h>
13#include <igl/edges.h>
14#include <igl/PI.h>
15
16#include <SimpleBVH/BVH.hpp>
17
18namespace polyfem::mesh
19{
20 namespace
21 {
22 // Assert there are no duplicates in elements
23 template <typename M>
24 bool has_duplicate_elements(const M &m, const std::vector<typename M::Tuple> &elements)
25 {
26 std::vector<size_t> ids;
27 for (const auto &t : elements)
28 ids.push_back(m.element_id(t));
29 std::sort(ids.begin(), ids.end());
30 ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
31 return ids.size() != elements.size();
32 }
33 } // namespace
34
37
38 void unique_facet_tuples(const wmtk::TriMesh &m, std::vector<wmtk::TriMesh::Tuple> &tuples)
39 {
40 wmtk::unique_edge_tuples(m, tuples);
41 }
42
43 void unique_facet_tuples(const wmtk::TetMesh &m, std::vector<wmtk::TetMesh::Tuple> &tuples)
44 {
45 wmtk::unique_face_tuples(m, tuples);
46 }
47
48 template <typename M>
50 const M &m,
51 const std::vector<Tuple> &element_tuples,
52 const bool include_global_boundary)
53 {
54 POLYFEM_SCOPED_TIMER("LocalMesh::LocalMesh");
55
56 std::unordered_set<size_t> global_element_ids;
57 {
58 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::LocalMesh -> init m_elements");
59 m_elements.resize(element_tuples.size(), m.dim() + 1);
60 m_body_ids.reserve(element_tuples.size());
61 for (int fi = 0; fi < num_elements(); fi++)
62 {
63 const Tuple &elem = element_tuples[fi];
64 global_element_ids.insert(m.element_id(elem));
65
66 const auto vids = m.element_vids(elem);
67
68 for (int i = 0; i < vids.size(); ++i)
69 {
70 if (m_global_to_local.find(vids[i]) == m_global_to_local.end())
71 m_global_to_local[vids[i]] = m_global_to_local.size();
72 m_elements(fi, i) = m_global_to_local[vids[i]];
73 }
74
75 m_body_ids.push_back(m.element_attrs[m.element_id(elem)].body_id);
76 }
77 }
78 // The above puts local vertices at front
79 m_num_local_vertices = m_global_to_local.size();
80
81 // ---------------------------------------------------------------------
82
83 std::vector<Tuple> local_boundary_facets;
84 {
85 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::LocalMesh -> init m_fixed_vertices");
86 for (int fi = 0; fi < num_elements(); fi++)
87 {
88 const Tuple &elem = element_tuples[fi];
89
90 for (int i = 0; i < M::FACETS_PER_ELEMENT; ++i)
91 {
92 const Tuple facet = m.tuple_from_facet(m.element_id(elem), i);
93
94 // Only fix internal facets
95 if (m.is_boundary_facet(facet))
96 {
97 local_boundary_facets.push_back(facet);
98 continue;
99 }
100
101 size_t adjacent_tid;
102 if constexpr (std::is_same_v<M, TriMesh>)
103 adjacent_tid = facet.switch_face(m)->fid(m);
104 else
105 adjacent_tid = facet.switch_tetrahedron(m)->tid(m);
106
107 // Only fix internal facets that do not have a neighbor in the local mesh
108 if (global_element_ids.find(adjacent_tid) != global_element_ids.end())
109 continue;
110
111 for (const size_t &vid : m.facet_vids(facet))
112 m_fixed_vertices.push_back(m_global_to_local[vid]);
113 }
114 }
115 }
116
117 // ---------------------------------------------------------------------
118 {
119 // Only build boundary ids for the local facets
120 std::vector<Tuple> facets_tuples;
121 facets_tuples.reserve(num_elements() * M::FACETS_PER_ELEMENT);
122 std::vector<Tuple> local_boundary_facets;
123 for (const Tuple &elem : element_tuples)
124 for (int i = 0; i < M::FACETS_PER_ELEMENT; ++i)
125 facets_tuples.push_back(m.tuple_from_facet(m.element_id(elem), i));
126 unique_facet_tuples(m, facets_tuples);
127 if constexpr (std::is_same_v<M, TriMesh>)
128 m_boundary_ids = Remesher::EdgeMap<int>();
129 else
130 m_boundary_ids = Remesher::FaceMap<int>();
131 for (const Tuple &t : facets_tuples)
132 {
133 auto vids = m.facet_vids(t);
134 for (auto &v : vids)
135 v = m_global_to_local[v];
136
137 const int boundary_id = m.boundary_attrs[m.facet_id(t)].boundary_id;
138
139 if constexpr (std::is_same_v<M, TriMesh>)
140 std::get<Remesher::EdgeMap<int>>(m_boundary_ids)[vids] = boundary_id;
141 else
142 std::get<Remesher::FaceMap<int>>(m_boundary_ids)[vids] = boundary_id;
143 }
144 }
145 // ---------------------------------------------------------------------
146
147 if (include_global_boundary)
148 {
149 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::LocalMesh -> include_global_boundary");
150 // Copy the global to local map so we can check if a vertex is new
151 const std::unordered_map<int, int> prev_global_to_local = m_global_to_local;
152
153 const std::vector<Tuple> global_boundary_facets = m.boundary_facets();
154
155 boundary_facets().resize(global_boundary_facets.size(), m_elements.cols() - 1);
156 for (int i = 0; i < global_boundary_facets.size(); i++)
157 {
158 const Tuple &facet = global_boundary_facets[i];
159 const auto vids = m.facet_vids(facet);
160
161 const bool is_new_facet = std::any_of(vids.begin(), vids.end(), [&](size_t vid) {
162 return prev_global_to_local.find(vid) == prev_global_to_local.end();
163 });
164
165 for (int j = 0; j < vids.size(); ++j)
166 {
167 if (m_global_to_local.find(vids[j]) == m_global_to_local.end())
168 m_global_to_local[vids[j]] = m_global_to_local.size();
169
170 boundary_facets()(i, j) = m_global_to_local[vids[j]];
171
172 if (is_new_facet)
173 m_fixed_vertices.push_back(boundary_facets()(i, j));
174 }
175 }
176 }
177 else
178 {
179 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::LocalMesh -> !include_global_boundary");
180
181 unique_facet_tuples(m, local_boundary_facets);
182 boundary_facets().resize(local_boundary_facets.size(), m_elements.cols() - 1);
183 for (int i = 0; i < local_boundary_facets.size(); i++)
184 {
185 const auto vids = m.facet_vids(local_boundary_facets[i]);
186 for (int j = 0; j < vids.size(); ++j)
187 boundary_facets()(i, j) = m_global_to_local[vids[j]];
188 }
189 }
190
191 if (m_boundary_faces.rows() > 0)
192 igl::edges(m_boundary_faces, m_boundary_edges);
193
194 remove_duplicate_fixed_vertices();
195
196 // ---------------------------------------------------------------------
197
198 init_vertex_attributes(m);
199
200 init_local_to_global();
201
202 const int tmp_num_vertices = num_vertices();
203
204 // Include the obstacle as part of the local mesh if including the global boundary
205 if (include_global_boundary && m.obstacle().n_vertices() > 0)
206 {
207 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::LocalMesh -> append obstacles");
208 const Obstacle &obstacle = m.obstacle();
209 utils::append_rows(m_rest_positions, obstacle.v());
210 utils::append_rows(m_positions, obstacle.v() + m.obstacle_displacements());
211 utils::append_rows(m_projection_quantities, m.obstacle_quantities());
212 if (obstacle.n_edges() > 0)
213 utils::append_rows(m_boundary_edges, obstacle.e().array() + tmp_num_vertices);
214 if (obstacle.n_faces() > 0)
215 utils::append_rows(m_boundary_faces, obstacle.f().array() + tmp_num_vertices);
216
217 for (int i = 0; i < obstacle.n_vertices(); i++)
218 m_fixed_vertices.push_back(i + tmp_num_vertices);
219 }
220 }
221
222 template <typename M>
223 std::vector<typename M::Tuple> LocalMesh<M>::n_ring(
224 const M &m, const Tuple &center, const int n)
225 {
226 return n_ring(m, m.get_one_ring_elements_for_vertex(center), n);
227 }
228
229 template <typename M>
230 std::vector<typename M::Tuple> LocalMesh<M>::n_ring(
231 const M &m, const std::vector<Tuple> &one_ring, const int n)
232 {
233 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::n_ring");
234 assert(!has_duplicate_elements(m, one_ring));
235
236 std::vector<Tuple> elements = one_ring;
237 std::unordered_set<size_t> visited_vertices;
238 std::unordered_set<size_t> visited_elements;
239 for (const auto &element : elements)
240 {
241 visited_elements.insert(m.element_id(element));
242 }
243
244 std::vector<Tuple> new_elements = elements;
245
246 for (int i = 1; i < n; i++)
247 {
248 std::vector<Tuple> new_new_elements;
249 for (const auto &elem : new_elements)
250 {
251 for (const Tuple &v : m.element_vertices(elem))
252 {
253 if (visited_vertices.find(v.vid(m)) != visited_vertices.end())
254 continue;
255 visited_vertices.insert(v.vid(m));
256
257 std::vector<Tuple> tmp = m.get_one_ring_elements_for_vertex(v);
258 for (auto &t1 : tmp)
259 {
260 if (visited_elements.find(m.element_id(t1)) != visited_elements.end())
261 continue;
262 visited_elements.insert(m.element_id(t1));
263 elements.push_back(t1);
264 new_new_elements.push_back(t1);
265 }
266 }
267 }
268 new_elements = new_new_elements;
269 if (new_elements.empty())
270 break;
271 }
272
273 assert(!has_duplicate_elements(m, elements));
274
275 return elements;
276 }
277
278 template <typename M>
279 std::vector<typename M::Tuple> LocalMesh<M>::flood_fill_n_ring(
280 const M &m, const Tuple &center, const double area)
281 {
282 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::flood_fill_n_ring");
283
284 double current_area = 0;
285
286 std::vector<Tuple> elements = m.get_one_ring_elements_for_vertex(center);
287 std::unordered_set<size_t> visited_vertices{{center.vid(m)}};
288 std::unordered_set<size_t> visited_faces;
289 for (const auto &element : elements)
290 visited_faces.insert(m.element_id(element));
291
292 std::vector<Tuple> new_elements = elements;
293
294 int n_ring = 0;
295 while (current_area < area)
296 {
297 n_ring++;
298 std::vector<Tuple> new_new_elements;
299 for (const auto &elem : new_elements)
300 {
301 current_area += m.element_volume(elem);
302 const auto vs = m.element_vertices(elem);
303 for (int vi = 0; vi < 3; vi++)
304 {
305 const Tuple &v = vs[vi];
306 if (visited_vertices.find(v.vid(m)) != visited_vertices.end())
307 continue;
308 visited_vertices.insert(v.vid(m));
309
310 std::vector<Tuple> tmp = m.get_one_ring_elements_for_vertex(v);
311 for (auto &t1 : tmp)
312 {
313 if (visited_faces.find(m.element_id(t1)) != visited_faces.end())
314 continue;
315 visited_faces.insert(m.element_id(t1));
316 elements.push_back(t1);
317 new_new_elements.push_back(t1);
318 }
319 }
320 }
321 new_elements = new_new_elements;
322 if (new_elements.empty())
323 break;
324 }
325 // logger().critical("target_area={:g} area={:g} n_ring={}", area, current_area, n_ring);
326
327 return elements;
328 }
329
330 template <typename M>
331 std::vector<typename M::Tuple> LocalMesh<M>::ball_selection(
332 const M &m, const VectorNd &center, const double volume, const int n_ring_size)
333 {
334 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::ball_selection");
335
336 const int dim = m.dim();
337 const double radius = dim == 2 ? std::sqrt(volume / igl::PI) : std::cbrt(0.75 * volume / igl::PI);
338 constexpr double eps_radius = 1e-10;
339 const VectorNd sphere_min = center.array() - radius;
340 const VectorNd sphere_max = center.array() + radius;
341
342 const std::vector<Tuple> elements = m.get_elements();
343
344 // ---------------------------------------------------------------------
345
346 // Loop over all elements and find those that intersect with the ball
347 std::vector<Tuple> intersecting_elements;
348 std::unordered_set<size_t> intersecting_fid;
349 double intersecting_volume = 0;
350 std::vector<Tuple> one_ring;
351 for (const Tuple &element : elements)
352 {
353 VectorNd el_min, el_max;
354 m.element_aabb(element, el_min, el_max);
355
356 // Quick AABB check to see if the element intersects the sphere
357 if (!utils::are_aabbs_intersecting(sphere_min, sphere_max, el_min, el_max))
358 continue;
359
360 // Accurate check to see if the element intersects the sphere
361 const auto vids = m.element_vids(element);
362 if constexpr (std::is_same_v<M, TriMesh>)
363 {
365 m.vertex_attrs[vids[0]].rest_position,
366 m.vertex_attrs[vids[1]].rest_position,
367 m.vertex_attrs[vids[2]].rest_position,
368 center, radius))
369 {
370 continue;
371 }
372 }
373 else
374 {
375 static_assert(std::is_same_v<M, TetMesh>);
377 m.vertex_attrs[vids[0]].rest_position,
378 m.vertex_attrs[vids[1]].rest_position,
379 m.vertex_attrs[vids[2]].rest_position,
380 m.vertex_attrs[vids[3]].rest_position,
381 center, radius))
382 {
383 continue;
384 }
385 }
386
387 intersecting_elements.push_back(element);
388 intersecting_fid.insert(m.element_id(element));
389 intersecting_volume += m.element_volume(element);
390
391 // Accurate check to see if the element intersects the center point
392 if constexpr (std::is_same_v<M, TriMesh>)
393 {
395 m.vertex_attrs[vids[0]].rest_position,
396 m.vertex_attrs[vids[1]].rest_position,
397 m.vertex_attrs[vids[2]].rest_position,
398 center, eps_radius))
399 {
400 continue;
401 }
402 }
403 else
404 {
405 static_assert(std::is_same_v<M, TetMesh>);
407 m.vertex_attrs[vids[0]].rest_position,
408 m.vertex_attrs[vids[1]].rest_position,
409 m.vertex_attrs[vids[2]].rest_position,
410 m.vertex_attrs[vids[3]].rest_position,
411 center, eps_radius))
412 {
413 continue;
414 }
415 }
416
417 one_ring.push_back(element);
418 }
419 assert(!intersecting_elements.empty());
420
421 // ---------------------------------------------------------------------
422
423 // Flood fill to fill out the desired volume
424 for (int i = 0; intersecting_volume < volume && i < intersecting_elements.size(); ++i)
425 {
426 const size_t element_id = m.element_id(intersecting_elements[i]);
427
428 for (int j = 0; j < M::FACETS_PER_ELEMENT; ++j)
429 {
430 const Tuple facet = m.tuple_from_facet(element_id, j);
431
432 std::optional<Tuple> neighbor;
433 if constexpr (std::is_same_v<M, TriMesh>)
434 neighbor = facet.switch_face(m);
435 else
436 neighbor = facet.switch_tetrahedron(m);
437
438 if (!neighbor.has_value() || intersecting_fid.find(m.element_id(neighbor.value())) != intersecting_fid.end())
439 continue;
440
441 intersecting_elements.push_back(neighbor.value());
442 intersecting_fid.insert(m.element_id(neighbor.value()));
443 intersecting_volume += m.element_volume(neighbor.value());
444 }
445 }
446 assert(intersecting_volume >= volume);
447
448 // ---------------------------------------------------------------------
449
450 // Expand the ball to include the n-ring
451 for (const auto &e : n_ring(m, one_ring, n_ring_size))
452 {
453 if (intersecting_fid.find(m.element_id(e)) == intersecting_fid.end())
454 {
455 intersecting_elements.push_back(e);
456 intersecting_fid.insert(m.element_id(e));
457 intersecting_volume += m.element_volume(e);
458 }
459 }
460
461 assert(!has_duplicate_elements(m, intersecting_elements));
462
463 return intersecting_elements;
464 }
465
466 template <typename M>
468 {
469 if constexpr (std::is_same_v<M, TriMesh>)
470 return m_boundary_edges;
471 else
472 return m_boundary_faces;
473 }
474
475 template <typename M>
476 const Eigen::MatrixXi &LocalMesh<M>::boundary_facets() const
477 {
478 if constexpr (std::is_same_v<M, TriMesh>)
479 return m_boundary_edges;
480 else
481 return m_boundary_faces;
482 }
483
484 template <typename M>
486 {
487 std::sort(m_fixed_vertices.begin(), m_fixed_vertices.end());
488 auto new_end = std::unique(m_fixed_vertices.begin(), m_fixed_vertices.end());
489 m_fixed_vertices.erase(new_end, m_fixed_vertices.end());
490 }
491
492 template <typename M>
494 {
495 m_local_to_global.resize(m_global_to_local.size(), -1);
496 for (const auto &[glob_vi, loc_vi] : m_global_to_local)
497 {
498 assert(loc_vi < m_local_to_global.size());
499 m_local_to_global[loc_vi] = glob_vi;
500 }
501 }
502
503 template <typename M>
505 {
506 const int num_vertices = m_global_to_local.size();
507 const int dim = m.dim();
508
509 m_rest_positions.resize(num_vertices, dim);
510 m_positions.resize(num_vertices, dim);
511 m_projection_quantities.resize(num_vertices * dim, m.n_quantities());
512
513 for (const auto &[glob_vi, loc_vi] : m_global_to_local)
514 {
515 m_rest_positions.row(loc_vi) = m.vertex_attrs[glob_vi].rest_position;
516 m_positions.row(loc_vi) = m.vertex_attrs[glob_vi].position;
517 m_projection_quantities.middleRows(dim * loc_vi, dim) =
518 m.vertex_attrs[glob_vi].projection_quantities;
519 }
520 }
521
522 template <typename M>
523 void LocalMesh<M>::reorder_vertices(const Eigen::VectorXi &permutation)
524 {
525 assert(permutation.size() == num_vertices());
526 m_rest_positions = utils::reorder_matrix(m_rest_positions, permutation);
527 m_positions = utils::reorder_matrix(m_positions, permutation);
528
529 m_projection_quantities = utils::reorder_matrix(
530 m_projection_quantities, permutation, /*out_blocks=*/-1,
531 /*block_size=*/M::DIM);
532
533 m_elements = utils::map_index_matrix(m_elements, permutation);
534 m_boundary_edges = utils::map_index_matrix(m_boundary_edges, permutation);
535 m_boundary_faces = utils::map_index_matrix(m_boundary_faces, permutation);
536
537 for (auto &[glob_vi, loc_vi] : m_global_to_local)
538 loc_vi = permutation[loc_vi];
539
540 std::vector<int> new_local_to_global(m_local_to_global.size());
541 for (int i = 0; i < m_local_to_global.size(); ++i)
542 new_local_to_global[permutation[i]] = m_local_to_global[i];
543 m_local_to_global = new_local_to_global;
544
545 for (int &vi : m_fixed_vertices)
546 vi = permutation[vi];
547
548 if constexpr (std::is_same_v<M, TriMesh>)
549 {
550 const Remesher::EdgeMap<int> &old_boundary_ids = std::get<Remesher::EdgeMap<int>>(m_boundary_ids);
551 Remesher::EdgeMap<int> new_boundary_ids;
552 for (const auto &[e, id] : old_boundary_ids)
553 {
554 const size_t v0 = permutation[e[0]], v1 = permutation[e[1]];
555 new_boundary_ids[{{v0, v1}}] = id;
556 }
557 m_boundary_ids = new_boundary_ids;
558 }
559 else
560 {
561 const Remesher::FaceMap<int> &old_boundary_ids = std::get<Remesher::FaceMap<int>>(m_boundary_ids);
562 Remesher::FaceMap<int> new_boundary_ids;
563 for (const auto &[f, id] : old_boundary_ids)
564 {
565 const size_t v0 = permutation[f[0]], v1 = permutation[f[1]], v2 = permutation[f[2]];
566 new_boundary_ids[{{v0, v1, v2}}] = id;
567 }
568 m_boundary_ids = new_boundary_ids;
569 }
570 }
571
572 template <typename M>
573 std::vector<polyfem::basis::ElementBases> LocalMesh<M>::build_bases(const std::string &formulation)
574 {
575 POLYFEM_REMESHER_SCOPED_TIMER("LocalMesh::build_bases");
576
577 std::vector<polyfem::basis::ElementBases> bases;
578
579 std::vector<LocalBoundary> local_boundary;
580 Eigen::VectorXi vertex_to_basis;
581 std::unique_ptr<Mesh> mesh = Mesh::create(rest_positions(), elements());
582 int n_bases = Remesher::build_bases(
583 *mesh, formulation, bases, local_boundary, vertex_to_basis);
584
585 assert(n_bases == num_local_vertices());
586 assert(vertex_to_basis.size() == num_local_vertices());
587 n_bases = num_vertices();
588 vertex_to_basis.conservativeResize(n_bases);
589
590 const int start_i = num_local_vertices();
591 if (start_i < n_bases)
592 {
593 // set tail to range [start_i, n_bases)
594 std::iota(vertex_to_basis.begin() + start_i, vertex_to_basis.end(), start_i);
595 }
596
597 assert(std::all_of(vertex_to_basis.begin(), vertex_to_basis.end(), [](const int basis_id) {
598 return basis_id >= 0;
599 }));
600
601 reorder_vertices(vertex_to_basis);
602
603 return bases;
604 }
605
606 template <typename M>
608 const std::string &path, const Eigen::MatrixXd &sol) const
609 {
610 Eigen::VectorXd is_free = Eigen::VectorXd::Ones(num_vertices());
611 is_free(fixed_vertices()) = Eigen::VectorXd::Zero(fixed_vertices().size());
612
613 paraviewo::VTUWriter writer;
614 writer.add_field("is_free", is_free);
615 writer.add_field("displacement", utils::unflatten(sol, M::DIM));
616 writer.write_mesh(path, rest_positions(), elements());
617 }
618
619 // -------------------------------------------------------------------------
620 // Template instantiations
621 template class LocalMesh<TriMesh>;
622 template class LocalMesh<TetMesh>;
623} // namespace polyfem::mesh
#define POLYFEM_REMESHER_SCOPED_TIMER(name)
Definition Remesher.hpp:15
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
void write_mesh(const std::string &path, const Eigen::MatrixXd &sol) const
Write the local mesh to a VTU file.
typename M::Tuple Tuple
Definition LocalMesh.hpp:15
std::vector< polyfem::basis::ElementBases > build_bases(const std::string &formulation)
Build the ElementBases for the local mesh.
void init_vertex_attributes(const M &m)
static std::vector< Tuple > n_ring(const M &m, const Tuple &center, const int n)
Construct a local mesh as an n-ring around a vertex.
static std::vector< Tuple > flood_fill_n_ring(const M &m, const Tuple &center, const double area)
Construct a local mesh as an n-ring around a vertex.
Eigen::MatrixXi & boundary_facets()
Get a reference to the boundary facets (edges in 2D or faces in 3D).
void reorder_vertices(const Eigen::VectorXi &permutation)
Reorder the vertices of the local mesh.
LocalMesh(const M &m, const std::vector< Tuple > &element_tuples, const bool include_global_boundary)
Definition LocalMesh.cpp:49
static std::vector< Tuple > ball_selection(const M &m, const VectorNd &center, const double rel_radius, const int n_ring_size)
static std::unique_ptr< Mesh > create(const std::string &path, const bool non_conforming=false)
factory to build the proper mesh
Definition Mesh.cpp:173
const Eigen::MatrixXi & e() const
Definition Obstacle.hpp:44
const Eigen::MatrixXi & f() const
Definition Obstacle.hpp:43
const Eigen::MatrixXd & v() const
Definition Obstacle.hpp:41
static int build_bases(const Mesh &mesh, const std::string &assembler_formulation, std::vector< polyfem::basis::ElementBases > &bases, std::vector< LocalBoundary > &local_boundary, Eigen::VectorXi &vertex_to_basis)
Build bases for a given mesh (V, F)
Definition Remesher.cpp:254
std::unordered_map< std::array< size_t, 2 >, T, polyfem::utils::HashUnorderedArray< size_t, 2 >, polyfem::utils::EqualUnorderedArray< size_t, 2 > > EdgeMap
Map from a (sorted) edge to an integer (ID)
Definition Remesher.hpp:29
std::unordered_map< std::array< size_t, 3 >, T, polyfem::utils::HashUnorderedArray< size_t, 3 >, polyfem::utils::EqualUnorderedArray< size_t, 3 > > FaceMap
Map from a (sorted) edge to an integer (ID)
Definition Remesher.hpp:35
void unique_facet_tuples(const wmtk::TriMesh &m, std::vector< wmtk::TriMesh::Tuple > &tuples)
Definition LocalMesh.cpp:38
bool tetrahedron_intersects_ball(const Eigen::Vector3d &t0, const Eigen::Vector3d &t1, const Eigen::Vector3d &t2, const Eigen::Vector3d &t3, const Eigen::Vector3d &center, const double radius)
Determine if a 3D tetrahedron intersects a 3D ball.
bool are_aabbs_intersecting(const VectorNd &aabb0_min, const VectorNd &aabb0_max, const VectorNd &aabb1_min, const VectorNd &aabb1_max)
Determine if two axis-aligned bounding boxes intersect.
Eigen::MatrixXd reorder_matrix(const Eigen::MatrixXd &in, const Eigen::VectorXi &in_to_out, int out_blocks=-1, const int block_size=1)
Reorder row blocks in a matrix.
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
void append_rows(DstMat &dst, const SrcMat &src)
bool triangle_intersects_disk(const Eigen::Vector2d &t0, const Eigen::Vector2d &t1, const Eigen::Vector2d &t2, const Eigen::Vector2d &center, const double radius)
Determine if a 2D triangle intersects a 2D disk.
Eigen::MatrixXi map_index_matrix(const Eigen::MatrixXi &in, const Eigen::VectorXi &index_mapping)
Map the entrys of an index matrix to new indices.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Definition Types.hpp:9