PolyFEM
Loading...
Searching...
No Matches
SizingFieldRemesher.cpp
Go to the documentation of this file.
2
3#include <ipc/collision_mesh.hpp>
4#include <ipc/candidates/candidates.hpp>
5#include <ipc/broad_phase/hash_grid.hpp>
6
7#include <Eigen/Dense>
8
9namespace polyfem::mesh
10{
11 // Edge splitting
12 template <class WMTKMesh>
14 {
15 Operations splits;
16 const std::unordered_map<size_t, double> edge_sizings = this->compute_edge_sizings();
17 double min = std::numeric_limits<double>::max();
18 double max = std::numeric_limits<double>::min();
19 for (const Tuple &e : WMTKMesh::get_edges())
20 {
21 min = std::min(min, edge_sizings.at(e.eid(*this)));
22 max = std::max(max, edge_sizings.at(e.eid(*this)));
23 if (edge_sizings.at(e.eid(*this)) >= 1)
24 splits.emplace_back("edge_split", e);
25 }
26 logger().debug("min sij: {}, max sij: {}", min, max);
27
28 if (splits.empty())
29 return;
30
31 executor.priority = [&](const WildRemesher<WMTKMesh> &m, std::string op, const Tuple &t) -> double {
32 return edge_sizings.at(t.eid(m));
33 };
34
35 executor(*this, splits);
36 }
37
38 // Edge collapse
39 template <class WMTKMesh>
41 {
42 Operations collapses;
43 const std::unordered_map<size_t, double> edge_sizings = this->compute_edge_sizings();
44 for (const Tuple &e : WMTKMesh::get_edges())
45 if (edge_sizings.at(e.eid(*this)) <= 0.8)
46 collapses.emplace_back("edge_collapse", e);
47
48 if (collapses.empty())
49 return;
50
51 executor.priority = [&](const WildRemesher<WMTKMesh> &m, std::string op, const Tuple &t) -> double {
52 // return -edge_sizings.at(t.eid(m));
53 return -m.rest_edge_length(t);
54 };
55
56 executor(*this, collapses);
57 }
58
59 template <class WMTKMesh>
61 {
62 if (!Super::split_edge_before(t))
63 return false;
64
65 // NOTE: this is a hack to avoid splitting edges that are too short
66 if (this->rest_edge_length(t) < 0.01)
67 return false;
68
69 return true;
70 }
71
72 template <class WMTKMesh>
74 {
75 if (!Super::collapse_edge_after(t))
76 return false;
77
78 if constexpr (Super::DIM == 2)
79 {
80 const std::unordered_map<size_t, double> edge_sizings = this->compute_edge_sizings();
81 for (const Tuple &e : WMTKMesh::get_one_ring_edges_for_vertex(t))
82 if (edge_sizings.at(e.eid(*this)) > 0.8)
83 return false;
84 }
85 else
86 {
87 const std::unordered_map<size_t, double> edge_sizings = this->compute_edge_sizings();
88 for (const Tuple &tet : WMTKMesh::get_one_ring_tets_for_vertex(t))
89 {
90 for (int i = 0; i < 6; ++i)
91 {
92 const Tuple e = WMTKMesh::tuple_from_edge(tet.tid(*this), i);
93 if (edge_sizings.at(e.eid(*this)) > 0.8)
94 return false;
95 }
96 }
97 }
98
99 return true;
100 }
101
102 template <typename WMTKMesh>
103 template <typename Candidates>
106 const Candidates &candidates,
107 const ipc::CollisionMesh &collision_mesh,
108 const Eigen::MatrixXd &V,
109 const double dhat) const
110 {
111 const Eigen::MatrixXd &V_rest = collision_mesh.rest_positions();
112 const Eigen::MatrixXi &E = collision_mesh.edges();
113 const Eigen::MatrixXi &F = collision_mesh.faces();
114
115 Eigen::VectorXd min_distance_per_vertex = Eigen::VectorXd::Constant(V.rows(), dhat * dhat);
116
117 SparseSizingField sizing_field;
118 for (const auto &candidate : candidates)
119 {
120 const double distance_sqr = candidate.compute_distance(candidate.dof(V, E, F));
121 const double rest_distance_sqr = candidate.compute_distance(candidate.dof(V_rest, E, F));
122 const ipc::VectorMax12d distance_grad = candidate.compute_distance_gradient(candidate.dof(V, E, F));
123 const auto vertices = candidate.vertex_ids(E, F);
124 for (int i = 0; i < 4; i++)
125 {
126 const long vi = vertices[i];
127 if (vi < 0 || vi > V.rows() - this->obstacle().n_vertices())
128 continue;
129 if (distance_sqr / rest_distance_sqr >= 0.1 || distance_sqr > min_distance_per_vertex[vi])
130 continue;
131
132 VectorNd g = distance_grad.segment<Super::DIM>(Super::DIM * i) / (2 * sqrt(distance_sqr));
133
134 sizing_field[collision_mesh.to_full_vertex_id(vi)] = g * (g.transpose() / distance_sqr);
135 min_distance_per_vertex[vi] = distance_sqr;
136 }
137 }
138
139 return sizing_field;
140 }
141
142 template <class WMTKMesh>
145 {
146 Eigen::MatrixXd V_rest = this->rest_positions();
147 utils::append_rows(V_rest, this->obstacle().v());
148
149 ipc::CollisionMesh collision_mesh = ipc::CollisionMesh::build_from_full_mesh(
150 V_rest, this->boundary_edges(), this->boundary_faces());
151
152 Eigen::MatrixXd V = this->positions();
153 if (this->obstacle().n_vertices())
154 utils::append_rows(V, this->obstacle().v() + this->obstacle_displacements());
155
156 V_rest = collision_mesh.rest_positions();
157 V = collision_mesh.vertices(V);
158 const Eigen::MatrixXi &E = collision_mesh.edges();
159 const Eigen::MatrixXi &F = collision_mesh.faces();
160
161 const double dhat = state.args["contact"]["dhat"].template get<double>();
162
163 ipc::HashGrid hash_grid;
164 hash_grid.build(V, E, F, dhat / 2);
165 if constexpr (Super::DIM == 2)
166 {
167 ipc::Candidates candidates;
168 hash_grid.detect_edge_vertex_candidates(candidates.ev_candidates);
169 return this->compute_contact_sizing_field_from_candidates(
170 candidates.ev_candidates, collision_mesh, V, dhat);
171 }
172 else
173 {
174 ipc::Candidates candidates;
175 hash_grid.detect_face_vertex_candidates(candidates.fv_candidates);
176 // NOTE: ignoring edge-edge candidates for now
177 // hash_grid.detect_edge_edge_candidates(candidates.ee_candidates);
178 return this->compute_contact_sizing_field_from_candidates(
179 candidates.fv_candidates, collision_mesh, V, dhat);
180 }
181 }
182
183 template <class WMTKMesh>
186 const SparseSizingField &sizing_field) const
187 {
188 SparseSizingField smoothed_sizing_field;
189 for (const Tuple &f : this->boundary_facets())
190 {
191 const auto vids = this->facet_vids(f);
192 MatrixNd M = MatrixNd::Zero();
193 for (const size_t vid : vids)
194 if (sizing_field.find(vid) != sizing_field.end())
195 M += sizing_field.at(vid);
196 M /= vids.size(); // average
197
198 std::vector<Tuple> edges;
199 if constexpr (Super::DIM == 2)
200 edges.push_back(f);
201 else
202 edges = {{f, f.switch_edge(*this), f.switch_vertex(*this).switch_edge(*this)}};
203
204 for (const Tuple &e : edges)
205 {
206 if (sizing_field.find(e.eid(*this)) == sizing_field.end())
207 smoothed_sizing_field[e.eid(*this)] = MatrixNd::Zero();
208 smoothed_sizing_field[e.eid(*this)] += M / edges.size();
209 }
210 }
211
212 return smoothed_sizing_field;
213 }
214
215 template <class WMTKMesh>
216 std::unordered_map<size_t, double>
218 {
219 const SparseSizingField sizing_field =
220 combine_sizing_fields(
221 compute_elasticity_sizing_field(),
222 smooth_contact_sizing_field(compute_contact_sizing_field()));
223
224 std::unordered_map<size_t, double> edge_sizings;
225 for (const Tuple &e : WMTKMesh::get_edges())
226 {
227 const std::array<size_t, 2> vids =
228 {{e.vid(*this), e.switch_vertex(*this).vid(*this)}};
229
230 const VectorNd xi_bar = vertex_attrs[vids[0]].rest_position;
231 const VectorNd xj_bar = vertex_attrs[vids[1]].rest_position;
232
233 const VectorNd xij_bar = xj_bar - xi_bar;
234
235 const MatrixNd M = sizing_field.at(e.eid(*this));
236
237 edge_sizings[e.eid(*this)] = sqrt(xij_bar.transpose() * M * xij_bar);
238 }
239 return edge_sizings;
240 }
241
242 template <class WMTKMesh>
245 {
246 std::unordered_map<size_t, MatrixNd> Fs, Fs_inv;
247 for (const Tuple &t : this->get_elements())
248 {
249 const auto vids = this->element_vids(t);
250 MatrixNd Dm, Ds;
251 if constexpr (std::is_same_v<WMTKMesh, wmtk::TriMesh>)
252 {
253 Dm.col(0) = vertex_attrs[vids[1]].rest_position - vertex_attrs[vids[0]].rest_position;
254 Dm.col(1) = vertex_attrs[vids[2]].rest_position - vertex_attrs[vids[0]].rest_position;
255 Ds.col(0) = vertex_attrs[vids[1]].position - vertex_attrs[vids[0]].position;
256 Ds.col(1) = vertex_attrs[vids[2]].position - vertex_attrs[vids[0]].position;
257 }
258 else
259 {
260 Dm.col(0) = vertex_attrs[vids[1]].rest_position - vertex_attrs[vids[0]].rest_position;
261 Dm.col(1) = vertex_attrs[vids[2]].rest_position - vertex_attrs[vids[0]].rest_position;
262 Dm.col(2) = vertex_attrs[vids[3]].rest_position - vertex_attrs[vids[0]].rest_position;
263 Ds.col(0) = vertex_attrs[vids[1]].position - vertex_attrs[vids[0]].position;
264 Ds.col(1) = vertex_attrs[vids[2]].position - vertex_attrs[vids[0]].position;
265 Ds.col(2) = vertex_attrs[vids[3]].position - vertex_attrs[vids[0]].position;
266 }
267 Fs[this->element_id(t)] = Ds * Dm.inverse();
268 Fs_inv[this->element_id(t)] = Dm * Ds.inverse();
269 }
270
271 const double k = 1.0;
272
273 std::unordered_map<size_t, VectorNd> element_centers;
274 for (const Tuple &t : this->get_elements())
275 {
276 VectorNd center = VectorNd::Zero();
277 const auto &vids = this->element_vids(t);
278 for (const size_t &vid : vids)
279 center += vertex_attrs[vid].rest_position;
280 element_centers[this->element_id(t)] = center / vids.size();
281 }
282
283 SparseSizingField element_sizing_field;
284 for (const Tuple &t : this->get_elements())
285 {
286 const size_t tid = this->element_id(t);
287
288 // std::unordered_set<size_t> adjacent_tids;
289 // for (const Tuple &v : this->element_vertices(t))
290 // for (const Tuple &adj_t : this->get_one_ring_elements_for_vertex(v))
291 // adjacent_tids.insert(this->element_id(adj_t));
292 // adjacent_tids.erase(tid);
293
294 // const VectorNd &t_center = element_centers.at(tid);
295
296 // MatrixNd M = MatrixNd::Zero();
297 // for (const size_t adj_tid : adjacent_tids)
298 // {
299 // const VectorNd dt = element_centers.at(adj_tid) - t_center;
300 // M += dt * (dt.transpose() / dt.squaredNorm())
301 // * std::max((Fs[tid] * Fs_inv[adj_tid]).norm(),
302 // (Fs[adj_tid] * Fs_inv[tid]).norm())
303 // / dt.norm();
304 // }
305 // M.array() *= k / adjacent_tids.size();
306
307 element_sizing_field[tid] = Fs[tid].transpose() * Fs[tid];
308 }
309 assert(!element_sizing_field.empty());
310
311 SparseSizingField sizing_field;
312 for (const Tuple &e : this->get_edges())
313 {
314 const size_t eid = e.eid(*this);
315 const auto &incident_elements = this->get_incident_elements_for_edge(e);
316 for (const Tuple &t : incident_elements)
317 {
318 if (sizing_field.find(eid) == sizing_field.end())
319 sizing_field[eid] = MatrixNd::Zero();
320 sizing_field[eid] += element_sizing_field[this->element_id(t)];
321 }
322 sizing_field[eid] /= incident_elements.size() * std::pow(state.starting_max_edge_length, 2);
323 }
324
325 return sizing_field;
326 }
327
328 template <class WMTKMesh>
331 const SparseSizingField &field1,
332 const SparseSizingField &field2)
333 {
334 SparseSizingField field = field1;
335 for (const auto &[eid, M] : field2)
336 {
337 if (field.find(eid) == field.end())
338 field[eid] = M;
339 else
340 field[eid] = (field[eid] + M) / 2;
341 }
342 return field;
343 }
344
345 // -------------------------------------------------------------------------
346 // Template specializations
347
350} // namespace polyfem::mesh
int V
bool split_edge_before(const Tuple &t) override
std::unordered_map< size_t, double > compute_edge_sizings() const
SparseSizingField compute_contact_sizing_field_from_candidates(const Candidates &candidates, const ipc::CollisionMesh &collision_mesh, const Eigen::MatrixXd &V, const double dhat) const
Eigen::Matrix< double, Super::DIM, Super::DIM > MatrixNd
SparseSizingField smooth_contact_sizing_field(const SparseSizingField &sizing_field) const
SparseSizingField compute_contact_sizing_field() const
typename Super::Operations Operations
bool collapse_edge_after(const Tuple &t) override
static SparseSizingField combine_sizing_fields(const SparseSizingField &field1, const SparseSizingField &field2)
std::unordered_map< size_t, MatrixNd > SparseSizingField
SparseSizingField compute_elasticity_sizing_field() const
double rest_edge_length(const Tuple &e) const
Compute the length of an edge.
void append_rows(DstMat &dst, const SrcMat &src)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42