10 #include "imstkTypes.h" 17 namespace CollisionUtils
26 isIntersect(
const double a,
const double b,
const double c,
const double d)
28 return ((a <= d && a >= c) || (c <= b && c >= a)) ? true :
false;
35 testAabbToAabb(
const double min1_x,
const double max1_x,
36 const double min1_y,
const double max1_y,
37 const double min1_z,
const double max1_z,
38 const double min2_x,
const double max2_x,
39 const double min2_y,
const double max2_y,
40 const double min2_z,
const double max2_z)
42 return (isIntersect(min1_x, max1_x, min2_x, max2_x)
43 && isIntersect(min1_y, max1_y, min2_y, max2_y)
44 && isIntersect(min1_z, max1_z, min2_z, max2_z));
51 testPointToTriAabb(
const double x1,
const double y1,
const double z1,
52 const double x2,
const double y2,
const double z2,
53 const double x3,
const double y3,
const double z3,
54 const double x4,
const double y4,
const double z4,
55 const double prox1,
const double prox2)
57 const auto min_x = std::min({ x2, x3, x4 });
58 const auto max_x = std::max({ x2, x3, x4 });
59 const auto min_y = std::min({ y2, y3, y4 });
60 const auto max_y = std::max({ y2, y3, y4 });
61 const auto min_z = std::min({ z2, z3, z4 });
62 const auto max_z = std::max({ z2, z3, z4 });
64 return testAabbToAabb(x1 - prox1, x1 + prox1, y1 - prox1, y1 + prox1,
65 z1 - prox1, z1 + prox1, min_x - prox2, max_x + prox2,
66 min_y - prox2, max_y + prox2, min_z - prox2, max_z + prox2);
78 bool testLineToLineAabb(
const double x1,
const double y1,
const double z1,
79 const double x2,
const double y2,
const double z2,
80 const double x3,
const double y3,
const double z3,
81 const double x4,
const double y4,
const double z4,
82 const double prox1 = VERY_SMALL_EPSILON_D,
const double prox2 = VERY_SMALL_EPSILON_D);
94 testLineToLineAabb(
const Vec3d& p1A,
const Vec3d& p1B,
95 const Vec3d& p2A,
const Vec3d& p2B,
96 const double prox1 = VERY_SMALL_EPSILON_D,
const double prox2 = VERY_SMALL_EPSILON_D)
98 const double* p1Aptr = &p1A[0];
99 const double* p1Bptr = &p1B[0];
100 const double* p2Aptr = &p2A[0];
101 const double* p2Bptr = &p2B[0];
102 return testLineToLineAabb(p1Aptr[0], p1Aptr[1], p1Aptr[2],
103 p1Bptr[0], p1Bptr[1], p1Bptr[2],
104 p2Aptr[0], p2Aptr[1], p2Aptr[2],
105 p2Bptr[0], p2Bptr[1], p2Bptr[2],
119 const Vec3d& cubePos,
const Mat3d& rot,
const Vec3d extents,
124 const Vec3d diff = (pt - cubePos);
125 const Vec3d proj = rot.transpose() * diff;
128 (std::abs(proj[0]) < extents[0])
129 && (std::abs(proj[1]) < extents[1])
130 && (std::abs(proj[2]) < extents[2]);
139 const Vec3d& cubePos,
const Mat3d& rot,
const Vec3d extents,
141 Vec3d& ptContactNormal, Vec3d& cubeContactPt,
double& penetrationDepth)
143 const Vec3d diff = (pt - cubePos);
144 const Vec3d proj = rot.transpose() * diff;
148 (std::abs(proj[0]) < extents[0]),
149 (std::abs(proj[1]) < extents[1]),
150 (std::abs(proj[2]) < extents[2])
152 bool isInsideCube = inside[0] && inside[1] && inside[2];
157 penetrationDepth = std::numeric_limits<double>::max();
158 for (
int i = 0; i < 3; i++)
160 double dist = proj[i];
161 const Vec3d& axes = rot.col(i);
163 if (dist < extents[i] && dist > 0.0)
165 const double unsignedDistToSide = (extents[i] - dist);
166 if (unsignedDistToSide < penetrationDepth)
168 cubeContactPt = pt + unsignedDistToSide * axes;
169 penetrationDepth = unsignedDistToSide;
170 ptContactNormal = axes;
173 else if (dist > -extents[i] && dist < 0.0)
175 const double unsignedDistToSide = (extents[i] + dist);
176 if (unsignedDistToSide < penetrationDepth)
178 cubeContactPt = pt - unsignedDistToSide * axes;
179 penetrationDepth = unsignedDistToSide;
180 ptContactNormal = -axes;
189 cubeContactPt = cubePos;
190 ptContactNormal = Vec3d::Zero();
191 for (
int i = 0; i < 3; i++)
193 double dist = proj[i];
194 const Vec3d& axes = rot.col(i);
197 if (dist >= extents[i])
199 cubeContactPt += extents[i] * axes;
200 ptContactNormal += axes;
202 else if (dist <= -extents[i])
204 cubeContactPt -= extents[i] * axes;
205 ptContactNormal -= axes;
208 ptContactNormal.normalize();
220 const Vec3d& planePt,
const Vec3d& planeNormal,
221 const Vec3d& spherePos,
const double r)
223 return (spherePos - planePt).dot(planeNormal) < r;
232 const Vec3d& planePt,
const Vec3d& planeNormal,
233 const Vec3d& spherePos,
const double r,
234 Vec3d& planeContactPt, Vec3d& planeContactNormal,
235 Vec3d& sphereContactPt, Vec3d& sphereContactNormal,
double& penetrationDepth)
237 const double d = (spherePos - planePt).dot(planeNormal);
239 planeContactNormal = -planeNormal;
240 sphereContactNormal = planeNormal;
241 planeContactPt = spherePos - d * planeNormal;
242 sphereContactPt = spherePos - r * planeNormal;
244 penetrationDepth = r - d;
253 testBidirectionalPlaneToSphere(
254 const Vec3d& planePt,
const Vec3d& planeNormal,
255 const Vec3d& spherePos,
const double r,
256 Vec3d& planeContactPt, Vec3d& planeContactNormal,
257 Vec3d& sphereContactPt, Vec3d& sphereContactNormal,
double& pentrationDepth)
259 const double d = (spherePos - planePt).dot(planeNormal);
261 planeContactPt = spherePos - d * planeNormal;
265 planeContactNormal = planeNormal;
266 sphereContactNormal = -planeNormal;
267 sphereContactPt = spherePos + r * planeNormal;
271 planeContactNormal = -planeNormal;
272 sphereContactNormal = planeNormal;
273 sphereContactPt = spherePos - r * planeNormal;
276 pentrationDepth = r - std::abs(d);
277 return pentrationDepth > 0.0;
286 const Vec3d& sphereAPos,
const double rA,
287 const Vec3d& sphereBPos,
const double rB)
289 const double rSum = rA + rB;
290 return (sphereBPos - sphereAPos).squaredNorm() < (rSum * rSum);
299 const Vec3d& sphereAPos,
const double rA,
300 const Vec3d& sphereBPos,
const double rB,
301 Vec3d& sphereAContactPt, Vec3d& sphereAContactNormal,
302 Vec3d& sphereBContactPt, Vec3d& sphereBContactNormal,
305 Vec3d dirAtoB = sphereBPos - sphereAPos;
307 const double d = dirAtoB.norm();
308 dirAtoB = dirAtoB / d;
310 sphereAContactPt = sphereAPos + dirAtoB * rA;
311 sphereAContactNormal = -dirAtoB;
312 sphereBContactPt = sphereBPos - dirAtoB * rB;
313 sphereBContactNormal = dirAtoB;
314 depth = (rA + rB) - d;
324 testSphereToCylinder(
325 const Vec3d& spherePos,
const double rSphere,
326 const Vec3d& cylinderPos,
const Vec3d& cylinderAxis,
const double rCylinder,
const double cylinderLength,
327 Vec3d& sphereContactPt, Vec3d& sphereContactNormal,
328 Vec3d& cylinderContactPt, Vec3d& cylinderContactNormal,
331 const double cylHalfLength = cylinderLength * 0.5;
334 const Vec3d cylToSphere = (spherePos - cylinderPos);
335 const Vec3d n = cylinderAxis;
338 const double distN = n.dot(cylToSphere);
339 const Vec3d distNVec = distN * n;
342 const Vec3d distPerpVec = cylToSphere - distNVec;
343 const double distPerp = distPerpVec.norm();
344 const Vec3d perp = distPerpVec.normalized();
347 if (std::abs(distN) < cylHalfLength)
355 sphereContactPt = spherePos - perp * rSphere;
356 cylinderContactPt = cylinderPos + distNVec + perp * rCylinder;
358 sphereContactNormal = perp;
359 cylinderContactNormal = -perp;
360 depth = (rSphere + rCylinder) - distPerp;
375 if (std::abs(distN) < (cylHalfLength + rSphere))
383 if (distPerp < rCylinder)
385 sphereContactPt = spherePos - n * rSphere;
386 cylinderContactPt = cylinderPos + n * cylHalfLength + distPerpVec;
388 sphereContactNormal = n;
389 cylinderContactNormal = -n;
390 depth = (rSphere + cylHalfLength) - std::abs(distN);
400 else if (distPerp < (rCylinder + rSphere))
402 cylinderContactPt = cylinderPos + n * cylHalfLength + perp * rCylinder;
403 const Vec3d diagDiff = spherePos - cylinderContactPt;
404 const double diagDist = diagDiff.norm();
406 sphereContactNormal = diagDiff / diagDist;
407 cylinderContactNormal = -sphereContactNormal;
409 sphereContactPt = spherePos + cylinderContactNormal * rSphere;
410 depth = rSphere - diagDist;
424 const Vec3d& capsulePos,
const Vec3d& capsuleAxis,
const double capsuleLength,
const double rCapsule,
428 const Vec3d a = capsulePos + 0.5 * capsuleAxis * capsuleLength;
429 const Vec3d b = 2.0 * capsulePos - a;
431 const Vec3d pa = point - a;
432 const Vec3d ba = b - a;
433 const double h = std::min(std::max(pa.dot(ba) / ba.dot(ba), 0.0), 1.0);
434 const double signedDist = ((pa - ba * h).norm() - rCapsule);
435 return signedDist < 0.0;
444 const Vec3d& capsulePos,
const Vec3d& capsuleAxis,
const double capsuleLength,
const double rCapsule,
446 Vec3d& capsuleContactPt, Vec3d& capsuleContactNormal, Vec3d& pointContactNormal,
double& depth)
450 const Vec3d mid = capsulePos;
451 const Vec3d p1 = mid + 0.5 * capsuleAxis * capsuleLength;
452 const Vec3d p0 = 2.0 * mid - p1;
453 const Vec3d pDiff = p1 - p0;
454 const double pDiffSqrLength = pDiff.dot(pDiff);
455 const double pDotp0 = pDiff.dot(p0);
458 if ((mid - point).norm() > (rCapsule + capsuleLength * 0.5))
464 const double alpha = (point.dot(pDiff) - pDotp0) / pDiffSqrLength;
470 else if (alpha < 0.0)
476 closestPoint = p0 + alpha * pDiff;
481 const Vec3d diff = (point - closestPoint);
482 const double dist = diff.norm();
485 depth = rCapsule - dist;
486 pointContactNormal = diff.normalized();
487 capsuleContactNormal = -pointContactNormal;
488 capsuleContactPt = closestPoint + pointContactNormal * rCapsule;
500 const Vec3d& cylinderPos,
const Vec3d& cylinderAxis,
const double cylinderLength,
const double cylinderRadius,
502 Vec3d& cylinderContactPt, Vec3d& cylinderContactNormal, Vec3d& pointContactNormal,
double& depth)
505 if ((cylinderPos - point).squaredNorm() > (cylinderRadius * cylinderRadius + 0.25 * cylinderLength * cylinderLength))
511 const Vec3d mid = cylinderPos;
512 const Vec3d p1 = mid + 0.5 * cylinderAxis * cylinderLength;
513 const Vec3d p0 = 2.0 * mid - p1;
514 const Vec3d pDiff = p1 - p0;
515 const double pDotp0 = pDiff.dot(p0);
518 const double alpha = (point.dot(pDiff) - pDotp0) / (cylinderLength * cylinderLength);
519 if ((alpha > 1.0) || (alpha < 0.0))
525 Vec3d closestPointOnAxis = p0 + alpha * pDiff;
526 const Vec3d diff = (point - closestPointOnAxis);
527 const double dist = diff.norm();
528 if (dist < cylinderRadius)
530 const double distToEnd = (alpha * pDiff).norm();
532 if (distToEnd < (cylinderRadius - dist))
535 cylinderContactNormal = pDiff.normalized();
536 pointContactNormal = -cylinderContactNormal;
537 cylinderContactPt = point + pointContactNormal * distToEnd;
539 else if ((cylinderLength - distToEnd) < (cylinderRadius - dist))
541 depth = cylinderLength - distToEnd;
542 cylinderContactNormal = -pDiff.normalized();
543 pointContactNormal = -cylinderContactNormal;
544 cylinderContactPt = point + pointContactNormal * (cylinderLength - distToEnd);
548 depth = cylinderRadius - dist;
549 pointContactNormal = diff.normalized();
550 cylinderContactNormal = -pointContactNormal;
551 cylinderContactPt = closestPointOnAxis + pointContactNormal * cylinderRadius;
565 const Vec3d& spherePos,
const double rSqr,
const Vec3d& point)
567 return (spherePos - point).squaredNorm() < rSqr;
576 const Vec3d& spherePos,
const double r,
const Vec3d& point,
577 Vec3d& sphereContactPt, Vec3d& sphereContactNormal,
double& penetrationDepth)
579 const Vec3d diff = point - spherePos;
580 penetrationDepth = diff.norm();
581 sphereContactNormal = diff / penetrationDepth;
582 sphereContactPt = spherePos + sphereContactNormal * r;
583 const double signedDist = penetrationDepth - r;
584 penetrationDepth = std::abs(signedDist);
585 return signedDist < 0.0;
594 const Vec3d& planePt,
const Vec3d& planeNormal,
597 return (point - planePt).dot(planeNormal) < 0.0;
606 const Vec3d& planePt,
const Vec3d& planeNormal,
608 Vec3d& planeContactPt, Vec3d& contactNormal,
double& pointPenetrationDepth)
610 contactNormal = planeNormal;
611 const double d = (point - planePt).dot(planeNormal);
612 planeContactPt = point - pointPenetrationDepth * contactNormal;
613 pointPenetrationDepth = std::abs(d);
621 testPlaneLine(
const Vec3d& p,
const Vec3d& q,
622 const Vec3d& planePt,
const Vec3d& planeNormal, Vec3d& iPt)
624 const Vec3d n = q - p;
625 const double denom = n.dot(planeNormal);
627 if (std::abs(denom) < IMSTK_DOUBLE_EPS)
632 const double t = (planePt - p).dot(planeNormal) / denom;
643 const Vec3d& p,
const Vec3d& q,
644 const Vec3d& a,
const Vec3d& b,
const Vec3d& c,
647 const Vec3d n = q - p;
648 const Vec3d planeNormal = (b - a).cross(c - a);
649 const double denom = n.dot(planeNormal);
651 if (std::abs(denom) < IMSTK_DOUBLE_EPS)
656 const double t1 = (a - p).dot(planeNormal);
657 const double t2 = (a - q).dot(planeNormal);
660 if ((t1 < 0.0 && t2 >= 0.0) || (t1 >= 0.0 && t2 < 0.0))
665 uvw = baryCentric(p + t1 / denom * n, a, b, c);
667 return (uvw[0] >= 0.0 && uvw[1] >= 0.0 && uvw[2] >= 0.0);
680 const Vec3d& p,
const Vec3d& q,
681 const Vec3d& a,
const Vec3d& b,
const Vec3d& c)
683 const Vec3d n = q - p;
684 const Vec3d planeNormal = (b - a).cross(c - a);
685 const double denom = n.dot(planeNormal);
687 if (std::abs(denom) < IMSTK_DOUBLE_EPS)
692 const double t1 = (a - p).dot(planeNormal);
693 const double t2 = (a - q).dot(planeNormal);
696 if ((t1 < 0.0 && t2 >= 0.0) || (t1 >= 0.0 && t2 < 0.0))
701 const Vec3d uvw = baryCentric(p + t1 / denom * n, a, b, c);
703 return (uvw[0] >= 0.0 && uvw[1] >= 0.0 && uvw[2] >= 0.0);
717 Vec3d closestPointOnSegment(
const Vec3d& point,
const Vec3d& x1,
const Vec3d& x2,
int& caseType);
729 Vec3d closestPointOnTriangle(
const Vec3d& p,
const Vec3d& a,
const Vec3d& b,
const Vec3d& c,
int& caseType);
736 testSphereToTriangle(
const Vec3d& spherePt,
const double sphereRadius,
737 const Vec3d& a,
const Vec3d& b,
const Vec3d& c,
738 Vec3d& sphereContactPt, Vec3d& sphereContactNormal,
double& penetrationDepth)
741 const Vec3d closestPtOnTriangle = closestPointOnTriangle(spherePt, a, b, c, type);
743 sphereContactNormal = (spherePt - closestPtOnTriangle);
744 const double dist = sphereContactNormal.norm();
745 penetrationDepth = sphereRadius - dist;
746 sphereContactNormal /= dist;
747 sphereContactPt = spherePt - sphereContactNormal * sphereRadius;
748 return dist < sphereRadius;
758 testSphereToTriangle(
const Vec3d& spherePt,
const double sphereRadius,
759 const Vec3i& tri,
const Vec3d& a,
const Vec3d& b,
const Vec3d& c,
760 Vec3d& triangleContactPt,
765 triangleContactPt = closestPointOnTriangle(spherePt, a, b, c, caseType);
766 const Vec3d diff = spherePt - triangleContactPt;
767 const double dist = diff.norm();
770 if (dist <= sphereRadius)
775 pointContact = tri[0];
778 pointContact = tri[1];
781 pointContact = tri[2];
784 edgeContact = { tri[0], tri[1] };
787 edgeContact = { tri[1], tri[2] };
790 edgeContact = { tri[2], tri[0] };
813 testSphereToTriangle(
const Vec3d& spherePt,
const double sphereRadius,
814 const Vec3d& a,
const Vec3d& b,
const Vec3d& c,
815 Vec3d& triangleContactPt)
818 triangleContactPt = closestPointOnTriangle(spherePt, a, b, c, caseType);
819 const double dist = (spherePt - triangleContactPt).squaredNorm();
822 if (dist <= sphereRadius * sphereRadius)
833 testPointToTetrahedron(
const std::array<Vec3d, 4>& inputTetVerts,
const Vec3d& p)
835 const Vec4d bCoord = baryCentric(p, inputTetVerts[0], inputTetVerts[1], inputTetVerts[2], inputTetVerts[3]);
837 constexpr
const double eps = IMSTK_DOUBLE_EPS;
838 if (bCoord[0] >= -eps
841 && bCoord[3] >= -eps)
855 const std::array<Vec3d, 4>& inputTetVerts,
856 const Vec3d& x1,
const Vec3d& x2)
858 static int faces[4][3] = { { 0, 1, 2 }, { 1, 2, 3 }, { 0, 2, 3 }, { 0, 1, 3 } };
859 for (
int i = 0; i < 4; i++)
861 if (testSegmentTriangle(x1, x2, inputTetVerts[faces[i][0]], inputTetVerts[faces[i][1]], inputTetVerts[faces[i][2]]))
868 if (testPointToTetrahedron(inputTetVerts, x1) || testPointToTetrahedron(inputTetVerts, x2))
883 const std::array<Vec3d, 4>& inputTetVerts,
884 const Vec3d& x1,
const Vec3d& x2,
885 int& intersectionFace0,
int& intersectionFace1,
886 Vec3d& iPt0, Vec3d& iPt1)
888 static int faces[4][3] = { { 0, 1, 2 }, { 1, 2, 3 }, { 0, 2, 3 }, { 0, 1, 3 } };
889 bool firstFound =
false;
890 for (
int i = 0; i < 4; i++)
892 const Vec3d& a = inputTetVerts[faces[i][0]];
893 const Vec3d& b = inputTetVerts[faces[i][1]];
894 const Vec3d& c = inputTetVerts[faces[i][2]];
896 if (testSegmentTriangle(x1, x2, a, b, c, uvw))
900 intersectionFace1 = i;
901 iPt1 = uvw[0] * a + uvw[1] * b + uvw[2] * c;
906 intersectionFace0 = i;
907 iPt0 = uvw[0] * a + uvw[1] * b + uvw[2] * c;
927 testRayToObb(
const Vec3d& rayOrigin,
const Vec3d& rayDir,
928 const Mat4d& worldToBox, Vec3d extents,
932 const Vec3d rd = (worldToBox * Vec4d(rayDir[0], rayDir[1], rayDir[2], 0.0)).head<3>();
933 const Vec3d ro = (worldToBox * Vec4d(rayOrigin[0], rayOrigin[1], rayOrigin[2], 1.0)).head<3>();
936 const Vec3d m = Vec3d(1.0, 1.0, 1.0).cwiseQuotient(rd);
937 const Vec3d s = Vec3d((rd[0] < 0.0) ? 1.0 : -1.0,
938 (rd[1] < 0.0) ? 1.0 : -1.0,
939 (rd[2] < 0.0) ? 1.0 : -1.0);
940 const Vec3d t1 = m.cwiseProduct(-ro + s.cwiseProduct(extents));
941 const Vec3d t2 = m.cwiseProduct(-ro - s.cwiseProduct(extents));
943 const double tN = std::max(std::max(t1[0], t1[1]), t1[2]);
944 const double tF = std::min(std::min(t2[0], t2[1]), t2[2]);
947 if (tN > tF || tF < 0.0)
961 testRayToSphere(
const Vec3d& rayOrigin,
const Vec3d& rayDir,
962 const Vec3d& spherePos,
const double radius,
965 const Vec3d m = rayOrigin - spherePos;
966 double b = m.dot(rayDir);
967 double c = m.dot(m) - radius * radius;
970 if (c > 0.0 && b > 0.0)
974 double discr = b * b - c;
982 double t = std::max(0.0, -b - std::sqrt(discr));
983 iPt = rayOrigin + t * rayDir;
991 testRayToPlane(
const Vec3d& rayOrigin,
const Vec3d& rayDir,
992 const Vec3d& planePos,
const Vec3d& planeNormal,
995 const double denom = rayDir.dot(planeNormal);
997 if (std::abs(denom) < IMSTK_DOUBLE_EPS)
1001 const double t = (planePos - rayOrigin).dot(planeNormal) / denom;
1007 iPt = rayOrigin + t * rayDir;
1014 double pointSegmentClosestDistance(
const Vec3d& point,
const Vec3d& x1,
const Vec3d& x2);
1019 double pointTriangleClosestDistance(
const Vec3d& point,
const Vec3d& x1,
const Vec3d& x2,
const Vec3d& x3);
1028 int triangleToTriangle(
1029 const Vec3i& tri_a,
const Vec3i& tri_b,
1030 const Vec3d& p0_a,
const Vec3d& p1_a,
const Vec3d& p2_a,
1031 const Vec3d& p0_b,
const Vec3d& p1_b,
const Vec3d& p2_b,
1032 std::pair<Vec2i, Vec2i>& edgeContact,
1033 std::pair<int, Vec3i>& vertexTriangleContact,
1034 std::pair<Vec3i, int>& triangleVertexContact);
1048 edgeToEdgeClosestPoints(
1049 const Vec3d& a0,
const Vec3d& a1,
1050 const Vec3d& b0,
const Vec3d& b1,
1051 Vec3d& ptA, Vec3d& ptB)
1057 const Vec3d d1 = a1 - a0;
1058 const Vec3d d2 = b1 - b0;
1059 const Vec3d r = a0 - b0;
1060 const double a = d1.dot(d1);
1061 const double e = d2.dot(d2);
1062 const double f = d2.dot(r);
1065 if (a <= IMSTK_DOUBLE_EPS && e <= IMSTK_DOUBLE_EPS)
1074 if (a <= IMSTK_DOUBLE_EPS)
1079 t = std::min(std::max(t, 0.0), 1.0);
1084 const double c = d1.dot(r);
1085 if (e <= IMSTK_DOUBLE_EPS)
1089 s = std::min(std::max(-c / a, 0.0), 1.0);
1095 const double b = d1.dot(d2);
1096 const double denom = a * e - b * b;
1101 s = std::min(std::max((b * f - c * e) / denom, 0.0), 1.0);
1110 t = (b * s + f) / e;
1117 s = std::min(std::max(-c / a, 0.0), 1.0);
1123 s = std::min(std::max((b - c) / a, 0.0), 1.0);