iMSTK
Interactive Medical Simulation Toolkit
imstkPbdConnectiveTissueConstraintGenerator.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 "imstkCollisionUtils.h"
8 #include "imstkPbdConnectiveTissueConstraintGenerator.h"
9 #include "imstkLineMesh.h"
10 #include "imstkPbdBaryPointToPointConstraint.h"
11 #include "imstkPbdConstraintFunctor.h"
12 #include "imstkPbdModel.h"
13 #include "imstkPbdModelConfig.h"
14 #include "imstkPbdObject.h"
15 #include "imstkSurfaceMesh.h"
16 #include "imstkTetrahedralMesh.h"
17 #include "imstkTriangleToTetMap.h"
18 
19 #include "imstkProximitySurfaceSelector.h"
20 #include "imstkConnectiveStrandGenerator.h"
21 
22 namespace imstk
23 {
24 void
26 {
27  auto tetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(pbdObj->getPhysicsGeometry());
28  std::shared_ptr<SurfaceMesh> surfMesh = tetMesh->extractSurfaceMesh();
29 
30  // Setup a map to figure out what tet the tri is from for attachment to the tet
31  TriangleToTetMap triToTetMap;
32  triToTetMap.setParentGeometry(tetMesh);
33  triToTetMap.setChildGeometry(surfMesh);
34  triToTetMap.setTolerance(m_tolerance);
35  triToTetMap.compute();
36 
37  auto lineMesh = std::dynamic_pointer_cast<LineMesh>(m_connectiveStrandObj->getPhysicsGeometry());
38  // Find all vertices of the line mesh that are coincident with the surface of mesh A
39  int verticesConnected = 0;
40  for (int vertId = 0; vertId < lineMesh->getNumVertices(); vertId++)
41  {
42  const Vec3d vertexPosition = lineMesh->getVertexPosition(vertId);
43  double minSqrDist = IMSTK_FLOAT_MAX;
44 
45  int nearestTriangleId = -1;
46  for (int triId = 0; triId < surfMesh->getNumCells(); triId++)
47  {
48  const Vec3d& x1 = surfMesh->getVertexPosition(surfMesh->getCells()->at(triId)[0]);
49  const Vec3d& x2 = surfMesh->getVertexPosition(surfMesh->getCells()->at(triId)[1]);
50  const Vec3d& x3 = surfMesh->getVertexPosition(surfMesh->getCells()->at(triId)[2]);
51 
52  int ptOnTriangleCaseType;
53  const Vec3d closestPtOnTri = CollisionUtils::closestPointOnTriangle(vertexPosition,
54  x1, x2, x3, ptOnTriangleCaseType);
55 
56  const double sqrDist = (closestPtOnTri - vertexPosition).squaredNorm();
57  if (sqrDist < minSqrDist)
58  {
59  minSqrDist = sqrDist;
60  nearestTriangleId = triId;
61  }
62  }
63 
64  // If the vertex is not on the surface mesh, ignore it.
65  if (minSqrDist > m_tolerance)
66  {
67  continue;
68  }
69 
70  verticesConnected++;
71 
72  const int tetId = triToTetMap.getParentTetId(nearestTriangleId);
73  const Vec4d weights = tetMesh->computeBarycentricWeights(tetId, vertexPosition);
74  const int objId = pbdObj->getPbdBody()->bodyHandle;
75 
76  // Constraint between point on tet to the vertex
77  auto vertToTetConstraint = std::make_shared<PbdBaryPointToPointConstraint>();
78  std::vector<PbdParticleId> ptsA = {
79  { objId, (*tetMesh->getCells())[tetId][0] },
80  { objId, (*tetMesh->getCells())[tetId][1] },
81  { objId, (*tetMesh->getCells())[tetId][2] },
82  { objId, (*tetMesh->getCells())[tetId][3] } };
83 
84  std::vector<double> weightsA = { weights[0], weights[1], weights[2], weights[3] };
85 
86  // Ligament vertex end on the mesh
87  std::vector<PbdParticleId> ptsB = { { m_connectiveStrandObj->getPbdBody()->bodyHandle, vertId } };
88  std::vector<double> weightsB = { 1.0 };
89  vertToTetConstraint->initConstraint(ptsA, weightsA, ptsB, weightsB, 1.0, 1.0);
90  constraints.addConstraint(vertToTetConstraint);
91  }
92 }
93 
94 void
96  std::shared_ptr<PbdObject> pbdObj,
97  PbdConstraintContainer& constraints)
98 {
99  auto surfMesh = std::dynamic_pointer_cast<SurfaceMesh>(pbdObj->getPhysicsGeometry());
100  auto lineMesh = std::dynamic_pointer_cast<LineMesh>(m_connectiveStrandObj->getPhysicsGeometry());
101 
102  // Find all vertices of the line mesh that are coincident with the surface of mesh A
103  int verticesConnected = 0;
104  for (int vertId = 0; vertId < lineMesh->getNumVertices(); vertId++)
105  {
106  const Vec3d vertexPosition = lineMesh->getVertexPosition(vertId);
107  double minSqrDist = IMSTK_FLOAT_MAX;
108 
109  int nearestTriangleId = -1;
110  for (int triId = 0; triId < surfMesh->getNumCells(); triId++)
111  {
112  const Vec3d& x1 = surfMesh->getVertexPosition(surfMesh->getCells()->at(triId)[0]);
113  const Vec3d& x2 = surfMesh->getVertexPosition(surfMesh->getCells()->at(triId)[1]);
114  const Vec3d& x3 = surfMesh->getVertexPosition(surfMesh->getCells()->at(triId)[2]);
115 
116  int ptOnTriangleCaseType;
117  const Vec3d closestPtOnTri = CollisionUtils::closestPointOnTriangle(vertexPosition,
118  x1, x2, x3, ptOnTriangleCaseType);
119 
120  const double sqrDist = (closestPtOnTri - vertexPosition).squaredNorm();
121  if (sqrDist < minSqrDist)
122  {
123  minSqrDist = sqrDist;
124  nearestTriangleId = triId;
125  }
126  }
127 
128  // If the vertex is not on the surface mesh, ignore it.
129  if (minSqrDist > m_tolerance)
130  {
131  continue;
132  }
133 
134  verticesConnected++;
135 
136  const Vec3d weights = surfMesh->computeBarycentricWeights(nearestTriangleId, vertexPosition);
137  const int objId = pbdObj->getPbdBody()->bodyHandle;
138 
139  // Constraint between point on surface triangle to the vertex
140  auto vertToTriConstraint = std::make_shared<PbdBaryPointToPointConstraint>();
141  std::vector<PbdParticleId> ptsA = {
142  { objId, (*surfMesh->getCells())[nearestTriangleId][0] },
143  { objId, (*surfMesh->getCells())[nearestTriangleId][1] },
144  { objId, (*surfMesh->getCells())[nearestTriangleId][2] } };
145 
146  std::vector<double> weightsA = { weights[0], weights[1], weights[2] };
147 
148  // Ligament vertex end on the gallblader
149  std::vector<PbdParticleId> ptsB = { { m_connectiveStrandObj->getPbdBody()->bodyHandle, vertId } };
150  std::vector<double> weightsB = { 1.0 };
151  vertToTriConstraint->initConstraint(ptsA, weightsA, ptsB, weightsB, 1.0, 1.0);
152  constraints.addConstraint(vertToTriConstraint);
153  }
154 }
155 
156 void
158 {
159  m_connectiveStrandObj->getPbdModel()->getConfig()->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, m_distStiffness,
160  m_connectiveStrandObj->getPbdBody()->bodyHandle);
161  // m_connectiveStrandObj->getPbdModel()->getConfig()->enableBendConstraint(.5, 1, false, m_connectiveStrandObj->getPbdBody()->bodyHandle);
162 }
163 
164 void
166 {
167  auto objAPhysMeshSurf = std::dynamic_pointer_cast<SurfaceMesh>(m_objA->getPhysicsGeometry());
168  if (objAPhysMeshSurf != nullptr)
169  {
170  connectLineToSurfMesh(m_objA, constraints);
171  }
172 
173  auto objAPhysMeshTet = std::dynamic_pointer_cast<TetrahedralMesh>(m_objA->getPhysicsGeometry());
174  if (objAPhysMeshTet != nullptr)
175  {
176  connectLineToTetMesh(m_objA, constraints);
177  }
178 
179  auto objBPhysMeshSurf = std::dynamic_pointer_cast<SurfaceMesh>(m_objB->getPhysicsGeometry());
180  if (objBPhysMeshSurf != nullptr)
181  {
182  connectLineToSurfMesh(m_objB, constraints);
183  }
184 
185  auto objBPhysMeshTet = std::dynamic_pointer_cast<TetrahedralMesh>(m_objB->getPhysicsGeometry());
186  if (objBPhysMeshTet != nullptr)
187  {
188  connectLineToTetMesh(m_objB, constraints);
189  }
190 }
191 
192 std::shared_ptr<PbdObject>
193 addConnectiveTissueConstraints(
194  std::shared_ptr<LineMesh> connectiveLineMesh,
195  std::shared_ptr<PbdObject> objA,
196  std::shared_ptr<PbdObject> objB,
197  std::shared_ptr<PbdModel> model,
198  double mass,
199  double distStiffness)
200 {
201  // Check inputs
202  CHECK(connectiveLineMesh != nullptr) << "NULL line mesh passes to generateConnectiveTissueConstraints";
203  CHECK(objA != nullptr) << "PbdObject objA is NULL in generateConnectiveTissueConstraints";
204  CHECK(objB != nullptr) << "PbdObject objB is NULL in generateConnectiveTissueConstraints";
205 
206  auto connectiveStrands = std::make_shared<PbdObject>("connectiveTissue");
207 
208  // Setup the Object
209  connectiveStrands->setVisualGeometry(connectiveLineMesh);
210  connectiveStrands->setPhysicsGeometry(connectiveLineMesh);
211  connectiveStrands->setCollidingGeometry(connectiveLineMesh);
212  connectiveStrands->setDynamicalModel(model);
213 
214  connectiveStrands->getPbdBody()->uniformMassValue = mass / connectiveLineMesh->getNumVertices();
215 
216  // Setup constraints between the gallblader and ligaments
217  auto attachmentConstraintFunctor = std::make_shared<PbdConnectiveTissueConstraintGenerator>();
218  attachmentConstraintFunctor->setDistStiffness(distStiffness);
219  attachmentConstraintFunctor->setConnectiveStrandObj(connectiveStrands);
220  attachmentConstraintFunctor->generateDistanceConstraints();
221  attachmentConstraintFunctor->setConnectedObjA(objA);
222  attachmentConstraintFunctor->setConnectedObjB(objB);
223  attachmentConstraintFunctor->setBodyIndex(connectiveStrands->getPbdBody()->bodyHandle);
224  model->getConfig()->addPbdConstraintFunctor(attachmentConstraintFunctor);
225 
226  return connectiveStrands;
227 }
228 
229 std::shared_ptr<PbdObject>
230 makeConnectiveTissue(
231  std::shared_ptr<PbdObject> objA,
232  std::shared_ptr<PbdObject> objB,
233  std::shared_ptr<PbdModel> model,
234  double maxDist,
235  double strandsPerFace,
236  int segmentsPerStrand,
237  std::shared_ptr<ProximitySurfaceSelector> proxSelector,
238  double mass,
239  double distStiffness,
240  double allowedAngleDeviation)
241 {
242  proxSelector = std::make_shared<ProximitySurfaceSelector>();
243 
244  // Check inputs
245  auto objASurf = std::dynamic_pointer_cast<SurfaceMesh>(objA->getCollidingGeometry());
246  CHECK(objASurf != nullptr) << "Object A " << objA->getName() << " Did not contain a surface mesh as colliding geometry in generateConnectiveTissue";
247 
248  auto objBSurf = std::dynamic_pointer_cast<SurfaceMesh>(objB->getCollidingGeometry());
249  CHECK(objBSurf != nullptr) << "Object B " << objB->getName() << " Did not contain a surface mesh as colliding geometry in generateConnectiveTissue";
250 
251  CHECK(model != nullptr) << "PbdModel in generateConnectiveTissue is NULL";
252 
253  Vec3d objACenter = std::dynamic_pointer_cast<SurfaceMesh>(objA->getCollidingGeometry())->getCenter();
254  Vec3d objBCenter = std::dynamic_pointer_cast<SurfaceMesh>(objB->getCollidingGeometry())->getCenter();
255 
256  if (fabs(maxDist) < 1.0e-6)
257  {
258  maxDist = (objACenter - objBCenter).norm();
259  }
260 
261  proxSelector->setInputMeshes(
262  std::dynamic_pointer_cast<SurfaceMesh>(objA->getCollidingGeometry()),
263  std::dynamic_pointer_cast<SurfaceMesh>(objB->getCollidingGeometry()));
264 
265  proxSelector->setProximity(maxDist);
266  proxSelector->update();
267 
268  // Create surface connector to generate geometry of connective tissue
269  auto surfConnector = std::make_shared<ConnectiveStrandGenerator>();
270  surfConnector->setInputMeshes(
271  std::dynamic_pointer_cast<SurfaceMesh>(proxSelector->getOutput(0)),
272  std::dynamic_pointer_cast<SurfaceMesh>(proxSelector->getOutput(1)));
273  surfConnector->setSegmentsPerStrand(segmentsPerStrand);
274  surfConnector->setStrandsPerFace(strandsPerFace);
275  surfConnector->setAllowedAngleDeviation(allowedAngleDeviation);
276  surfConnector->update();
277 
278  // Get mesh for connective strands
279  auto connectiveLineMesh = std::dynamic_pointer_cast<LineMesh>(surfConnector->getOutput(0));
280 
281  // Create PBD object of connective strands with associated constraints
282  auto connectiveStrands = addConnectiveTissueConstraints(
283  connectiveLineMesh, objA, objB, model, mass);
284 
285  return connectiveStrands;
286 }
287 
288 std::shared_ptr<imstk::PbdObject>
289 makeConnectiveTissue(std::shared_ptr<PbdObject> objA, std::shared_ptr<PbdObject> objB, std::shared_ptr<PbdModel> model, double maxDist /*= 0.0*/, double strandsPerFace /*= 1*/,
290  int segmentsPerStrand /*= 3*/, double mass /*= 0.005*/, double distStiffness /*= 1.0e10*/, double allowedAngleDeviation /*= PI*/)
291 {
292  return makeConnectiveTissue(objA, objB, model, maxDist, strandsPerFace, segmentsPerStrand, nullptr, mass, distStiffness, allowedAngleDeviation);
293 }
294 } // namespace imstk
std::shared_ptr< PbdObject > m_objA
Organ being connected.
Container for pbd constraints.
void connectLineToTetMesh(std::shared_ptr< PbdObject > pbdObj, PbdConstraintContainer &constraints)
Used to generate connecting constraints when the body being attached uses a tet mesh as the physics m...
void compute() override
Compute the map.
void setParentGeometry(std::shared_ptr< Geometry > parent)
Get/Set parent geometry.
int getParentTetId(const int triId) const
Get the tet id that contains the triangle.
Compound Geometry.
std::shared_ptr< PbdObject > m_objB
Organ being connected.
std::shared_ptr< PbdObject > m_connectiveStrandObj
Connective tissue that is made.
std::shared_ptr< SurfaceMesh > extractSurfaceMesh() override
This method extracts the conforming triangular mesh from the tetrahedral mesh.
double m_distStiffness
Stiffness used for distance constraints.
SurfaceToTetrahedralMap serves as a PointwiseMap but also maps tets to triangle faces.
Represents a set of tetrahedrons & vertices via an array of Vec3d double vertices & Vec4i integer ind...
void setChildGeometry(std::shared_ptr< Geometry > child)
Get/Set child geometry.
const Vec3d & getVertexPosition(const size_t vertNum, DataType type=DataType::PostTransform) const
Returns the position of a vertex given its index.
Base class for all volume mesh types.
Definition: imstkLineMesh.h:18
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
void connectLineToSurfMesh(std::shared_ptr< PbdObject > pbdObj, PbdConstraintContainer &constraints)
Used to generate connecting constraints when the body being attached uses a surface mesh as the physi...
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
double m_tolerance
Tolerance for checking coincidence of surface to line mesh.
virtual void addConstraint(std::shared_ptr< PbdConstraint > constraint)
Adds a constraint to the system, thread safe.
void generateDistanceConstraints()
Creates distance constraints for the connective strands using the default m_distStiffness value...
void setTolerance(const double tolerance)
Set/Get the tolerance. The distance to consider two points equivalent/corresponding.