iMSTK
Interactive Medical Simulation Toolkit
imstkRigidBodyCH.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 "imstkRigidBodyCH.h"
8 #include "imstkCollisionData.h"
9 #include "imstkCollisionUtils.h"
10 #include "imstkLineMesh.h"
11 #include "imstkRbdContactConstraint.h"
12 #include "imstkRbdFrictionConstraint.h"
13 #include "imstkRigidBodyModel2.h"
14 #include "imstkRigidObject2.h"
15 #include "imstkSurfaceMesh.h"
16 #include "imstkVecDataArray.h"
17 
18 namespace imstk
19 {
20 void
21 RigidBodyCH::setInputRigidObjectA(std::shared_ptr<RigidObject2> rbdObjA)
22 {
23  setInputObjectA(rbdObjA);
24 }
25 
26 void
27 RigidBodyCH::setInputRigidObjectB(std::shared_ptr<RigidObject2> rbdObjB)
28 {
29  setInputObjectB(rbdObjB);
30 }
31 
32 void
33 RigidBodyCH::setInputCollidingObjectB(std::shared_ptr<CollidingObject> colObjB)
34 {
35  setInputObjectB(colObjB);
36 }
37 
38 std::shared_ptr<RigidObject2>
39 RigidBodyCH::getRigidObjA()
40 {
41  return std::dynamic_pointer_cast<RigidObject2>(getInputObjectA());
42 }
43 
44 std::shared_ptr<RigidObject2>
45 RigidBodyCH::getRigidObjB()
46 {
47  return std::dynamic_pointer_cast<RigidObject2>(getInputObjectB());
48 }
49 
50 void
52  const std::vector<CollisionElement>& elementsA,
53  const std::vector<CollisionElement>& elementsB)
54 {
55  std::shared_ptr<RigidObject2> rbdObjA = getRigidObjA();
56  std::shared_ptr<RigidObject2> rbdObjB = getRigidObjB();
57  std::shared_ptr<CollidingObject> colObjB = getInputObjectB();
58 
59  // If both objects are rigid objects
60  if (rbdObjA != nullptr && rbdObjB != nullptr)
61  {
62  // If we only have elements of A, process one-sided rigid
63  if (elementsB.size() == 0 && elementsA.size() != 0)
64  {
65  handleRbdStaticOneWay(rbdObjA, colObjB, elementsA, elementsB);
66  }
67  // If we have both elements
68  else
69  {
70  std::shared_ptr<RigidBodyModel2> rbdModelA = rbdObjA->getRigidBodyModel2();
71  std::shared_ptr<RigidBodyModel2> rbdModelB = rbdObjB->getRigidBodyModel2();
72 
73  // If the two bodies exist in separate rbd models, add two one-way constraints
74  // to each system
75  if (rbdModelA != rbdModelB)
76  {
77  if (rbdModelA != nullptr)
78  {
79  handleRbdStaticOneWay(rbdObjA, nullptr, elementsA, elementsB);
80  }
81  if (rbdModelB != nullptr)
82  {
83  handleRbdStaticOneWay(rbdObjB, nullptr, elementsB, elementsA);
84  }
85  }
86  // If in the same model use one two-way constraint
87  else
88  {
89  handleRbdRbdTwoWay(rbdObjA, rbdObjB, elementsA, elementsB);
90  }
91  }
92  }
93  // If objA is rigid and b is colliding
94  else if (rbdObjA != nullptr && colObjB != nullptr)
95  {
96  // Process one sided with both
97  handleRbdStaticOneWay(rbdObjA, colObjB, elementsA, elementsB);
98  }
99 }
100 
101 void
103  std::shared_ptr<RigidObject2> rbdObjA,
104  std::shared_ptr<RigidObject2> rbdObjB,
105  const std::vector<CollisionElement>& elementsA,
106  const std::vector<CollisionElement>& elementsB)
107 {
108  if (elementsA.size() != elementsB.size())
109  {
110  return;
111  }
112 
113  auto geom = std::dynamic_pointer_cast<PointSet>(rbdObjA->getCollidingGeometry());
114 
115  // Generate one two-way constraint
116  std::shared_ptr<RigidBodyModel2> rbdModelAB = rbdObjA->getRigidBodyModel2();
117  for (size_t i = 0; i < elementsA.size(); i++)
118  {
119  const CollisionElement& colElemA = elementsA[i];
120  if (colElemA.m_type == CollisionElementType::PointDirection)
121  {
122  const Vec3d& dir = colElemA.m_element.m_PointDirectionElement.dir;
123  const double depth = colElemA.m_element.m_PointDirectionElement.penetrationDepth;
124  const Vec3d& contactPt = colElemA.m_element.m_PointDirectionElement.pt;
125 
126  addConstraint(rbdObjA, rbdObjB, contactPt, dir, depth);
127  }
128  else if (colElemA.m_type == CollisionElementType::PointIndexDirection)
129  {
130  // Doesn't support mapping yet
131  const Vec3d& dir = colElemA.m_element.m_PointIndexDirectionElement.dir;
132  const double depth = colElemA.m_element.m_PointIndexDirectionElement.penetrationDepth;
133  const Vec3d& contactPt = (*geom->getVertexPositions())[colElemA.m_element.m_PointIndexDirectionElement.ptIndex];
134 
135  addConstraint(rbdObjA, rbdObjB, contactPt, dir, depth);
136  }
137  }
138 }
139 
140 void
142  std::shared_ptr<RigidObject2> rbdObj,
143  std::shared_ptr<CollidingObject> colObj,
144  const std::vector<CollisionElement>& elementsA,
145  const std::vector<CollisionElement>& elementsB)
146 {
147  // First handle one-way point-direction constraints
148  for (size_t i = 0; i < elementsA.size(); i++)
149  {
150  const CollisionElement& colElem = elementsA[i];
151  if (colElem.m_type == CollisionElementType::PointDirection)
152  {
153  const Vec3d& dir = colElem.m_element.m_PointDirectionElement.dir;
154  const double depth = colElem.m_element.m_PointDirectionElement.penetrationDepth;
155  const Vec3d& contactPt = colElem.m_element.m_PointDirectionElement.pt;
156 
157  addConstraint(rbdObj, contactPt, dir, depth);
158  }
159  else if (colElem.m_type == CollisionElementType::PointIndexDirection)
160  {
161  // Doesn't support mapping yet
162  auto geom = std::dynamic_pointer_cast<PointSet>(rbdObj->getCollidingGeometry());
163  const Vec3d& dir = colElem.m_element.m_PointIndexDirectionElement.dir;
164  const double depth = colElem.m_element.m_PointIndexDirectionElement.penetrationDepth;
165  const Vec3d& contactPt = (*geom->getVertexPositions())[colElem.m_element.m_PointIndexDirectionElement.ptIndex];
166 
167  addConstraint(rbdObj, contactPt, dir, depth);
168  }
169  }
170 
171  // So long as both sides were filled we may have two-way
172  if (elementsA.size() != elementsB.size() || colObj == nullptr)
173  {
174  return;
175  }
176 
177  // Two-way is only supported through mesh-mesh
178  auto geomA = std::dynamic_pointer_cast<PointSet>(rbdObj->getCollidingGeometry());
179  auto geomB = std::dynamic_pointer_cast<PointSet>(colObj->getCollidingGeometry());
180  if (geomA == nullptr || geomB == nullptr)
181  {
182  return;
183  }
184 
185  std::shared_ptr<VecDataArray<double, 3>> verticesAPtr = geomA->getVertexPositions();
186  const VecDataArray<double, 3>& verticesA = *verticesAPtr;
187  std::shared_ptr<VecDataArray<double, 3>> verticesBPtr = geomB->getVertexPositions();
188  const VecDataArray<double, 3>& verticesB = *verticesBPtr;
189 
190  // Generate one two-way constraint
191  std::shared_ptr<RigidBodyModel2> rbdModelA = rbdObj->getRigidBodyModel2();
192  for (size_t i = 0; i < elementsA.size(); i++)
193  {
194  const CollisionElement& colElemA = elementsA[i];
195  const CollisionElement& colElemB = elementsB[i];
196 
197  // Only handle mesh-mesh constraints here
198  if (colElemA.m_type != CollisionElementType::CellIndex || colElemB.m_type != CollisionElementType::CellIndex)
199  {
200  continue;
201  }
202 
203  const CellIndexElement& elemA = colElemA.m_element.m_CellIndexElement;
204  const CellIndexElement& elemB = colElemB.m_element.m_CellIndexElement;
205 
206  // Vertex vs Triangle
207  if (elemA.cellType == IMSTK_VERTEX && elemB.cellType == IMSTK_TRIANGLE)
208  {
209  const Vec3d& p = verticesA[elemA.ids[0]];
210 
211  Vec3i tri = Vec3i::Zero();
212  if (elemB.idCount == 1)
213  {
214  tri = (*dynamic_cast<SurfaceMesh*>(geomB.get())->getCells().get())[elemB.ids[0]];
215  }
216  else if (elemB.idCount == 3)
217  {
218  tri = Vec3i(elemB.ids[0], elemB.ids[1], elemB.ids[2]);
219  }
220  const Vec3d& a = verticesB[tri[0]];
221  const Vec3d& b = verticesB[tri[1]];
222  const Vec3d& c = verticesB[tri[2]];
223 
224  // Project the vertex onto the triangle
225  Vec3d n;
226  double depth;
227  {
228  const Vec3d v0 = b - a;
229  const Vec3d v1 = c - a;
230  const Vec3d v2 = p - a;
231  n = v0.cross(v1).normalized();
232  depth = v2.dot(n);
233  }
234  Vec3d contactPt = p + n * depth; // Point n triangle
235 
236  addConstraint(rbdObj, contactPt, n, depth);
237  }
238  // Edge vs Edge
239  else if (elemA.cellType == IMSTK_EDGE && elemB.cellType == IMSTK_EDGE)
240  {
241  Vec2i edgeA = Vec2i::Zero();
242  if (elemA.idCount == 1)
243  {
244  edgeA = (*dynamic_cast<LineMesh*>(geomA.get())->getCells().get())[elemA.ids[0]];
245  }
246  else if (elemA.idCount == 2)
247  {
248  edgeA = Vec2i(elemA.ids[0], elemA.ids[1]);
249  }
250  Vec2i edgeB = Vec2i::Zero();
251  if (elemB.idCount == 1)
252  {
253  edgeB = (*dynamic_cast<LineMesh*>(geomB.get())->getCells().get())[elemB.ids[0]];
254  }
255  else if (elemB.idCount == 2)
256  {
257  edgeB = Vec2i(elemB.ids[0], elemB.ids[1]);
258  }
259 
260  // Measure closest distances
261  Vec3d pA, pB;
262  CollisionUtils::edgeToEdgeClosestPoints(
263  verticesA[edgeA[0]], verticesA[edgeA[1]],
264  verticesB[edgeB[0]], verticesB[edgeB[1]], pA, pB);
265 
266  const Vec3d diff = pB - pA;
267  const double l = diff.norm();
268  if (l > 0.0)
269  {
270  // If A is within/behind edge B, then pB-pA gives direction to move A out of B
271  const Vec3d n = diff / l;
272 
273  // pA is point of contact on objA which is rigid body
274  addConstraint(rbdObj, pA, n, l);
275  }
276  }
277  // Edge vs Vertex
278  else if (elemA.cellType == IMSTK_EDGE && elemB.cellType == IMSTK_VERTEX)
279  {
280  Vec2i edge = Vec2i::Zero();
281  if (elemA.idCount == 1)
282  {
283  edge = (*dynamic_cast<LineMesh*>(geomA.get())->getCells().get())[elemA.ids[0]];
284  }
285  else if (elemA.idCount == 2)
286  {
287  edge = Vec2i(elemA.ids[0], elemA.ids[1]);
288  }
289  const Vec3d& a = verticesA[edge[0]];
290  const Vec3d& b = verticesA[edge[1]];
291 
292  const Vec3d& pt = verticesB[elemB.ids[0]];
293 
294  const Vec3d ab = b - a;
295  const double length = ab.norm();
296  const Vec3d dir1 = ab / length;
297 
298  // Project onto the line
299  const Vec3d diff = pt - a;
300  const double p = dir1.dot(diff);
301 
302  // Remove tangent component to get normal
303  const Vec3d diff1 = diff - p * dir1;
304  const double l = diff1.norm();
305  if (l > 0.0)
306  {
307  const Vec3d n = diff1 / l;
308  const Vec3d contactPt = pt - n * l;
309  addConstraint(rbdObj, contactPt, n, l);
310  }
311  }
312  else if (elemA.cellType == IMSTK_VERTEX && elemB.cellType == IMSTK_EDGE)
313  {
314  const Vec3d& pt = verticesA[elemA.ids[0]];
315 
316  Vec2i edge = Vec2i::Zero();
317  if (elemB.idCount == 1)
318  {
319  edge = (*dynamic_cast<LineMesh*>(geomB.get())->getCells().get())[elemB.ids[0]];
320  }
321  else if (elemB.idCount == 2)
322  {
323  edge = Vec2i(elemB.ids[0], elemB.ids[1]);
324  }
325  const Vec3d& a = verticesB[edge[0]];
326  const Vec3d& b = verticesB[edge[1]];
327 
328  const Vec3d ab = b - a;
329  const double length = ab.norm();
330  const Vec3d dir1 = ab / length;
331 
332  // Project onto the line
333  const Vec3d diff = pt - a;
334  const double p = dir1.dot(diff);
335 
336  // Remove tangent component to get normal
337  const Vec3d diff1 = diff - p * dir1;
338  const double l = diff1.norm();
339  if (l > 0.0)
340  {
341  const Vec3d n = diff1 / l;
342  const Vec3d contactPt = pt + n * l;
343  addConstraint(rbdObj, contactPt, -n, l);
344  }
345  }
346  else if (elemA.cellType == IMSTK_VERTEX && elemB.cellType == IMSTK_VERTEX)
347  {
348  const Vec3d& a = verticesA[elemA.ids[0]]; // Vertex to resolve
349  const Vec3d& b = verticesB[elemB.ids[0]];
350 
351  const Vec3d diff = b - a;
352  const double l = diff.norm();
353  if (l > 0.0)
354  {
355  addConstraint(rbdObj, a, diff / l, l);
356  }
357  }
358  }
359 }
360 
361 void
363  std::shared_ptr<RigidObject2> rbdObj,
364  const Vec3d& contactPt, const Vec3d& contactNormal,
365  const double contactDepth)
366 {
367  auto contactConstraint = std::make_shared<RbdContactConstraint>(
368  rbdObj->getRigidBody(), nullptr,
369  contactNormal.normalized(), contactPt, contactDepth,
370  m_beta,
371  RbdConstraint::Side::A);
372  contactConstraint->compute(rbdObj->getRigidBodyModel2()->getTimeStep());
373  rbdObj->getRigidBodyModel2()->addConstraint(contactConstraint);
374 
375  if (m_useFriction)
376  {
377  std::shared_ptr<RbdFrictionConstraint> frictionConstraint =
378  std::make_shared<RbdFrictionConstraint>(
379  rbdObj->getRigidBody(), nullptr,
380  contactPt, contactNormal.normalized(), contactDepth,
381  m_frictionalCoefficient,
382  RbdConstraint::Side::A);
383  frictionConstraint->compute(rbdObj->getRigidBodyModel2()->getTimeStep());
384  rbdObj->getRigidBodyModel2()->addConstraint(frictionConstraint);
385  }
386 }
387 
388 void
390  std::shared_ptr<RigidObject2> rbdObjA,
391  std::shared_ptr<RigidObject2> rbdObjB,
392  const Vec3d& contactPt, const Vec3d& contactNormal,
393  const double contactDepth)
394 {
395  // Add a two-way constraint to solve both with one constraint
396  if (rbdObjA->getRigidBodyModel2() == rbdObjB->getRigidBodyModel2())
397  {
398  auto contactConstraint = std::make_shared<RbdContactConstraint>(
399  rbdObjA->getRigidBody(), rbdObjB->getRigidBody(),
400  contactNormal.normalized(), contactPt, contactDepth,
401  m_beta);
402  contactConstraint->compute(rbdObjA->getRigidBodyModel2()->getTimeStep());
403  rbdObjA->getRigidBodyModel2()->addConstraint(contactConstraint);
404 
405  if (m_useFriction)
406  {
407  auto frictionConstraint = std::make_shared<RbdFrictionConstraint>(
408  rbdObjA->getRigidBody(), rbdObjB->getRigidBody(),
409  contactPt, contactNormal.normalized(), contactDepth,
410  m_frictionalCoefficient,
411  RbdConstraint::Side::AB);
412  frictionConstraint->compute(rbdObjA->getRigidBodyModel2()->getTimeStep());
413  rbdObjA->getRigidBodyModel2()->addConstraint(frictionConstraint);
414  }
415  }
416  // If both belong to differing systems then use two one-way constraints
417  else
418  {
419  addConstraint(rbdObjA, contactPt, contactNormal, contactDepth);
420  addConstraint(rbdObjB, contactPt, -contactNormal, contactDepth);
421  }
422 }
423 } // 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 setInputCollidingObjectB(std::shared_ptr< CollidingObject > colObjB)
Second input colliding object is optional.
void setInputRigidObjectB(std::shared_ptr< RigidObject2 > rbdObjB)
Second input rigid object is optional.
Compound Geometry.
void handleRbdStaticOneWay(std::shared_ptr< RigidObject2 > rbdObj, std::shared_ptr< CollidingObject > colObj, const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB)
Handle rigid vs static one-way edge-edge, vertex-triangle, etc.
Union of collision elements. We use a union to avoid polymorphism. There may be many elements and acc...
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
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.
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
Represents a cell by a single cell id OR by N vertex ids. Which case can be determined by the idCount...
std::shared_ptr< CollidingObject > getInputObjectA() const
Get the input objects.
Scene objects that are governed by rigid body dynamics under the RigidBodyModel2. ...
virtual void addConstraint(std::shared_ptr< RigidObject2 > rbdObj, const Vec3d &contactPt, const Vec3d &contactNormal, const double contactDepth)
Add constraint for the rigid body given contact.
void handleRbdRbdTwoWay(std::shared_ptr< RigidObject2 > rbdObjA, std::shared_ptr< RigidObject2 > rbdObjB, const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB)
Handle rigid vs rigid two-way edge-edge, vertex-triangle, etc.