OGS
OctTree-impl.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4namespace GeoLib
5{
6template <typename POINT, std::size_t MAX_POINTS>
8 Eigen::Vector3d ll, Eigen::Vector3d ur, double eps)
9{
10 // compute an axis aligned cube around the points ll and ur
11 const double dx(ur[0] - ll[0]);
12 const double dy(ur[1] - ll[1]);
13 const double dz(ur[2] - ll[2]);
14 if (dx >= dy && dx >= dz)
15 {
16 ll[1] -= (dx - dy) / 2.0;
17 ur[1] += (dx - dy) / 2.0;
18 ll[2] -= (dx - dz) / 2.0;
19 ur[2] += (dx - dz) / 2.0;
20 }
21 else
22 {
23 if (dy >= dx && dy >= dz)
24 {
25 ll[0] -= (dy - dx) / 2.0;
26 ur[0] += (dy - dx) / 2.0;
27 ll[2] -= (dy - dz) / 2.0;
28 ur[2] += (dy - dz) / 2.0;
29 }
30 else
31 {
32 ll[0] -= (dz - dx) / 2.0;
33 ur[0] += (dz - dx) / 2.0;
34 ll[1] -= (dz - dy) / 2.0;
35 ur[1] += (dz - dy) / 2.0;
36 }
37 }
38 if (eps == 0.0)
39 {
40 eps = std::numeric_limits<double>::epsilon();
41 }
42 for (std::size_t k(0); k < 3; ++k)
43 {
44 if (ur[k] - ll[k] > 0.0)
45 {
46 ur[k] += (ur[k] - ll[k]) * 1e-6;
47 }
48 else
49 {
50 ur[k] += eps;
51 }
52 }
53 Eigen::Vector3d lower_left{ll[0], ll[1], ll[2]};
54 Eigen::Vector3d upper_right{ur[0], ur[1], ur[2]};
55 return new OctTree<POINT, MAX_POINTS>(lower_left, upper_right, eps);
56}
57
58template <typename POINT, std::size_t MAX_POINTS>
60{
61 for (auto c : _children)
62 {
63 delete c;
64 }
65}
66
67template <typename POINT, std::size_t MAX_POINTS>
69{
70 // first do a range query using a small box around the point pnt
71 std::vector<POINT*> query_pnts;
72 Eigen::Vector3d const min = pnt->asEigenVector3d().array() - _eps;
73 Eigen::Vector3d const max = {
74 std::abs(((*pnt)[0] + _eps) - (*pnt)[0]) > 0.0
75 ? (*pnt)[0] + _eps
76 : std::nextafter((*pnt)[0],
77 std::numeric_limits<double>::infinity()),
78 std::abs(((*pnt)[1] + _eps) - (*pnt)[1]) > 0.0
79 ? (*pnt)[1] + _eps
80 : std::nextafter((*pnt)[1],
81 std::numeric_limits<double>::infinity()),
82 std::abs(((*pnt)[2] + _eps) - (*pnt)[2]) > 0.0
83 ? (*pnt)[2] + _eps
84 : std::nextafter((*pnt)[2],
85 std::numeric_limits<double>::infinity())};
86 getPointsInRange(min, max, query_pnts);
87 auto const it =
88 std::find_if(query_pnts.begin(), query_pnts.end(),
89 [pnt, this](auto const* p)
90 {
91 return (p->asEigenVector3d() - pnt->asEigenVector3d())
92 .squaredNorm() < _eps * _eps;
93 });
94 if (it != query_pnts.end())
95 {
96 ret_pnt = *it;
97 return false;
98 }
99
100 // the point pnt is not yet in the OctTree
101 if (isOutside(pnt))
102 {
103 ret_pnt = nullptr;
104 return false;
105 }
106
107 // at this place it holds true that the point is within [_ll, _ur]
108 if (!_is_leaf)
109 {
110 for (auto c : _children)
111 {
112 if (c->addPoint_(pnt, ret_pnt))
113 {
114 return true;
115 }
116 if (ret_pnt != nullptr)
117 {
118 return false;
119 }
120 }
121 }
122
123 ret_pnt = pnt;
124
125 if (_pnts.size() < MAX_POINTS)
126 {
127 _pnts.push_back(pnt);
128 }
129 else
130 { // i.e. _pnts.size () == MAX_POINTS
131 splitNode(pnt);
132 _pnts.clear();
133 }
134 return true;
135}
136
137template <typename POINT, std::size_t MAX_POINTS>
138template <typename T>
140 T const& min, T const& max, std::vector<POINT*>& pnts) const
141{
142 if (max[0] == min[0] || max[1] == min[1] || max[2] == min[2])
143 {
144 ERR("The search range [{}, {}) x [{}, {}) x [{}, {}) is empty.", min[0],
145 max[0], min[1], max[1], min[2], max[2]);
146 return;
147 }
148
149 return getPointsInRange_(min, max, pnts);
150}
151
152template <typename POINT, std::size_t MAX_POINTS>
153template <typename T>
155 T const& min, T const& max, std::vector<POINT*>& pnts) const
156{
157 if (_ur[0] < min[0] || _ur[1] < min[1] || _ur[2] < min[2])
158 {
159 return;
160 }
161
162 if (max[0] < _ll[0] || max[1] < _ll[1] || max[2] < _ll[2])
163 {
164 return;
165 }
166
167 if (_is_leaf)
168 {
169 std::copy_if(_pnts.begin(), _pnts.end(), std::back_inserter(pnts),
170 [&min, &max](auto const* p)
171 {
172 return (min[0] <= (*p)[0] && (*p)[0] < max[0] &&
173 min[1] <= (*p)[1] && (*p)[1] < max[1] &&
174 min[2] <= (*p)[2] && (*p)[2] < max[2]);
175 });
176 }
177 else
178 {
179 for (std::size_t k(0); k < 8; k++)
180 {
181 _children[k]->getPointsInRange_(min, max, pnts);
182 }
183 }
184}
185
186template <typename POINT, std::size_t MAX_POINTS>
187OctTree<POINT, MAX_POINTS>::OctTree(Eigen::Vector3d const& ll,
188 Eigen::Vector3d const& ur, double eps)
189 : _ll(ll), _ur(ur), _is_leaf(true), _eps(eps)
190{
191 _children.fill(nullptr);
192}
193
194template <typename POINT, std::size_t MAX_POINTS>
196{
197 if (isOutside(pnt))
198 {
199 ret_pnt = nullptr;
200 return false;
201 }
202
203 // at this place it holds true that the point is within [_ll, _ur]
204 if (!_is_leaf)
205 {
206 for (auto c : _children)
207 {
208 if (c->addPoint_(pnt, ret_pnt))
209 {
210 return true;
211 }
212 if (ret_pnt != nullptr)
213 {
214 return false;
215 }
216 }
217 }
218
219 ret_pnt = pnt;
220 if (_pnts.size() < MAX_POINTS)
221 {
222 _pnts.push_back(pnt);
223 }
224 else
225 { // i.e. _pnts.size () == MAX_POINTS
226 splitNode(pnt);
227 _pnts.clear();
228 }
229 return true;
230}
231
232template <typename POINT, std::size_t MAX_POINTS>
234{
235 if (isOutside(pnt))
236 {
237 return false;
238 }
239
240 if (_pnts.size() < MAX_POINTS)
241 {
242 _pnts.push_back(pnt);
243 }
244 else
245 { // i.e. _pnts.size () == MAX_POINTS
246 splitNode(pnt);
247 _pnts.clear();
248 }
249 return true;
250}
251
252template <typename POINT, std::size_t MAX_POINTS>
254{
255 const double x_mid((_ur[0] + _ll[0]) / 2.0);
256 const double y_mid((_ur[1] + _ll[1]) / 2.0);
257 const double z_mid((_ur[2] + _ll[2]) / 2.0);
258 Eigen::Vector3d p0{x_mid, y_mid, _ll[2]};
259 Eigen::Vector3d p1{_ur[0], _ur[1], z_mid};
260
261 // create child NEL
262 _children[static_cast<std::int8_t>(Quadrant::NEL)] =
263 new OctTree<POINT, MAX_POINTS>(p0, p1, _eps);
264
265 // create child NWL
266 p0[0] = _ll[0];
267 p1[0] = x_mid;
268 _children[static_cast<std::int8_t>(Quadrant::NWL)] =
269 new OctTree<POINT, MAX_POINTS>(p0, p1, _eps);
270
271 // create child SWL
272 p0[1] = _ll[1];
273 p1[1] = y_mid;
274 _children[static_cast<std::int8_t>(Quadrant::SWL)] =
276
277 // create child NEU
278 _children[static_cast<std::int8_t>(Quadrant::NEU)] =
280
281 // create child SEL
282 p0[0] = x_mid;
283 p1[0] = _ur[0];
284 _children[static_cast<std::int8_t>(Quadrant::SEL)] =
285 new OctTree<POINT, MAX_POINTS>(p0, p1, _eps);
286
287 // create child NWU
288 p0[0] = _ll[0];
289 p0[1] = y_mid;
290 p0[2] = z_mid;
291 p1[0] = x_mid;
292 p1[1] = _ur[1];
293 p1[2] = _ur[2];
294 _children[static_cast<std::int8_t>(Quadrant::NWU)] =
295 new OctTree<POINT, MAX_POINTS>(p0, p1, _eps);
296
297 // create child SWU
298 p0[1] = _ll[1];
299 p1[1] = y_mid;
300 _children[static_cast<std::int8_t>(Quadrant::SWU)] =
301 new OctTree<POINT, MAX_POINTS>(p0, p1, _eps);
302
303 // create child SEU
304 p0[0] = x_mid;
305 p1[0] = _ur[0];
306 p1[1] = y_mid;
307 p1[2] = _ur[2];
308 _children[static_cast<std::int8_t>(Quadrant::SEU)] =
309 new OctTree<POINT, MAX_POINTS>(p0, p1, _eps);
310
311 // add the passed point pnt to the children at first
312 for (std::size_t k(0); k < 8; k++)
313 {
314 if (_children[k]->addPointToChild(pnt))
315 {
316 break;
317 }
318 }
319
320 // distribute points to sub quadtrees
321 const std::size_t n_pnts(_pnts.size());
322 for (std::size_t j(0); j < n_pnts; j++)
323 {
324 if (std::any_of(begin(_children), end(_children),
325 [&](auto* child)
326 { return child->addPointToChild(_pnts[j]); }))
327 {
328 continue;
329 }
330 }
331 _is_leaf = false;
332}
333
334template <typename POINT, std::size_t MAX_POINTS>
336{
337 if ((*pnt)[0] < _ll[0] || (*pnt)[1] < _ll[1] || (*pnt)[2] < _ll[2])
338 {
339 return true;
340 }
341 if ((*pnt)[0] >= _ur[0] || (*pnt)[1] >= _ur[1] || (*pnt)[2] >= _ur[2])
342 {
343 return true;
344 }
345 return false;
346}
347} // end namespace GeoLib
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Eigen::Vector3d const _ur
upper right back face point of the cube
Definition OctTree.h:138
bool addPoint(POINT *pnt, POINT *&ret_pnt)
bool addPointToChild(POINT *pnt)
bool addPoint_(POINT *pnt, POINT *&ret_pnt)
void splitNode(POINT *pnt)
void getPointsInRange(T const &min, T const &max, std::vector< POINT * > &pnts) const
bool _is_leaf
flag if this OctTree is a leaf
Definition OctTree.h:142
OctTree(Eigen::Vector3d const &ll, Eigen::Vector3d const &ur, double eps)
void getPointsInRange_(T const &min, T const &max, std::vector< POINT * > &pnts) const
@ NEU
south west upper
Definition OctTree.h:88
@ NEL
north east lower
Definition OctTree.h:84
@ SWU
south west upper
Definition OctTree.h:90
@ SEU
south east upper
Definition OctTree.h:91
@ NWL
north west lower
Definition OctTree.h:85
@ SWL
south west lower
Definition OctTree.h:86
@ NWU
south west upper
Definition OctTree.h:89
@ SEL
south east lower
Definition OctTree.h:87
std::array< OctTree< POINT, MAX_POINTS > *, 8 > _children
Definition OctTree.h:134
std::vector< POINT * > _pnts
vector of pointers to POINT objects
Definition OctTree.h:140
bool isOutside(POINT *pnt) const
double const _eps
threshold for point uniqueness
Definition OctTree.h:144
static OctTree< POINT, MAX_POINTS > * createOctTree(Eigen::Vector3d ll, Eigen::Vector3d ur, double eps=std::numeric_limits< double >::epsilon())
Definition OctTree-impl.h:7
virtual ~OctTree()
Eigen::Vector3d const _ll
lower left front face point of the cube
Definition OctTree.h:136