iMSTK
Interactive Medical Simulation Toolkit
imstkNeedlePbdCH.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 #pragma once
8 
9 #include "imstkNeedlePbdCH.h"
10 #include "imstkCollisionUtils.h"
11 #include "imstkCollisionData.h"
12 #include "imstkLineMesh.h"
13 #include "imstkNeedle.h"
14 #include "imstkPbdBaryPointToPointConstraint.h"
15 #include "imstkPbdModel.h"
16 #include "imstkPbdObject.h"
17 #include "imstkPbdSolver.h"
18 #include "imstkPointwiseMap.h"
19 #include "imstkPuncturable.h"
20 #include "imstkSurfaceMesh.h"
21 #include "imstkTetrahedralMesh.h"
22 
23 #include <cmath>
24 #include "imstkPbdPointPointConstraint.h"
25 #include "imstkPbdDistanceConstraint.h"
26 #include "imstkPbdContactConstraint.h"
27 
28 #include <iostream>
29 
30 // using namespace imstk;
31 namespace imstk
32 {
33 // Initialize interaction data
34 void
35 NeedlePbdCH::init(std::shared_ptr<PbdObject> threadObj)
36 {
37  // Setup pbd tissue object
38  m_pbdTissueObj = std::dynamic_pointer_cast<PbdObject>(getInputObjectA());
39  m_tissueSurfMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_pbdTissueObj->getCollidingGeometry());
40 
41  // Set up needle object
42  m_needleObj = std::dynamic_pointer_cast<PbdObject>(getInputObjectB());
43  m_needleMesh = std::dynamic_pointer_cast<LineMesh>(m_needleObj->getCollidingGeometry());
44 
45  // set up thread mesh
46  m_threadObj = threadObj;
47  m_threadMesh = std::dynamic_pointer_cast<LineMesh>(m_threadObj->getCollidingGeometry());
48 }
49 
50 void
51 NeedlePbdCH::generateNewPunctureData()
52 {
53  // Unpack needle and tissue data
54  auto needle = m_needleObj->getComponent<Needle>();
55  auto puncturable = m_pbdTissueObj->getComponent<Puncturable>();
56 
57  // Get one to one map between the physics mesh and the surface mesh
58  auto one2one = std::dynamic_pointer_cast<PointwiseMap>(m_pbdTissueObj->getPhysicsToCollidingMap());
59  CHECK(one2one != nullptr) << "No one to one map in NeedlePbdCH for tissue to surface";
60 
61  auto physMesh = std::dynamic_pointer_cast<TetrahedralMesh>(m_pbdTissueObj->getPhysicsGeometry());
62 
63  // First, find new penetration points using the tip of the needle (needle mesh is reversed)
64  int tipSegmentId = m_needleMesh->getNumCells() - 1;
65 
66  Vec2i nodeIds = m_needleMesh->getCells()->at(tipSegmentId);
67  const Vec3d tip1 = m_needleMesh->getVertexPositions()->at(nodeIds[0]);
68  const Vec3d tip2 = m_needleMesh->getVertexPositions()->at(nodeIds[1]);
69 
70  // For every triangle, check if segment is in triangle (if so, puncture)
71  for (int triangleId = 0; triangleId < m_tissueSurfMesh->getNumCells(); triangleId++)
72  {
73  const Vec3i& surfTriIds = m_tissueSurfMesh->getCells()->at(triangleId);
74 
75  // Indices of the vertices on the physics mesh (which could be a tet mesh)
76  Vec3i physTriIds;
77  physTriIds[0] = one2one->getParentVertexId(surfTriIds[0]);
78  physTriIds[1] = one2one->getParentVertexId(surfTriIds[1]);
79  physTriIds[2] = one2one->getParentVertexId(surfTriIds[2]);
80 
81  const Vec3d& a = physMesh->getVertexPositions()->at(physTriIds[0]);
82  const Vec3d& b = physMesh->getVertexPositions()->at(physTriIds[1]);
83  const Vec3d& c = physMesh->getVertexPositions()->at(physTriIds[2]);
84 
85  // Barycentric coordinates of intersection point
86  Vec3d uvw = Vec3d::Zero();
87 
88  // If this triangle has not already been punctured
89  const PunctureId punctureId = getPunctureId(needle, puncturable, triangleId);
90  if (needle->getState(punctureId) != Puncture::State::INSERTED)
91  {
92  // Check for intersection
93  if (CollisionUtils::testSegmentTriangle(tip1, tip2, a, b, c, uvw))
94  {
95  needle->setState(punctureId, Puncture::State::INSERTED);
96 
97  // Save the puncture data to the needle
98  Puncture& data = *needle->getPuncture(punctureId);
99  data.userData.id = triangleId;
100  data.userData.ids[0] = physTriIds[0];
101  data.userData.ids[1] = physTriIds[1];
102  data.userData.ids[2] = physTriIds[2];
103  data.userData.weights[0] = uvw[0];
104  data.userData.weights[1] = uvw[1];
105  data.userData.weights[2] = uvw[2];
106 
107  // Create penetration data for constraints
108  PuncturePoint newPuncture;
109 
110  newPuncture.triId = triangleId;
111  newPuncture.triVertIds = physTriIds;
112  newPuncture.baryCoords = uvw;
113  newPuncture.segId = tipSegmentId;
114 
115  pData.needle.push_back(newPuncture);
116 
117  m_needlePunctured = true;
118  LOG(DEBUG) << "Needle punctured triangle: " << triangleId;
119  }
120  }
121  }
122 }
123 
124 void
125 NeedlePbdCH::addPunctureConstraints()
126 {
127  // Unpack needle and tissue data
128  auto needle = m_needleObj->getComponent<Needle>();
129  auto physMesh = std::dynamic_pointer_cast<TetrahedralMesh>(m_pbdTissueObj->getPhysicsGeometry());
130  auto puncturable = m_pbdTissueObj->getComponent<Puncturable>();
131 
132  // Loop over penetration points and find nearest point on the needle
133  // Note: Nearest point will likely be the point between two segments,
134  // its dualy defined, but thats ok
135  for (auto puncture = pData.needle.begin(); puncture != pData.needle.end();)
136  {
137  // Start with large value
138  Vec3d closestPoint = { IMSTK_DOUBLE_MAX, IMSTK_DOUBLE_MAX, IMSTK_DOUBLE_MAX };
139  double closestDist = IMSTK_DOUBLE_MAX;
140 
141  // Variable for storing the segement nearest to the needle
142  int nearestSegmentId = -1;
143 
144  const Vec3d& a = physMesh->getVertexPositions()->at(puncture->triVertIds[0]);
145  const Vec3d& b = physMesh->getVertexPositions()->at(puncture->triVertIds[1]);
146  const Vec3d& c = physMesh->getVertexPositions()->at(puncture->triVertIds[2]);
147 
148  const Vec3d baryPoint = puncture->baryCoords.head<3>();
149  const Vec3d puncturePt = baryPoint[0] * a + baryPoint[1] * b + baryPoint[2] * c;
150 
151  // Only check segments within checkStride of previous segments.
152  // This helps with thread switching directions
153  int checkStride = 1;
154  int previousSegment = puncture->segId;
155 
156  int lowerBound = previousSegment - checkStride;
157  int upperBound = previousSegment + checkStride;
158 
159  int strideStart = (lowerBound > 0) ? lowerBound : 0;
160  int strideEnd = (upperBound < m_needleMesh->getNumCells()) ? upperBound : m_needleMesh->getNumCells() - 1;
161 
162  for (int segmentId = strideStart; segmentId <= strideEnd; segmentId++)
163  {
164  const Vec2i& needleSegNodeIds = m_needleMesh->getCells()->at(segmentId);
165  const Vec3d& x1 = m_needleMesh->getVertexPositions()->at(needleSegNodeIds[0]);
166  const Vec3d& x2 = m_needleMesh->getVertexPositions()->at(needleSegNodeIds[1]);
167 
168  int caseType = -1;
169 
170  // Find the closest point on this segment
171  const Vec3d segClosestPoint = CollisionUtils::closestPointOnSegment(puncturePt, x1, x2, caseType);
172 
173  double newDist = (segClosestPoint - puncturePt).squaredNorm();
174  if (newDist < closestDist)
175  {
176  closestDist = newDist;
177  closestPoint = segClosestPoint;
178  nearestSegmentId = segmentId;
179  }
180  }
181 
182  puncture->segId = nearestSegmentId;
183 
184  // Check and see if the closest point is at the tips of the needle
185  // Note: Needle mesh is backwards
186  Vec3d diffTail = closestPoint - m_needleMesh->getVertexPositions()->at(0);
187  Vec3d diffTip = closestPoint - m_needleMesh->getVertexPositions()->at(m_needleMesh->getNumVertices() - 1);
188 
189  // If the closest point is sufficiently close to the tip or tail then unpuncture can occur
190  const double unpunctureEpsilon = 1e-8;
191  if (diffTail.norm() < unpunctureEpsilon || diffTip.norm() < unpunctureEpsilon)
192  {
193  const PunctureId punctureId = getPunctureId(needle, puncturable, puncture->triId);
194  needle->setState(punctureId, Puncture::State::REMOVED);
195  puncture = pData.needle.erase(puncture);
196  continue;
197  }
198 
199  // If the tissue is on the last segment of the needle, then transition onto the thread
200  if (puncture->segId == 0)
201  {
202  // Switch to thread
203  puncture->segId = 0; // start at first segment on thread
204  pData.thread.push_back(*puncture);
205 
206  const PunctureId punctureId = getPunctureId(needle, puncturable, puncture->triId);
207  needle->setState(punctureId, Puncture::State::REMOVED);
208 
209  LOG(DEBUG) << "Thread punctured triangle: " << puncture->triId;
210  m_threadPunctured = true;
211  puncture = pData.needle.erase(puncture);
212 
213  continue;
214  }
215 
216  // Now that we have the closest point on the needle to this penetration point, we can
217  // generate and solve the constraint
218  const int bodyId = m_pbdTissueObj->getPbdBody()->bodyHandle;
219  const int needleBodyId = m_needleObj->getPbdBody()->bodyHandle;
220  auto pointTriangleConstraint = std::make_shared<SurfaceInsertionConstraint>();
221  pointTriangleConstraint->initConstraint(puncturePt,
222  { needleBodyId, 0 },
223  { bodyId, puncture->triVertIds[0] },
224  { bodyId, puncture->triVertIds[1] },
225  { bodyId, puncture->triVertIds[2] },
226  closestPoint,
227  baryPoint,
228  m_needleToSurfaceStiffness, m_surfaceToNeedleStiffness // stiffness parameters
229  );
230  m_constraints.push_back(pointTriangleConstraint);
231 
232  puncture++;
233  }
234 
235  // Use to keep track of the segments that have puncture points
236  std::vector<std::pair<Vec2i, Vec2d>> punctureSegments;
237 
238  // Loop over thread penetration points and find nearest point on the thread
239  // Note: Nearest point will likely be the point between two segments, its dualy defined
240  // for (auto puncture : m_threadPData)
241  for (auto puncture = pData.thread.begin(); puncture != pData.thread.end();)
242  {
243  const Vec3d& a = physMesh->getVertexPositions()->at(puncture->triVertIds[0]);
244  const Vec3d& b = physMesh->getVertexPositions()->at(puncture->triVertIds[1]);
245  const Vec3d& c = physMesh->getVertexPositions()->at(puncture->triVertIds[2]);
246 
247  const Vec3d& baryPoint = puncture->baryCoords;
248  const Vec3d& puncturePt = baryPoint[0] * a + baryPoint[1] * b + baryPoint[2] * c;
249 
250  // Only check segments within checkStride of previous segments.
251  // This helps with thread switching directions
252  const int checkStride = 1;
253  const int previousSegment = puncture->segId;
254 
255  const int lowerBound = previousSegment - checkStride;
256  const int upperBound = previousSegment + checkStride;
257 
258  const int numSegsNonPenetrating = 4;
259 
260  const int strideStart = (lowerBound > 0) ? lowerBound : 0;
261  const int strideEnd = (upperBound < m_threadMesh->getNumCells() - numSegsNonPenetrating) ? upperBound : m_threadMesh->getNumCells() - numSegsNonPenetrating;
262 
263  int closestSegmentId = -1;
264 
265  Vec3d closestPoint = { IMSTK_DOUBLE_MAX, IMSTK_DOUBLE_MAX, IMSTK_DOUBLE_MAX };
266  double closestDist = IMSTK_DOUBLE_MAX;
267 
268  int caseType = -1;
269 
270  // Note: stopping before last segment for visualization
271  for (int segmentId = strideStart; segmentId <= strideEnd; segmentId++)
272  {
273  const Vec2i& threadSegNodeIds = m_threadMesh->getCells()->at(segmentId);
274  const Vec3d& x1 = m_threadMesh->getVertexPositions()->at(threadSegNodeIds[0]);
275  const Vec3d& x2 = m_threadMesh->getVertexPositions()->at(threadSegNodeIds[1]);
276 
277  const Vec3d segClosestPoint = CollisionUtils::closestPointOnSegment(puncturePt, x1, x2, caseType);
278 
279  double newDist = (segClosestPoint - puncturePt).squaredNorm();
280 
281  if (newDist < closestDist)
282  {
283  closestPoint = segClosestPoint;
284  closestDist = newDist;
285  closestSegmentId = segmentId;
286  }
287  } // end loop over thread segments
288 
289  puncture->segId = closestSegmentId;
290 
291  // NOTE: Commented out to force thread to stay inserted once inserted
292  // If uncommented, the thread would be able to slide through the mesh and unpuncture.
293  // Unpuncture if puncture moves past last segment of the thread
294  //const double unpunctureEpsilon = 1e-8;
295  //int numCells = m_threadMesh->getNumCells();
296  //if (closestSegmentId == numCells-1) {
297 
298  // puncture = pData.thread.erase(puncture);
299  // continue;
300  //}
301 
302  // Set of VM pairs for thread
303  const Vec2i& closestSegment = m_threadMesh->getCells()->at(puncture->segId);
304 
305  if (closestSegment[0] < 0 || closestSegment[0] >= m_threadMesh->getNumCells()
306  || closestSegment[1] < 0 || closestSegment[1] >= m_threadMesh->getNumCells())
307  {
308  LOG(FATAL) << "Invalid thread segment id: " << closestSegment[0] << ", " <<
309  closestSegment[1];
310  }
311 
312  const Vec3d& p = m_threadMesh->getVertexPositions()->at(closestSegment[0]);
313  const Vec3d& q = m_threadMesh->getVertexPositions()->at(closestSegment[1]);
314 
315  // Thread barycentric intersection point
316  const Vec2d segBary = baryCentric(closestPoint, p, q);
317 
318  punctureSegments.push_back(std::make_pair(closestSegment, segBary));
319 
320  puncture++;
321  }
322 
323  // To match the order of the closest segments to the puncture points we
324  // sort as the order of the puncture points is know to be in order of the thread
325  std::sort(punctureSegments.begin(), punctureSegments.end(), [](const auto& seg1, const auto& seg2) {
326  return seg1.first[0] > seg2.first[0] || (seg1.first[0] == seg2.first[0] && seg1.second[1] > seg2.second[1]);
327  });
328 
329  // Generate constraints for each puncture point and the appropriate thread segment
330  for (int i = 0; i < pData.thread.size() && i < punctureSegments.size(); ++i)
331  {
332  const auto& puncture = pData.thread[i];
333  const auto& nearestSegNodeIds = punctureSegments[i].first;
334  const auto& segBary = punctureSegments[i].second;
335 
336  const int tissueBodyId = m_pbdTissueObj->getPbdBody()->bodyHandle;
337  const int threadBodyId = m_threadObj->getPbdBody()->bodyHandle;
338 
339  auto threadTriangleConstraint = std::make_shared<ThreadInsertionConstraint>();
340 
341  threadTriangleConstraint->initConstraint(
342  m_pbdTissueObj->getPbdModel()->getBodies(),
343  { threadBodyId, nearestSegNodeIds[0] },
344  { threadBodyId, nearestSegNodeIds[1] },
345  segBary,
346  { tissueBodyId, puncture.triVertIds[0] },
347  { tissueBodyId, puncture.triVertIds[1] },
348  { tissueBodyId, puncture.triVertIds[2] },
349  puncture.baryCoords,
350  m_threadToSurfaceStiffness, m_surfaceToThreadStiffness); // Tissue is not currently moved
351  m_constraints.push_back(threadTriangleConstraint);
352  }
353 }
354 
355 void
357  const std::vector<CollisionElement>& elementsA,
358  const std::vector<CollisionElement>& elementsB) //override
359 {
360  auto needleObj = std::dynamic_pointer_cast<PbdObject>(getInputObjectB());
361  auto needleMesh = std::dynamic_pointer_cast<LineMesh>(needleObj->getCollidingGeometry());
362 
363  m_constraints.clear();
364 
365  auto threadBodyId = m_threadObj->getPbdBody()->bodyHandle;
366  auto needleBodyId = m_needleObj->getPbdBody()->bodyHandle;
367 
368  // Add constraint to connect the needle to the thread
369  int numTiedSegments = 1;
370  for (int i = 0; i <= numTiedSegments; i++)
371  {
372  auto needleThreadConstraint = std::make_shared<PbdVertexToBodyConstraint>();
373  needleThreadConstraint->initConstraint(
374  m_threadObj->getPbdModel()->getBodies(),
375  { needleBodyId, 0 },
376  needleMesh->getVertexPositions()->at(numTiedSegments - i),
377  { threadBodyId, i },
378  0.0001);
379  m_constraints.push_back(needleThreadConstraint);
380  }
381 
382  // Add constraints for stitched tissue
383  for (size_t i = 0; i < m_stitchConstraints.size(); i++)
384  {
385  m_constraints.push_back(m_stitchConstraints[i]);
386  }
387 
388  // Handle needle collision normally if no insertion
389  m_needlePunctured = didPuncture(elementsA, elementsB) || m_needlePunctured;
390  if (!m_needlePunctured)
391  {
392  PbdCollisionHandling::handle(elementsA, elementsB); // (PBD Object, Needle Object)
393  }
394  if (m_needlePunctured == true || m_threadPunctured == true)
395  {
396  // If a new puncture is found, generate data for constraints
397  if (m_needlePunctured == true)
398  {
399  generateNewPunctureData();
400  }
401 
402  // Handle generating constraints for both needle
403  // and thread with existing puncture points
404  addPunctureConstraints();
405 
406  // Solve stitching constraint
407  //if (m_stitch)
408  //{
409  // for (size_t i = 0; i < m_stitchConstraints.size(); i++)
410  // {
411  // m_constraints.push_back(m_stitchConstraints[i]);
412  // }
413  //}
414 
415  if (pData.needle.size() == 0)
416  {
417  m_needlePunctured = false;
418  }
419 
420  if (pData.thread.size() == 0)
421  {
422  m_threadPunctured = false;
423  }
424  }
425 
426  m_solverConstraints.resize(m_constraints.size());
427  for (int i = 0; i < m_constraints.size(); i++)
428  {
429  m_solverConstraints[i] = m_constraints[i].get();
430  }
431  m_pbdTissueObj->getPbdModel()->getSolver()->addConstraints(&m_solverConstraints);
432 }
433 
434 // Create stitching constraints
435 void
437 {
438  // First, verify that at least 4 points have been penetrated by the thread
439  if (pData.thread.size() < 4)
440  {
441  LOG(INFO) << "Cant stitch less than 4 points punctured by thread, currently only " << pData.thread.size() << " points";
442  return;
443  }
444 
445  // If stitching, move the puncture data from the thread to the stitch, and generate
446  // constraints to solve for the stitch.
447  if (pData.thread.size() >= 4)
448  {
449  LOG(INFO) << "Stitching!";
450 
451  auto physMesh = std::dynamic_pointer_cast<TetrahedralMesh>(m_pbdTissueObj->getPhysicsGeometry());
452  std::shared_ptr<VecDataArray<double, 3>> tissueVerticesPtr = physMesh->getVertexPositions();
453  VecDataArray<double, 3>& tissueVertices = *tissueVerticesPtr;
454 
455  std::vector<PuncturePoint> stitchedPoints;
456  for (size_t pPointId = 0; pPointId < pData.thread.size(); pPointId++)
457  {
458  stitchedPoints.push_back(pData.thread[pPointId]);
459  }
460 
461  pData.stitch.push_back(stitchedPoints);
462  pData.thread.clear();
463 
464  // Calculate the average position of the points punctured by thread
465  Vec3d center = Vec3d::Zero();
466  Vec3d pPoint = Vec3d::Zero();
467  for (size_t pPointId = 0; pPointId < stitchedPoints.size(); pPointId++)
468  {
469  pPoint += tissueVertices[stitchedPoints[pPointId].triVertIds[0]] * stitchedPoints[pPointId].baryCoords[0]
470  + tissueVertices[stitchedPoints[pPointId].triVertIds[1]] * stitchedPoints[pPointId].baryCoords[1]
471  + tissueVertices[stitchedPoints[pPointId].triVertIds[2]] * stitchedPoints[pPointId].baryCoords[2];
472  }
473 
474  center = pPoint / stitchedPoints.size();
475  m_stitchPoints.push_back(center);
476 
477  m_stitch = true;
478 
479  // Create constraints to pull the puncture points to the center location
480  auto points = pData.stitch.back();
481  for (int pPointId = 0; pPointId < points.size(); pPointId++)
482  {
483  // Now create values for the central point
484  const PbdParticleId& stitchCenterPt = m_pbdTissueObj->getPbdModel()->addVirtualParticle(center, 0.0, Vec3d::Zero(), true);
485 
486  const int bodyId = m_pbdTissueObj->getPbdBody()->bodyHandle;
487  const PbdParticleId p0 = { bodyId, points[pPointId].triVertIds[0] };
488  const PbdParticleId p1 = { bodyId, points[pPointId].triVertIds[1] };
489  const PbdParticleId p2 = { bodyId, points[pPointId].triVertIds[2] };
490 
491  auto constraint = std::make_shared<PbdBaryPointToPointConstraint>();
492  constraint->initConstraint(
493  { p0, p1, p2 },
494  { points[pPointId].baryCoords[0],
495  points[pPointId].baryCoords[1],
496  points[pPointId].baryCoords[2] },
497  { stitchCenterPt }, { 1.0 },
498  0.01, 0.0);
499 
500  // Add to list of constraints to be solved together in the handler
501  m_stitchConstraints.push_back(constraint);
502  }
503  }
504 }
505 
506 bool
507 NeedlePbdCH::didPuncture(const std::vector<CollisionElement>& elementsA, const std::vector<CollisionElement>& elementsB)
508 {
509  // Pack all the data needed for a particular side into a struct so we can
510  // swap it with the contact & pass it around
512  CollisionSideData needleData = getDataFromObject(getInputObjectB());
513 
514  // Old code expects the needle to be object A (and therefor elementsA)
515  // and the mesh to be object B (and therefor elementsB)
516  std::shared_ptr<CollidingObject> tissueObj = getInputObjectA();
517  auto puncturable = tissueObj->getComponent<Puncturable>();
518  std::shared_ptr<CollidingObject> needleObj = getInputObjectB();
519  auto needle = needleObj->getComponent<Needle>();
520 
521  CHECK(tissueObj != nullptr) << "NeedlePbdCH: tissueObj is null";
522  CHECK(needleObj != nullptr) << "NeedlePbdCH: needleObj is null";
523  CHECK(puncturable != nullptr) << "NeedlePbdCH: puncturable is null";
524  CHECK(needle != nullptr) << "NeedlePbdCH: needle is null";
525 
526  CHECK(elementsA.size() == elementsB.size()) << "Number of elements in A and B must be the same";
527  if (elementsA.empty())
528  {
529  return false;
530  }
531 
532  // Check for ordering expecting triangle indices in either A or B
533  ;
534 
535  auto* needleElements = &elementsB;
536  auto* tissueElements = &elementsA;
537 
538  for (int i = 0; i < needleElements->size(); ++i)
539  {
540  const auto& needleElement = needleElements->at(i);
541  const auto& tissueElement = tissueElements->at(i);
542 
543  CHECK(tissueElement.m_type == CollisionElementType::CellIndex)
544  << "Suturing only works with CDs that report CellIndex contact";
545  CHECK(tissueElement.m_element.m_CellIndexElement.parentId != -1)
546  << "Suturing only works with CDs that report parent ids";
547  // if (deformableElement.m_type != CollisionElementType::CellIndex || deformableElement.m_element.m_CellIndexElement.parentId == -1) continue;
548 
549  // Get info about what point on the needle is in contact, used to only allow tip to create puncture
550  int contactPtId = -1;
551  if (needleElement.m_type == CollisionElementType::CellIndex)
552  {
553  contactPtId = needleElement.m_element.m_CellIndexElement.ids[0];
554  }
555 
556  const PunctureId punctureId = getPunctureId(needle, puncturable, tissueElement.m_element.m_CellIndexElement.parentId);
557 
558  // If removed and we are here, we must now be touching
559  if (needle->getState(punctureId) == Puncture::State::REMOVED) // Removed
560  {
561  needle->setState(punctureId, Puncture::State::TOUCHING);
562  puncturable->setPuncture(punctureId, needle->getPuncture(punctureId));
563  }
564 
565  auto needleMesh = std::dynamic_pointer_cast<LineMesh>(needleObj->getCollidingGeometry());
566  std::shared_ptr<VecDataArray<double, 3>> needleVerticesPtr = needleMesh->getVertexPositions();
567  VecDataArray<double, 3>& needleVertices = *needleVerticesPtr;
568  // Save the direction of the tip of the needle. NOTE: Needle indices are backwards
569  int endIndex = needleVertices.size() - 1;
570  const Vec3d needleDirection = (needleVertices[endIndex] - needleVertices[endIndex - 1]).normalized();
571 
572  const Vec3d needleTip = needleVertices[endIndex];
573 
574  // If touching we may test for insertion
575  // Calculate the surface normal using the set of vertices associated with the triangle and normalize
576  // use dot product to project onto the needle stabbing direction, if close to 1 assume its inserted
577  // Possibly add contact time or pseudo force calculation to know if penetration occurs
578 
579  // Note: assumes closed mesh
580 
581  // Assuming triangle has points a,b,c
582  if (tissueElement.m_element.m_CellIndexElement.cellType != IMSTK_TRIANGLE)
583  {
584  continue;
585  }
586  std::array<PbdParticleId, 3> ptsB = PbdCollisionHandling::getTriangle(tissueElement, tissueData);
587  const PbdState& bodies = m_pbdTissueObj->getPbdModel()->getBodies();
588  const Vec3d ab = bodies.getPosition(ptsB[1]) - bodies.getPosition(ptsB[0]);
589  const Vec3d ac = bodies.getPosition(ptsB[2]) - bodies.getPosition(ptsB[0]);
590 
591  // Calculate surface normal
592  const Vec3d surfNormal = ac.cross(ab).normalized();
593 
594  // Get vector pointing in direction of needle
595  // Use absolute value to ignore direction issues
596  const double dotProduct = std::fabs(needleDirection.dot(surfNormal));
597 
598  if (contactPtId == endIndex)
599  {
600  if (needle->getState(punctureId) == Puncture::State::TOUCHING) // Touching
601  {
602  // If the needle is close to perpendicular to the face if may insert
603  // Note: This is a short term solution
604  if (dotProduct > m_threshold)
605  {
606  // LOG(INFO) << "Needle inserted";
607  return true;
608  }
609  }
610  }
611  }
612  return false;
613 }
614 } // namespace imstk
void handle(const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB) override
Handles puncture constraints for both the needle and the thread.
CollisionSideData getDataFromObject(std::shared_ptr< CollidingObject > obj)
Creates a CollisionSideData struct from the provided object, this gives all the info needed to respon...
std::pair< int, int > PbdParticleId
Index pair that refers to a particle in a PbdState. Index 0 is the body id, Index 1 is the particle i...
PunctureId getPunctureId(std::shared_ptr< Needle > needle, std::shared_ptr< Puncturable > puncturable, const int supportId)
Get puncture id between needle and puncturable.
Place this on an object to make it puncturable by a needle. This allows puncturables to know they&#39;ve ...
Compound Geometry.
void handle(const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB) override
Add collision constraints based off contact data.
void push_back(const ValueType &val)
Append the data array to hold the new value, resizes if neccesary.
void stitch()
Create stitching constraints on button press for four or more puncture points.
Represents a set of tetrahedrons & vertices via an array of Vec3d double vertices & Vec4i integer ind...
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
Base for all needles in imstk it supports global puncture state, per object puncture state...
Definition: imstkNeedle.h:20
Base class for all volume mesh types.
Definition: imstkLineMesh.h:18
void init(std::shared_ptr< PbdObject > threadObj)
Initialize interaction data.
Base class for scene objects that move and/or deform under position based dynamics formulation...
The puncture itself is composed of a state and extra non-essential user data.
Definition: imstkPuncture.h:27
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
std::shared_ptr< CollidingObject > getInputObjectA() const
Get the input objects.
PointwiseMap can compute & apply a mapping between parent and child PointSet geometries.
Provides interface for accessing particles from a 2d array of PbdBody,Particles.
Definition: imstkPbdBody.h:229
std::tuple< int, int, int > PunctureId
Punctures are identified via three ints. The needle id, the puncturable id, and a local id that allow...
Definition: imstkPuncture.h:21