Caribou
HashGrid.h
1 #pragma once
2 
3 #include <Caribou/config.h>
4 #include <Caribou/Geometry/Element.h>
5 #include <unordered_map>
6 #include <vector>
7 #include <set>
8 #include <Eigen/Core>
9 #include <bitset>
10 
11 namespace caribou::topology {
12 
13 struct CwiseRound {
14  auto operator()(const FLOATING_POINT_TYPE& x) const -> FLOATING_POINT_TYPE { return std::round(x); }
15 };
16 struct CwiseFloor {
17  auto operator()(const FLOATING_POINT_TYPE& x) const -> FLOATING_POINT_TYPE { return std::floor(x); }
18 };
19 
20 template <typename Element>
21 class HashGrid {
22 public:
23 
24  static constexpr UNSIGNED_INTEGER_TYPE Dimension = caribou::geometry::traits<Element>::Dimension;
25 
26  using Index = UNSIGNED_INTEGER_TYPE;
27  using GridCoordinates = Eigen::Matrix<INTEGER_TYPE, Dimension, 1>;
28  using WorldCoordinates = Eigen::Matrix<FLOATING_POINT_TYPE, Dimension, 1>;
29  using VecFloat = Eigen::Matrix<FLOATING_POINT_TYPE, Dimension, 1>;
30 
31  HashGrid (const FLOATING_POINT_TYPE & cell_size) : p_cell_size(cell_size) {}
32 
33  HashGrid (const FLOATING_POINT_TYPE & cell_size, const UNSIGNED_INTEGER_TYPE & number_of_elements) : p_cell_size(cell_size) {
34  p_hash_table.reserve(2*number_of_elements);
35  }
36 
42  inline
43  void add(const Element & e, const Index & id) {
44  const auto min = (e.nodes().colwise().minCoeff() * 1/p_cell_size).unaryExpr(CwiseFloor()).eval();
45  const auto max = (e.nodes().colwise().maxCoeff() * 1/p_cell_size).unaryExpr(CwiseFloor()).eval();
46 
47  for (INTEGER_TYPE i = min[0]; i <= max[0]; ++i) {
48  if constexpr (Dimension == 1) {
49  p_hash_table[GridCoordinates {i}].emplace_back(id);
50  } else {
51  for (INTEGER_TYPE j = min[1]; j <= max[1]; ++j) {
52  if constexpr (Dimension == 2) {
53  p_hash_table[GridCoordinates {i, j}].emplace_back(id);
54  } else {
55  for (INTEGER_TYPE k = min[2]; k <= max[2]; ++k) {
56  p_hash_table[GridCoordinates {i, j, k}].emplace_back(id);
57  }
58  }
59  }
60  }
61  }
62  }
63 
69  inline
70  auto get(const WorldCoordinates & p) const -> std::set<Index> {
71  const VecFloat absolute = p / p_cell_size;
72  const VecFloat rounded = absolute.unaryExpr(CwiseRound());
73  const VecFloat distance = absolute - rounded;
74 
75  auto close_to_axis = std::bitset<Dimension>();
76 
77  std::vector<GridCoordinates> cells;
78  cells.reserve((unsigned) 1<<Dimension);
79 
80  // Let's find the axis for which our coordinate is very close to
81  for (UNSIGNED_INTEGER_TYPE axis = 0; axis < Dimension; ++axis) {
82  if (distance[axis]*distance[axis] < EPSILON*EPSILON )
83  close_to_axis[axis] = true;
84  }
85 
86  if (close_to_axis.none()) {
87  // We are not near any axis, which means we are well inside a cell's boundaries
88  cells.emplace_back(absolute.unaryExpr(CwiseFloor()). template cast<INTEGER_TYPE>());
89  } else {
90  const VecFloat d = VecFloat::Constant(1 / 2.);
91 
92  std::array<std::vector<INTEGER_TYPE>, Dimension> axis_indices;
93  for (auto & indices : axis_indices) {
94  indices.reserve(Dimension);
95  }
96 
97  for (UNSIGNED_INTEGER_TYPE axis = 0; axis < Dimension; ++axis) {
98  if (close_to_axis[axis]) {
99  axis_indices[axis].emplace_back(floor(rounded[axis]-d[axis]));
100  axis_indices[axis].emplace_back(floor(rounded[axis]+d[axis]));
101  } else {
102  axis_indices[axis].emplace_back(floor(absolute[axis]));
103  }
104  }
105 
106  for (const INTEGER_TYPE & i : axis_indices[0]) {
107  if constexpr (Dimension == 1) {
108  cells.emplace_back(GridCoordinates{i});
109  } else {
110  for (const INTEGER_TYPE & j : axis_indices[1]) {
111  if constexpr (Dimension == 2) {
112  cells.emplace_back(GridCoordinates{i, j});
113  } else {
114  for (const INTEGER_TYPE & k : axis_indices[2]) {
115  cells.emplace_back(GridCoordinates{i, j, k});
116  }
117  }
118  }
119  }
120  }
121  }
122 
123  std::set<Index> elements;
124  for (const auto & cell_coordinates : cells) {
125  const auto & iter = p_hash_table.find(cell_coordinates);
126  if (iter != p_hash_table.end()) {
127  for (const auto &element_data : iter->second) {
128  elements.emplace(element_data);
129  }
130  }
131  }
132 
133  return elements;
134  }
135 
136 private:
137 
138  struct HashFunction
139  {
140  auto operator()(const GridCoordinates & coordinates) const -> std::size_t
141  {
142  // We use the large prime numbers proposed in paper:
143  // M.Teschner et al "Optimized Spatial Hashing for Collision Detection of Deformable Objects" (2003)
144  INTEGER_TYPE h = 73856093 * coordinates[0] ;
145  if constexpr (Dimension > 1)
146  h ^= 19349663*coordinates[1];
147  if constexpr (Dimension > 2)
148  h ^= 83492791*coordinates[2];
149 
150  return h;
151  }
152  };
153 
154  struct HashEqual
155  {
156  auto operator()(const GridCoordinates &a, const GridCoordinates &b) const -> bool
157  {
158  return ((a - b).norm() == 0);
159  }
160  };
161 
162 
163  FLOATING_POINT_TYPE p_cell_size;
164  std::unordered_map<GridCoordinates, std::vector<Index>, HashFunction, HashEqual> p_hash_table;
165 };
166 
167 } // namespace caribou::topology
caribou::topology::HashGrid::add
void add(const Element &e, const Index &id)
Add an element's data to the hash grid.
Definition: HashGrid.h:43
caribou::geometry::traits
Definition: Element.h:14
caribou::topology::HashGrid
Definition: HashGrid.h:21
caribou::topology::HashGrid::get
auto get(const WorldCoordinates &p) const -> std::set< Index >
Get all the data of all elements that are very close to the point p.
Definition: HashGrid.h:70