PolyFEM
Loading...
Searching...
No Matches
GeometryUtils.cpp
Go to the documentation of this file.
1#include "GeometryUtils.hpp"
2
3#include <cassert>
4
6
7#include <Eigen/Geometry>
8
9#include <igl/barycentric_coordinates.h>
10
11#include <ipc/distance/point_edge.hpp>
12#include <ipc/distance/point_triangle.hpp>
13
14namespace polyfem::utils
15{
16 namespace
17 {
18 double triangle_jacobian(const Eigen::VectorXd &v1, const Eigen::VectorXd &v2, const Eigen::VectorXd &v3)
19 {
20 Eigen::VectorXd a = v2 - v1, b = v3 - v1;
21 return a(0) * b(1) - b(0) * a(1);
22 }
23 } // namespace
24
26 const Eigen::Vector2d &a,
27 const Eigen::Vector2d &b,
28 const Eigen::Vector2d &c)
29 {
30 return ((b.x() - a.x()) * (c.y() - a.y()) - (c.x() - a.x()) * (b.y() - a.y())) / 2.0;
31 }
32
34 const Eigen::Vector3d &a,
35 const Eigen::Vector3d &b,
36 const Eigen::Vector3d &c)
37 {
38 return (b - a).cross(c - a).norm() / 2.0;
39 }
40
41 double triangle_area(const Eigen::MatrixXd V)
42 {
43 assert(V.rows() == 3);
44 if (V.cols() == 2)
45 {
46 return triangle_area_2D(V.row(0), V.row(1), V.row(2));
47 }
48 else
49 {
50 return triangle_area_3D(V.row(0), V.row(1), V.row(2));
51 }
52 }
53
55 const Eigen::Vector3d &a,
56 const Eigen::Vector3d &b,
57 const Eigen::Vector3d &c,
58 const Eigen::Vector3d &d)
59 {
60 return (b - a).cross(c - a).dot(d - a) / 6.0;
61 }
62
63 double tetrahedron_volume(const Eigen::MatrixXd V)
64 {
65 assert(V.rows() == 4 && V.cols() == 3);
66 return tetrahedron_volume(V.row(0), V.row(1), V.row(2), V.row(3));
67 }
68
69 Eigen::MatrixXd triangle_to_clockwise_order(const Eigen::MatrixXd &triangle)
70 {
71 // Only works for 2D triangles.
72 assert(triangle.rows() == 3 && triangle.cols() == 2);
73
74 if (triangle_area(triangle) <= 0)
75 return triangle; // triangle aleady in clockwise order.
76
77 Eigen::Matrix<double, 3, 2> triangle_clockwise;
78 triangle_clockwise.row(0) = triangle.row(2);
79 triangle_clockwise.row(1) = triangle.row(1);
80 triangle_clockwise.row(2) = triangle.row(0);
81
82 return triangle_clockwise;
83 }
84
85 std::vector<Eigen::MatrixXd> triangle_fan(const Eigen::MatrixXd &convex_polygon)
86 {
87 assert(convex_polygon.rows() >= 3);
88 std::vector<Eigen::MatrixXd> triangles;
89 for (int i = 1; i < convex_polygon.rows() - 1; ++i)
90 {
91 triangles.emplace_back(3, convex_polygon.cols());
92 triangles.back().row(0) = convex_polygon.row(0);
93 triangles.back().row(1) = convex_polygon.row(i);
94 triangles.back().row(2) = convex_polygon.row(i + 1);
95 }
96 return triangles;
97 }
98
99 Eigen::VectorXd barycentric_coordinates(
100 const Eigen::VectorXd &p,
101 const Eigen::MatrixXd &V)
102 {
103 Eigen::MatrixXd A(p.size() + 1, p.size() + 1);
104 A.topRows(p.size()) = V.transpose();
105 A.bottomRows(1).setOnes();
106
107 Eigen::VectorXd rhs(p.size() + 1);
108 rhs.topRows(p.size()) = p;
109 rhs.bottomRows(1).setOnes();
110
111 // TODO: Can we use better than LU?
112 const Eigen::VectorXd bc = A.partialPivLu().solve(rhs);
113 assert((A * bc - rhs).norm() / rhs.norm() < 1e-12);
114
115 return bc;
116 }
117
119 const Eigen::Vector2d &t0,
120 const Eigen::Vector2d &t1,
121 const Eigen::Vector2d &t2,
122 const Eigen::Vector2d &center,
123 const double radius)
124 {
125 Eigen::RowVector3d bc;
126 igl::barycentric_coordinates(
127 center.transpose(), t0.transpose(), t1.transpose(), t2.transpose(),
128 bc);
129
130 if (bc.minCoeff() >= 0) // point is inside triangle
131 {
132 assert(bc.maxCoeff() <= 1 + 1e-12); // bc.sum() == 1
133 return true;
134 }
135
136 const double radius_sqr = radius * radius;
137
138 if (ipc::point_edge_distance(center, t0, t1) <= radius_sqr
139 || ipc::point_edge_distance(center, t1, t2) <= radius_sqr
140 || ipc::point_edge_distance(center, t2, t0) <= radius_sqr)
141 {
142 return true;
143 }
144
145 return false;
146 }
147
149 const Eigen::Vector3d &t0,
150 const Eigen::Vector3d &t1,
151 const Eigen::Vector3d &t2,
152 const Eigen::Vector3d &t3,
153 const Eigen::Vector3d &center,
154 const double radius)
155 {
156 Eigen::RowVector4d bc;
157 igl::barycentric_coordinates(
158 center.transpose(), t0.transpose(), t1.transpose(),
159 t2.transpose(), t3.transpose(), bc);
160
161 if (bc.minCoeff() >= 0)
162 {
163 assert(bc.maxCoeff() <= 1 + 1e-12); // bc.sum() == 1
164 return true;
165 }
166
167 const double radius_sqr = radius * radius;
168
169 if (ipc::point_triangle_distance(center, t0, t1, t2) <= radius_sqr
170 || ipc::point_triangle_distance(center, t0, t1, t3) <= radius_sqr
171 || ipc::point_triangle_distance(center, t0, t2, t3) <= radius_sqr
172 || ipc::point_triangle_distance(center, t1, t2, t3) <= radius_sqr)
173 {
174 return true;
175 }
176
177 return false;
178 }
179
181 const VectorNd &ea0,
182 const VectorNd &ea1,
183 const VectorNd &eb0,
184 const VectorNd &eb1,
185 const double tol)
186 {
187 assert((ea0 - ea1).norm() != 0 && (eb1 - eb0).norm() != 0);
188 const VectorNd ea = (ea1 - ea0).normalized();
189 const VectorNd eb = (eb1 - eb0).normalized();
190 return abs(ea.dot(eb)) > 1 - tol;
191 }
192
194 const Eigen::Vector3d &t00,
195 const Eigen::Vector3d &t01,
196 const Eigen::Vector3d &t02,
197 const Eigen::Vector3d &t10,
198 const Eigen::Vector3d &t11,
199 const Eigen::Vector3d &t12,
200 const double tol)
201 {
202 const Eigen::Vector3d n0 = (t01 - t00).cross(t02 - t00).normalized();
203 const Eigen::Vector3d n1 = (t11 - t10).cross(t12 - t10).normalized();
204 return abs(n0.dot(n1)) > 1 - tol;
205 }
206
208 const VectorNd &aabb0_min,
209 const VectorNd &aabb0_max,
210 const VectorNd &aabb1_min,
211 const VectorNd &aabb1_max)
212 {
213 return (aabb0_min.array() <= aabb1_max.array()).all() && (aabb1_min.array() <= aabb0_max.array()).all();
214 }
215
216 // =========================================================================
217 // The following automatically generated using SymPy
218 // =========================================================================
219
220 void triangle_area_2D_gradient(double ax, double ay, double bx, double by, double cx, double cy, double g[6])
221 {
222 const auto t0 = -cy;
223 const auto t1 = -cx;
224 g[0] = (1.0 / 2.0) * by + (1.0 / 2.0) * t0;
225 g[1] = -1.0 / 2.0 * bx - 1.0 / 2.0 * t1;
226 g[2] = -1.0 / 2.0 * ay - 1.0 / 2.0 * t0;
227 g[3] = (1.0 / 2.0) * ax + (1.0 / 2.0) * t1;
228 g[4] = (1.0 / 2.0) * (ay - by);
229 g[5] = (1.0 / 2.0) * (-ax + bx);
230 }
231
232 void triangle_area_2D_hessian(double ax, double ay, double bx, double by, double cx, double cy, double H[36])
233 {
234 H[0] = 0;
235 H[1] = 0;
236 H[2] = 0;
237 H[3] = 1.0 / 2.0;
238 H[4] = 0;
239 H[5] = -1.0 / 2.0;
240 H[6] = 0;
241 H[7] = 0;
242 H[8] = -1.0 / 2.0;
243 H[9] = 0;
244 H[10] = 1.0 / 2.0;
245 H[11] = 0;
246 H[12] = 0;
247 H[13] = -1.0 / 2.0;
248 H[14] = 0;
249 H[15] = 0;
250 H[16] = 0;
251 H[17] = 1.0 / 2.0;
252 H[18] = 1.0 / 2.0;
253 H[19] = 0;
254 H[20] = 0;
255 H[21] = 0;
256 H[22] = -1.0 / 2.0;
257 H[23] = 0;
258 H[24] = 0;
259 H[25] = 1.0 / 2.0;
260 H[26] = 0;
261 H[27] = -1.0 / 2.0;
262 H[28] = 0;
263 H[29] = 0;
264 H[30] = -1.0 / 2.0;
265 H[31] = 0;
266 H[32] = 1.0 / 2.0;
267 H[33] = 0;
268 H[34] = 0;
269 H[35] = 0;
270 }
271
272 void tetrahedron_volume_gradient(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz, double dx, double dy, double dz, double g[12])
273 {
274 const auto t0 = az - dz;
275 const auto t1 = -cy;
276 const auto t2 = by + t1;
277 const auto t3 = ay - dy;
278 const auto t4 = -cz;
279 const auto t5 = bz + t4;
280 const auto t6 = ay - by;
281 const auto t7 = az + t4;
282 const auto t8 = ay + t1;
283 const auto t9 = az - bz;
284 const auto t10 = t6 * t7 - t8 * t9;
285 const auto t11 = -cx;
286 const auto t12 = bx + t11;
287 const auto t13 = ax - dx;
288 const auto t14 = ax - bx;
289 const auto t15 = ax + t11;
290 const auto t16 = t14 * t7 - t15 * t9;
291 const auto t17 = t14 * t8 - t15 * t6;
292 g[0] = -1.0 / 6.0 * t0 * t2 - 1.0 / 6.0 * t10 + (1.0 / 6.0) * t3 * t5;
293 g[1] = (1.0 / 6.0) * t0 * t12 - 1.0 / 6.0 * t13 * t5 + (1.0 / 6.0) * t16;
294 g[2] = -1.0 / 6.0 * t12 * t3 + (1.0 / 6.0) * t13 * t2 - 1.0 / 6.0 * t17;
295 g[3] = (1.0 / 6.0) * t0 * t8 - 1.0 / 6.0 * t3 * t7;
296 g[4] = -1.0 / 6.0 * t0 * t15 + (1.0 / 6.0) * t13 * t7;
297 g[5] = -1.0 / 6.0 * t13 * t8 + (1.0 / 6.0) * t15 * t3;
298 g[6] = -1.0 / 6.0 * t0 * t6 + (1.0 / 6.0) * t3 * t9;
299 g[7] = (1.0 / 6.0) * t0 * t14 - 1.0 / 6.0 * t13 * t9;
300 g[8] = (1.0 / 6.0) * t13 * t6 - 1.0 / 6.0 * t14 * t3;
301 g[9] = (1.0 / 6.0) * t10;
302 g[10] = -1.0 / 6.0 * t16;
303 g[11] = (1.0 / 6.0) * t17;
304 }
305
306 void tetrahedron_volume_hessian(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz, double dx, double dy, double dz, double H[144])
307 {
308 const auto t0 = -dz;
309 const auto t1 = cz + t0;
310 const auto t2 = -1.0 / 6.0 * t1;
311 const auto t3 = -dy;
312 const auto t4 = cy + t3;
313 const auto t5 = (1.0 / 6.0) * t4;
314 const auto t6 = bz + t0;
315 const auto t7 = (1.0 / 6.0) * t6;
316 const auto t8 = by + t3;
317 const auto t9 = -1.0 / 6.0 * t8;
318 const auto t10 = -cz;
319 const auto t11 = bz + t10;
320 const auto t12 = -1.0 / 6.0 * t11;
321 const auto t13 = -cy;
322 const auto t14 = by + t13;
323 const auto t15 = (1.0 / 6.0) * t14;
324 const auto t16 = (1.0 / 6.0) * t1;
325 const auto t17 = -dx;
326 const auto t18 = cx + t17;
327 const auto t19 = -1.0 / 6.0 * t18;
328 const auto t20 = -1.0 / 6.0 * t6;
329 const auto t21 = bx + t17;
330 const auto t22 = (1.0 / 6.0) * t21;
331 const auto t23 = (1.0 / 6.0) * t11;
332 const auto t24 = -cx;
333 const auto t25 = bx + t24;
334 const auto t26 = -1.0 / 6.0 * t25;
335 const auto t27 = -1.0 / 6.0 * t4;
336 const auto t28 = (1.0 / 6.0) * t18;
337 const auto t29 = (1.0 / 6.0) * t8;
338 const auto t30 = -1.0 / 6.0 * t21;
339 const auto t31 = -1.0 / 6.0 * t14;
340 const auto t32 = (1.0 / 6.0) * t25;
341 const auto t33 = az + t0;
342 const auto t34 = -1.0 / 6.0 * t33;
343 const auto t35 = ay + t3;
344 const auto t36 = (1.0 / 6.0) * t35;
345 const auto t37 = az + t10;
346 const auto t38 = (1.0 / 6.0) * t37;
347 const auto t39 = ay + t13;
348 const auto t40 = -1.0 / 6.0 * t39;
349 const auto t41 = (1.0 / 6.0) * t33;
350 const auto t42 = ax + t17;
351 const auto t43 = -1.0 / 6.0 * t42;
352 const auto t44 = -1.0 / 6.0 * t37;
353 const auto t45 = ax + t24;
354 const auto t46 = (1.0 / 6.0) * t45;
355 const auto t47 = -1.0 / 6.0 * t35;
356 const auto t48 = (1.0 / 6.0) * t42;
357 const auto t49 = (1.0 / 6.0) * t39;
358 const auto t50 = -1.0 / 6.0 * t45;
359 const auto t51 = az - bz;
360 const auto t52 = -1.0 / 6.0 * t51;
361 const auto t53 = ay - by;
362 const auto t54 = (1.0 / 6.0) * t53;
363 const auto t55 = (1.0 / 6.0) * t51;
364 const auto t56 = ax - bx;
365 const auto t57 = -1.0 / 6.0 * t56;
366 const auto t58 = -1.0 / 6.0 * t53;
367 const auto t59 = (1.0 / 6.0) * t56;
368 H[0] = 0;
369 H[1] = 0;
370 H[2] = 0;
371 H[3] = 0;
372 H[4] = t2;
373 H[5] = t5;
374 H[6] = 0;
375 H[7] = t7;
376 H[8] = t9;
377 H[9] = 0;
378 H[10] = t12;
379 H[11] = t15;
380 H[12] = 0;
381 H[13] = 0;
382 H[14] = 0;
383 H[15] = t16;
384 H[16] = 0;
385 H[17] = t19;
386 H[18] = t20;
387 H[19] = 0;
388 H[20] = t22;
389 H[21] = t23;
390 H[22] = 0;
391 H[23] = t26;
392 H[24] = 0;
393 H[25] = 0;
394 H[26] = 0;
395 H[27] = t27;
396 H[28] = t28;
397 H[29] = 0;
398 H[30] = t29;
399 H[31] = t30;
400 H[32] = 0;
401 H[33] = t31;
402 H[34] = t32;
403 H[35] = 0;
404 H[36] = 0;
405 H[37] = t16;
406 H[38] = t27;
407 H[39] = 0;
408 H[40] = 0;
409 H[41] = 0;
410 H[42] = 0;
411 H[43] = t34;
412 H[44] = t36;
413 H[45] = 0;
414 H[46] = t38;
415 H[47] = t40;
416 H[48] = t2;
417 H[49] = 0;
418 H[50] = t28;
419 H[51] = 0;
420 H[52] = 0;
421 H[53] = 0;
422 H[54] = t41;
423 H[55] = 0;
424 H[56] = t43;
425 H[57] = t44;
426 H[58] = 0;
427 H[59] = t46;
428 H[60] = t5;
429 H[61] = t19;
430 H[62] = 0;
431 H[63] = 0;
432 H[64] = 0;
433 H[65] = 0;
434 H[66] = t47;
435 H[67] = t48;
436 H[68] = 0;
437 H[69] = t49;
438 H[70] = t50;
439 H[71] = 0;
440 H[72] = 0;
441 H[73] = t20;
442 H[74] = t29;
443 H[75] = 0;
444 H[76] = t41;
445 H[77] = t47;
446 H[78] = 0;
447 H[79] = 0;
448 H[80] = 0;
449 H[81] = 0;
450 H[82] = t52;
451 H[83] = t54;
452 H[84] = t7;
453 H[85] = 0;
454 H[86] = t30;
455 H[87] = t34;
456 H[88] = 0;
457 H[89] = t48;
458 H[90] = 0;
459 H[91] = 0;
460 H[92] = 0;
461 H[93] = t55;
462 H[94] = 0;
463 H[95] = t57;
464 H[96] = t9;
465 H[97] = t22;
466 H[98] = 0;
467 H[99] = t36;
468 H[100] = t43;
469 H[101] = 0;
470 H[102] = 0;
471 H[103] = 0;
472 H[104] = 0;
473 H[105] = t58;
474 H[106] = t59;
475 H[107] = 0;
476 H[108] = 0;
477 H[109] = t23;
478 H[110] = t31;
479 H[111] = 0;
480 H[112] = t44;
481 H[113] = t49;
482 H[114] = 0;
483 H[115] = t55;
484 H[116] = t58;
485 H[117] = 0;
486 H[118] = 0;
487 H[119] = 0;
488 H[120] = t12;
489 H[121] = 0;
490 H[122] = t32;
491 H[123] = t38;
492 H[124] = 0;
493 H[125] = t50;
494 H[126] = t52;
495 H[127] = 0;
496 H[128] = t59;
497 H[129] = 0;
498 H[130] = 0;
499 H[131] = 0;
500 H[132] = t15;
501 H[133] = t26;
502 H[134] = 0;
503 H[135] = t40;
504 H[136] = t46;
505 H[137] = 0;
506 H[138] = t54;
507 H[139] = t57;
508 H[140] = 0;
509 H[141] = 0;
510 H[142] = 0;
511 H[143] = 0;
512 }
513
514 bool is_flipped(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
515 {
516 assert(F.cols() == 3 || F.cols() == 4 && "Only triangle and tetrahedron are supported.");
517
518 // Triangle.
519 if (F.cols() == 3)
520 {
521 for (int i = 0; i < F.rows(); i++)
522 {
523 double area = triangle_jacobian(V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2)));
524 if (area <= 0.0f)
525 {
526 return true;
527 }
528 }
529 }
530 else if (F.cols() == 4)
531 {
532 // Tetrahedron.
533 for (int i = 0; i < F.rows(); i++)
534 {
535 double volume = tetrahedron_volume(V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2)), V.row(F(i, 3)));
536 if (volume <= 0.0f)
537 {
538 return true;
539 }
540 }
541 }
542 else
543 {
544 return true;
545 }
546
547 return false;
548 }
549
550} // namespace polyfem::utils
int V
Eigen::Matrix< double, dim, 1 > cross(const Eigen::Matrix< double, dim, 1 > &x, const Eigen::Matrix< double, dim, 1 > &y)
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.
bool is_flipped(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
Determine if any simplex is inverted or collapses.
void triangle_area_2D_hessian(double ax, double ay, double bx, double by, double cx, double cy, double H[36])
Compute the Hessian of the signed area of a 2D triangle defined by three points.
void tetrahedron_volume_gradient(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz, double dx, double dy, double dz, double g[12])
Compute the gradient of the signed volume of a tetrahedron defined by four points.
void tetrahedron_volume_hessian(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz, double dx, double dy, double dz, double H[144])
Compute the gradient of the signed area of a 2D triangle defined by three points.
double tetrahedron_volume(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c, const Eigen::Vector3d &d)
Compute the signed volume of a tetrahedron defined by four points.
void triangle_area_2D_gradient(double ax, double ay, double bx, double by, double cx, double cy, double g[6])
Compute the gradient of the signed area of a 2D triangle defined by three points.
bool are_edges_collinear(const VectorNd &ea0, const VectorNd &ea1, const VectorNd &eb0, const VectorNd &eb1, const double tol)
Determine if two edges are collinear.
double triangle_area_3D(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c)
Compute the area of a 3D triangle defined by three points.
Eigen::MatrixXd triangle_to_clockwise_order(const Eigen::MatrixXd &triangle)
Reorder the vertices of a triangle so they are in clockwise order.
std::vector< Eigen::MatrixXd > triangle_fan(const Eigen::MatrixXd &convex_polygon)
Convert a convex polygon to a list of triangles fanned around the first vertex.
bool are_triangles_coplanar(const Eigen::Vector3d &t00, const Eigen::Vector3d &t01, const Eigen::Vector3d &t02, const Eigen::Vector3d &t10, const Eigen::Vector3d &t11, const Eigen::Vector3d &t12, const double tol)
Determine if two triangles are coplanar.
Eigen::VectorXd barycentric_coordinates(const Eigen::VectorXd &p, const Eigen::MatrixXd &V)
Compute barycentric coordinates for point p with respect to a simplex.
double triangle_area(const Eigen::MatrixXd V)
Compute the signed area of a triangle defined by three points.
double triangle_area_2D(const Eigen::Vector2d &a, const Eigen::Vector2d &b, const Eigen::Vector2d &c)
Compute the signed area of a 2D triangle defined by three points.
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::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Definition Types.hpp:11