OGS
Grid.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
4#pragma once
5
6#include <array>
7#include <bitset>
8#include <vector>
9
10#include "BaseLib/Logging.h"
11
12// GeoLib
13#include "AABB.h"
14#ifndef NDEBUG
15#include "GEOObjects.h"
16#endif
17
18namespace GeoLib
19{
20template <typename POINT>
21class Grid final : public GeoLib::AABB
22{
23public:
40 template <typename InputIterator>
41 Grid(InputIterator first, InputIterator last,
42 std::size_t max_num_per_grid_cell = 1);
43
44 Grid(Grid const&) = delete;
45 Grid& operator=(Grid const&) = delete;
46
52
67 template <typename P>
68 POINT* getNearestPoint(P const& pnt) const;
69
70 template <typename P>
71 std::vector<std::size_t> getPointsInEpsilonEnvironment(P const& pnt,
72 double eps) const;
73
85 template <typename P>
86 std::vector<std::vector<POINT*> const*>
88 double half_len) const;
89
90 std::vector<std::vector<POINT*> const*>
92 Eigen::Vector3d const& min_pnt, Eigen::Vector3d const& max_pnt) const;
93
94#ifndef NDEBUG
101#endif
102
103private:
117 void initNumberOfSteps(std::size_t n_per_cell, std::size_t n_pnts,
118 std::array<double, 3> const& extensions);
119
127 template <typename T>
128 std::array<std::size_t, 3> getGridCoords(T const& pnt) const;
129
160 template <typename P>
161 std::array<double, 6> getPointCellBorderDistances(
162 P const& pnt, std::array<std::size_t, 3> const& coords) const;
163
164 template <typename P>
165 bool calcNearestPointInGridCell(P const& pnt,
166 std::array<std::size_t, 3> const& coords,
167 double& sqr_min_dist,
168 POINT*& nearest_pnt) const;
169
170 static POINT* copyOrAddress(POINT& p) { return &p; }
171 static POINT const* copyOrAddress(POINT const& p) { return &p; }
172 static POINT* copyOrAddress(POINT* p) { return p; }
173
174 std::array<std::size_t, 3> _n_steps = {{1, 1, 1}};
175 std::array<double, 3> _step_sizes = {{0.0, 0.0, 0.0}};
179 std::vector<POINT*>* _grid_cell_nodes_map = nullptr;
180};
181
182template <typename POINT>
183template <typename InputIterator>
184Grid<POINT>::Grid(InputIterator first, InputIterator last,
185 std::size_t max_num_per_grid_cell)
186 : GeoLib::AABB(first, last)
187{
188 auto const n_pnts(std::distance(first, last));
189
190 auto const& max{getMaxPoint()};
191 auto const& min{getMinPoint()};
192 std::array<double, 3> delta = {
193 {max[0] - min[0], max[1] - min[1], max[2] - min[2]}};
194
195 // enlarge delta
196 constexpr double direction = std::numeric_limits<double>::max();
197 std::transform(begin(delta), end(delta), begin(delta),
198 [](double const d) { return std::nextafter(d, direction); });
199
200 assert(n_pnts > 0);
201 initNumberOfSteps(max_num_per_grid_cell, static_cast<std::size_t>(n_pnts),
202 delta);
203
204 const std::size_t n_plane(_n_steps[0] * _n_steps[1]);
205 _grid_cell_nodes_map = new std::vector<POINT*>[n_plane * _n_steps[2]];
206
207 // some frequently used expressions to fill the grid vectors
208 for (std::size_t k(0); k < 3; k++)
209 {
210 if (std::abs(delta[k]) < std::numeric_limits<double>::epsilon())
211 {
212 delta[k] = std::numeric_limits<double>::epsilon();
213 }
214 _step_sizes[k] = delta[k] / _n_steps[k];
215 }
216
217 // fill the grid vectors
218 InputIterator it(first);
219 while (it != last)
220 {
221 std::array<std::size_t, 3> coords(getGridCoords(*copyOrAddress(*it)));
222 if (coords < _n_steps)
223 {
224 std::size_t const pos(coords[0] + coords[1] * _n_steps[0] +
225 coords[2] * n_plane);
226 _grid_cell_nodes_map[pos].push_back(
227 const_cast<POINT*>(copyOrAddress(*it)));
228 }
229 else
230 {
231 ERR("Grid constructor: error computing indices [{:d}, {:d}, {:d}], "
232 "max indices [{:d}, {:d}, {:d}].",
233 coords[0], coords[1], coords[2], _n_steps[0], _n_steps[1],
234 _n_steps[2]);
235 }
236 it++;
237 }
238}
239
240template <typename POINT>
241template <typename P>
242std::vector<std::vector<POINT*> const*>
244 double half_len) const
245{
246 Eigen::Vector3d const c{center[0], center[1], center[2]};
247 std::array<std::size_t, 3> min_coords(getGridCoords(c.array() - half_len));
248 std::array<std::size_t, 3> max_coords(getGridCoords(c.array() + half_len));
249
250 std::vector<std::vector<POINT*> const*> pnts;
251 pnts.reserve(
252 (Eigen::Map<Eigen::Matrix<std::size_t, 3, 1>>(max_coords.data()) -
253 Eigen::Map<Eigen::Matrix<std::size_t, 3, 1>>(min_coords.data()))
254 .prod());
255
256 std::size_t const steps0_x_steps1(_n_steps[0] * _n_steps[1]);
257 for (std::size_t c0 = min_coords[0]; c0 < max_coords[0] + 1; c0++)
258 {
259 for (std::size_t c1 = min_coords[1]; c1 < max_coords[1] + 1; c1++)
260 {
261 const std::size_t coords0_p_coords1_x_steps0(c0 + c1 * _n_steps[0]);
262 for (std::size_t c2 = min_coords[2]; c2 < max_coords[2] + 1; c2++)
263 {
264 pnts.push_back(
265 &(_grid_cell_nodes_map[coords0_p_coords1_x_steps0 +
266 c2 * steps0_x_steps1]));
267 }
268 }
269 }
270 return pnts;
271}
272
273template <typename POINT>
274std::vector<std::vector<POINT*> const*>
276 Eigen::Vector3d const& min_pnt, Eigen::Vector3d const& max_pnt) const
277{
278 std::array<std::size_t, 3> min_coords(getGridCoords(min_pnt));
279 std::array<std::size_t, 3> max_coords(getGridCoords(max_pnt));
280
281 std::vector<std::vector<POINT*> const*> pnts;
282 pnts.reserve(
283 (Eigen::Map<Eigen::Matrix<std::size_t, 3, 1>>(max_coords.data()) -
284 Eigen::Map<Eigen::Matrix<std::size_t, 3, 1>>(min_coords.data()))
285 .prod());
286
287 std::size_t const steps0_x_steps1(_n_steps[0] * _n_steps[1]);
288 for (std::size_t c0 = min_coords[0]; c0 < max_coords[0] + 1; c0++)
289 {
290 for (std::size_t c1 = min_coords[1]; c1 < max_coords[1] + 1; c1++)
291 {
292 const std::size_t coords0_p_coords1_x_steps0(c0 + c1 * _n_steps[0]);
293 for (std::size_t c2 = min_coords[2]; c2 < max_coords[2] + 1; c2++)
294 {
295 pnts.push_back(
296 &(_grid_cell_nodes_map[coords0_p_coords1_x_steps0 +
297 c2 * steps0_x_steps1]));
298 }
299 }
300 }
301 return pnts;
302}
303
304#ifndef NDEBUG
305template <typename POINT>
307{
308 std::vector<std::string> grid_names;
309
310 GeoLib::Point const& llf(getMinPoint());
311 GeoLib::Point const& urb(getMaxPoint());
312
313 const double dx((urb[0] - llf[0]) / _n_steps[0]);
314 const double dy((urb[1] - llf[1]) / _n_steps[1]);
315 const double dz((urb[2] - llf[2]) / _n_steps[2]);
316
317 // create grid names and grid boxes as geometry
318 for (std::size_t i(0); i < _n_steps[0]; i++)
319 {
320 for (std::size_t j(0); j < _n_steps[1]; j++)
321 {
322 for (std::size_t k(0); k < _n_steps[2]; k++)
323 {
324 std::string name("Grid-");
325 name += std::to_string(i);
326 name += "-";
327 name += std::to_string(j);
328 name += "-";
329 name += std::to_string(k);
330 grid_names.push_back(name);
331
332 {
333 std::vector<GeoLib::Point*> points;
334 points.push_back(new GeoLib::Point(
335 llf[0] + i * dx, llf[1] + j * dy, llf[2] + k * dz));
336 points.push_back(new GeoLib::Point(llf[0] + i * dx,
337 llf[1] + (j + 1) * dy,
338 llf[2] + k * dz));
339 points.push_back(new GeoLib::Point(llf[0] + (i + 1) * dx,
340 llf[1] + (j + 1) * dy,
341 llf[2] + k * dz));
342 points.push_back(new GeoLib::Point(llf[0] + (i + 1) * dx,
343 llf[1] + j * dy,
344 llf[2] + k * dz));
345 points.push_back(new GeoLib::Point(llf[0] + i * dx,
346 llf[1] + j * dy,
347 llf[2] + (k + 1) * dz));
348 points.push_back(new GeoLib::Point(llf[0] + i * dx,
349 llf[1] + (j + 1) * dy,
350 llf[2] + (k + 1) * dz));
351 points.push_back(new GeoLib::Point(llf[0] + (i + 1) * dx,
352 llf[1] + (j + 1) * dy,
353 llf[2] + (k + 1) * dz));
354 points.push_back(new GeoLib::Point(llf[0] + (i + 1) * dx,
355 llf[1] + j * dy,
356 llf[2] + (k + 1) * dz));
357 geo_obj->addPointVec(std::move(points), grid_names.back());
358 }
359
360 std::vector<GeoLib::Polyline*> plys;
361 auto const& points = *geo_obj->getPointVec(grid_names.back());
362
363 auto* ply0(new GeoLib::Polyline(points));
364 for (std::size_t l(0); l < 4; l++)
365 ply0->addPoint(l);
366 ply0->addPoint(0);
367 plys.push_back(ply0);
368
369 auto* ply1(new GeoLib::Polyline(points));
370 for (std::size_t l(4); l < 8; l++)
371 ply1->addPoint(l);
372 ply1->addPoint(4);
373 plys.push_back(ply1);
374
375 auto* ply2(new GeoLib::Polyline(points));
376 ply2->addPoint(0);
377 ply2->addPoint(4);
378 plys.push_back(ply2);
379
380 auto* ply3(new GeoLib::Polyline(points));
381 ply3->addPoint(1);
382 ply3->addPoint(5);
383 plys.push_back(ply3);
384
385 auto* ply4(new GeoLib::Polyline(points));
386 ply4->addPoint(2);
387 ply4->addPoint(6);
388 plys.push_back(ply4);
389
390 auto* ply5(new GeoLib::Polyline(points));
391 ply5->addPoint(3);
392 ply5->addPoint(7);
393 plys.push_back(ply5);
394
395 geo_obj->addPolylineVec(std::move(plys), grid_names.back(),
397 }
398 }
399 }
400 std::string merged_geo_name("Grid");
401
402 geo_obj->mergeGeometries(grid_names, merged_geo_name);
403}
404#endif
405
406template <typename POINT>
407template <typename T>
408std::array<std::size_t, 3> Grid<POINT>::getGridCoords(T const& pnt) const
409{
410 auto const [min_point, max_point] = getMinMaxPoints();
411
412 std::array<std::size_t, 3> coords{0, 0, 0};
413 for (std::size_t k(0); k < 3; k++)
414 {
415 if (pnt[k] < min_point[k])
416 {
417 continue;
418 }
419 if (pnt[k] >= max_point[k])
420 {
421 coords[k] = _n_steps[k] - 1;
422 continue;
423 }
424 coords[k] = static_cast<std::size_t>(
425 std::floor((pnt[k] - min_point[k]) /
426 std::nextafter(_step_sizes[k],
427 std::numeric_limits<double>::max())));
428 }
429 return coords;
430}
431
432template <typename POINT>
433template <typename P>
435 P const& pnt, std::array<std::size_t, 3> const& coords) const
436{
437 auto const& min_point{getMinPoint()};
438 std::array<double, 6> dists{};
439 dists[0] =
440 std::abs(pnt[2] - min_point[2] + coords[2] * _step_sizes[2]); // bottom
441 dists[5] = std::abs(pnt[2] - min_point[2] +
442 (coords[2] + 1) * _step_sizes[2]); // top
443
444 dists[1] =
445 std::abs(pnt[1] - min_point[1] + coords[1] * _step_sizes[1]); // front
446 dists[3] = std::abs(pnt[1] - min_point[1] +
447 (coords[1] + 1) * _step_sizes[1]); // back
448
449 dists[4] =
450 std::abs(pnt[0] - min_point[0] + coords[0] * _step_sizes[0]); // left
451 dists[2] = std::abs(pnt[0] - min_point[0] +
452 (coords[0] + 1) * _step_sizes[0]); // right
453 return dists;
454}
455
456template <typename POINT>
457template <typename P>
459{
460 std::array<std::size_t, 3> coords(getGridCoords(pnt));
461 auto const& min_point{getMinPoint()};
462 auto const& max_point{getMaxPoint()};
463
464 double sqr_min_dist = (max_point - min_point).squaredNorm();
465 POINT* nearest_pnt(nullptr);
466
467 std::array<double, 6> const dists(getPointCellBorderDistances(pnt, coords));
468
469 if (calcNearestPointInGridCell(pnt, coords, sqr_min_dist, nearest_pnt))
470 {
471 double min_dist(std::sqrt(sqr_min_dist));
472 if (dists[0] >= min_dist && dists[1] >= min_dist &&
473 dists[2] >= min_dist && dists[3] >= min_dist &&
474 dists[4] >= min_dist && dists[5] >= min_dist)
475 {
476 return nearest_pnt;
477 }
478 }
479 else
480 {
481 // search in all border cells for at least one neighbor
482 double sqr_min_dist_tmp;
483 POINT* nearest_pnt_tmp(nullptr);
484 std::size_t offset(1);
485
486 while (nearest_pnt == nullptr)
487 {
488 std::array<std::size_t, 3> ijk{
489 {coords[0] < offset ? 0 : coords[0] - offset,
490 coords[1] < offset ? 0 : coords[1] - offset,
491 coords[2] < offset ? 0 : coords[2] - offset}};
492 for (; ijk[0] < coords[0] + offset; ijk[0]++)
493 {
494 for (; ijk[1] < coords[1] + offset; ijk[1]++)
495 {
496 for (; ijk[2] < coords[2] + offset; ijk[2]++)
497 {
498 // do not check the origin grid cell twice
499 if (ijk[0] == coords[0] && ijk[1] == coords[1] &&
500 ijk[2] == coords[2])
501 {
502 continue;
503 }
504 // check if temporary grid cell coordinates are valid
505 if (ijk[0] >= _n_steps[0] || ijk[1] >= _n_steps[1] ||
506 ijk[2] >= _n_steps[2])
507 {
508 continue;
509 }
510
512 pnt, ijk, sqr_min_dist_tmp, nearest_pnt_tmp))
513 {
514 if (sqr_min_dist_tmp < sqr_min_dist)
515 {
516 sqr_min_dist = sqr_min_dist_tmp;
517 nearest_pnt = nearest_pnt_tmp;
518 }
519 }
520 } // end k
521 } // end j
522 } // end i
523 offset++;
524 } // end while
525 } // end else
526
527 double const len(
528 (pnt.asEigenVector3d() - nearest_pnt->asEigenVector3d()).norm());
529 // search all other grid cells within the cube with the edge nodes
530 std::vector<std::vector<POINT*> const*> vecs_of_pnts(
532
533 const std::size_t n_vecs(vecs_of_pnts.size());
534 for (std::size_t j(0); j < n_vecs; j++)
535 {
536 std::vector<POINT*> const& pnts(*(vecs_of_pnts[j]));
537 const std::size_t n_pnts(pnts.size());
538 for (std::size_t k(0); k < n_pnts; k++)
539 {
540 const double sqr_dist(
541 (pnt.asEigenVector3d() - pnts[k]->asEigenVector3d())
542 .squaredNorm());
543 if (sqr_dist < sqr_min_dist)
544 {
545 sqr_min_dist = sqr_dist;
546 nearest_pnt = pnts[k];
547 }
548 }
549 }
550
551 return nearest_pnt;
552}
553
554template <typename POINT>
555void Grid<POINT>::initNumberOfSteps(std::size_t n_per_cell, std::size_t n_pnts,
556 std::array<double, 3> const& extensions)
557{
558 double const max_extension(
559 *std::max_element(extensions.cbegin(), extensions.cend()));
560
561 std::bitset<3> dim; // all bits set to zero
562 for (std::size_t k(0); k < 3; ++k)
563 {
564 // set dimension if the ratio kth-extension/max_extension >= 1e-4
565 if (extensions[k] >= 1e-4 * max_extension)
566 {
567 dim[k] = true;
568 }
569 }
570
571 // structured grid: n_cells = _n_steps[0] * _n_steps[1] * _n_steps[2]
572 // *** condition: n_pnts / n_cells < n_per_cell
573 // => n_pnts / n_per_cell < n_cells
574 // _n_steps[1] = _n_steps[0] * extensions[1]/extensions[0],
575 // _n_steps[2] = _n_steps[0] * extensions[2]/extensions[0],
576 // => n_cells = _n_steps[0]^3 * extensions[1]/extensions[0] *
577 // extensions[2]/extensions[0],
578 // => _n_steps[0] = cbrt(n_cells * extensions[0]^2 /
579 // (extensions[1]*extensions[2]))
580 auto sc_ceil = [](double v) { return static_cast<std::size_t>(ceil(v)); };
581
582 switch (dim.count())
583 {
584 case 3: // 3d case
585 _n_steps[0] = sc_ceil(
586 std::cbrt(n_pnts * (extensions[0] / extensions[1]) *
587 (extensions[0] / extensions[2]) / n_per_cell));
588 _n_steps[1] = sc_ceil(
589 _n_steps[0] * std::min(extensions[1] / extensions[0], 100.0));
590 _n_steps[2] = sc_ceil(
591 _n_steps[0] * std::min(extensions[2] / extensions[0], 100.0));
592 break;
593 case 2: // 2d cases
594 if (dim[0] && dim[1])
595 { // xy
596 _n_steps[0] = sc_ceil(std::sqrt(n_pnts * extensions[0] /
597 (n_per_cell * extensions[1])));
598 _n_steps[1] =
599 sc_ceil(_n_steps[0] *
600 std::min(extensions[1] / extensions[0], 100.0));
601 }
602 else if (dim[0] && dim[2])
603 { // xz
604 _n_steps[0] = sc_ceil(std::sqrt(n_pnts * extensions[0] /
605 (n_per_cell * extensions[2])));
606 _n_steps[2] =
607 sc_ceil(_n_steps[0] *
608 std::min(extensions[2] / extensions[0], 100.0));
609 }
610 else if (dim[1] && dim[2])
611 { // yz
612 _n_steps[1] = sc_ceil(std::sqrt(n_pnts * extensions[1] /
613 (n_per_cell * extensions[2])));
614 _n_steps[2] =
615 sc_ceil(std::min(extensions[2] / extensions[1], 100.0));
616 }
617 break;
618 case 1: // 1d cases
619 for (std::size_t k(0); k < 3; ++k)
620 {
621 if (dim[k])
622 {
623 _n_steps[k] =
624 sc_ceil(static_cast<double>(n_pnts) / n_per_cell);
625 }
626 }
627 }
628}
629
630template <typename POINT>
631template <typename P>
633 P const& pnt,
634 std::array<std::size_t, 3> const& coords,
635 double& sqr_min_dist,
636 POINT*& nearest_pnt) const
637{
638 const std::size_t grid_idx(coords[0] + coords[1] * _n_steps[0] +
639 coords[2] * _n_steps[0] * _n_steps[1]);
640 std::vector<typename std::add_pointer<
641 typename std::remove_pointer<POINT>::type>::type> const&
642 pnts(_grid_cell_nodes_map[grid_idx]);
643 if (pnts.empty())
644 return false;
645
646 const std::size_t n_pnts(pnts.size());
647 sqr_min_dist =
648 (pnts[0]->asEigenVector3d() - pnt.asEigenVector3d()).squaredNorm();
649 nearest_pnt = pnts[0];
650 for (std::size_t i(1); i < n_pnts; i++)
651 {
652 const double sqr_dist(
653 (pnts[i]->asEigenVector3d() - pnt.asEigenVector3d()).squaredNorm());
654 if (sqr_dist < sqr_min_dist)
655 {
656 sqr_min_dist = sqr_dist;
657 nearest_pnt = pnts[i];
658 }
659 }
660 return true;
661}
662
663template <typename POINT>
664template <typename P>
666 P const& pnt, double eps) const
667{
668 std::vector<std::vector<POINT*> const*> vec_pnts(
670
671 double const sqr_eps(eps * eps);
672
673 std::vector<std::size_t> pnts;
674 for (auto vec : vec_pnts)
675 {
676 for (auto const p : *vec)
677 {
678 if ((p->asEigenVector3d() - pnt.asEigenVector3d()).squaredNorm() <=
679 sqr_eps)
680 {
681 pnts.push_back(p->getID());
682 }
683 }
684 }
685
686 return pnts;
687}
688
689} // end namespace GeoLib
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Class AABB is an axis aligned bounding box around a given set of geometric points of (template) type ...
Definition AABB.h:45
Eigen::Vector3d const & getMaxPoint() const
Definition AABB.h:176
AABB(std::vector< PNT_TYPE * > const &pnts, std::vector< std::size_t > const &ids)
Definition AABB.h:53
Eigen::Vector3d const & getMinPoint() const
Definition AABB.h:169
MinMaxPoints getMinMaxPoints() const
Definition AABB.h:163
Container class for geometric objects.
Definition GEOObjects.h:46
void addPolylineVec(std::vector< Polyline * > &&lines, std::string const &name, PolylineVec::NameIdMap &&ply_names)
const std::vector< Point * > * getPointVec(const std::string &name) const
void addPointVec(std::vector< Point * > &&points, std::string &name, PointVec::NameIdMap &&pnt_id_name_map, double const eps=std::sqrt(std::numeric_limits< double >::epsilon()))
int mergeGeometries(std::vector< std::string > const &geo_names, std::string &merged_geo_name)
std::vector< std::vector< POINT * > const * > getPntVecsOfGridCellsIntersectingCuboid(Eigen::Vector3d const &min_pnt, Eigen::Vector3d const &max_pnt) const
Definition Grid.h:275
POINT * getNearestPoint(P const &pnt) const
Definition Grid.h:458
std::array< std::size_t, 3 > getGridCoords(T const &pnt) const
Definition Grid.h:408
static POINT * copyOrAddress(POINT &p)
Definition Grid.h:170
Grid(Grid const &)=delete
static POINT const * copyOrAddress(POINT const &p)
Definition Grid.h:171
std::array< double, 6 > getPointCellBorderDistances(P const &pnt, std::array< std::size_t, 3 > const &coords) const
Definition Grid.h:434
std::vector< std::vector< POINT * > const * > getPntVecsOfGridCellsIntersectingCube(P const &center, double half_len) const
Definition Grid.h:243
void createGridGeometry(GeoLib::GEOObjects *geo_obj) const
Definition Grid.h:306
bool calcNearestPointInGridCell(P const &pnt, std::array< std::size_t, 3 > const &coords, double &sqr_min_dist, POINT *&nearest_pnt) const
Definition Grid.h:632
std::array< std::size_t, 3 > _n_steps
Definition Grid.h:174
std::array< double, 3 > _step_sizes
Definition Grid.h:175
std::vector< POINT * > * _grid_cell_nodes_map
Definition Grid.h:179
Grid & operator=(Grid const &)=delete
Grid(InputIterator first, InputIterator last, std::size_t max_num_per_grid_cell=1)
The constructor of the grid object takes a vector of points or nodes. Furthermore the user can specif...
Definition Grid.h:184
static POINT * copyOrAddress(POINT *p)
Definition Grid.h:172
std::vector< std::size_t > getPointsInEpsilonEnvironment(P const &pnt, double eps) const
Definition Grid.h:665
void initNumberOfSteps(std::size_t n_per_cell, std::size_t n_pnts, std::array< double, 3 > const &extensions)
Definition Grid.h:555
Class Polyline consists mainly of a reference to a point vector and a vector that stores the indices ...
Definition Polyline.h:29
std::map< std::string, std::size_t > NameIdMap
Definition TemplateVec.h:30