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