iMSTK
Interactive Medical Simulation Toolkit
imstkPbdObject.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 "imstkCellMesh.h"
8 #include "imstkLogger.h"
9 #include "imstkPbdModel.h"
10 #include "imstkPbdModelConfig.h"
11 #include "imstkPbdObject.h"
12 #include "imstkPointSet.h"
13 
14 namespace imstk
15 {
16 std::shared_ptr<PbdModel>
18 {
19  m_pbdModel = std::dynamic_pointer_cast<PbdModel>(m_dynamicalModel);
20  return m_pbdModel;
21 }
22 
23 void
25 {
26  std::shared_ptr<PbdBody> body = getPbdBody();
27  if (body->bodyType == PbdBody::Type::RIGID)
28  {
29  setRigidBody(*body);
30  }
31  else
32  {
33  if (m_physicsGeometry != nullptr)
34  {
35  auto pointSet = std::dynamic_pointer_cast<PointSet>(m_physicsGeometry);
36  CHECK(pointSet != nullptr) << "PbdObject " << m_name << " only supports PointSet geometries";
37  setDeformBodyFromGeometry(*body, pointSet);
38  }
39  }
40 
41  // Set the geometry to all the functors that need it
42  // Not a good solution
43  auto functors = m_pbdModel->getConfig()->getFunctors();
44  for (auto& functorArray : functors)
45  {
46  for (auto& functor : functorArray.second)
47  {
48  if (auto bodyFunctor = std::dynamic_pointer_cast<PbdBodyConstraintFunctor>(functor))
49  {
50  if (bodyFunctor->m_bodyIndex == body->bodyHandle)
51  {
52  auto pointSet = std::dynamic_pointer_cast<PointSet>(m_physicsGeometry);
53  CHECK(pointSet != nullptr) <<
54  "Tried to generate constraints with functor on PbdObject " << m_name << " but "
55  " object does not have PointSet geometry";
56  bodyFunctor->setGeometry(pointSet);
57  }
58  }
59  }
60  }
61 }
62 
63 bool
65 {
66  m_pbdModel = std::dynamic_pointer_cast<PbdModel>(m_dynamicalModel);
67  if (m_pbdModel == nullptr)
68  {
69  LOG(FATAL) << "PbdObject " << m_name << " was not given a PbdModel. Please PbdObject::setDynamicalModel";
70  return false;
71  }
72 
74 
75  // Set up maps before updating geometry
77 
79 
80  return true;
81 }
82 
83 void
84 PbdObject::setDynamicalModel(std::shared_ptr<AbstractDynamicalModel> dynaModel)
85 {
86  // todo: If already has another model, should remove the corresponding body?
87  m_pbdModel = std::dynamic_pointer_cast<PbdModel>(dynaModel);
88  m_dynamicalModel = dynaModel;
89 
90  // If the model already has a pbd body for this PbdObject remove it from
91  // that prior model
92  if (m_pbdBody != nullptr)
93  {
94  CHECK(m_pbdModel != nullptr) <<
95  "PbdObject has a PbdBody but cannot find associated PbdModel?";
96  m_pbdModel->removeBody(m_pbdBody);
97  }
98  m_pbdBody = m_pbdModel->addBody();
99 }
100 
101 void
103 {
104  CHECK(m_physicsGeometry != nullptr) << "DynamicObject \"" << m_name
105  << "\" expects a physics geometry, none was provided";
106 
108 
109  if (m_pbdBody->bodyType == PbdBody::Type::RIGID)
110  {
111  // Pbd has a special case here for rigid body as it cannot apply the
112  // Apply the transform back to the geometry
113  // If called before body is init'd use initial pose
114  if (m_pbdBody->vertices->size() > 0)
115  {
116  m_physicsGeometry->setTranslation((*m_pbdBody->vertices)[0]);
117  m_physicsGeometry->setRotation((*m_pbdBody->orientations)[0]);
118  }
119  m_physicsGeometry->updatePostTransformData();
120  }
121 }
122 
127 template<typename T>
128 static void
129 setOrAllocate(std::shared_ptr<T>& bodyArrPtr,
130  std::shared_ptr<PointSet> pointSet, const std::string& attributeName,
131  typename T::ValueType initValue)
132 {
133  // If the input mesh has the attribute already use those
134  std::shared_ptr<AbstractDataArray> velocities = pointSet->getVertexAttribute(attributeName);
135  if (velocities != nullptr
136  && velocities->getNumberOfComponents() == T::NumComponents
137  && velocities->getScalarType() == IMSTK_DOUBLE
138  && std::dynamic_pointer_cast<T>(velocities)->size() == pointSet->getNumVertices())
139  {
140  bodyArrPtr = std::dynamic_pointer_cast<T>(velocities);
141  }
142  // If not, put existing (0 initialized velocities) on mesh
143  else
144  {
145  bodyArrPtr = std::make_shared<T>(pointSet->getNumVertices());
146  bodyArrPtr->fill(initValue);
147  pointSet->setVertexAttribute(attributeName, bodyArrPtr);
148  }
149 }
150 
151 void
152 PbdObject::setDeformBodyFromGeometry(PbdBody& body, std::shared_ptr<PointSet> geom)
153 {
154  body.vertices = geom->getVertexPositions();
155  body.prevVertices = std::make_shared<VecDataArray<double, 3>>(*body.vertices);
156 
157  const int numParticles = body.vertices->size();
158 
159  // Initialize Mass+InvMass
160  {
161  // If the input mesh has per vertex masses, use those
162  std::shared_ptr<AbstractDataArray> masses = geom->getVertexAttribute("Mass");
163  if (masses != nullptr && masses->getNumberOfComponents() == 1
164  && masses->getScalarType() == IMSTK_DOUBLE && masses->size() == numParticles)
165  {
166  body.masses = std::dynamic_pointer_cast<DataArray<double>>(masses);
167  body.invMasses->resize(body.masses->size());
168  for (int i = 0; i < body.masses->size(); i++)
169  {
170  (*body.invMasses)[i] = ((*body.masses)[i] == 0.0) ? 0.0 : 1.0 / (*body.masses)[i];
171  }
172  }
173  // If not, initialize as uniform and put on mesh
174  else
175  {
176  // Initialize as uniform
177  body.masses = std::make_shared<DataArray<double>>(numParticles);
178  body.invMasses = std::make_shared<DataArray<double>>(numParticles);
179 
180  body.masses->fill(body.uniformMassValue);
181  body.invMasses->fill((body.uniformMassValue != 0.0) ?
182  1.0 / body.uniformMassValue : 0.0);
183 
184  geom->setVertexAttribute("Mass", body.masses);
185  }
186  geom->setVertexAttribute("InvMass", body.invMasses);
187  }
188 
189  setOrAllocate(body.velocities, geom, "Velocities", Vec3d::Zero());
190 
191  if (body.getOriented())
192  {
193  // Initialize Inertia + Inv tensors
194  {
195  // Initialize as uniform
196  body.inertias = std::make_shared<StdVectorOfMat3d>(numParticles);
197  body.invInertias = std::make_shared<StdVectorOfMat3d>(numParticles);
198 
199  std::fill(body.inertias->begin(), body.inertias->end(), Mat3d::Identity());
200  std::fill(body.invInertias->begin(), body.invInertias->end(), Mat3d::Identity());
201  }
202 
203  // Initialize orientations
204  // Expects Orientation Data to be Quaternions in wxyz order,
205  // initialize with Identity otherwise
206  {
207  auto orientations = std::dynamic_pointer_cast<VecDataArray<double, 4>>(geom->getVertexAttribute("Orientations"));
208  if (orientations != nullptr && orientations->size() == numParticles)
209  {
210  body.orientations = std::make_shared<StdVectorOfQuatd>(numParticles);
211  for (int i = 0; i < orientations->size(); i++)
212  {
213  (*body.orientations)[i] = Quatd((*orientations)[i][0], (*orientations)[i][1],
214  (*orientations)[i][2], (*orientations)[i][3]);
215  }
216  }
217  else
218  {
219  body.orientations = std::make_shared<StdVectorOfQuatd>(numParticles, Quatd::Identity());
220  }
221  }
222  body.prevOrientations = std::make_shared<StdVectorOfQuatd>(*body.orientations);
223 
224  setOrAllocate(body.angularVelocities, geom, "AngularVelocities", Vec3d::Zero());
225  }
226 
227  // Overwrite some masses for specified fixed points
228  body.fixedNodeInvMass = std::unordered_map<int, double>();
229  for (auto i : body.fixedNodeIds)
230  {
231  DataArray<double>& invMasses = *body.invMasses;
232  CHECK(i < numParticles && i >= 0) << "Tried to fix particle " << i
233  << " but there only exist " << numParticles << " particles";
234  body.fixedNodeInvMass[i] = invMasses[i];
235  invMasses[i] = 0.0;
236  }
237 }
238 
242 template<typename ArrPtrType, typename ValueType>
243 static void
244 setOrAllocateRigid(ArrPtrType& array, const ValueType val)
245 {
246  using ArrType = typename ArrPtrType::element_type;
247  // If array already exists, resize to 1, if no existing elements set val
248  if (array != nullptr)
249  {
250  array->resize(1);
251  if (array->size() == 0)
252  {
253  *array = { val };
254  }
255  }
256  // If array doesn't exist create it with value val
257  else
258  {
259  array = std::make_shared<ArrType>(1);
260  *array = { val };
261  }
262 }
263 
264 void
266 {
267  // Basically a PbdBody with a single particle
268  setOrAllocateRigid(body.vertices, Vec3d::Zero());
269  body.prevVertices = std::make_shared<VecDataArray<double, 3>>(*body.vertices);
270 
271  // Initialize Mass+InvMass
272  body.masses = std::make_shared<DataArray<double>>();
273  body.invMasses = std::make_shared<DataArray<double>>();
274  *body.masses = { body.uniformMassValue };
275  *body.invMasses = { (body.uniformMassValue != 0.0) ? 1.0 / body.uniformMassValue : 0.0 };
276 
277  setOrAllocateRigid(body.velocities, Vec3d::Zero());
278  setOrAllocateRigid(body.inertias, Mat3d::Identity());
279  body.invInertias = std::make_shared<StdVectorOfMat3d>();
280  *body.invInertias = { (*body.inertias)[0].inverse() };
281 
282  setOrAllocateRigid(body.orientations, Quatd::Identity());
283  body.prevOrientations = std::make_shared<StdVectorOfQuatd>(*body.orientations);
284  setOrAllocateRigid(body.angularVelocities, Vec3d::Zero());
285 
286  // Overwrite some masses for specified fixed points
287  body.fixedNodeInvMass = std::unordered_map<int, double>();
288 }
289 
290 void
292 {
293  // Note: The PBD Object and constraints must be initialized before calling this function
294  this->initialize();
295 
296  CHECK(m_physicsGeometry != nullptr) << "PbdObject \"" << m_name
297  << "\" requires physics geometry to compute CellConstraint map";
298 
299  // If the map already exists, clear it and recalculate
300  if (m_pbdBody->cellConstraintMap.empty() == false)
301  {
302  m_pbdBody->cellConstraintMap.clear();
303  LOG(INFO) << "PbdObject \"" << m_name
304  << "\" already has a CellConstraintMap. Cleared and recalculated \n";
305  }
306 
307  // Get body id
308  int bodyId = m_pbdBody->bodyHandle;
309 
310  // Mesh data
311  auto cellMesh = std::dynamic_pointer_cast<AbstractCellMesh>(this->getPhysicsGeometry());
312  auto cellVerts = std::dynamic_pointer_cast<DataArray<int>>(cellMesh->getAbstractCells()); // underlying 1D array
313  const int vertsPerCell = cellMesh->getAbstractCells()->getNumberOfComponents();
314 
315  // Constraint Data for all currently existing constraints
316  std::shared_ptr<PbdConstraintContainer> constraintsPtr = this->getPbdModel()->getConstraints();
317  CHECK(constraintsPtr != nullptr) << "PbdObject \"" << m_name
318  << "\" does not have constraints in computeCellConstraintMap";
319 
320  const std::vector<std::shared_ptr<PbdConstraint>>& constraints = constraintsPtr->getConstraints();
321 
322  //For each cell, find all associated constraints
323  std::vector<int> cellVertIds(vertsPerCell);
324  for (int cellId = 0; cellId < cellMesh->getNumCells(); cellId++)
325  {
326  // Get all the vertex ids for this cell
327  // The whole range gets overwritten for each iteration
328  for (int vertId = 0; vertId < vertsPerCell; vertId++)
329  {
330  cellVertIds[vertId] = (*cellVerts)[cellId * vertsPerCell + vertId];
331  }
332 
333  // Search all constraints for those that involve the same vertices as the cell
334  for (auto& constraint : constraints)
335  {
336  const std::vector<PbdParticleId>& cVertexIds = constraint->getParticles();
337  std::unordered_set<int> constraintVertIds;
338 
339  // Check that constraint involves this body and get associated vertices
340  bool isBody = false;
341  for (int cVertId = 0; cVertId < cVertexIds.size(); cVertId++)
342  {
343  if (cVertexIds[cVertId].first == bodyId)
344  {
345  constraintVertIds.insert(cVertexIds[cVertId].second);
346  isBody = true;
347  }
348  }
349  if (isBody == false)
350  {
351  continue;
352  }
353 
354  // Check if cell vertices exists in the constraint
355  for (int cellVertId = 0; cellVertId < vertsPerCell; cellVertId++)
356  {
357  std::unordered_set<int>::const_iterator indx = constraintVertIds.find(cellVertIds[cellVertId]);
358 
359  if (indx != constraintVertIds.end())
360  {
361  // Make sure constraint has not already been added
362  bool exists = false;
363  for (int j = 0; j < m_pbdBody->cellConstraintMap[cellId].size(); j++)
364  {
365  if (constraint == m_pbdBody->cellConstraintMap[cellId][j])
366  {
367  exists = true;
368  }
369  }
370  if (exists == false)
371  {
372  std::shared_ptr<PbdConstraint> cpy = constraint;
373  m_pbdBody->cellConstraintMap[cellId].push_back(std::move(cpy));
374  }
375  }
376  }
377  }
378  }
379 }
380 } // namespace imstk
Represents a pbd body in the model. This is a data only object. It does no function. PbdBody can be of different types. The types effect what properties it has.
Definition: imstkPbdBody.h:53
This class implements the position based dynamics model. The PbdModel is a constraint based model tha...
Definition: imstkPbdModel.h:41
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
void setDeformBodyFromGeometry(PbdBody &body, std::shared_ptr< PointSet > geom)
Creates a deformable PbdBody from Geometry.
virtual int getNumberOfComponents() const override
Returns the number of components.
std::shared_ptr< PbdModel > getPbdModel()
Compound Geometry.
std::shared_ptr< PbdBody > m_pbdBody
Handle to this object in the model/system.
std::unordered_map< int, double > fixedNodeInvMass
Map for archiving fixed nodes&#39; mass.
Definition: imstkPbdBody.h:217
bool initialize() override
Initialize the Pbd scene object.
Simple dynamic array implementation that also supports event posting and viewing/facade.
void setBodyFromGeometry()
Sets the PbdBody representing this object given its geometry.
virtual void updatePhysicsGeometry()
Update only the physics geometry and apply collision map.
std::vector< int > fixedNodeIds
Nodal/vertex IDs of the nodes that are fixed.
Definition: imstkPbdBody.h:206
bool initialize() override
Initialize the scene object.
std::shared_ptr< AbstractDynamicalModel > m_dynamicalModel
Dynamical model.
std::shared_ptr< PbdModel > m_pbdModel
Pbd mathematical model.
void computeCellConstraintMap()
std::shared_ptr< PbdBody > getPbdBody()
Returns body in the model.
void updateGeometries() final
Update the physics geometry and the apply the maps (if defined)
void setDynamicalModel(std::shared_ptr< AbstractDynamicalModel > dynaModel) override
Sets the model, and creates the body within the model.
std::shared_ptr< Geometry > getPhysicsGeometry() const
Set/Get the geometry used for Physics computations.
void setRigidBody(PbdBody &body)
Creates a rigid PbdBody from values.
void updatePhysicsGeometry() override
Update physics geometry, overrided to set transform should the PbdObject be a rigid body...
double uniformMassValue
Mass properties, not used if per vertex masses are given in geometry attributes.
Definition: imstkPbdBody.h:208
std::string m_name
Not unique name.
Definition: imstkEntity.h:157
bool getOriented() const
The body should have orientations if its DEFORMABLE_ORIENTED or RIGID.
Definition: imstkPbdBody.h:100
std::shared_ptr< Geometry > m_physicsGeometry
Geometry used for Physics.
Provides non templated base for cell based meshes.