iMSTK
Interactive Medical Simulation Toolkit
imstkCollisionUtils.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkCollisionUtils.h"
8 #include "imstkLogger.h"
9 
10 namespace imstk
11 {
12 namespace CollisionUtils
13 {
14 bool
15 testLineToLineAabb(const double x1, const double y1, const double z1,
16  const double x2, const double y2, const double z2,
17  const double x3, const double y3, const double z3,
18  const double x4, const double y4, const double z4,
19  const double prox1 /*= VERY_SMALL_EPSILON_D*/, const double prox2 /*= VERY_SMALL_EPSILON_D*/)
20 {
21  double min1_x, max1_x, min1_y, max1_y, min1_z, max1_z;
22 
23  if (x1 < x2)
24  {
25  min1_x = x1;
26  max1_x = x2;
27  }
28  else
29  {
30  min1_x = x2;
31  max1_x = x1;
32  }
33 
34  if (y1 < y2)
35  {
36  min1_y = y1;
37  max1_y = y2;
38  }
39  else
40  {
41  min1_y = y2;
42  max1_y = y1;
43  }
44 
45  if (z1 < z2)
46  {
47  min1_z = z1;
48  max1_z = z2;
49  }
50  else
51  {
52  min1_z = z2;
53  max1_z = z1;
54  }
55 
56  double min2_x, max2_x, min2_y, max2_y, min2_z, max2_z;
57 
58  if (x3 < x4)
59  {
60  min2_x = x3;
61  max2_x = x4;
62  }
63  else
64  {
65  min2_x = x4;
66  max2_x = x3;
67  }
68 
69  if (y3 < y4)
70  {
71  min2_y = y3;
72  max2_y = y4;
73  }
74  else
75  {
76  min2_y = y4;
77  max2_y = y3;
78  }
79 
80  if (z3 < z4)
81  {
82  min2_z = z3;
83  max2_z = z4;
84  }
85  else
86  {
87  min2_z = z4;
88  max2_z = z3;
89  }
90 
91  return testAabbToAabb(min1_x - prox1, max1_x + prox1, min1_y - prox1, max1_y + prox1,
92  min1_z - prox1, max1_z + prox1, min2_x - prox2, max2_x + prox2,
93  min2_y - prox2, max2_y + prox2, min2_z - prox2, max2_z + prox2);
94 }
95 
96 double
97 pointSegmentClosestDistance(const Vec3d& point, const Vec3d& x1, const Vec3d& x2)
98 {
99  Vec3d dx = x2 - x1;
100  double m2 = dx.squaredNorm();
101  if (m2 < 1e-20)
102  {
103  return (point - x1).norm();
104  }
105 
106  // find parameter value of closest point on segment
107  double s12 = dx.dot(x2 - point) / m2;
108 
109  if (s12 < 0)
110  {
111  s12 = 0;
112  }
113  else if (s12 > 1.0)
114  {
115  s12 = 1.0;
116  }
117 
118  return (point - (s12 * x1 + (1.0 - s12) * x2)).eval().norm();
119 }
120 
121 double
122 pointTriangleClosestDistance(const Vec3d& point, const Vec3d& x1, const Vec3d& x2, const Vec3d& x3)
123 {
124  int unusedType;
125  const Vec3d closestPtOnTri = closestPointOnTriangle(point, x1, x2, x3, unusedType);
126 
127  return (point - closestPtOnTri).norm();
128 }
129 
130 Vec3d
131 closestPointOnSegment(const Vec3d& point, const Vec3d& x1, const Vec3d& x2, int& caseType)
132 {
133  Vec3d dx = x2 - x1;
134  double m2 = dx.squaredNorm();
135  if (m2 < 1e-20)
136  {
137  caseType = 0;
138  return x1;
139  }
140 
141  // find parameter value of closest point on segment
142  double s12 = dx.dot(x2 - point) / m2;
143  caseType = 2;
144  if (s12 < 0)
145  {
146  s12 = 0;
147  caseType = 1;
148  }
149  else if (s12 > 1.0)
150  {
151  s12 = 1.0;
152  caseType = 0;
153  }
154 
155  return (s12 * x1 + (1.0 - s12) * x2).eval();
156 }
157 
158 Vec3d
159 closestPointOnTriangle(const Vec3d& p, const Vec3d& a, const Vec3d& b, const Vec3d& c, int& caseType)
160 {
161  // Assumes counterclockwise indexed triangle ABC
162 
163  const Vec3d ab = b - a;
164  const Vec3d ac = c - a;
165  const Vec3d ap = p - a;
166 
167  const double d1 = ab.dot(ap);
168  const double d2 = ac.dot(ap);
169  caseType = -1;
170 
171  // Check if closest point on triangle is point A
172  if (d1 <= 0.0 && d2 <= 0.0)
173  {
174  caseType = 0;
175  }
176 
177  // Check if P in region outside B, so B is closest point
178  const Vec3d bp = p - b;
179  const double d3 = ab.dot(bp);
180  const double d4 = ac.dot(bp);
181  if (d3 >= 0.0 && d4 <= d3)
182  {
183  caseType = 1;
184  }
185 
186  // Check if P in vertex region outside C
187  const Vec3d cp = p - c;
188  const double d5 = ab.dot(cp);
189  const double d6 = ac.dot(cp);
190  if (d6 >= 0.0 && d5 <= d6)
191  {
192  caseType = 2;
193  }
194 
195  // Check if P in edge region of AB, if so return projection of P onto AB
196  const double vc = d1 * d4 - d3 * d2;
197  // if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0)
198  if (vc <= 0.0 && d1 > 0.0 && d3 < 0.0)
199  {
200  caseType = 3;
201  }
202 
203  // Check if P in edge region of BC, if so return projection of P onto BC
204  const double va = d3 * d6 - d5 * d4;
205  if (va <= 0.0 && (d4 - d3) > 0.0 && (d5 - d6) > 0.0)
206  {
207  caseType = 4;
208  }
209 
210  // Check if P in edge region of AC, if so return projection of P onto AC
211  const double vb = d5 * d2 - d1 * d6;
212  if (vb <= 0.0 && d2 > 0.0 && d6 < 0.0)
213  {
214  caseType = 5;
215  }
216 
217  // If neartest point is not a point or edge, then it must be inside of the face
218  if (caseType == -1)
219  {
220  caseType = 6;
221  }
222 
223  // Variables for switch
224  double v, w, denom;
225 
226  switch (caseType)
227  {
228  case 0: // Neartest point it A
229  return a;
230  case 1: // Neartest point it B
231  return b;
232  case 2: // Neartest point it C
233  return c;
234  case 3: // Neartest point on edge AB
235  v = d1 / (d1 - d3);
236  return a + v * ab;
237  case 4: // Neartest point on edge BC
238  w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
239  return b + w * (c - b); // barycentric coordinates (0,1-w,w)
240  case 5: // Neartest point on edge AC
241  w = d2 / (d2 - d6);
242  return a + w * ac; // barycentric coordinates (1-w,0,w)
243  case 6: // Neartest point on face
244  denom = 1.0 / (va + vb + vc);
245  v = vb * denom;
246  w = vc * denom;
247  return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0f-v-w
248  default:
249  LOG(FATAL) << "Unexpected casetype in closestPointOnTriangle " << caseType;
250  return Vec3d(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN());
251  }
252 }
253 
254 int
255 triangleToTriangle(
256  const Vec3i& tri_a, const Vec3i& tri_b,
257  const Vec3d& p0_a, const Vec3d& p1_a, const Vec3d& p2_a,
258  const Vec3d& p0_b, const Vec3d& p1_b, const Vec3d& p2_b,
259  std::pair<Vec2i, Vec2i>& edgeContact,
260  std::pair<int, Vec3i>& vertexTriangleContact,
261  std::pair<Vec3i, int>& triangleVertexContact)
262 {
263  // \todo: One edge case where both triangles are coplanar with vertices from the other
264  int contactType = -1;
265 
266  const std::array<Vec3d, 3> triAVerts = { p0_a, p1_a, p2_a };
267  const std::array<Vec3d, 3> triBVerts = { p0_b, p1_b, p2_b };
268 
269  // Edges of the first triangle
270  const std::pair<Vec3d, Vec3d> triAEdges[3]{
271  { triAVerts[0], triAVerts[1] },
272  { triAVerts[0], triAVerts[2] },
273  { triAVerts[1], triAVerts[2] }
274  };
275 
276  // Edges of the second triangle
277  const std::pair<Vec3d, Vec3d> triBEdges[3]{
278  { triBVerts[0], triBVerts[1] },
279  { triBVerts[0], triBVerts[2] },
280  { triBVerts[1], triBVerts[2] }
281  };
282 
283  // Test if segments of a intersected triangle b
284  bool aIntersected[3];
285  for (int i = 0; i < 3; i++)
286  {
287  aIntersected[i] = CollisionUtils::testSegmentTriangle(triAEdges[i].first, triAEdges[i].second,
288  triBVerts[0], triBVerts[1], triBVerts[2]);
289  }
290 
291  // Count number of edge-triangle intersections
292  const int numIntersectionsA = aIntersected[0] + aIntersected[1] + aIntersected[2];
293  if (numIntersectionsA == 2)
294  {
295  if (aIntersected[0])
296  {
297  const int vertIdx = aIntersected[1] ? tri_a[0] : tri_a[1];
298  vertexTriangleContact = std::pair<int, Vec3i>(vertIdx, tri_b);
299  }
300  else
301  {
302  vertexTriangleContact = std::pair<int, Vec3i>(tri_a[2], tri_b);
303  }
304  contactType = 1;
305  }
306  else if (numIntersectionsA == 1)
307  {
308  Vec2i edgeIdA;
309  if (aIntersected[0])
310  {
311  edgeIdA = { tri_a[0], tri_a[1] };
312  }
313  else if (aIntersected[1])
314  {
315  edgeIdA = { tri_a[0], tri_a[2] };
316  }
317  else
318  {
319  edgeIdA = { tri_a[1], tri_a[2] };
320  }
321 
322  bool bFound = false; // Due to numerical round-off errors, the other triangle may not intersect with the current one
323 
324  // Find the only edge of triangle2 that intersects with triangle1s
325  Vec2i edgeIdB;
326  for (int i = 0; i < 3; i++)
327  {
328  if (CollisionUtils::testSegmentTriangle(triBEdges[i].first, triBEdges[i].second,
329  triAVerts[0], triAVerts[1], triAVerts[2]))
330  {
331  if (i == 0)
332  {
333  edgeIdB = { tri_b[0], tri_b[1] };
334  }
335  else if (i == 1)
336  {
337  edgeIdB = { tri_b[0], tri_b[2] };
338  }
339  else
340  {
341  edgeIdB = { tri_b[1], tri_b[2] };
342  }
343  bFound = true;
344  break;
345  }
346  }
347  // If there is numIntersections == 1 doesn't this garuntee one will be found?
348  if (bFound)
349  {
350  edgeContact = std::pair<Vec2i, Vec2i>(edgeIdA, edgeIdB);
351  contactType = 0;
352  }
353  }
354  else
355  {
356  // Test if segments of b intersected triangle a
357  bool bIntersected[3];
358  for (int i = 0; i < 3; i++)
359  {
360  bIntersected[i] = CollisionUtils::testSegmentTriangle(triBEdges[i].first, triBEdges[i].second,
361  triAVerts[0], triAVerts[1], triAVerts[2]);
362  }
363 
364  // We don't need to cover edge-edge case since its symmetric and the other one should have caught it
365  const int numIntersectionsB = bIntersected[0] + bIntersected[1] + bIntersected[2];
366  if (numIntersectionsB == 2)
367  {
368  if (bIntersected[0])
369  {
370  const int vertIdx = bIntersected[1] ? tri_b[0] : tri_b[1];
371  triangleVertexContact = std::pair<Vec3i, int>(tri_a, vertIdx);
372  }
373  else
374  {
375  triangleVertexContact = std::pair<Vec3i, int>(tri_a, tri_b[2]);
376  }
377  contactType = 2;
378  }
379  }
380  return contactType;
381 }
382 } // namespace CollisionUtils
383 } // namespace imstk
Compound Geometry.