iMSTK
Interactive Medical Simulation Toolkit
imstkBoneDrillingCH.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 "imstkBoneDrillingCH.h"
8 #include "imstkCollidingObject.h"
9 #include "imstkCollisionData.h"
10 #include "imstkParallelFor.h"
11 #include "imstkRbdConstraint.h"
12 #include "imstkRigidObject2.h"
13 #include "imstkTetrahedralMesh.h"
14 
15 namespace imstk
16 {
17 void
18 BoneDrillingCH::setInputObjectDrill(std::shared_ptr<RigidObject2> drillObject)
19 {
20  setInputObjectB(drillObject);
21 }
22 
23 std::shared_ptr<RigidObject2>
24 BoneDrillingCH::getDrillObj() const
25 {
26  return std::dynamic_pointer_cast<RigidObject2>(getInputObjectB());
27 }
28 
29 void
31  const std::vector<CollisionElement>& elementsA,
32  const std::vector<CollisionElement>& elementsB)
33 {
34  auto boneTetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(getBoneObj()->getCollidingGeometry());
35 
36  // BoneDrillingCH process tetra-pointdirection elements
37  ParallelUtils::parallelFor(elementsA.size(),
38  [&](const size_t idx)
39  {
40  const CollisionElement& elementA = elementsA[idx];
41  const CollisionElement& elementB = elementsB[idx];
42 
43  if ((elementB.m_type != CollisionElementType::PointDirection && elementB.m_type != CollisionElementType::PointIndexDirection)
44  || elementA.m_type != CollisionElementType::CellIndex || elementA.m_element.m_CellIndexElement.cellType != IMSTK_TETRAHEDRON)
45  {
46  return;
47  }
48  // Currently ony supports CDs that report the cell id
49  if (elementA.m_element.m_CellIndexElement.idCount != 1)
50  {
51  return;
52  }
53 
54  const int tetIndex = elementA.m_element.m_CellIndexElement.ids[0];
55  double depth = 0.0;
56  if (elementB.m_type == CollisionElementType::PointDirection)
57  {
58  depth = elementB.m_element.m_PointDirectionElement.penetrationDepth;
59  }
60  else if (elementB.m_type == CollisionElementType::PointIndexDirection)
61  {
62  depth = elementB.m_element.m_PointIndexDirectionElement.penetrationDepth;
63  }
64 
65  if (m_nodeRemovalStatus[tetIndex])
66  {
67  return;
68  }
69 
70  m_nodalDensity[tetIndex] -= 0.001 * (m_angularSpeed / m_BoneHardness) * m_stiffness * depth * 0.001;
71 
72  if (m_nodalDensity[tetIndex] <= 0.)
73  {
75  // lock.lock();
76  // m_erodedNodes.push_back(cd.nodeId);
77  // lock.unlock();
78  m_nodeRemovalStatus[tetIndex] = true;
79 
80  // tag the tetra that will be removed
81  for (auto& tetId : m_nodalCardinalSet[tetIndex])
82  {
83  boneTetMesh->setTetrahedraAsRemoved(static_cast<unsigned int>(tetId));
84  }
85  }
86  });
87 }
88 
89 void
91  const std::vector<CollisionElement>& elementsA,
92  const std::vector<CollisionElement>& elementsB)
93 {
94  std::shared_ptr<CollidingObject> bone = getBoneObj();
95  std::shared_ptr<RigidObject2> drill = getDrillObj();
96 
97  auto boneMesh = std::dynamic_pointer_cast<TetrahedralMesh>(bone->getCollidingGeometry());
98 
99  if (m_nodalDensity.size() != boneMesh->getNumVertices())
100  {
101  // Initialize bone density values
102  m_nodalDensity.reserve(boneMesh->getNumVertices());
103  for (size_t i = 0; i < boneMesh->getNumVertices(); ++i)
104  {
105  m_nodalDensity.push_back(m_initialBoneDensity);
106  }
107 
108  m_nodeRemovalStatus.reserve(boneMesh->getNumVertices());
109  for (size_t i = 0; i < boneMesh->getNumVertices(); ++i)
110  {
111  m_nodeRemovalStatus.push_back(false);
112  }
113 
114  m_nodalCardinalSet.reserve(boneMesh->getNumVertices());
115  for (size_t i = 0; i < boneMesh->getNumVertices(); ++i)
116  {
117  std::vector<size_t> row;
118  m_nodalCardinalSet.push_back(row);
119  }
120 
121  // Pre-compute the nodal cardinality set
122  for (size_t tetId = 0; tetId < boneMesh->getNumCells(); ++tetId)
123  {
124  const Vec4i& indices = (*boneMesh->getCells())[tetId];
125  for (int i = 0; i < 4; i++)
126  {
127  m_nodalCardinalSet[indices[i]].push_back(tetId);
128  }
129  }
130  }
131 
132  // BoneDrillingCH uses both sides of collision data
133  if (elementsA.size() != elementsB.size())
134  {
135  return;
136  }
137 
138  // Check if any collisions
139  const auto devicePosition = drill->getCollidingGeometry()->getTranslation();
140  if (elementsA.empty() && elementsB.empty())
141  {
142  // Set the visual object position same as the colliding object position
143  drill->getVisualGeometry()->setTranslation(devicePosition);
144  return;
145  }
146 
147  // Update visual object position
148 
149  // Aggregate collision data
150  Vec3d t = Vec3d::Zero();
151  double maxDepthSqr = MIN_D;
152  for (size_t i = 0; i < elementsB.size(); i++)
153  {
154  const CollisionElement& elementA = elementsA[i];
155  const CollisionElement& elementB = elementsB[i];
156 
157  if ((elementB.m_type != CollisionElementType::PointDirection && elementB.m_type != CollisionElementType::PointIndexDirection)
158  || elementA.m_type != CollisionElementType::CellIndex || elementA.m_element.m_CellIndexElement.cellType != IMSTK_TETRAHEDRON)
159  {
160  return;
161  }
162  // Currently ony supports CDs that report the cell id
163  if (elementA.m_element.m_CellIndexElement.idCount != 1)
164  {
165  return;
166  }
167 
168  const int tetIndex = elementA.m_element.m_CellIndexElement.ids[0];
169  double depth = 0.0;
170  Vec3d dir = Vec3d::Zero();
171  if (elementB.m_type == CollisionElementType::PointDirection)
172  {
173  depth = elementB.m_element.m_PointDirectionElement.penetrationDepth;
174  dir = elementB.m_element.m_PointDirectionElement.dir;
175  }
176  else if (elementB.m_type == CollisionElementType::PointIndexDirection)
177  {
178  depth = elementB.m_element.m_PointIndexDirectionElement.penetrationDepth;
179  dir = elementB.m_element.m_PointIndexDirectionElement.dir;
180  }
181 
182  if (m_nodeRemovalStatus[tetIndex])
183  {
184  continue;
185  }
186 
187  const double dSqr = depth * depth;
188  if (dSqr > maxDepthSqr)
189  {
190  maxDepthSqr = dSqr;
191  t = dir;
192  }
193  }
194  drill->getVisualGeometry()->setTranslation(devicePosition + t);
195 
196  // Spring force
197  Vec3d force = m_stiffness * (drill->getVisualGeometry()->getTranslation() - devicePosition);
198 
199  // Damping force
200  const double dt = 0.1; // Time step size to calculate the object velocity
201  force += m_initialStep ? Vec3d(0.0, 0.0, 0.0) : m_damping * (devicePosition - m_prevPos) / dt;
202 
203  // Set external force on body
204  (*drill->getRigidBody()->m_force) = force;
205 
206  // Decrease the density at the nodal points and remove if the density goes below 0
207  this->erodeBone(elementsA, elementsB);
208 
209  // Housekeeping
210  m_initialStep = false;
211  m_prevPos = devicePosition;
212 }
213 } // namespace imstk
void erodeBone(const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB)
Decrease the density at the nodal points and remove if the density goes below 0.
Compound Geometry.
virtual void handle(const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB) override
Does the bone drilling.
void setInputObjectDrill(std::shared_ptr< RigidObject2 > drillObject)
Set the input drill.
Union of collision elements. We use a union to avoid polymorphism. There may be many elements and acc...
Represents a set of tetrahedrons & vertices via an array of Vec3d double vertices & Vec4i integer ind...
Scene objects that are governed by rigid body dynamics under the RigidBodyModel2. ...