iMSTK
Interactive Medical Simulation Toolkit
imstkSpatialHashTableSeparateChaining.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 "imstkSpatialHashTableSeparateChaining.h"
8 
9 namespace imstk
10 {
13  m_table(std::make_shared<std::unordered_set<PointEntry>>())
14 {
15  this->clear();
16 }
17 
18 void
20 {
21  for (int i = 0; i < points.size(); i++)
22  {
23  this->insertPoint(points[i]);
24  }
25 }
26 
27 void
29 {
30  PointEntry entry;
31  entry.point = point;
32  entry.ID = m_currentID;
33  entry.cellSize = m_cellSize;
34 
35  m_table->insert(entry);
36  m_currentID++;
37 }
38 
39 void
41 {
42  m_table->clear();
43  m_currentID = 0;
44 }
45 
46 std::vector<size_t>
47 SpatialHashTableSeparateChaining::getPointsInAABB(const Vec3d& corner1, const Vec3d& corner2)
48 {
49  std::vector<size_t> result;
50  getPointsInAABB(result, corner1, corner2);
51  return result;
52 }
53 
54 void
55 SpatialHashTableSeparateChaining::getPointsInAABB(std::vector<size_t>& result, const Vec3d& corner1, const Vec3d& corner2)
56 {
57  auto min_x = std::fmin(corner1.x(), corner2.x());
58  auto max_x = std::fmax(corner1.x(), corner2.x());
59  auto min_y = std::fmin(corner1.y(), corner2.y());
60  auto max_y = std::fmax(corner1.y(), corner2.y());
61  auto min_z = std::fmin(corner1.z(), corner2.z());
62  auto max_z = std::fmax(corner1.z(), corner2.z());
63 
64  std::unordered_set<PointEntry> tempPoints(0);
65 
66  // Coarse iteration (false positives may exist)
67  for (double x = min_x; x < max_x + m_cellSize[0]; x += m_cellSize[0])
68  {
69  for (double y = min_y; y < max_y + m_cellSize[1]; y += m_cellSize[1])
70  {
71  for (double z = min_z; z < max_z + m_cellSize[2]; z += m_cellSize[2])
72  {
73  PointEntry point;
74  point.point = Vec3d(x, y, z);
75  point.cellSize = m_cellSize;
76 
77  auto bucket = m_table->bucket(point);
78 
79  auto first = m_table->begin(bucket);
80  auto last = m_table->end(bucket);
81 
82  for (auto p = first; p != last; ++p)
83  {
84  tempPoints.insert(*p);
85  }
86  }
87  }
88  }
89 
90  // clear old data (if applicable) and allocate memory beforehand
91  result.resize(0);
92  result.reserve(tempPoints.size());
93 
94  // Fine iteration
95  for (auto p = tempPoints.begin(); p != tempPoints.end(); ++p)
96  {
97  Vec3d point = p->point;
98  if (point.x() >= min_x && point.x() <= max_x
99  && point.y() >= min_y && point.y() <= max_y
100  && point.z() >= min_z && point.z() <= max_z)
101  {
102  result.push_back(p->ID);
103  }
104  }
105 }
106 
107 std::vector<size_t>
109 {
110  std::vector<size_t> result;
111  getPointsInSphere(result, ppos, radius);
112  return result;
113 }
114 
115 void
116 SpatialHashTableSeparateChaining::getPointsInSphere(std::vector<size_t>& result, const Vec3d& ppos, const double radius)
117 {
118  int cellSpan[3];
119  for (int d = 0; d < 3; ++d)
120  {
121  cellSpan[d] = static_cast<int>(std::ceil(radius / m_cellSize[d]));
122  }
123 
124  double radiusSqr = radius * radius;
125  std::unordered_set<size_t> visited;
126  visited.reserve(static_cast<size_t>(cellSpan[0] * cellSpan[1] * cellSpan[2]));
127 
128  // clear the old result (if applicable)
129  result.resize(0);
130 
131  for (int i = -cellSpan[0]; i <= cellSpan[0]; ++i)
132  {
133  for (int j = -cellSpan[1]; j <= cellSpan[1]; ++j)
134  {
135  for (int k = -cellSpan[2]; k <= cellSpan[2]; ++k)
136  {
137  PointEntry point;
138  point.point = Vec3d(ppos[0] + m_cellSize[0] * i,
139  ppos[1] + m_cellSize[1] * j,
140  ppos[2] + m_cellSize[2] * k);
141  point.cellSize = m_cellSize;
142 
143  auto bucket = m_table->bucket(point);
144 
145  // avoid visiting a bucket more than once
146  // (that happens due to numerical round-off)
147  if (visited.find(bucket) != visited.end())
148  {
149  continue;
150  }
151  visited.insert(bucket);
152 
153  auto first = m_table->begin(bucket);
154  auto last = m_table->end(bucket);
155 
156  for (auto it = first; it != last; ++it)
157  {
158  const Vec3d& qpos = it->point;
159  const Vec3d diff = ppos - qpos;
160  const auto d2 = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
161  if (d2 < radiusSqr)
162  {
163  result.push_back(it->ID);
164  }
165  }
166  }
167  }
168  }
169 }
170 
171 void
173 {
174  m_loadFactorMax = loadFactorMax;
175  m_table->max_load_factor(m_loadFactorMax);
176  m_table->rehash(m_table->bucket_count());
177 }
178 
179 void
181 {
182  m_cellSize[0] = x;
183  m_cellSize[1] = y;
184  m_cellSize[2] = z;
185 
187 }
188 
189 void
191 {
192  // copy points from the hash table to a vector, then clear the table
193  std::vector<PointEntry> points;
194  points.reserve(m_table->size());
195  points.insert(points.end(), m_table->begin(), m_table->end());
196  m_table->clear();
197 
198  for (auto& point : points)
199  {
200  point.cellSize = m_cellSize;
201  }
202 
203  // insert points back to the table
204  for (auto& point : points)
205  {
206  m_table->insert(point);
207  }
208 }
209 
210 void
212 {
213  m_table->rehash(m_table->bucket_count());
214 }
215 } // namespace imstk
virtual void setCellSize(double x, double y, double z) override
Protected constructor.
void insertPoint(const Vec3d &point)
Insert an array of points.
Compound Geometry.
void insertPoints(const VecDataArray< double, 3 > &points)
Insert an array of points.
std::vector< size_t > getPointsInAABB(const Vec3d &corner1, const Vec3d &corner2)
Finds IDs of all points in an AABB.
virtual void rehash() override
Rehash the hash table.
void recomputePointHash()
Update cell size for all points and rehash. This is called after changing the cell dimensions...
Abstract class for spatial hash tables.
std::vector< size_t > getPointsInSphere(const Vec3d &ppos, double radius)
Find IDs of all points in a sphere centered at ppos and having given radius.
void setLoadFactorMax(float loadFactorMax)
Sets the max load factor.