iMSTK
Interactive Medical Simulation Toolkit
imstkPointwiseMap.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 "imstkPointwiseMap.h"
8 #include "imstkParallelUtils.h"
9 #include "imstkLogger.h"
10 #include "imstkPointSet.h"
11 
12 namespace imstk
13 {
14 PointwiseMap::PointwiseMap()
15 {
16  setRequiredInputType<PointSet>(0);
17  setRequiredInputType<PointSet>(1);
18 }
19 
20 PointwiseMap::PointwiseMap(
21  std::shared_ptr<Geometry> parent,
22  std::shared_ptr<Geometry> child)
23 {
24  setParentGeometry(parent);
25  setChildGeometry(child);
26 
27  setRequiredInputType<PointSet>(0);
28  setRequiredInputType<PointSet>(1);
29 }
30 
31 void
33 {
34  if (!areInputsValid())
35  {
36  LOG(WARNING) << "PointwiseMap failed to run, inputs not satisfied";
37  return;
38  }
39 
40  m_oneToOneMap.clear();
42 
43  // Copy data from map to vector for parallel processing
44  m_oneToOneMapVector.clear();
45  for (const auto& kv : m_oneToOneMap)
46  {
47  m_oneToOneMapVector.push_back({ kv.first, kv.second });
48  }
49 }
50 
51 void
52 PointwiseMap::computeMap(std::unordered_map<int, int>& tetVertToSurfVertMap)
53 {
54  tetVertToSurfVertMap.clear();
55 
56  if (!areInputsValid())
57  {
58  LOG(WARNING) << "PointwiseMap failed to run, inputs not satisfied";
59  return;
60  }
61 
62  auto meshParent = std::dynamic_pointer_cast<PointSet>(getParentGeometry());
63  auto meshChild = std::dynamic_pointer_cast<PointSet>(getChildGeometry());
64 
65  std::shared_ptr<VecDataArray<double, 3>> parentVerticesPtr = meshParent->getVertexPositions();
66  const VecDataArray<double, 3>& parentVertices = *parentVerticesPtr;
67  std::shared_ptr<VecDataArray<double, 3>> childVerticesPtr = meshChild->getVertexPositions();
68  const VecDataArray<double, 3>& childVertices = *childVerticesPtr;
69 
70  // For every vertex on the child, find corresponding one on the parent
72  ParallelUtils::parallelFor(meshChild->getNumVertices(),
73  [&](const int nodeId)
74  {
75  // Find the enclosing or closest tetrahedron
76  int matchingNodeId = findMatchingVertex(parentVertices, childVertices[nodeId]);
77  if (matchingNodeId == -1)
78  {
79  return;
80  }
81 
82  // Add to the map
83  // Note: This replaces the map if one with <nodeId> already exists
84  lock.lock();
85  tetVertToSurfVertMap[nodeId] = matchingNodeId; // child index -> parent index
86  lock.unlock();
87  });
88 }
89 
90 int
91 PointwiseMap::findMatchingVertex(const VecDataArray<double, 3>& parentVertices, const Vec3d& p)
92 {
93  for (int idx = 0; idx < parentVertices.size(); idx++)
94  {
95  if (p.isApprox(parentVertices[idx], m_epsilon))
96  {
97  return idx;
98  }
99  }
100  return -1;
101 }
102 
103 void
104 PointwiseMap::setMap(const std::unordered_map<int, int>& sourceMap)
105 {
106  m_oneToOneMap = sourceMap;
107 
108  // Copy data from map to vector for parallel processing
109  m_oneToOneMapVector.resize(0);
110  for (auto kv : m_oneToOneMap)
111  {
112  m_oneToOneMapVector.push_back({ kv.first, kv.second });
113  }
114 }
115 
116 void
118 {
119  auto meshParent = std::dynamic_pointer_cast<PointSet>(getParentGeometry());
120  auto meshChild = std::dynamic_pointer_cast<PointSet>(getChildGeometry());
121 
122  // Check data
123  CHECK(m_oneToOneMap.size() == m_oneToOneMapVector.size()) << "Internal data is corrupted";
124  if (m_oneToOneMap.size() == 0)
125  {
126  return;
127  }
128 
129  std::shared_ptr<VecDataArray<double, 3>> childVerticesPtr = meshChild->getVertexPositions();
130  VecDataArray<double, 3>& childVertices = *childVerticesPtr;
131  std::shared_ptr<VecDataArray<double, 3>> parentVerticesPtr = meshParent->getVertexPositions();
132  const VecDataArray<double, 3>& parentVertices = *parentVerticesPtr;
133 
134  // For this loop 8000 is about the break even point for parallel vs serial
135  // see GeometryMapperBenchmark.cpp
136  ParallelUtils::parallelFor(m_oneToOneMapVector.size(),
137  [&](const size_t idx)
138  {
139  const auto& mapValue = m_oneToOneMapVector[idx];
140  childVertices[mapValue.first] = parentVertices[mapValue.second];
141  }, m_oneToOneMapVector.size() > 8000);
142  meshChild->postModified();
143 
144  setOutput(meshChild);
145 }
146 
147 int
148 PointwiseMap::getParentVertexId(const int childVertexId) const
149 {
150  auto citer = m_oneToOneMap.find(childVertexId);
151  return (citer != m_oneToOneMap.end()) ? citer->second : -1;
152 }
153 } // namespace imstk
void setMap(const std::unordered_map< int, int > &sourceMap)
Sets the one-to-one correspondence directly.
The SpinLock class.
Definition: imstkSpinLock.h:20
void unlock()
End a thread-safe region.
Definition: imstkSpinLock.h:53
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
void computeMap(std::unordered_map< int, int > &tetVertToSurfVertMap)
Compute tet vertex id to surf vertex id map.
int findMatchingVertex(const VecDataArray< double, 3 > &parentMesh, const Vec3d &p)
Returns the first matching vertex, -1 if not found.
void setParentGeometry(std::shared_ptr< Geometry > parent)
Get/Set parent geometry.
Compound Geometry.
void requestUpdate() override
Apply (if active) the tetra-triangle mesh map.
int getParentVertexId(const int childVertexId) const
Get the mapped/corresponding parent index, given a child index. returns -1 if no correspondence found...
void setChildGeometry(std::shared_ptr< Geometry > child)
Get/Set child geometry.
std::unordered_map< int, int > m_oneToOneMap
One to one mapping data.
void lock()
Start a thread-safe region, where only one thread can execute at a time until a call to the unlock fu...
Definition: imstkSpinLock.h:45
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
void postModified()
emits signal to all observers, informing them on the current address in memory and size of array ...
virtual bool areInputsValid()
Check inputs are correct.
void setOutput(std::shared_ptr< Geometry > inputGeometry, const size_t port=0)
Set the output at the port.
void compute() override
Compute the tetra-triangle mesh map.
double m_epsilon
Tolernace for considering two points equivalent/mapped. The tolerance is set a bit higher here since ...
std::vector< std::pair< int, int > > m_oneToOneMapVector
One to one mapping data.