iMSTK
Interactive Medical Simulation Toolkit
imstkPbdConstraintFunctor.h
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 #pragma once
8 
9 #include "imstkLineMesh.h"
10 #include "imstkLogger.h"
11 #include "imstkParallelUtils.h"
12 #include "imstkPbdAreaConstraint.h"
13 #include "imstkPbdBendConstraint.h"
14 #include "imstkPbdConstantDensityConstraint.h"
15 #include "imstkPbdConstraint.h"
16 #include "imstkPbdDihedralConstraint.h"
17 #include "imstkPbdDistanceConstraint.h"
18 #include "imstkPbdFemConstraint.h"
19 #include "imstkPbdFemTetConstraint.h"
20 #include "imstkPbdVolumeConstraint.h"
21 #include "imstkPointSet.h"
22 #include "imstkSurfaceMesh.h"
23 #include "imstkTetrahedralMesh.h"
24 #include "imstkPbdConstraintContainer.h"
25 
26 #include <set>
27 
28 namespace imstk
29 {
39 {
40  public:
41  PbdConstraintFunctor() = default;
42  virtual ~PbdConstraintFunctor() = default;
43 
47  virtual void operator()(PbdConstraintContainer& constraints) = 0;
48 
54  virtual void addConstraints(
55  PbdConstraintContainer& imstkNotUsed(constraints),
56  std::shared_ptr<std::unordered_set<size_t>> imstkNotUsed(vertices)) { }
57 };
58 
65 {
66  public:
67  PbdConstraintFunctorLambda() = default;
68  PbdConstraintFunctorLambda(std::function<void(PbdConstraintContainer&)> f) :
69  m_func(f) { }
70 
71  void operator()(PbdConstraintContainer& constraints) override
72  {
73  m_func(constraints);
74  }
75 
76  std::function<void(PbdConstraintContainer&)> m_func = nullptr;
77 };
78 
85 {
86  public:
87  void setGeometry(std::shared_ptr<PointSet> geom)
88  {
89  m_geom = geom;
90  }
91 
92  void setBodyIndex(const int bodyIndex) { m_bodyIndex = bodyIndex; }
93 
94  int m_bodyIndex = 1;
95  std::shared_ptr<PointSet> m_geom = nullptr;
96 };
97 
105 {
106  public:
107  PbdDistanceConstraintFunctor() = default;
108  ~PbdDistanceConstraintFunctor() override = default;
109 
113  virtual std::shared_ptr<PbdDistanceConstraint> makeDistConstraint(
114  const VecDataArray<double, 3>& vertices,
115  int i1, int i2)
116  {
117  auto constraint = std::make_shared<PbdDistanceConstraint>();
118  constraint->initConstraint(vertices[i1], vertices[i2],
119  { m_bodyIndex, i1 }, { m_bodyIndex, i2 }, m_stiffness);
120  constraint->m_restLength *= m_stretch;
121  return constraint;
122  }
123 
124  void operator()(PbdConstraintContainer& constraints) override
125  {
126  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = m_geom->getVertexPositions();
127  const VecDataArray<double, 3>& vertices = *verticesPtr;
128 
129  auto addDistConstraint = [&](
130  std::vector<std::vector<bool>>& E, int i1, int i2)
131  {
132  // Make sure i1 is always smaller than i2 for duplicate testing
133  if (i1 > i2)
134  {
135  std::swap(i1, i2);
136  }
137  if (E[i1][i2])
138  {
139  E[i1][i2] = 0;
140 
141  auto c = makeDistConstraint(vertices, i1, i2);
142  constraints.addConstraint(c);
143  }
144  };
145 
146  if (auto tetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(m_geom))
147  {
148  std::shared_ptr<VecDataArray<int, 4>> elementsPtr = tetMesh->getCells();
149  const VecDataArray<int, 4>& elements = *elementsPtr;
150  const auto nV = tetMesh->getNumVertices();
151  std::vector<std::vector<bool>> E(nV, std::vector<bool>(nV, 1)); // To test for duplicates
152 
153  for (int k = 0; k < elements.size(); ++k)
154  {
155  auto& tet = elements[k];
156  addDistConstraint(E, tet[0], tet[1]);
157  addDistConstraint(E, tet[0], tet[2]);
158  addDistConstraint(E, tet[0], tet[3]);
159  addDistConstraint(E, tet[1], tet[2]);
160  addDistConstraint(E, tet[1], tet[3]);
161  addDistConstraint(E, tet[2], tet[3]);
162  }
163  }
164  else if (auto triMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_geom))
165  {
166  std::shared_ptr<VecDataArray<int, 3>> elementsPtr = triMesh->getCells();
167  const VecDataArray<int, 3>& elements = *elementsPtr;
168  const auto nV = triMesh->getNumVertices();
169  std::vector<std::vector<bool>> E(nV, std::vector<bool>(nV, 1)); // To test for duplicates
170 
171  for (int k = 0; k < elements.size(); ++k)
172  {
173  auto& tri = elements[k];
174  addDistConstraint(E, tri[0], tri[1]);
175  addDistConstraint(E, tri[0], tri[2]);
176  addDistConstraint(E, tri[1], tri[2]);
177  }
178  }
179  else if (auto lineMesh = std::dynamic_pointer_cast<LineMesh>(m_geom))
180  {
181  std::shared_ptr<VecDataArray<int, 2>> elementsPtr = lineMesh->getCells();
182  const VecDataArray<int, 2>& elements = *elementsPtr;
183  const auto& nV = lineMesh->getNumVertices();
184  std::vector<std::vector<bool>> E(nV, std::vector<bool>(nV, 1)); // To test for duplicates
185 
186  for (int k = 0; k < elements.size(); k++)
187  {
188  auto& seg = elements[k];
189  addDistConstraint(E, seg[0], seg[1]);
190  }
191  }
192  else
193  {
194  LOG(WARNING) << "PbdDistanceConstraint can only be generated with a TetrahedralMesh, SurfaceMesh, or LineMesh";
195  }
196  }
197 
202  std::shared_ptr<std::unordered_set<size_t>> vertices) override
203  {
204  std::shared_ptr<VecDataArray<double, 3>> initVerticesPtr = m_geom->getInitialVertexPositions();
205  const VecDataArray<double, 3>& initVertices = *initVerticesPtr;
206 
207  std::set<std::pair<int, int>> distanceSet;
208  if (auto surfMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_geom))
209  {
210  std::shared_ptr<VecDataArray<int, 3>> elementsPtr = surfMesh->getCells();
211  const VecDataArray<int, 3>& elements = *elementsPtr;
212 
213  // Build vertex to tri map
214  std::vector<std::vector<int>> vertexToCellMap(surfMesh->getNumVertices());
215  for (int i = 0; i < elements.size(); i++)
216  {
217  const Vec3i& cell = elements[i];
218  vertexToCellMap[cell[0]].push_back(i);
219  vertexToCellMap[cell[1]].push_back(i);
220  vertexToCellMap[cell[2]].push_back(i);
221  }
222  for (std::vector<int>& faceIds : vertexToCellMap)
223  {
224  std::sort(faceIds.begin(), faceIds.end());
225  }
226 
227  for (const size_t vertIdx : *vertices)
228  {
229  const int vid = static_cast<int>(vertIdx);
230  for (const int cellId : vertexToCellMap[vertIdx])
231  {
232  const Vec3i& cell = elements[cellId];
233  int i1 = 0;
234  int i2 = 0;
235  for (int i = 0; i < 3; i++)
236  {
237  if (cell[i] == static_cast<int>(vertIdx))
238  {
239  i1 = cell[(i + 1) % 3];
240  i2 = cell[(i + 2) % 3];
241  break;
242  }
243  }
244  auto pair1 = std::make_pair(std::min(vid, i1), std::max(vid, i1));
245  auto pair2 = std::make_pair(std::min(vid, i2), std::max(vid, i2));
246  distanceSet.insert(pair1);
247  distanceSet.insert(pair2);
248  }
249  }
250  }
251  else if (auto lineMesh = std::dynamic_pointer_cast<LineMesh>(m_geom))
252  {
253  std::shared_ptr<VecDataArray<int, 2>> elementsPtr = lineMesh->getCells();
254  const VecDataArray<int, 2>& elements = *elementsPtr;
255 
256  // Build vertex to cell map
257  std::vector<std::vector<int>> vertexToCellMap(lineMesh->getNumVertices());
258  for (int k = 0; k < elements.size(); k++)
259  {
260  const Vec2i& cell = elements[k];
261  vertexToCellMap[cell[0]].push_back(k);
262  vertexToCellMap[cell[1]].push_back(k);
263  }
264  for (std::vector<int>& faceIds : vertexToCellMap)
265  {
266  std::sort(faceIds.begin(), faceIds.end());
267  }
268 
269  for (const size_t vertIdx : *vertices)
270  {
271  for (const int triIdx : vertexToCellMap[vertIdx])
272  {
273  const Vec2i& cell = elements[triIdx];
274 
275  auto pair1 = std::make_pair(std::min(cell[0], cell[1]), std::max(cell[0], cell[1]));
276  distanceSet.insert(pair1);
277  }
278  }
279  }
280  else
281  {
282  LOG(FATAL) << "Add element constraints does not support current mesh type";
283  }
284 
285  constraints.reserve(constraints.getConstraints().size() + distanceSet.size());
286  for (auto& c : distanceSet)
287  {
288  auto constraint = makeDistConstraint(initVertices, c.first, c.second);
289  constraints.addConstraint(constraint);
290  }
291  }
292 
296  void setStiffness(const double stiffness) { m_stiffness = stiffness; }
297  double getStiffness() const { return m_stiffness; }
299 
303  double getStretch() const { return m_stretch; }
304  void setStretch(double val) { m_stretch = val; }
306 
307  protected:
308  double m_stiffness = 0.0;
309  double m_stretch = 1.0;
310 };
311 
318 {
319  public:
320  PbdFemTetConstraintFunctor() = default;
321  ~PbdFemTetConstraintFunctor() override = default;
322 
323  void operator()(PbdConstraintContainer& constraints) override
324  {
325  // Check for correct mesh type
326  CHECK(std::dynamic_pointer_cast<TetrahedralMesh>(m_geom) != nullptr)
327  << "PbdFemTetConstraint can only be generated with a TetrahedralMesh";
328 
329  // Create constraints
330  auto tetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(m_geom);
331  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = m_geom->getVertexPositions();
332  const VecDataArray<double, 3>& vertices = *verticesPtr;
333  std::shared_ptr<VecDataArray<int, 4>> elementsPtr = tetMesh->getCells();
334  const VecDataArray<int, 4>& elements = *elementsPtr;
335 
336  std::shared_ptr<VecDataArray<double, 3>> strainParameters;
337 
338  if (tetMesh->hasCellAttribute(TetrahedralMesh::StrainParameterName))
339  {
340  strainParameters = tetMesh->getStrainParameters();
341  }
342 
343  ParallelUtils::parallelFor(elements.size(),
344  [&](const size_t k)
345  {
346  const Vec4i& tet = elements[k];
347 
348  PbdFemTetConstraint::MaterialType materialType = m_matType;
349  PbdFemConstraintConfig config = *m_femConfig;
350  if (strainParameters && strainParameters->at(k)[0] >= 0)
351  {
352  materialType = static_cast<PbdFemTetConstraint::MaterialType>((int)strainParameters->at(k)[0]);
353  config.setYoungAndPoisson(strainParameters->at(k)[1], strainParameters->at(k)[2]);
354  }
355 
356  auto c = std::make_shared<PbdFemTetConstraint>(materialType);
357 
358  c->initConstraint(
359  vertices[tet[0]], vertices[tet[1]], vertices[tet[2]], vertices[tet[3]],
360  { m_bodyIndex, tet[0] }, { m_bodyIndex, tet[1] },
361  { m_bodyIndex, tet[2] }, { m_bodyIndex, tet[3] },
362  config);
363  constraints.addConstraint(c);
364  }, elements.size() > 100);
365  }
366 
367  void setMaterialType(const PbdFemTetConstraint::MaterialType materialType) { m_matType = materialType; }
368  PbdFemTetConstraint::MaterialType getMaterialType() const { return m_matType; }
369 
370  void setFemConfig(std::shared_ptr<PbdFemConstraintConfig> femConfig) { m_femConfig = femConfig; }
371  std::shared_ptr<PbdFemConstraintConfig> getFemConfig() const { return m_femConfig; }
372 
373  protected:
374  PbdFemTetConstraint::MaterialType m_matType = PbdFemTetConstraint::MaterialType::StVK;
375  std::shared_ptr<PbdFemConstraintConfig> m_femConfig = nullptr;
376 };
377 
384 {
385  public:
386  PbdVolumeConstraintFunctor() = default;
387  ~PbdVolumeConstraintFunctor() override = default;
388 
389  void operator()(PbdConstraintContainer& constraints) override
390  {
391  // Check for correct mesh type
392  CHECK(std::dynamic_pointer_cast<TetrahedralMesh>(m_geom) != nullptr)
393  << "PbdVolumeConstraint can only be generated with a TetrahedralMesh";
394 
395  // Create constraints
396  auto tetMesh = std::dynamic_pointer_cast<TetrahedralMesh>(m_geom);
397  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = m_geom->getVertexPositions();
398  const VecDataArray<double, 3>& vertices = *verticesPtr;
399  std::shared_ptr<VecDataArray<int, 4>> elementsPtr = tetMesh->getCells();
400  const VecDataArray<int, 4>& elements = *elementsPtr;
401 
402  ParallelUtils::parallelFor(elements.size(),
403  [&](const size_t k)
404  {
405  auto& tet = elements[k];
406  auto c = std::make_shared<PbdVolumeConstraint>();
407  c->initConstraint(
408  vertices[tet[0]], vertices[tet[1]], vertices[tet[2]], vertices[tet[3]],
409  { m_bodyIndex, tet[0] }, { m_bodyIndex, tet[1] },
410  { m_bodyIndex, tet[2] }, { m_bodyIndex, tet[3] },
411  m_stiffness);
412  constraints.addConstraint(c);
413  });
414  }
415 
419  void setStiffness(const double stiffness) { m_stiffness = stiffness; }
420  double getStiffness() const { return m_stiffness; }
422 
423  protected:
424  double m_stiffness = 0.0;
425 };
426 
433 {
434  public:
435  PbdAreaConstraintFunctor() = default;
436  ~PbdAreaConstraintFunctor() override = default;
437 
438  void operator()(PbdConstraintContainer& constraints) override
439  {
440  // Check for correct mesh type
441  CHECK(std::dynamic_pointer_cast<SurfaceMesh>(m_geom) != nullptr)
442  << "PbdAreaConstraint can only be generated with a SurfaceMesh";
443 
444  // ok, now create constraints
445  auto triMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_geom);
446  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = m_geom->getVertexPositions();
447  const VecDataArray<double, 3>& vertices = *verticesPtr;
448  std::shared_ptr<VecDataArray<int, 3>> elemenstPtr = triMesh->getCells();
449  const VecDataArray<int, 3>& elements = *elemenstPtr;
450 
451  ParallelUtils::parallelFor(elements.size(),
452  [&](const size_t k)
453  {
454  auto& tri = elements[k];
455  auto c = std::make_shared<PbdAreaConstraint>();
456  c->initConstraint(vertices[tri[0]], vertices[tri[1]], vertices[tri[2]],
457  { m_bodyIndex, tri[0] }, { m_bodyIndex, tri[1] }, { m_bodyIndex, tri[2] },
458  m_stiffness);
459  constraints.addConstraint(c);
460  });
461  }
462 
463  void addConstraints(PbdConstraintContainer& constraints,
464  std::shared_ptr<std::unordered_set<size_t>> vertices) override
465  {
466  // Check for correct mesh type
467  CHECK(std::dynamic_pointer_cast<SurfaceMesh>(m_geom) != nullptr)
468  << "Add element constraints does not support current mesh type.";
469 
470  auto triMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_geom);
471  std::shared_ptr<VecDataArray<double, 3>> initVerticesPtr = m_geom->getInitialVertexPositions();
472  std::shared_ptr<VecDataArray<int, 3>> elementsPtr = triMesh->getCells();
473 
474  const VecDataArray<double, 3>& initVertices = *initVerticesPtr;
475  const VecDataArray<int, 3>& elements = *elementsPtr;
476 
477  // Build vertex to tri map
478  std::vector<std::vector<size_t>> vertexToTriMap(triMesh->getNumVertices());
479  for (int k = 0; k < elements.size(); k++)
480  {
481  const Vec3i& tri = elements[k];
482  vertexToTriMap[tri[0]].push_back(k);
483  vertexToTriMap[tri[1]].push_back(k);
484  vertexToTriMap[tri[2]].push_back(k);
485  }
486  for (std::vector<size_t>& faceIds : vertexToTriMap)
487  {
488  std::sort(faceIds.begin(), faceIds.end());
489  }
490 
491  std::unordered_set<size_t> areaSet;
492  for (const size_t& vertIdx : *vertices)
493  {
494  for (const size_t& triIdx : vertexToTriMap[vertIdx])
495  {
496  areaSet.insert(triIdx);
497  }
498  }
499 
500  auto addAreaConstraint =
501  [&](size_t k, double stiffness)
502  {
503  const Vec3i& tri = elements[k];
504  auto c = std::make_shared<PbdAreaConstraint>();
505  c->initConstraint(initVertices[tri[0]], initVertices[tri[1]], initVertices[tri[2]],
506  { m_bodyIndex, tri[0] }, { m_bodyIndex, tri[1] }, { m_bodyIndex, tri[2] },
507  stiffness);
508  constraints.addConstraint(c);
509  };
510 
511  constraints.reserve(constraints.getConstraints().size() + areaSet.size());
512  for (auto& c : areaSet)
513  {
514  addAreaConstraint(c, m_stiffness);
515  }
516  }
517 
521  void setStiffness(const double stiffness) { m_stiffness = stiffness; }
522  double getStiffness() const { return m_stiffness; }
524 
525  protected:
526  double m_stiffness = 0.0;
527 };
528 
535 {
536  public:
537  PbdBendConstraintFunctor() = default;
538  ~PbdBendConstraintFunctor() override = default;
539 
540  void operator()(PbdConstraintContainer& constraints) override
541  {
542  // Check for correct mesh type
543  CHECK(std::dynamic_pointer_cast<LineMesh>(m_geom) != nullptr)
544  << "PbdBendConstraint can only be generated with a LineMesh";
545 
546  auto lineMesh = std::dynamic_pointer_cast<LineMesh>(m_geom);
547  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = m_geom->getVertexPositions();
548  const VecDataArray<double, 3>& vertices = *verticesPtr;
549  /*std::shared_ptr< VecDataArray<int, 2>> indicesPtr = lineMesh->getCells();
550  const VecDataArray<int, 2>& indices = *indicesPtr*/
551 
552  auto addBendConstraint =
553  [&](const double k, int i1, int i2, int i3)
554  {
555  // i1 should always come first
556  if (i2 < i1)
557  {
558  std::swap(i1, i2);
559  }
560  // i3 should always come last
561  if (i2 > i3)
562  {
563  std::swap(i2, i3);
564  }
565 
566  auto c = std::make_shared<PbdBendConstraint>();
567  if (m_restLengthOverride >= 0.0)
568  {
569  c->initConstraint({ m_bodyIndex, i1 }, { m_bodyIndex, i2 }, { m_bodyIndex, i3 },
570  m_restLengthOverride, k);
571  }
572  else
573  {
574  c->initConstraint(vertices[i1], vertices[i2], vertices[i3],
575  { m_bodyIndex, i1 }, { m_bodyIndex, i2 }, { m_bodyIndex, i3 }, k);
576  }
577  constraints.addConstraint(c);
578  };
579 
580  // Iterate sets of stride # of segments
581  for (int k = 0; k < vertices.size() - m_stride * 2; k++)
582  {
583  addBendConstraint(m_stiffness, k, k + m_stride, k + 2 * m_stride);
584  }
585  }
586 
590  void setStiffness(const double stiffness) { m_stiffness = stiffness; }
591  double getStiffness() const { return m_stiffness; }
593 
598  void setStride(const int stride)
599  {
600  m_stride = stride;
601  CHECK(m_stride >= 1) << "Stride should be at least 1.";
602  }
603 
604  int getStride() const { return m_stride; }
606 
611  double getRestLength() const { return m_restLengthOverride; }
612  void setRestLength(const double restLength) { m_restLengthOverride = restLength; }
614 
615  protected:
616  double m_stiffness = 0.0;
617  int m_stride = 3;
618  double m_restLengthOverride = -1.0;
619 };
620 
628 {
629  public:
630  PbdDihedralConstraintFunctor() = default;
631  ~PbdDihedralConstraintFunctor() override = default;
632 
633  void operator()(PbdConstraintContainer& constraints) override
634  {
635  // Check for correct mesh type
636  CHECK(std::dynamic_pointer_cast<SurfaceMesh>(m_geom) != nullptr)
637  << "PbdDihedralConstraint can only be generated with a SurfaceMesh";
638 
639  // Create constraints
640  auto triMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_geom);
641  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = triMesh->getVertexPositions();
642  const VecDataArray<double, 3>& vertices = *verticesPtr;
643  std::shared_ptr<VecDataArray<int, 3>> elementsPtr = triMesh->getCells();
644  const VecDataArray<int, 3>& elements = *elementsPtr;
645  const int nV = triMesh->getNumVertices();
646  std::vector<std::vector<int>> vertIdsToTriangleIds(nV);
647 
648  for (int k = 0; k < elements.size(); ++k)
649  {
650  const Vec3i& tri = elements[k];
651  vertIdsToTriangleIds[tri[0]].push_back(k);
652  vertIdsToTriangleIds[tri[1]].push_back(k);
653  vertIdsToTriangleIds[tri[2]].push_back(k);
654  }
655 
656  // Used to resolve duplicates
657  std::vector<std::vector<bool>> E(nV, std::vector<bool>(nV, 1));
658 
659  auto addDihedralConstraint =
660  [&](const std::vector<int>& r1, const std::vector<int>& r2,
661  const int k, int i1, int i2)
662  {
663  if (i1 > i2) // Make sure i1 is always smaller than i2
664  {
665  std::swap(i1, i2);
666  }
667  if (E[i1][i2])
668  {
669  E[i1][i2] = 0;
670 
671  // Find the shared edge
672  std::vector<size_t> rs(2);
673  auto it = std::set_intersection(r1.begin(), r1.end(), r2.begin(), r2.end(), rs.begin());
674  rs.resize(static_cast<size_t>(it - rs.begin()));
675  if (rs.size() > 1)
676  {
677  int idx = (rs[0] == k) ? 1 : 0;
678  const Vec3i& tri0 = elements[k];
679  const Vec3i& tri1 = elements[rs[idx]];
680  int idx0 = 0;
681  int idx1 = 0;
682  for (int i = 0; i < 3; i++)
683  {
684  if (tri0[i] != i1 && tri0[i] != i2)
685  {
686  idx0 = tri0[i];
687  }
688  if (tri1[i] != i1 && tri1[i] != i2)
689  {
690  idx1 = tri1[i];
691  }
692  }
693  auto c = std::make_shared<PbdDihedralConstraint>();
694  c->initConstraint(vertices[idx0], vertices[idx1], vertices[i1], vertices[i2],
695  { m_bodyIndex, idx0 }, { m_bodyIndex, idx1 },
696  { m_bodyIndex, i1 }, { m_bodyIndex, i2 }, m_stiffness);
697  constraints.addConstraint(c);
698  }
699  }
700  };
701 
702  for (size_t i = 0; i < vertIdsToTriangleIds.size(); i++)
703  {
704  std::sort(vertIdsToTriangleIds[i].begin(), vertIdsToTriangleIds[i].end());
705  }
706 
707  // For every triangle
708  for (int k = 0; k < elements.size(); ++k)
709  {
710  const Vec3i& tri = elements[k];
711 
712  // Get all the neighbor triangles (to the vertices)
713  std::vector<int>& neighborTriangles0 = vertIdsToTriangleIds[tri[0]];
714  std::vector<int>& neighborTriangles1 = vertIdsToTriangleIds[tri[1]];
715  std::vector<int>& neighborTriangles2 = vertIdsToTriangleIds[tri[2]];
716 
717  // Add constraints between all the triangles
718  addDihedralConstraint(neighborTriangles0, neighborTriangles1, k, tri[0], tri[1]);
719  addDihedralConstraint(neighborTriangles0, neighborTriangles2, k, tri[0], tri[2]);
720  addDihedralConstraint(neighborTriangles1, neighborTriangles2, k, tri[1], tri[2]);
721  }
722  }
723 
724  void addConstraints(PbdConstraintContainer& constraints,
725  std::shared_ptr<std::unordered_set<size_t>> vertices) override
726  {
727  // Check for correct mesh type
728  CHECK(std::dynamic_pointer_cast<SurfaceMesh>(m_geom) != nullptr)
729  << "Add element constraints does not support current mesh type.";
730 
731  auto triMesh = std::dynamic_pointer_cast<SurfaceMesh>(m_geom);
732  std::shared_ptr<VecDataArray<double, 3>> initVerticesPtr = m_geom->getInitialVertexPositions();
733  std::shared_ptr<VecDataArray<int, 3>> elementsPtr = triMesh->getCells();
734 
735  const VecDataArray<double, 3>& initVertices = *initVerticesPtr;
736  const VecDataArray<int, 3>& elements = *elementsPtr;
737 
738  // Build vertex to tri map
739  std::vector<std::vector<size_t>> vertexToTriMap(triMesh->getNumVertices());
740  for (int k = 0; k < elements.size(); k++)
741  {
742  const Vec3i& tri = elements[k];
743  vertexToTriMap[tri[0]].push_back(k);
744  vertexToTriMap[tri[1]].push_back(k);
745  vertexToTriMap[tri[2]].push_back(k);
746  }
747  for (std::vector<size_t>& faceIds : vertexToTriMap)
748  {
749  std::sort(faceIds.begin(), faceIds.end());
750  }
751 
752  std::map<std::pair<size_t, size_t>, std::pair<size_t, size_t>> dihedralSet;
753  for (const size_t& vertIdx : *vertices)
754  {
755  for (const size_t& triIdx : vertexToTriMap[vertIdx])
756  {
757  const Vec3i& tri = elements[triIdx];
758  for (size_t i = 0; i < 3; i++)
759  {
760  size_t j = (i + 1) % 3;
761  size_t i0 = tri[i];
762  size_t i1 = tri[j];
763  if (i0 > i1)
764  {
765  std::swap(i0, i1);
766  }
767  const std::vector<size_t>& r0 = vertexToTriMap[i0];
768  const std::vector<size_t>& r1 = vertexToTriMap[i1];
769  std::vector<size_t> rs(2);
770  auto it = std::set_intersection(r0.begin(), r0.end(), r1.begin(), r1.end(), rs.begin());
771  rs.resize(static_cast<size_t>(it - rs.begin()));
772  if (rs.size() > 1)
773  {
774  dihedralSet[std::make_pair(i0, i1)] = std::make_pair(rs[0], rs[1]);
775  }
776  }
777  }
778  }
779 
780  auto addDihedralConstraint =
781  [&](int t0, int t1, int i1, int i2, double stiffness)
782  {
783  const Vec3i& tri0 = elements[t0];
784  const Vec3i& tri1 = elements[t1];
785  int idx0 = 0;
786  int idx1 = 0;
787  for (int i = 0; i < 3; i++)
788  {
789  if (tri0[i] != i1 && tri0[i] != i2)
790  {
791  idx0 = tri0[i];
792  }
793  if (tri1[i] != i1 && tri1[i] != i2)
794  {
795  idx1 = tri1[i];
796  }
797  }
798  auto c = std::make_shared<PbdDihedralConstraint>();
799  c->initConstraint(initVertices[idx0], initVertices[idx1], initVertices[i1], initVertices[i2],
800  { m_bodyIndex, idx0 }, { m_bodyIndex, idx1 },
801  { m_bodyIndex, i1 }, { m_bodyIndex, i2 },
802  stiffness);
803  constraints.addConstraint(c);
804  };
805 
806  constraints.reserve(constraints.getConstraints().size() + dihedralSet.size());
807  for (auto& c : dihedralSet)
808  {
809  addDihedralConstraint(
810  static_cast<int>(c.second.first), static_cast<int>(c.second.second),
811  static_cast<int>(c.first.first), static_cast<int>(c.first.second),
812  m_stiffness);
813  }
814  }
815 
819  void setStiffness(const double stiffness) { m_stiffness = stiffness; }
820  double getStiffness() const { return m_stiffness; }
822 
823  protected:
824  double m_stiffness = 0.0;
825 };
826 
834 {
835  public:
837  ~PbdConstantDensityConstraintFunctor() override = default;
838 
839  void operator()(PbdConstraintContainer& constraints) override
840  {
841  // Check for correct mesh type
842  CHECK(std::dynamic_pointer_cast<PointSet>(m_geom) != nullptr)
843  << "PbdConstantDensityConstraint can only be generated with a PointSet";
844 
845  auto c = std::make_shared<PbdConstantDensityConstraint>();
846  c->initConstraint(m_geom->getNumVertices(), m_bodyIndex, m_particleRadius, m_restDensity);
847  constraints.addConstraint(c);
848  }
849 
853  void setStiffness(const double stiffness) { m_stiffness = stiffness; }
854  double getStiffness() const { return m_stiffness; }
856 
860  void setParticleRadius(const double particleRadius) { m_particleRadius = particleRadius; }
861  double getParticleRadius() const { return m_particleRadius; }
863 
867  void setRestDensity(const double restDensity) { m_restDensity = restDensity; }
868  double getRestDensity() const { return m_restDensity; }
870 
871  protected:
872  double m_stiffness = 0.0;
873  double m_particleRadius = 0.2;
874  double m_restDensity = 6378.0;
875 };
876 } // namespace imstk
PbdBendConstraintFunctor generates constraints per cell of a LineMesh.
void setRestDensity(const double restDensity)
Get/Set the resting density, default rest density of water for m.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
Container for pbd constraints.
virtual void reserve(const size_t n)
Reserve an amount of constraints in the pool, if you know ahead of time the number of constraints...
virtual std::shared_ptr< PbdDistanceConstraint > makeDistConstraint(const VecDataArray< double, 3 > &vertices, int i1, int i2)
Create the distance constraint.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
void setParticleRadius(const double particleRadius)
Get/Set the stiffness, how hard the constraint is.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
const std::vector< std::shared_ptr< PbdConstraint > > & getConstraints() const
Get the underlying container.
Compound Geometry.
void setStiffness(const double stiffness)
Get/Set the stiffness, how hard the constraint is.
void setStiffness(const double stiffness)
Get/Set the stiffness, how hard the constraint is.
void setStiffness(const double stiffness)
Get/Set the stiffness, how hard the constraint is.
void push_back(const ValueType &val)
Append the data array to hold the new value, resizes if neccesary.
void setStiffness(const double stiffness)
Get/Set the stiffness, how hard the constraint is.
virtual void addConstraints(PbdConstraintContainer &imstkNotUsed(constraints), std::shared_ptr< std::unordered_set< size_t >> imstkNotUsed(vertices))
Appends a set of constraint to the container given a geometry and a set of newly inserted vertices Th...
Represents a set of tetrahedrons & vertices via an array of Vec3d double vertices & Vec4i integer ind...
PbdFemTetConstraintFunctor generates constraints per cell of a TetrahedralMesh.
PbdConstantDensityConstraintFunctor generates a global constant density constraint for an entire Poin...
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
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
PbdConstraintFunctor that can be forwarded out to a function pointer.
void setStiffness(const double stiffness)
Get/Set the stiffness, how hard the constraint is.
void addConstraints(PbdConstraintContainer &constraints, std::shared_ptr< std::unordered_set< size_t >> vertices) override
Called when topology changed.
void setStiffness(const double stiffness)
Get/Set the stiffness, how hard the constraint is.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
PbdAreaConstraintFunctor generates constraints per cell of a SurfaceMesh.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
void operator()(PbdConstraintContainer &constraints) override
Appends a set of constraint to the container given a geometry & body.
PbdDistanceConstraintFunctor generates constraints between the edges of the input TetrahedralMesh...
std::shared_ptr< VecDataArray< double, 3 > > getInitialVertexPositions() const
Returns the vector of initial positions of the mesh vertices.
Definition: imstkPointSet.h:62
PbdConstraintFunctor take input geometry and produce constraints. It exists to allow extensible const...
virtual void addConstraint(std::shared_ptr< PbdConstraint > constraint)
Adds a constraint to the system, thread safe.
virtual void operator()(PbdConstraintContainer &constraints)=0
Appends a set of constraint to the container given a geometry & body.
double getRestLength() const
Get/Set the rest length, if not specified or negative the rest length will default to what it was whe...
PbdDihedralConstraintFunctor generates constraints per pair of triangle neighbors in a SurfaceMesh...
void setStride(const int stride)
Get/Set the stride, that is the amount of lines between every bend constraint.
PbdVolumeConstraintFunctor generates constraints per cell of a TetrahedralMesh.
Either mu/lambda used, may be computed from youngs modulus and poissons ratio.
Abstract PbdConstraintFunctor that assumes usage of a singular body & geometry.
double getStretch() const
Get/Set the factor that modifies the restlength.