iMSTK
Interactive Medical Simulation Toolkit
NeedleRigidBodyCH.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 "NeedleRigidBodyCH.h"
8 #include "imstkLineMesh.h"
9 #include "imstkNeedle.h"
10 #include "imstkPuncturable.h"
11 #include "imstkRbdContactConstraint.h"
12 #include "imstkRigidBodyModel2.h"
13 #include "imstkRigidObject2.h"
14 #include "imstkVecDataArray.h"
15 #include "RbdLineToPointRotationConstraint.h"
16 #include "RbdLineToPointTranslationConstraint.h"
17 
18 using namespace imstk;
19 
20 void
22  const std::vector<CollisionElement>& elementsA,
23  const std::vector<CollisionElement>& elementsB)
24 {
25  // Do it the normal way
26  RigidBodyCH::handle(elementsA, elementsB);
27 
28  // If no collision, needle must be removed
29  // If using point based collision in an SDF you may want a differing unpuncturing constraint
30  std::shared_ptr<CollidingObject> needleObj = getInputObjectA();
31  auto needle = needleObj->getComponent<Needle>();
32  std::shared_ptr<CollidingObject> tissueObj = getInputObjectB();
33  auto puncturable = tissueObj->getComponent<Puncturable>();
34  const PunctureId punctureId = getPunctureId(needle, puncturable);
35 
36  Puncture::State state = needle->getState(punctureId);
37  if (elementsA.size() == 0)
38  {
39  if (state == Puncture::State::INSERTED)
40  {
41  needle->setState(punctureId, Puncture::State::REMOVED);
42  LOG(INFO) << "Unpuncture!\n";
43  }
44  else if (state == Puncture::State::TOUCHING)
45  {
46  needle->setState(punctureId, Puncture::State::REMOVED);
47  }
48  }
49 }
50 
51 void
53  std::shared_ptr<RigidObject2> rbdObj,
54  const Vec3d& contactPt, const Vec3d& contactNormal,
55  const double contactDepth)
56 {
57  auto needle = rbdObj->getComponent<Needle>();
58  std::shared_ptr<CollidingObject> tissueObj = getInputObjectB();
59  auto puncturable = tissueObj->getComponent<Puncturable>();
60  const PunctureId punctureId = getPunctureId(needle, puncturable);
61 
62  // If the normal force exceeds threshold the needle may insert
63  if (needle->getState(punctureId) == Puncture::State::REMOVED)
64  {
65  needle->setState(punctureId, Puncture::State::TOUCHING);
66  }
67 
68  const Vec3d n = contactNormal.normalized();
69  if (needle->getState(punctureId) == Puncture::State::TOUCHING)
70  {
71  // Get all inwards force
72  const Vec3d needleAxes = needle->getNeedleDirection();
73  const double fN = std::max(needleAxes.dot(rbdObj->getRigidBody()->getForce()), 0.0);
74 
75  if (fN > 50.0)
76  {
77  LOG(INFO) << "Puncture!\n";
78  needle->setState(punctureId, Puncture::State::INSERTED);
79  puncturable->setPuncture(punctureId, needle->getPuncture(punctureId));
80 
81  // Record the axes to constrain too
82  m_initNeedleAxes = needleAxes;
83  m_initNeedleOrientation = Quatd::FromTwoVectors(Vec3d(0.0, -1.0, 0.0), needleAxes);
84  m_initContactPt = contactPt;
85  }
86  }
87 
88  // Only add contact normal constraint if not inserted
89  Puncture::State state = needle->getState(punctureId);
90  if (state == Puncture::State::TOUCHING)
91  {
92  auto contactConstraint = std::make_shared<RbdContactConstraint>(
93  rbdObj->getRigidBody(), nullptr,
94  n, contactPt, contactDepth,
95  m_beta,
96  RbdConstraint::Side::A);
97  contactConstraint->compute(rbdObj->getRigidBodyModel2()->getTimeStep());
98  rbdObj->getRigidBodyModel2()->addConstraint(contactConstraint);
99  }
100  // Lock to the initial axes when the needle is inserted
101  else if (state == Puncture::State::INSERTED)
102  {
103  auto lineMesh = std::dynamic_pointer_cast<LineMesh>(rbdObj->getPhysicsGeometry());
104  Vec3d* p = &(*lineMesh->getVertexPositions())[0];
105  Vec3d* q = &(*lineMesh->getVertexPositions())[1];
106 
107  // This constraint solves for the translation to bring line p,q to m_initContactPt
108  auto translationConstraint = std::make_shared<RbdLineToPointTranslationConstraint>(
109  rbdObj->getRigidBody(), m_initContactPt, p, q, 0.1);
110  translationConstraint->compute(rbdObj->getRigidBodyModel2()->getTimeStep());
111  rbdObj->getRigidBodyModel2()->addConstraint(translationConstraint);
112 
113  // Bit of a cheat, but I parameterize the inertia by depth linearly. I use a large
114  // jump after x=1 to really lock in the orientation after the needle has go so far
115  double x = contactDepth / 0.02;
116  if (x > 1.0)
117  {
118  x = 100.0;
119  }
120  rbdObj->getRigidBody()->m_intertiaTensor = Mat3d::Identity() *
121  (10000.0 + x * 10000.0);
122  rbdObj->getRigidBodyModel2()->updateMass();
123  }
124 }
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
Handle the collision/contact data.
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
virtual void handle(const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB) override
Add rigid body constraints according to contacts.
void addConstraint(std::shared_ptr< RigidObject2 > rbdObj, const Vec3d &contactPt, const Vec3d &contactNormal, const double contactDepth) override
Add constraint for the rigid body given contact.
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