iMSTK
Interactive Medical Simulation Toolkit
imstkConnectiveStrandGenerator.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 "imstkConnectiveStrandGenerator.h"
8 #include "imstkLogger.h"
9 #include "imstkSurfaceMesh.h"
10 #include "imstkLineMesh.h"
11 #include "imstkVecDataArray.h"
12 
13 #include <random>
14 #include <iostream>
15 
16 namespace imstk
17 {
18 ConnectiveStrandGenerator::ConnectiveStrandGenerator()
19 {
21  setRequiredInputType<SurfaceMesh>(0);
22  setRequiredInputType<SurfaceMesh>(1);
23 
25  setOutput(std::make_shared<LineMesh>());
26 }
27 
28 void
29 ConnectiveStrandGenerator::setInputMeshes(
30  std::shared_ptr<SurfaceMesh> inputMeshA,
31  std::shared_ptr<SurfaceMesh> inputMeshB)
32 {
33  setInput(inputMeshA, 0);
34  setInput(inputMeshB, 1);
35 }
36 
37 std::shared_ptr<LineMesh>
38 ConnectiveStrandGenerator::getOutputMesh() const
39 {
40  return std::static_pointer_cast<LineMesh>(getOutput(0));
41 }
42 
43 void
45 {
46  // Unpacking meshes
47  std::shared_ptr<SurfaceMesh> meshA = std::dynamic_pointer_cast<SurfaceMesh>(getInput(0));
48  std::shared_ptr<SurfaceMesh> meshB = std::dynamic_pointer_cast<SurfaceMesh>(getInput(1));
49 
50  // Verify normals are up to date
51  meshA->computeTrianglesNormals();
52  meshB->computeTrianglesNormals();
53 
54  std::vector<int> meshAFiltered = filterCells(meshA.get(), meshB.get());
55 
56  setOutput(createStrands(meshA.get(), meshAFiltered, meshB.get()), 0);
57 }
58 
59 std::vector<int>
61 {
62  std::vector<int> result;
63  for (int cell_idA = 0; cell_idA < meshA->getNumCells(); cell_idA++)
64  {
65  // Find nearest cell center on mesh B
66  int nearestId = -1;
67  double nearestDistSquared = IMSTK_DOUBLE_MAX;
68 
69  Vec3d cellACenter = (meshA->getVertexPosition(meshA->getCells()->at(cell_idA)[0])
70  + meshA->getVertexPosition(meshA->getCells()->at(cell_idA)[1])
71  + meshA->getVertexPosition(meshA->getCells()->at(cell_idA)[2])) / 3.0;
72 
73  for (int cell_idB = 0; cell_idB < meshB->getNumCells(); cell_idB++)
74  {
75  Vec3d cellBCenter = (meshB->getVertexPosition(meshB->getCells()->at(cell_idB)[0])
76  + meshB->getVertexPosition(meshB->getCells()->at(cell_idB)[1])
77  + meshB->getVertexPosition(meshB->getCells()->at(cell_idB)[2])) / 3.0;
78 
79  double distSquared = (cellBCenter - cellACenter).squaredNorm();
80 
81  if (distSquared < nearestDistSquared)
82  {
83  nearestDistSquared = distSquared;
84  nearestId = cell_idB;
85  }
86  }
87 
88  //Check the normal of the nearest cell to verify facing towards each other
89  double dotCheck = meshA->getCellNormals()->at(cell_idA).dot(meshB->getCellNormals()->at(nearestId));
90  if (dotCheck < -0.1)
91  {
92  result.push_back(cell_idA);
93  }
94  } // end loop over faces on mesh A
95  return result;
96 }
97 
98 std::shared_ptr<LineMesh>
100  SurfaceMesh* meshA,
101  const std::vector<int>& faces,
102  SurfaceMesh* meshB) const
103 {
104  // RNG for selecting faces to connect
105  static std::random_device rd; // obtain a random number from hardware
106  static std::mt19937 gen((static_cast<unsigned int>(time(nullptr)))); // seed the generator
107  std::uniform_int_distribution<> faceDistr(0, meshB->getNumCells() - 1); // define the range over cells of mesh B
108 
109  const int maxIteration = 10;
110  const double angleThreshold = cos(m_allowedAngleDeviation);
111  const Vec3d cardinalDirection = (meshA->getCenter() - meshB->getCenter()).normalized();
112 
113  // Storage for connectivity of line mesh
114  auto lineMeshVerticesPtr = std::make_shared<VecDataArray<double, 3>>();
115  auto lineMeshIndicesPtr = std::make_shared<VecDataArray<int, 2>>();
116 
117  for (int cell_idA = 0; cell_idA < faces.size(); cell_idA++)
118  {
119  // Turn the float strands per face into an int count
120  // the fractional part turns into a chance to have an "extra"
121  // strand on the triangle
122  int strandCount = static_cast<int>(m_strandsPerFace);
123  double remainder = m_strandsPerFace - strandCount;
124 
125  if (static_cast<float>(rand()) / static_cast<float>(RAND_MAX) < remainder)
126  {
127  strandCount++;
128  }
129 
130  // Loop over generated random points in this cell
131  for (int surfNodeId = 0; surfNodeId < strandCount; surfNodeId++)
132  {
133  const Vec3d positionOnA = generateRandomPointOnFace(meshA, faces[cell_idA]);
134  Vec3d positionOnB = Vec3d::Zero();
135 
136  double bestThreshold = std::numeric_limits<double>::min();
137  Vec3d bestPositionB = Vec3d::Zero();
138  int iterationCount = 0;
139 
140  // Get index of cell on B that creates a strand that does not penetrate mesh B
141  while (true)
142  {
143  int sideBindx = faceDistr(gen);
144  positionOnB = generateRandomPointOnFace(meshB, sideBindx);
145 
146  // Check that direction is not inside of mesh B
147  Vec3d directionBA = (positionOnA - positionOnB).normalized();
148  double dotCheck = meshB->getCellNormals()->at(sideBindx).dot(directionBA);
149  double inCardinalDirection = cardinalDirection.dot(directionBA);
150 
151  // Limit angular test to
152  if (dotCheck > 0.1)
153  {
154  ++iterationCount;
155  if (inCardinalDirection > angleThreshold)
156  {
157  break;
158  }
159 
160  if (inCardinalDirection > bestThreshold)
161  {
162  bestThreshold = angleThreshold;
163  bestPositionB = positionOnB;
164  }
165 
166  if (iterationCount > maxIteration)
167  {
168  positionOnB = bestPositionB;
169  break;
170  }
171  }
172  }
173 
174  if (positionOnB.isApprox(Vec3d::Zero()))
175  {
176  continue;
177  }
178 
179  Vec3d stepVec = (positionOnB - positionOnA) / static_cast<double>(m_segmentsPerStrand);
180 
181  int strandStartIndex = lineMeshVerticesPtr->size();
182  for (int i = 0; i < m_segmentsPerStrand + 1; i++)
183  {
184  lineMeshVerticesPtr->push_back(positionOnA + static_cast<double>(i) * stepVec);
185  }
186  for (int i = 0; i < m_segmentsPerStrand; ++i)
187  {
188  lineMeshIndicesPtr->push_back(Vec2i(strandStartIndex + i, strandStartIndex + i + 1));
189  }
190  }
191  } // end loop over cells in mesh A
192 
193  auto result = std::make_shared<LineMesh>();
194  result->initialize(lineMeshVerticesPtr, lineMeshIndicesPtr);
195  return result;
196 }
197 
198 const Vec3d
200 {
201  float r0 = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
202  float r1 = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
203 
204  const Vec3d& ptA = mesh->getVertexPosition(mesh->getCells()->at(face)[0]);
205  const Vec3d& ptB = mesh->getVertexPosition(mesh->getCells()->at(face)[1]);
206  const Vec3d& ptC = mesh->getVertexPosition(mesh->getCells()->at(face)[2]);
207 
208  return (1.0 - sqrt(r0)) * ptA + (sqrt(r0) * (1 - r1)) * ptB + (r1 * sqrt(r0)) * ptC;
209 }
210 } // namespace imstk
std::shared_ptr< Geometry > getInput(size_t port=0) const
Returns input geometry given port, returns nullptr if doesn&#39;t exist.
Compound Geometry.
void requestUpdate() override
Users can implement this for the logic to be run.
std::vector< int > filterCells(SurfaceMesh *meshA, SurfaceMesh *meshB) const
Filter faces on meshA to remove those facing away from meshB Checks nearest faces, if nearest face normal points in same general direction then ignore.
void setNumOutputPorts(const size_t numPorts)
Get/Set the amount of output ports.
int getNumCells() const override
Returns the number of cells.
virtual Vec3d getCenter()
Returns the bounding box center.
Definition: imstkGeometry.h:96
std::shared_ptr< Geometry > getOutput(size_t port=0) const
Returns output geometry given port, returns nullptr if doesn&#39;t exist.
const Vec3d & getVertexPosition(const size_t vertNum, DataType type=DataType::PostTransform) const
Returns the position of a vertex given its index.
void setInput(std::shared_ptr< Geometry > inputGeometry, size_t port=0)
Set the input at the port.
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
void setOutput(std::shared_ptr< Geometry > inputGeometry, const size_t port=0)
Set the output at the port.
void setNumInputPorts(const size_t numPorts)
Get/Set the amount of input ports.
const Vec3d generateRandomPointOnFace(SurfaceMesh *mesh, int face) const
void computeTrianglesNormals()
Compute the normals of all the triangles.
std::shared_ptr< LineMesh > createStrands(SurfaceMesh *meshA, const std::vector< int > &faces, SurfaceMesh *meshB) const
Creates a line mesh by connecting points of the given faces of meshA with with random points on rando...