PolyFEM
Loading...
Searching...
No Matches
RefElementSampler.cpp
Go to the documentation of this file.
3
5
6#include <igl/predicates/ear_clipping.h>
7
8#include <igl/edges.h>
9#include <igl/boundary_loop.h>
10#include <igl/doublearea.h>
11#include <igl/upsample.h>
12
13#ifdef POLYFEM_WITH_TRIANGLE
14#include <igl/triangle/triangulate.h>
15#endif
16
17#include <cassert>
18
19namespace polyfem
20{
21 using namespace mesh;
22 namespace utils
23 {
24
33 void regular_2d_grid(const int n, bool tri, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
34 {
35
36 V.resize(n * n, 2);
37 F.resize((n - 1) * (n - 1) * 2, 3);
38 const double delta = 1. / (n - 1.);
39 std::vector<int> map(n * n, -1);
40
41 int index = 0;
42 for (int i = 0; i < n; ++i)
43 {
44 for (int j = 0; j < n; ++j)
45 {
46 if (tri && i + j >= n)
47 continue;
48 map[i + j * n] = index;
49 V.row(index) << i * delta, j * delta;
50 ++index;
51 }
52 }
53
54 V.conservativeResize(index, 2);
55
56 std::array<int, 3> tmp;
57
58 index = 0;
59 for (int i = 0; i < n - 1; ++i)
60 {
61 for (int j = 0; j < n - 1; ++j)
62 {
63 tmp = {{map[i + j * n], map[i + 1 + j * n], map[i + (j + 1) * n]}};
64 if (tmp[0] >= 0 && tmp[1] >= 0 && tmp[2] >= 0)
65 {
66 F.row(index) << tmp[0], tmp[1], tmp[2];
67 ++index;
68 }
69
70 tmp = {{map[i + 1 + j * n], map[i + 1 + (j + 1) * n], map[i + (j + 1) * n]}};
71 if (tmp[0] >= 0 && tmp[1] >= 0 && tmp[2] >= 0)
72 {
73 F.row(index) << tmp[0], tmp[1], tmp[2];
74 ++index;
75 }
76 }
77 }
78
79 F.conservativeResize(index, 3);
80 }
81
82 namespace
83 {
84
85 void add_tet(const std::array<int, 4> &tmp, const Eigen::MatrixXd &V, int &index, Eigen::MatrixXi &T)
86 {
87 if (tmp[0] >= 0 && tmp[1] >= 0 && tmp[2] >= 0 && tmp[3] >= 0)
88 {
89 const Eigen::Vector3d e0 = V.row(tmp[1]) - V.row(tmp[0]);
90 const Eigen::Vector3d e1 = V.row(tmp[2]) - V.row(tmp[0]);
91 const Eigen::Vector3d e2 = V.row(tmp[3]) - V.row(tmp[0]);
92 double vol = (e0.cross(e1)).dot(e2);
93 if (vol < 0)
94 T.row(index) << tmp[0], tmp[1], tmp[2], tmp[3];
95 else
96 T.row(index) << tmp[0], tmp[1], tmp[3], tmp[2];
97#ifndef NDEBUG
98 const Eigen::Vector3d ed0 = V.row(T(index, 1)) - V.row(T(index, 0));
99 const Eigen::Vector3d ed1 = V.row(T(index, 2)) - V.row(T(index, 0));
100 const Eigen::Vector3d ed2 = V.row(T(index, 3)) - V.row(T(index, 0));
101 assert((ed0.cross(ed1)).dot(ed2) < 0);
102#endif
103 ++index;
104 }
105 }
106
107 } // anonymous namespace
108
119 void regular_3d_grid(const int nn,
120 bool tet,
121 bool prism,
122 Eigen::MatrixXd &V,
123 Eigen::MatrixXi &F,
124 Eigen::MatrixXi &T)
125 {
126 const int n = nn;
127 const double delta = 1. / (n - 1.);
128 T.resize((n - 1) * (n - 1) * (n - 1) * 6, 4);
129 V.resize(n * n * n, 3);
130 std::vector<int> map(n * n * n, -1);
131
132 int index = 0;
133 for (int i = 0; i < n; ++i)
134 {
135 for (int j = 0; j < n; ++j)
136 {
137 for (int k = 0; k < n; ++k)
138 {
139 if (tet && i + j + k >= n)
140 continue;
141 if (prism && i + j >= n)
142 continue;
143 map[(i + j * n) * n + k] = index;
144 V.row(index) << i * delta, j * delta, k * delta;
145 ++index;
146 }
147 }
148 }
149 V.conservativeResize(index, 3);
150
151 std::array<int, 8> indices;
152 std::array<int, 4> tmp;
153 index = 0;
154 for (int i = 0; i < n - 1; ++i)
155 {
156 for (int j = 0; j < n - 1; ++j)
157 {
158 for (int k = 0; k < n - 1; ++k)
159 {
160 indices = {{(i + j * n) * n + k,
161 (i + 1 + j * n) * n + k,
162 (i + 1 + (j + 1) * n) * n + k,
163 (i + (j + 1) * n) * n + k,
164
165 (i + j * n) * n + k + 1,
166 (i + 1 + j * n) * n + k + 1,
167 (i + 1 + (j + 1) * n) * n + k + 1,
168 (i + (j + 1) * n) * n + k + 1}};
169
170 tmp = {{map[indices[1 - 1]], map[indices[2 - 1]], map[indices[4 - 1]], map[indices[5 - 1]]}};
171 add_tet(tmp, V, index, T);
172
173 tmp = {{map[indices[6 - 1]], map[indices[3 - 1]], map[indices[7 - 1]], map[indices[8 - 1]]}};
174 add_tet(tmp, V, index, T);
175
176 tmp = {{map[indices[5 - 1]], map[indices[2 - 1]], map[indices[6 - 1]], map[indices[4 - 1]]}};
177 add_tet(tmp, V, index, T);
178
179 tmp = {{map[indices[5 - 1]], map[indices[4 - 1]], map[indices[8 - 1]], map[indices[6 - 1]]}};
180 add_tet(tmp, V, index, T);
181
182 tmp = {{map[indices[4 - 1]], map[indices[2 - 1]], map[indices[6 - 1]], map[indices[3 - 1]]}};
183 add_tet(tmp, V, index, T);
184
185 tmp = {{map[indices[3 - 1]], map[indices[4 - 1]], map[indices[8 - 1]], map[indices[6 - 1]]}};
186 add_tet(tmp, V, index, T);
187 }
188 }
189 }
190
191 T.conservativeResize(index, 4);
192
193 F.resize(4 * index, 3);
194
195 F.block(0, 0, index, 1) = T.col(1);
196 F.block(0, 1, index, 1) = T.col(0);
197 F.block(0, 2, index, 1) = T.col(2);
198
199 F.block(index, 0, index, 1) = T.col(0);
200 F.block(index, 1, index, 1) = T.col(1);
201 F.block(index, 2, index, 1) = T.col(3);
202
203 F.block(2 * index, 0, index, 1) = T.col(1);
204 F.block(2 * index, 1, index, 1) = T.col(2);
205 F.block(2 * index, 2, index, 1) = T.col(3);
206
207 F.block(3 * index, 0, index, 1) = T.col(2);
208 F.block(3 * index, 1, index, 1) = T.col(0);
209 F.block(3 * index, 2, index, 1) = T.col(3);
210 }
211
212 void RefElementSampler::init(const bool is_volume, const int n_elements, double target_rel_area)
213 {
214 is_volume_ = is_volume;
215
216 area_param_ = target_rel_area * n_elements;
217#ifndef NDEBUG
218 area_param_ *= 10.0;
219#endif
220
221 build();
222 }
223
225 {
226 using namespace Eigen;
227
228 if (is_volume_)
229 {
230 // cube
231 {
232 MatrixXd pts(8, 3);
233 pts << 0, 0, 0,
234 0, 1, 0,
235 1, 1, 0,
236 1, 0, 0,
237
238 // 4
239 0, 0, 1,
240 0, 1, 1,
241 1, 1, 1,
242 1, 0, 1;
243
244 Eigen::MatrixXi faces(12, 3);
245 faces << 1, 2, 0,
246 0, 2, 3,
247
248 5, 4, 6,
249 4, 7, 6,
250
251 1, 0, 4,
252 1, 4, 5,
253
254 2, 1, 5,
255 2, 5, 6,
256
257 3, 2, 6,
258 3, 6, 7,
259
260 0, 3, 7,
261 0, 7, 4;
262
263 regular_3d_grid(std::max(2., round(1. / pow(area_param_, 1. / 3.) + 1) / 2.), false, false, cube_points_, cube_faces_, cube_tets_);
264
265 // Extract sampled edges matching the base element edges
266 Eigen::MatrixXi edges(12, 2);
267 edges << 0, 1,
268 1, 2,
269 2, 3,
270 3, 0,
271
272 4, 5,
273 5, 6,
274 6, 7,
275 7, 4,
276
277 0, 4,
278 1, 5,
279 2, 6,
280 3, 7;
281 igl::edges(cube_faces_, cube_edges_);
283
284 // Same local order as in FEMBasis3d
285 cube_corners_.resize(8, 3);
286 cube_corners_ << 0, 0, 0,
287 1, 0, 0,
288 1, 1, 0,
289 0, 1, 0,
290 0, 0, 1,
291 1, 0, 1,
292 1, 1, 1,
293 0, 1, 1;
294 }
295
296 // tet
297 {
298 MatrixXd pts(4, 3);
299 pts << 0, 0, 0,
300 1, 0, 0,
301 0, 1, 0,
302 0, 0, 1;
303
304 Eigen::MatrixXi faces(4, 3);
305 faces << 0, 1, 2,
306
307 3, 1, 0,
308 2, 1, 3,
309 0, 2, 3;
310
311 regular_3d_grid(std::max(2., round(1. / pow(area_param_, 1. / 3.) + 1)), true, false, simplex_points_, simplex_faces_, simplex_tets_);
312
313 // Extract sampled edges matching the base element edges
314 Eigen::MatrixXi edges;
315 igl::edges(faces, edges);
316 igl::edges(simplex_faces_, simplex_edges_);
318
319 // Same local order as in FEMBasis3d
320 simplex_corners_.resize(4, 3);
321 simplex_corners_ << 0, 0, 0,
322 1, 0, 0,
323 0, 1, 0,
324 0, 0, 1;
325 }
326
327 // prism
328 {
329 MatrixXd pts(6, 3);
330 pts << 0, 0, 0,
331 0, 1, 0,
332 1, 0, 0,
333 0, 0, 1,
334 0, 1, 1,
335 1, 0, 1;
336
337 regular_3d_grid(std::max(2., round(1. / pow(area_param_, 1. / 3.) + 1) / 2.),
338 false, true, prism_points_, prism_faces_, prism_tets_);
339
340 // Extract sampled edges matching the base element edges
341 Eigen::MatrixXi edges(9, 2);
342 edges << 0, 1,
343 1, 2,
344 2, 0,
345
346 3, 4,
347 4, 5,
348 5, 3,
349
350 0, 3,
351 1, 4,
352 2, 5;
353
354 igl::edges(prism_faces_, prism_edges_);
356
357 // Same local order as in FEMBasis3d
358 prism_corners_.resize(6, 3);
359 prism_corners_ << 0, 0, 0,
360 0, 1, 0,
361 1, 0, 0,
362 0, 0, 1,
363 0, 1, 1,
364 1, 0, 1;
365 }
366 }
367 else
368 {
369 {
370 MatrixXd pts(4, 2);
371 pts << 0, 0,
372 0, 1,
373 1, 1,
374 1, 0;
375
376 MatrixXi E(4, 2);
377 E << 0, 1,
378 1, 2,
379 2, 3,
380 3, 0;
381
382 regular_2d_grid(std::max(2., round(1. / sqrt(area_param_) + 1)), false, cube_points_, cube_faces_);
383
384 // Extract sampled edges matching the base element edges
385 igl::edges(cube_faces_, cube_edges_);
387
388 // Same local order as in FEMBasis2d
389 cube_corners_.resize(4, 2);
390 cube_corners_ << 0, 0,
391 1, 0,
392 1, 1,
393 0, 1;
394 }
395 {
396 MatrixXd pts(3, 2);
397 pts << 0, 0,
398 0, 1,
399 1, 0;
400
401 MatrixXi E(3, 2);
402 E << 0, 1,
403 1, 2,
404 2, 0;
405
406 regular_2d_grid(std::max(2., round(1. / sqrt(area_param_) + 1)), true, simplex_points_, simplex_faces_);
407
408 // Extract sampled edges matching the base element edges
409 igl::edges(simplex_faces_, simplex_edges_);
411
412 // Same local order as in FEMBasis2d
413 simplex_corners_.resize(3, 2);
414 simplex_corners_ << 0, 0,
415 1, 0,
416 0, 1;
417 }
418 }
419 }
420
421 void RefElementSampler::sample_polygon(const Eigen::MatrixXd &poly, Eigen::MatrixXd &pts, Eigen::MatrixXi &faces, Eigen::MatrixXi &edges) const
422 {
423
424#ifdef POLYFEM_WITH_TRIANGLE
425 Eigen::MatrixXi E(poly.rows(), 2);
426 const Eigen::MatrixXd H(0, 2);
427 const std::string flags = "Qzqa" + std::to_string(area_param_ / 10.0);
428
429 for (int i = 0; i < poly.rows(); ++i)
430 E.row(i) << i, (i + 1) % poly.rows();
431
432 igl::triangle::triangulate(poly, E, H, flags, pts, faces);
433#else
434
435 const Eigen::MatrixXi rt = Eigen::MatrixXi::Zero(poly.rows(), 1);
436 faces.resize(0, 0);
437 Eigen::VectorXi I;
438 Eigen::MatrixXd area;
439 igl::predicates::ear_clipping(poly, rt, faces, I);
440
441 igl::doublearea(poly, faces, area);
442
443 const double area_avg = area.array().sum() / poly.rows() / 2;
444
445 const int n_refs = area_avg / area_param_ * 40;
446
447 Eigen::MatrixXi new_faces;
448
449 igl::upsample(poly, faces, pts, new_faces, n_refs);
450
451 faces = new_faces;
452#endif
453 std::vector<int> loop;
454 igl::default_num_threads(1);
455 igl::boundary_loop(faces, loop);
456 igl::default_num_threads(0);
457 edges.resize(loop.size(), 2);
458 for (int i = 0; i < loop.size(); ++i)
459 edges.row(i) << loop[i], loop[(i + 1) % loop.size()];
460 }
461
462 void RefElementSampler::sample_polyhedron(const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &f, Eigen::MatrixXd &pts, Eigen::MatrixXi &tets, Eigen::MatrixXi &faces) const
463 {
464 const Eigen::MatrixXd kernel = vertices.colwise().mean();
465
466 polyfem::tertrahedralize_star_shaped_surface(vertices, f, kernel, pts, faces, tets);
467 }
468 } // namespace utils
469} // namespace polyfem
int V
std::vector< Eigen::VectorXi > faces
void init(const bool is_volume, const int n_elements, const double target_rel_area)
void sample_polygon(const Eigen::MatrixXd &poly, Eigen::MatrixXd &pts, Eigen::MatrixXi &faces, Eigen::MatrixXi &edges) const
void sample_polyhedron(const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &f, Eigen::MatrixXd &pts, Eigen::MatrixXi &faces, Eigen::MatrixXi &edges) const
void extract_parent_edges(const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IE, const Eigen::MatrixXd &BV, const Eigen::MatrixXi &BE, Eigen::MatrixXi &OE)
Extract a set of edges that are overlap with a set given set of parent edges, using vertices position...
void regular_2d_grid(const int n, bool tri, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
Generate a canonical triangle/quad subdivided from a regular grid.
void regular_3d_grid(const int nn, bool tet, bool prism, Eigen::MatrixXd &V, Eigen::MatrixXi &F, Eigen::MatrixXi &T)
Generate a canonical tet/hex subdivided from a regular grid.
void tertrahedralize_star_shaped_surface(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::RowVector3d &kernel, Eigen::MatrixXd &OV, Eigen::MatrixXi &OF, Eigen::MatrixXi &OT)
Tetrahedralize a star-shaped mesh, with a given point in its kernel.