iMSTK
Interactive Medical Simulation Toolkit
imstkImplicitGeometryToPointSetCCD.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 "imstkImplicitGeometryToPointSetCCD.h"
8 #include "imstkCollisionData.h"
9 #include "imstkImageData.h"
10 #include "imstkParallelUtils.h"
11 #include "imstkSurfaceMesh.h"
12 
13 namespace imstk
14 {
15 static bool
16 findFirstRoot(std::shared_ptr<ImplicitGeometry> implicitGeomA, const Vec3d& start, const Vec3d& end, Vec3d& root)
17 {
18  const Vec3d displacement = end - start;
19 
20  Vec3d currPos = start;
21  Vec3d prevPos = start;
22  double currDist = implicitGeomA->getFunctionValue(start);
23  //double prevDist = currDist;
24 
25  // Root find (could be multiple roots, we want the first, so start march from front)
26  // Gradient could be used for SDFs to converge faster but not for levelsets
27  const double stepRatio = 0.01; // 100/5=20, this will fail if object moves 20xwidth of the object
28  const double length = displacement.norm();
29  const double stepLength = length * stepRatio;
30  const Vec3d dir = displacement * (1.0 / length);
31  for (double x = stepLength; x < length; x += stepLength)
32  {
33  prevPos = currPos;
34  currPos = start + dir * x;
35 
36  //prevDist = currDist;
37  currDist = implicitGeomA->getFunctionValue(currPos);
38 
39  if (currDist <= 0.0)
40  {
41  // Pick midpoint
42  root = (prevPos + currPos) * 0.5;
43  return true;
44  }
45  }
46  return false;
47 }
48 
49 ImplicitGeometryToPointSetCCD::ImplicitGeometryToPointSetCCD()
50 {
51  setRequiredInputType<ImplicitGeometry>(0);
52  setRequiredInputType<PointSet>(1);
53 }
54 
55 void
56 ImplicitGeometryToPointSetCCD::setupFunctions(std::shared_ptr<ImplicitGeometry> implicitGeom, std::shared_ptr<PointSet> pointSet)
57 {
58  m_centralGrad.setFunction(implicitGeom);
59  if (auto sdf = std::dynamic_pointer_cast<SignedDistanceField>(implicitGeom))
60  {
61  m_centralGrad.setDx(sdf->getImage()->getSpacing());
62  }
63 
64  // If the point set does not have displacements (or has them but not the right type), add them
65  m_displacementsPtr = std::dynamic_pointer_cast<VecDataArray<double, 3>>(pointSet->getVertexAttribute("displacements"));
66  if (m_displacementsPtr == nullptr)
67  {
68  m_displacementsPtr = std::make_shared<VecDataArray<double, 3>>(pointSet->getNumVertices());
69  pointSet->setVertexAttribute("displacements", m_displacementsPtr);
70  m_displacementsPtr->fill(Vec3d::Zero());
71  }
72 }
73 
74 void
76  std::shared_ptr<Geometry> geomA,
77  std::shared_ptr<Geometry> geomB,
78  std::vector<CollisionElement>& elementsA,
79  std::vector<CollisionElement>& elementsB)
80 {
81  std::shared_ptr<ImplicitGeometry> implicitGeom = std::dynamic_pointer_cast<ImplicitGeometry>(geomA);
82  std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(geomB);
83  setupFunctions(implicitGeom, pointSet);
84 
85  // We are going to try to catch a contact before updating via marching along
86  // the displacements of every point in the mesh
87 
88  // First we project the mesh to the next timepoint (without collision)
89  const VecDataArray<double, 3>& displacements = *m_displacementsPtr;
90 
91  std::shared_ptr<VecDataArray<double, 3>> vertexData = pointSet->getVertexPositions();
92  const VecDataArray<double, 3>& vertices = *vertexData; // Vertices in tentative state
93 
94  for (int i = 0; i < vertices.size(); i++)
95  {
96  const Vec3d& pt = vertices[i];
97  const Vec3d& displacement = displacements[i];
98  const double limit = displacement.norm() * m_depthRatioLimit;
99  const Vec3d prevPt = pt - displacement;
100 
101  Vec3d prevPos = prevPt;
102  double prevDist = implicitGeom->getFunctionValue(prevPos);
103  bool prevIsInside = std::signbit(prevDist);
104 
105  Vec3d currPos = pt;
106  double currDist = implicitGeom->getFunctionValue(currPos);
107  bool currIsInside = std::signbit(currDist);
108 
109  // If both inside
110  if (prevIsInside && currIsInside)
111  {
112  if (m_prevOuterElementCounter[i] > 0)
113  {
114  // Static or persistant
115  m_prevOuterElementCounter[i]++;
116 
117  const Vec3d start = m_prevOuterElement[i]; // The last outside point in its movement history
118  const Vec3d end = pt;
119  Vec3d contactPt = Vec3d::Zero();
120  if (findFirstRoot(implicitGeom, start, end, contactPt))
121  {
122  const Vec3d n = -m_centralGrad(contactPt).normalized();
123  const double depth = std::max(0.0, (contactPt - end).dot(n));
124 
125  if (depth <= limit)
126  {
127  PointDirectionElement elemA;
128  elemA.dir = n;
129  elemA.pt = pt;
130  elemA.penetrationDepth = depth;
131 
133  elemB.dir = -n;
134  elemB.ptIndex = i;
135  elemB.penetrationDepth = depth;
136 
137  elementsA.push_back(elemA);
138  elementsB.push_back(elemB);
139  }
140  }
141  }
142  }
143  // If it just entered
144  else if (!prevIsInside && currIsInside)
145  {
146  const Vec3d start = prevPt;
147  const Vec3d end = pt;
148  Vec3d contactPt = Vec3d::Zero();
149  if (findFirstRoot(implicitGeom, start, end, contactPt))
150  {
151  const Vec3d n = -m_centralGrad(contactPt).normalized();
152  const double depth = std::max(0.0, (contactPt - end).dot(n));
153 
154  if (depth <= limit)
155  {
156  PointDirectionElement elemA;
157  elemA.dir = n;
158  elemA.pt = pt;
159  elemA.penetrationDepth = depth;
160 
162  elemB.dir = -n;
163  elemB.ptIndex = i;
164  elemB.penetrationDepth = depth;
165 
166  elementsA.push_back(elemA);
167  elementsB.push_back(elemB);
168  }
169  m_prevOuterElementCounter[i] = 1;
170  // Store the previous exterior point
171  m_prevOuterElement[i] = start;
172  }
173  else
174  {
175  m_prevOuterElementCounter[i] = 0;
176  }
177  }
178  else
179  {
180  m_prevOuterElementCounter[i] = 0;
181  }
182  }
183 }
184 
185 void
187  std::shared_ptr<Geometry> geomA,
188  std::shared_ptr<Geometry> geomB,
189  std::vector<CollisionElement>& elementsA)
190 {
191  // Note: Duplicate of AB, but does not add one side to avoid inner loop branching
192  std::shared_ptr<ImplicitGeometry> implicitGeom = std::dynamic_pointer_cast<ImplicitGeometry>(geomA);
193  std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(geomB);
194  setupFunctions(implicitGeom, pointSet);
195 
196  const VecDataArray<double, 3>& displacements = *m_displacementsPtr;
197 
198  std::shared_ptr<VecDataArray<double, 3>> vertexData = pointSet->getVertexPositions();
199  const VecDataArray<double, 3>& vertices = *vertexData; // Vertices in tentative state
200 
201  for (int i = 0; i < vertices.size(); i++)
202  {
203  const Vec3d& pt = vertices[i];
204  const Vec3d& displacement = displacements[i];
205  const double limit = displacement.norm() * m_depthRatioLimit;
206  const Vec3d prevPt = pt - displacement;
207 
208  Vec3d prevPos = prevPt;
209  double prevDist = implicitGeom->getFunctionValue(prevPos);
210  bool prevIsInside = std::signbit(prevDist);
211 
212  Vec3d currPos = pt;
213  double currDist = implicitGeom->getFunctionValue(currPos);
214  bool currIsInside = std::signbit(currDist);
215 
216  // If both inside
217  if (prevIsInside && currIsInside)
218  {
219  if (m_prevOuterElementCounter[i] > 0)
220  {
221  // Static or persistant
222  m_prevOuterElementCounter[i]++;
223 
224  const Vec3d start = m_prevOuterElement[i]; // The last outside point in its movement history
225  const Vec3d end = pt;
226  Vec3d contactPt = Vec3d::Zero();
227  if (findFirstRoot(implicitGeom, start, end, contactPt))
228  {
229  const Vec3d n = -m_centralGrad(contactPt).normalized();
230  const double depth = std::max(0.0, (contactPt - end).dot(n));
231 
232  if (depth <= limit)
233  {
234  PointDirectionElement elemA;
235  elemA.dir = n;
236  elemA.pt = pt;
237  elemA.penetrationDepth = depth;
238 
239  elementsA.push_back(elemA);
240  }
241  }
242  }
243  }
244  // If it just entered
245  else if (!prevIsInside && currIsInside)
246  {
247  const Vec3d start = prevPt;
248  const Vec3d end = pt;
249  Vec3d contactPt = Vec3d::Zero();
250  if (findFirstRoot(implicitGeom, start, end, contactPt))
251  {
252  const Vec3d n = -m_centralGrad(contactPt).normalized();
253  const double depth = std::max(0.0, (contactPt - end).dot(n));
254 
255  if (depth <= limit)
256  {
257  PointDirectionElement elemA;
258  elemA.dir = n;
259  elemA.pt = pt;
260  elemA.penetrationDepth = depth;
261 
262  elementsA.push_back(elemA);
263  }
264  m_prevOuterElementCounter[i] = 1;
265  // Store the previous exterior point
266  m_prevOuterElement[i] = start;
267  }
268  else
269  {
270  m_prevOuterElementCounter[i] = 0;
271  }
272  }
273  else
274  {
275  m_prevOuterElementCounter[i] = 0;
276  }
277  }
278 }
279 
280 void
282  std::shared_ptr<Geometry> geomA,
283  std::shared_ptr<Geometry> geomB,
284  std::vector<CollisionElement>& elementsB)
285 {
286  // Note: Duplicate of AB, but does not add one side to avoid inner loop branching
287  std::shared_ptr<ImplicitGeometry> implicitGeom = std::dynamic_pointer_cast<ImplicitGeometry>(geomA);
288  std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(geomB);
289  setupFunctions(implicitGeom, pointSet);
290 
291  const VecDataArray<double, 3>& displacements = *m_displacementsPtr;
292 
293  std::shared_ptr<VecDataArray<double, 3>> vertexData = pointSet->getVertexPositions();
294  const VecDataArray<double, 3>& vertices = *vertexData; // Vertices in tentative state
295 
296  for (int i = 0; i < vertices.size(); i++)
297  {
298  const Vec3d& pt = vertices[i];
299  const Vec3d& displacement = displacements[i];
300  const double limit = displacement.norm() * m_depthRatioLimit;
301  const Vec3d prevPt = pt - displacement;
302 
303  Vec3d prevPos = prevPt;
304  double prevDist = implicitGeom->getFunctionValue(prevPos);
305  bool prevIsInside = std::signbit(prevDist);
306 
307  Vec3d currPos = pt;
308  double currDist = implicitGeom->getFunctionValue(currPos);
309  bool currIsInside = std::signbit(currDist);
310 
311  // If both inside
312  if (prevIsInside && currIsInside)
313  {
314  if (m_prevOuterElementCounter[i] > 0)
315  {
316  // Static or persistant
317  m_prevOuterElementCounter[i]++;
318 
319  const Vec3d start = m_prevOuterElement[i]; // The last outside point in its movement history
320  const Vec3d end = pt;
321  Vec3d contactPt = Vec3d::Zero();
322  if (findFirstRoot(implicitGeom, start, end, contactPt))
323  {
324  const Vec3d n = -m_centralGrad(contactPt).normalized();
325  const double depth = std::max(0.0, (contactPt - end).dot(n));
326 
327  if (depth <= limit)
328  {
330  elemB.dir = -n;
331  elemB.ptIndex = i;
332  elemB.penetrationDepth = depth;
333 
334  elementsB.push_back(elemB);
335  }
336  }
337  }
338  }
339  // If it just entered
340  else if (!prevIsInside && currIsInside)
341  {
342  const Vec3d start = prevPt;
343  const Vec3d end = pt;
344  Vec3d contactPt = Vec3d::Zero();
345  if (findFirstRoot(implicitGeom, start, end, contactPt))
346  {
347  const Vec3d n = -m_centralGrad(contactPt).normalized();
348  const double depth = std::max(0.0, (contactPt - end).dot(n));
349 
350  if (depth <= limit)
351  {
353  elemB.dir = -n;
354  elemB.ptIndex = i;
355  elemB.penetrationDepth = depth;
356 
357  elementsB.push_back(elemB);
358  }
359  m_prevOuterElementCounter[i] = 1;
360  // Store the previous exterior point
361  m_prevOuterElement[i] = start;
362  }
363  else
364  {
365  m_prevOuterElementCounter[i] = 0;
366  }
367  }
368  else
369  {
370  m_prevOuterElementCounter[i] = 0;
371  }
372  }
373 }
374 } // namespace imstk
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
void computeCollisionDataA(std::shared_ptr< Geometry > geomA, std::shared_ptr< Geometry > geomB, std::vector< CollisionElement > &elementsA) override
Compute collision data for side A.
Compound Geometry.
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.
Direclty gives a point-direction contact as its collision data, point given by index.
void computeCollisionDataB(std::shared_ptr< Geometry > geomA, std::shared_ptr< Geometry > geomB, std::vector< CollisionElement > &elementsB) override
Compute collision data for side B.
Class that can represent the geometry of multiple implicit geometries as boolean functions One may su...
Direclty gives a point-direction contact as its collision data.