PolyFEM
Loading...
Searching...
No Matches
Split.cpp
Go to the documentation of this file.
2
3namespace polyfem::mesh
4{
5 template <class WMTKMesh>
7 {
8 op_cache = decltype(op_cache)::element_type::split_edge(*this, e);
9 }
10
11 template <class WMTKMesh>
13 {
14 // POLYFEM_REMESHER_SCOPED_TIMER("Split edges before");
15
16 if (!WMTKMesh::split_edge_before(e))
17 return false;
18
19 // Dont split if the edge is too small
20 double min_edge_length = args["split"]["min_edge_length"];
21 if (is_boundary_facet(e) && state.is_contact_enabled())
22 min_edge_length = std::max(min_edge_length, 2.0 * state.args["contact"]["dhat"].template get<double>());
23
24 if (rest_edge_length(e) < min_edge_length)
25 {
26 executor.m_cnt_fail--; // do not count this as a failed split
27 return false;
28 }
29
30 // Cache necessary local data
31 cache_split_edge(e);
32
33 return true;
34 }
35
36 template <class WMTKMesh>
38 {
39 // POLYFEM_REMESHER_SCOPED_TIMER("Split edges before");
40
41 if (!Super::split_edge_before(e)) // NOTE: also calls cache_split_edge
42 return false;
44 if (this->edge_attr(e.eid(*this)).op_attempts++ >= this->max_op_attempts
45 || this->edge_attr(e.eid(*this)).op_depth >= args["split"]["max_depth"].template get<int>())
46 {
47 this->executor.m_cnt_fail--; // do not count this as a failed split
48 return false;
49 }
50
51 const auto &v0 = this->vertex_attrs[e.vid(*this)].rest_position;
52 const auto &v1 = this->vertex_attrs[e.switch_vertex(*this).vid(*this)].rest_position;
53 this->op_cache->local_energy = local_mesh_energy((v0 + v1) / 2);
54 // assert(this->op_cache->local_energy >= 0);
55 // Do not split if the energy of the local mesh is too small
56 // if (this->op_cache->local_energy < args["split"]["acceptance_tolerance"].template get<double>())
57 // return false;
58
59 return true;
60 }
61
62 // -------------------------------------------------------------------------
63
64 namespace
65 {
66 template <typename T>
67 inline T lerp(const T &a, const T &b, double t)
68 {
69 return a + t * (b - a);
70 }
71 } // namespace
72
73 template <class WMTKMesh>
75 {
76 if (!WMTKMesh::split_edge_after(t))
77 return false;
78
79 // 0) perform operation (done before this function)
80
81 // 1a) Update rest position of new vertex
82 Tuple new_vertex;
83 if constexpr (std::is_same_v<WMTKMesh, wmtk::TriMesh>)
84 new_vertex = t.switch_vertex(*this);
85 else
86 new_vertex = t;
87
88 const auto &[old_v0_id, v0] = op_cache->v0();
89 const auto &[old_v1_id, v1] = op_cache->v1();
90
91 VertexAttributes &new_vertex_attr = vertex_attrs[new_vertex.vid(*this)];
92 constexpr double alpha = 0.5; // TODO: maybe we want to use a different barycentric coordinate?
93 new_vertex_attr.rest_position = lerp(v0.rest_position, v1.rest_position, alpha);
94 new_vertex_attr.fixed = v0.fixed && v1.fixed;
95 new_vertex_attr.partition_id = v0.partition_id; // TODO: what should this be?
96
97 if (state.is_contact_enabled() && is_boundary_vertex(new_vertex))
98 {
99 const double dhat = state.args["contact"]["dhat"].template get<double>();
100
101 // only enforce this invariant if it started valid
102 if (state.min_boundary_edge_length >= dhat)
103 {
104 for (const Tuple &e : get_one_ring_boundary_edges_for_vertex(new_vertex))
105 {
106 if (rest_edge_length(e) < dhat)
107 return false; // produced too small edge
108 }
109 }
110 }
111
112 // 1b) Assign edge attributes to the new edges
113 map_edge_split_edge_attributes(new_vertex);
114 map_edge_split_boundary_attributes(new_vertex);
115 map_edge_split_element_attributes(t);
116
117 // 2) Project quantities so to minimize the L2 error
118 new_vertex_attr.position = lerp(v0.position, v1.position, alpha);
119 new_vertex_attr.projection_quantities =
120 lerp(v0.projection_quantities, v1.projection_quantities, alpha);
121
122 // 3) No need to check for intersections because we are not moving the vertices
123 return true;
124 }
126 template <class WMTKMesh>
128 {
129 utils::Timer timer(this->timings["Split edges after"]);
130 timer.start();
131 if (!Super::split_edge_after(t))
132 return false;
133 // local relaxation has its own timers
134 timer.stop();
135
136 Tuple new_vertex;
137 if constexpr (std::is_same_v<WMTKMesh, wmtk::TriMesh>)
138 new_vertex = t.switch_vertex(*this);
139 else
140 new_vertex = t;
141
142 std::vector<Tuple> local_mesh_tuples = this->local_mesh_tuples(new_vertex);
143
144 // Perform a local relaxation of the n-ring to get an estimate of the energy decrease.
145 if (!local_relaxation(local_mesh_tuples, args["split"]["acceptance_tolerance"]))
146 return false;
147
148 // Increase the hash of the triangles that have been modified
149 // to invalidate all tuples that point to them.
150 this->extend_local_patch(local_mesh_tuples);
151 for (Tuple &t : local_mesh_tuples)
152 {
153 assert(t.is_valid(*this));
154 if constexpr (std::is_same_v<wmtk::TriMesh, WMTKMesh>)
155 this->m_tri_connectivity[t.fid(*this)].hash++;
156 else
157 this->m_tet_connectivity[t.tid(*this)].hash++;
158 assert(!t.is_valid(*this));
159 t.update_hash(*this);
160 assert(t.is_valid(*this));
161 }
162
163 return true;
164 }
165
166 // -------------------------------------------------------------------------
167
168 template <class WMTKMesh>
170 {
171 std::vector<Tuple> included_edges;
172 {
173 const std::vector<Tuple> edges = WMTKMesh::get_edges();
174 std::copy_if(edges.begin(), edges.end(), std::back_inserter(included_edges), [this](const Tuple &e) {
175 return this->edge_attr(e.eid(*this)).energy_rank == Super::EdgeAttributes::EnergyRank::TOP;
176 });
177 }
178
179 if (included_edges.empty())
180 return;
181
182 Operations splits;
183 splits.reserve(included_edges.size());
184 for (const Tuple &e : included_edges)
185 splits.emplace_back("edge_split", e);
186
187 executor.priority = [&](const WildRemesher<WMTKMesh> &, std::string op, const Tuple &t) -> double {
188 return this->edge_elastic_energy(t);
189 };
190
191 executor(*this, splits);
192 }
193
194 // =========================================================================
195 // Map attributes
196 // =========================================================================
197
198 template <>
199 void WildTriRemesher::map_edge_split_edge_attributes(const Tuple &new_vertex) { return; }
200
201 template <>
202 void WildTetRemesher::map_edge_split_edge_attributes(const Tuple &new_vertex)
203 {
204 const auto &[old_v0_id, old_v0] = op_cache->v0();
205 const auto &[old_v1_id, old_v1] = op_cache->v1();
206 const auto &old_edges = op_cache->edges();
207
208 EdgeAttributes old_split_edge = old_edges.at({{old_v0_id, old_v1_id}});
209 old_split_edge.op_attempts = 0;
210 EdgeAttributes interior_edge; // default
211 interior_edge.op_depth = old_split_edge.op_depth;
212 interior_edge.energy_rank = old_split_edge.energy_rank;
213
214 const size_t new_vid = new_vertex.vid(*this);
215
216 const std::vector<Tuple> new_tets = get_one_ring_tets_for_vertex(new_vertex);
217 for (const auto &t : new_tets)
218 {
219 for (int i = 0; i < 6; i++)
220 {
221 const Tuple e = tuple_from_edge(t.tid(*this), i);
222
223 size_t v0_id = e.vid(*this);
224 size_t v1_id = e.switch_vertex(*this).vid(*this);
225 if (v0_id > v1_id)
226 std::swap(v0_id, v1_id);
227
228 assert(v0_id != new_vid); // new_vid should have a higher id than any other vertex
229 if (v1_id == new_vid)
230 {
231 edge_attrs[e.eid(*this)] =
232 (v0_id == old_v0_id || v0_id == old_v1_id) ? old_split_edge : interior_edge;
233 edge_attrs[e.eid(*this)].op_depth++;
234 }
235 else
236 {
237 edge_attrs[e.eid(*this)] = old_edges.at({{v0_id, v1_id}});
238 }
239 }
240 }
241 }
242
243 // -------------------------------------------------------------------------
244
245 namespace
246 {
247 template <typename Container>
248 inline bool contains(const Container &container, const typename Container::value_type &val)
249 {
250 return std::find(container.begin(), container.end(), val) != container.end();
251 }
252 } // namespace
253
254 template <>
255 void WildTriRemesher::map_edge_split_boundary_attributes(const Tuple &new_vertex)
256 {
257 const auto &[old_v0_id, v0] = op_cache->v0();
258 const auto &[old_v1_id, v1] = op_cache->v1();
259 const auto &old_edges = op_cache->edges();
260
261 BoundaryAttributes old_split_edge = old_edges.at({{old_v0_id, old_v1_id}});
262 old_split_edge.op_attempts = 0;
263 BoundaryAttributes interior_edge; // default
264 interior_edge.op_depth = old_split_edge.op_depth;
265 interior_edge.energy_rank = old_split_edge.energy_rank;
266
267 const size_t new_vid = new_vertex.vid(*this);
268
269 for (const auto &new_face : get_one_ring_tris_for_vertex(new_vertex))
270 {
271 for (int i = 0; i < 3; i++)
272 {
273 const Tuple e = tuple_from_edge(new_face.fid(*this), i);
274
275 size_t v0_id = e.vid(*this);
276 size_t v1_id = e.switch_vertex(*this).vid(*this);
277 if (v0_id > v1_id)
278 std::swap(v0_id, v1_id);
279
280 assert(v0_id != new_vid); // new_vid should have a higher id than any other vertex
281 if (v1_id == new_vid)
282 {
283 boundary_attrs[e.eid(*this)] =
284 (v0_id == old_v0_id || v0_id == old_v1_id) ? old_split_edge : interior_edge;
285 boundary_attrs[e.eid(*this)].op_depth++;
286 }
287 else
288 {
289 boundary_attrs[e.eid(*this)] = old_edges.at({{v0_id, v1_id}});
290 }
291
292#ifndef NDEBUG
293 if (is_boundary_facet(e))
294 assert(boundary_attrs[facet_id(e)].boundary_id >= 0);
295 else
296 assert(boundary_attrs[facet_id(e)].boundary_id == -1);
297#endif
299 }
300 }
301
302 template <>
303 void WildTetRemesher::map_edge_split_boundary_attributes(const Tuple &new_vertex)
304 {
305 const auto &[old_v0_id, old_v0] = op_cache->v0();
306 const auto &[old_v1_id, old_v1] = op_cache->v1();
307 const auto &old_faces = op_cache->faces();
308
309 const size_t new_vid = new_vertex.vid(*this);
310 for (const auto &t : get_one_ring_tets_for_vertex(new_vertex))
311 {
312 for (int i = 0; i < 4; i++)
313 {
314 const Tuple f = tuple_from_face(t.tid(*this), i);
315
316 std::array<size_t, 3> vids = facet_vids(f);
317
318 auto new_v_itr = std::find(vids.begin(), vids.end(), new_vid);
319
320 if (new_v_itr != vids.end())
321 {
322 const bool contains_old_v0 = contains(vids, old_v0_id);
323 const bool contains_old_v1 = contains(vids, old_v1_id);
324 assert(!(contains_old_v0 && contains_old_v1));
325
326 // New interior face, use default boundary attributes
327 if (!contains_old_v0 && !contains_old_v1)
328 {
329 assert(!is_boundary_facet(f));
330 boundary_attrs[facet_id(f)].boundary_id = -1;
331 continue;
332 }
333
334 *new_v_itr = contains_old_v0 ? old_v1_id : old_v0_id;
335 }
336 // else: new vertex is not part of this face, so retain the old boundary attributes
337
338 boundary_attrs[f.fid(*this)] = old_faces.at(vids);
339
340#ifndef NDEBUG
341 if (is_boundary_facet(f))
342 assert(boundary_attrs[facet_id(f)].boundary_id >= 0);
343 else
344 assert(boundary_attrs[facet_id(f)].boundary_id == -1);
345#endif
346 }
347 }
348 }
349
350 // -------------------------------------------------------------------------
351
352 template <>
353 void WildTriRemesher::map_edge_split_element_attributes(const Tuple &t)
354 {
355 const auto &old_faces = op_cache->faces();
356
357 Tuple nav = t.switch_vertex(*this);
358 element_attrs[nav.fid(*this)] = old_faces[0];
359 nav = nav.switch_edge(*this).switch_face(*this).value();
360 element_attrs[nav.fid(*this)] = old_faces[0];
361 nav = nav.switch_edge(*this);
362 if (nav.switch_face(*this))
363 {
364 nav = nav.switch_face(*this).value();
365 element_attrs[nav.fid(*this)] = old_faces[1];
366 nav = nav.switch_edge(*this).switch_face(*this).value();
367 element_attrs[nav.fid(*this)] = old_faces[1];
368 }
369 }
370
371 template <>
372 void WildTetRemesher::map_edge_split_element_attributes(const Tuple &new_vertex)
373 {
374 const auto &[old_v0_id, old_v0] = op_cache->v0();
375 const auto &[old_v1_id, old_v1] = op_cache->v1();
376 const auto &old_tets = op_cache->tets();
377
378 const size_t new_vid = new_vertex.vid(*this);
379 const std::vector<Tuple> new_tets = get_one_ring_tets_for_vertex(new_vertex);
380
381 for (const auto &t : new_tets)
382 {
383 std::array<size_t, 4> vids = oriented_tet_vids(t);
384 auto new_v_itr = std::find(vids.begin(), vids.end(), new_vid);
385 assert(new_v_itr != vids.end());
386
387 const bool contains_old_v0 = contains(vids, old_v0_id);
388 assert(contains_old_v0 ^ contains(vids, old_v1_id));
389 *new_v_itr = contains_old_v0 ? old_v1_id : old_v0_id;
390
391 element_attrs[t.tid(*this)] = old_tets.at(vids);
392 }
393 }
394
395 // -------------------------------------------------------------------------
396 // Template specializations
397
398 template class WildRemesher<wmtk::TriMesh>;
399 template class WildRemesher<wmtk::TetMesh>;
400 template class PhysicsRemesher<wmtk::TriMesh>;
401 template class PhysicsRemesher<wmtk::TetMesh>;
402
403} // namespace polyfem::mesh
double val
Definition Assembler.cpp:86
typename Super::Operations Operations
void split_edges() override
Definition Split.cpp:169
bool split_edge_after(const Tuple &t) override
Definition Split.cpp:127
bool split_edge_before(const Tuple &t) override
Definition Split.cpp:37
typename WMTKMesh::Tuple Tuple
void cache_split_edge(const Tuple &e)
Cache the split edge operation.
Definition Split.cpp:6
virtual bool split_edge_after(const Tuple &t) override
Definition Split.cpp:74
void map_edge_split_edge_attributes(const Tuple &t)
virtual bool split_edge_before(const Tuple &t) override
Definition Split.cpp:12
WildTetRemesher::Tuple Tuple
Eigen::MatrixXd projection_quantities
Quantities to be projected (dim × n_quantities)