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