iMSTK
Interactive Medical Simulation Toolkit
imstkLevelSetCH.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 "imstkLevelSetCH.h"
8 #include "imstkCollisionData.h"
9 #include "imstkImageData.h"
10 #include "imstkLevelSetDeformableObject.h"
11 #include "imstkLevelSetModel.h"
12 #include "imstkRbdConstraint.h"
13 #include "imstkRigidObject2.h"
14 
15 namespace imstk
16 {
17 LevelSetCH::LevelSetCH()
18 {
19  setKernel(m_kernelSize, m_kernelSigma);
20 }
21 
22 LevelSetCH::~LevelSetCH()
23 {
24  if (m_kernelWeights != nullptr)
25  {
26  delete[] m_kernelWeights;
27  }
28 }
29 
30 void
31 LevelSetCH::setInputLvlSetObj(std::shared_ptr<LevelSetDeformableObject> lvlSetObj)
32 {
33  setInputObjectA(lvlSetObj);
34 }
35 
36 void
37 LevelSetCH::setInputRigidObj(std::shared_ptr<RigidObject2> rbdObj)
38 {
39  setInputObjectB(rbdObj);
40  maskAllPoints();
41 }
42 
43 std::shared_ptr<LevelSetDeformableObject>
44 LevelSetCH::getLvlSetObj()
45 {
46  return std::dynamic_pointer_cast<LevelSetDeformableObject>(getInputObjectA());
47 }
48 
49 std::shared_ptr<RigidObject2>
50 LevelSetCH::getRigidObj()
51 {
52  return std::dynamic_pointer_cast<RigidObject2>(getInputObjectB());
53 }
54 
55 void
56 LevelSetCH::setKernel(const int size, const double sigma)
57 {
58  m_kernelSize = size;
59  m_kernelSigma = sigma;
60  if (size % 2 == 0)
61  {
62  LOG(WARNING) << "LevelSetCH kernel size must be odd, increasing by 1";
63  m_kernelSize++;
64  }
65  if (m_kernelWeights != nullptr)
66  {
67  delete[] m_kernelWeights;
68  }
69  m_kernelWeights = new double[size * size * size];
70 
71  const double invDiv = 1.0 / (2.0 * sigma * sigma);
72  const int halfSize = static_cast<int>(size * 0.5);
73  int i = 0;
74  for (int z = -halfSize; z < halfSize + 1; z++)
75  {
76  for (int y = -halfSize; y < halfSize + 1; y++)
77  {
78  for (int x = -halfSize; x < halfSize + 1; x++)
79  {
80  const double dist = Vec3i(x, y, z).cast<double>().norm();
81  m_kernelWeights[i++] = std::exp(-dist * invDiv);
82  }
83  }
84  }
85 }
86 
87 void
89  const std::vector<CollisionElement>& elementsA,
90  const std::vector<CollisionElement>& elementsB)
91 {
92  std::shared_ptr<LevelSetDeformableObject> lvlSetObj = getLvlSetObj();
93  std::shared_ptr<RigidObject2> rbdObj = getRigidObj();
94 
95  if (lvlSetObj == nullptr || rbdObj == nullptr)
96  {
97  return;
98  }
99 
100  std::shared_ptr<LevelSetModel> lvlSetModel = lvlSetObj->getLevelSetModel();
101  std::shared_ptr<ImageData> grid = std::dynamic_pointer_cast<SignedDistanceField>(lvlSetModel->getModelGeometry())->getImage();
102 
103  if (grid == nullptr)
104  {
105  LOG(FATAL) << "Error: level set model geometry is not ImageData";
106  return;
107  }
108 
109  //const Vec3i& dim = grid->getDimensions();
110  const Vec3d& invSpacing = grid->getInvSpacing();
111  const Vec3d& origin = grid->getOrigin();
112 
113  // LevelSetCH requires both sides
114  if (elementsA.size() != elementsB.size())
115  {
116  return;
117  }
118 
119  if (m_useProportionalForce)
120  {
121  // Apply impulses at points of contacts
122  for (size_t i = 0; i < elementsA.size(); i++)
123  {
124  const CollisionElement& lsmContactElement = elementsA[i];
125  const CollisionElement& rbdContactElement = elementsB[i];
126 
127  if (lsmContactElement.m_type != CollisionElementType::PointDirection
128  || rbdContactElement.m_type != CollisionElementType::PointIndexDirection)
129  {
130  continue;
131  }
132 
133  // If the point is in the mask, let it apply impulses
134  if (m_ptIdMask.count(rbdContactElement.m_element.m_PointIndexDirectionElement.ptIndex) != 0)
135  {
136  const Vec3d& pos = lsmContactElement.m_element.m_PointDirectionElement.pt;
137  const Vec3d& normal = lsmContactElement.m_element.m_PointDirectionElement.dir;
138  const Vec3i coord = (pos - origin).cwiseProduct(invSpacing).cast<int>();
139 
140  // Scale the applied impulse by the normal force
141  const double fN = normal.normalized().dot(rbdObj->getRigidBody()->getForce()) / rbdObj->getRigidBody()->getForce().norm();
142  const double S = std::max(fN, 0.0) * m_velocityScaling;
143 
144  const int halfSize = static_cast<int>(m_kernelSize * 0.5);
145  int j = 0;
146  for (int z = -halfSize; z < halfSize + 1; z++)
147  {
148  for (int y = -halfSize; y < halfSize + 1; y++)
149  {
150  for (int x = -halfSize; x < halfSize + 1; x++)
151  {
152  const Vec3i fCoord = coord + Vec3i(x, y, z);
153  lvlSetModel->addImpulse(fCoord, S * m_kernelWeights[j++]);
154  }
155  }
156  }
157  }
158  }
159  }
160  else
161  {
162  // Apply impulses at points of contacts
163  for (size_t i = 0; i < elementsA.size(); i++)
164  {
165  const CollisionElement& lsmContactElement = elementsA[i];
166  const CollisionElement& rbdContactElement = elementsB[i];
167 
168  if (lsmContactElement.m_type != CollisionElementType::PointDirection
169  || rbdContactElement.m_type != CollisionElementType::PointIndexDirection)
170  {
171  continue;
172  }
173 
174  // If the point is in the mask, let it apply impulses
175  if (m_ptIdMask.count(rbdContactElement.m_element.m_PointIndexDirectionElement.ptIndex) != 0)
176  {
177  const Vec3d& pos = lsmContactElement.m_element.m_PointDirectionElement.pt;
178  //const Vec3d& normal = pdColData[i].dirAtoB;
179  const Vec3i coord = (pos - origin).cwiseProduct(invSpacing).cast<int>();
180  const double S = m_velocityScaling;
181 
182  const int halfSize = static_cast<int>(m_kernelSize * 0.5);
183  int j = 0;
184  for (int z = -halfSize; z < halfSize + 1; z++)
185  {
186  for (int y = -halfSize; y < halfSize + 1; y++)
187  {
188  for (int x = -halfSize; x < halfSize + 1; x++)
189  {
190  const Vec3i fCoord = coord + Vec3i(x, y, z);
191  lvlSetModel->addImpulse(fCoord, S * m_kernelWeights[j++]);
192  }
193  }
194  }
195  }
196  }
197  }
198 }
199 
200 void
202 {
203  std::shared_ptr<PointSet> pointSet = std::dynamic_pointer_cast<PointSet>(getRigidObj()->getCollidingGeometry());
204  for (int i = 0; i < pointSet->getNumVertices(); i++)
205  {
206  m_ptIdMask.insert(i);
207  }
208 }
209 } // namespace imstk
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
void setInputObjectA(std::shared_ptr< CollidingObject > objectA)
Set the input objects.
void maskAllPoints()
Allow all points to effect the levelset.
Compound Geometry.
void handle(const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB) override
Compute forces and velocities based on collision data.
Union of collision elements. We use a union to avoid polymorphism. There may be many elements and acc...
void setKernel(const int size, const double sigma=1.0)
Set/Get the size + sigma of gaussian kernel used to apply impulse in levelset.
std::shared_ptr< CollidingObject > getInputObjectA() const
Get the input objects.
Structured field of signed distances implemented with ImageData The SDF differs in that when you scal...