iMSTK
Interactive Medical Simulation Toolkit
imstkLineMeshToCapsuleCD.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 "imstkLineMeshToCapsuleCD.h"
8 #include "imstkCollisionData.h"
9 #include "imstkCollisionUtils.h"
10 #include "imstkParallelUtils.h"
11 #include "imstkCapsule.h"
12 #include "imstkLineMesh.h"
13 
14 namespace imstk
15 {
16 LineMeshToCapsuleCD::LineMeshToCapsuleCD()
17 {
18  setRequiredInputType<LineMesh>(0);
19  setRequiredInputType<Capsule>(1);
20 }
21 
22 void
24  std::shared_ptr<Geometry> geomA,
25  std::shared_ptr<Geometry> geomB,
26  std::vector<CollisionElement>& elementsA,
27  std::vector<CollisionElement>& elementsB)
28 {
29  std::shared_ptr<LineMesh> lineMesh = std::dynamic_pointer_cast<LineMesh>(geomA);
30  std::shared_ptr<Capsule> capsule = std::dynamic_pointer_cast<Capsule>(geomB);
31 
32  const Vec3d& capsulePos = capsule->getPosition();
33  const double capsuleRadius = capsule->getRadius();
34  const double capsuleLength = capsule->getLength();
35  const Quatd& capsuleOrientation = capsule->getOrientation();
36  const Vec3d& capsulePosA = capsulePos - 0.5 * capsuleLength * capsuleOrientation.toRotationMatrix().col(1);
37  const Vec3d& capsulePosB = capsulePos + (capsulePos - capsulePosA);
38 
39  std::shared_ptr<VecDataArray<int, 2>> indicesPtr = lineMesh->getCells();
40  const VecDataArray<int, 2>& indices = *indicesPtr;
41  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = lineMesh->getVertexPositions();
42  const VecDataArray<double, 3>& vertices = *verticesPtr;
43 
44  Eigen::AlignedBox3d box1, box2;
45  Vec3d lower, upper;
46  geomA->computeBoundingBox(lower, upper);
47  box1.extend(lower);
48  box1.extend(upper);
49  geomB->computeBoundingBox(lower, upper);
50  box2.extend(lower);
51  box2.extend(upper);
52 
53  if (!box1.intersects(box2))
54  {
55  return;
56  }
57 
59  ParallelUtils::parallelFor(indices.size(), [&](int i)
60  {
61  const Vec2i& cell = indices[i];
62  const Vec3d& x1 = vertices[cell[0]];
63  const Vec3d& x2 = vertices[cell[1]];
64 
65  double capBoundingRaidus = (capsuleLength / 2.0) + capsuleRadius;
66 
67  // This approach does a built in sphere sweep
68  // \todo: Spatial accelerators need to be abstracted
69  const Vec3d centroid = (x1 + x2) / 2.0;
70 
71  // Find the maximal point from centroid for radius
72  const double rSqr1 = (centroid - x1).squaredNorm();
73 
74  const double lineBoundingRadius = std::sqrt(rSqr1);
75 
76  const double distSqr = (centroid - capsulePos).squaredNorm();
77  const double rSum = lineBoundingRadius + capBoundingRaidus;
78 
79  // Test for intersection of bounding sphere
80  if (distSqr < rSum * rSum)
81  {
82  // Choose the closest point on capsule axis to create a virtual sphere for CD
83  int unusedCaseType = 0;
84  Vec3d capClosestPt = Vec3d::Zero();
85  Vec3d segClosestPt = Vec3d::Zero();
86 
87  unusedCaseType = CollisionUtils::edgeToEdgeClosestPoints(
88  capsulePosA, capsulePosB,
89  x1, x2,
90  capClosestPt, segClosestPt);
91 
92  double seperationDistance = (capClosestPt - segClosestPt).norm();
93 
94  // test if closest point is less than capsule raidus
95  if (seperationDistance <= capsuleRadius)
96  {
97  // Get type (point-point or point-edge)
98  int caseType = -1;
99  if ((x1 - segClosestPt).norm() <= 1E-12)
100  {
101  caseType = 0;
102  }
103  else if ((x2 - segClosestPt).norm() <= 1E-12)
104  {
105  caseType = 1;
106  }
107  else if (seperationDistance <= 1E-12)
108  {
109  caseType = 3;
110  }
111  else
112  {
113  caseType = 2;
114  }
115 
116  Vec3d contactNormal = (capClosestPt - segClosestPt);
117 
118  // Capsule contact with x1
119  if (caseType == 0)
120  {
121  const double penetrationDepth = capsuleRadius - seperationDistance;
122  contactNormal /= seperationDistance;
123 
125  elemA.ptIndex = cell[0]; // Point on line
126  elemA.dir = -contactNormal; // Direction to resolve point on line
127  elemA.penetrationDepth = penetrationDepth;
128 
129  PointDirectionElement elemB;
130  elemB.pt = capClosestPt - capsuleRadius * contactNormal; // Contact point on capsule
131  elemB.dir = contactNormal; // Direction to resolve point on capsuel
132  elemB.penetrationDepth = penetrationDepth;
133 
134  lock.lock();
135  elementsA.push_back(elemA);
136  elementsB.push_back(elemB);
137  lock.unlock();
138  }
139  // Capsule contact with x2
140  else if (caseType == 1)
141  {
142  const double penetrationDepth = capsuleRadius - seperationDistance;
143  contactNormal /= seperationDistance;
144 
146  elemA.ptIndex = cell[1]; // Point on line
147  elemA.dir = -contactNormal; // Direction to resolve point on line
148  elemA.penetrationDepth = penetrationDepth;
149 
150  PointDirectionElement elemB;
151  elemB.pt = capClosestPt - capsuleRadius * contactNormal; // Contact point on capsule
152  elemB.dir = contactNormal; // Direction to resolve point on capsuel
153  elemB.penetrationDepth = penetrationDepth;
154 
155  lock.lock();
156  elementsA.push_back(elemA);
157  elementsB.push_back(elemB);
158  lock.unlock();
159  }
160  // Capsule contact between x1 and x2
161  else if (caseType == 2)
162  {
163  CellIndexElement elemA;
164  elemA.ids[0] = cell[0];
165  elemA.ids[1] = cell[1];
166  elemA.idCount = 2;
167  elemA.parentId = i; // Edge id
168  elemA.cellType = IMSTK_EDGE;
169 
170  const double penetrationDepth = capsuleRadius - seperationDistance;
171  contactNormal /= seperationDistance;
172 
173  PointDirectionElement elemB;
174  elemB.dir = contactNormal; // Direction to resolve capsule
175  elemB.pt = capClosestPt - capsuleRadius * contactNormal; // Contact point on capsule
176  elemB.penetrationDepth = penetrationDepth;
177 
178  lock.lock();
179  elementsA.push_back(elemA);
180  elementsB.push_back(elemB);
181  lock.unlock();
182  }
183  // Capsule centerline coincident with segment
184  else if (caseType == 3)
185  {
186  Vec3d segVec = x2 - x1;
187  Vec3d capVec = capsulePosB - capsulePosA;
188  Vec3d escapeDirection = capVec.cross(segVec).normalized();
189 
190  CellIndexElement elemA;
191  elemA.ids[0] = cell[0];
192  elemA.ids[1] = cell[1];
193  elemA.idCount = 2;
194  elemA.parentId = i; // Edge id
195  elemA.cellType = IMSTK_EDGE;
196 
197  const double penetrationDepth = capsuleRadius - seperationDistance;
198  contactNormal /= seperationDistance;
199 
200  PointDirectionElement elemB;
201  elemB.dir = escapeDirection; // Direction to resolve sphere
202  elemB.pt = capClosestPt - capsuleRadius * escapeDirection; // Contact point on sphere
203  elemB.penetrationDepth = penetrationDepth;
204 
205  lock.lock();
206  elementsA.push_back(elemA);
207  elementsB.push_back(elemB);
208  lock.unlock();
209  }
210  }
211  }
212  }, indices.size() > 500);
213 }
214 } // namespace imstk
The SpinLock class.
Definition: imstkSpinLock.h:20
void unlock()
End a thread-safe region.
Definition: imstkSpinLock.h:53
Vec3d getPosition(DataType type=DataType::PostTransform)
Get the local or global position (post transformed)
Compound Geometry.
void lock()
Start a thread-safe region, where only one thread can execute at a time until a call to the unlock fu...
Definition: imstkSpinLock.h:45
Base class for all volume mesh types.
Definition: imstkLineMesh.h:18
Represents a cell by a single cell id OR by N vertex ids. Which case can be determined by the idCount...
Direclty gives a point-direction contact as its collision data, point given by index.
Capsule geometry, default configuration is centered at origin with length running up and down the y a...
Definition: imstkCapsule.h:21
Direclty gives a point-direction contact as its collision data.
void computeCollisionDataAB(std::shared_ptr< Geometry > geomA, std::shared_ptr< Geometry > geomB, std::vector< CollisionElement > &elementsA, std::vector< CollisionElement > &elementsB) override
Compute collision data for AB simultaneously.