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 {
366 ply0->addPoint(l);
367 }
368 ply0->addPoint(0);
369 plys.push_back(ply0);
370
371 auto* ply1(new GeoLib::Polyline(points));
372 for (std::size_t l(4); l < 8; l++)
373 {
374 ply1->addPoint(l);
375 }
376 ply1->addPoint(4);
377 plys.push_back(ply1);
378
379 auto* ply2(new GeoLib::Polyline(points));
380 ply2->addPoint(0);
381 ply2->addPoint(4);
382 plys.push_back(ply2);
383
384 auto* ply3(new GeoLib::Polyline(points));
385 ply3->addPoint(1);
386 ply3->addPoint(5);
387 plys.push_back(ply3);
388
389 auto* ply4(new GeoLib::Polyline(points));
390 ply4->addPoint(2);
391 ply4->addPoint(6);
392 plys.push_back(ply4);
393
394 auto* ply5(new GeoLib::Polyline(points));
395 ply5->addPoint(3);
396 ply5->addPoint(7);
397 plys.push_back(ply5);
398
399 geo_obj->addPolylineVec(std::move(plys), grid_names.back(),
401 }
402 }
403 }
404 std::string merged_geo_name("Grid");
405
406 geo_obj->mergeGeometries(grid_names, merged_geo_name);
407}
408#endif
409
410template <typename POINT>
411template <typename T>
412std::array<std::size_t, 3> Grid<POINT>::getGridCoords(T const& pnt) const
413{
414 auto const [min_point, max_point] = getMinMaxPoints();
415
416 std::array<std::size_t, 3> coords{0, 0, 0};
417 for (std::size_t k(0); k < 3; k++)
418 {
419 if (pnt[k] < min_point[k])
420 {
421 continue;
422 }
423 if (pnt[k] >= max_point[k])
424 {
425 coords[k] = _n_steps[k] - 1;
426 continue;
427 }
428 coords[k] = static_cast<std::size_t>(
429 std::floor((pnt[k] - min_point[k]) /
430 std::nextafter(_step_sizes[k],
431 std::numeric_limits<double>::max())));
432 }
433 return coords;
434}
435
436template <typename POINT>
437template <typename P>
439 P const& pnt, std::array<std::size_t, 3> const& coords) const
440{
441 auto const& min_point{getMinPoint()};
442 std::array<double, 6> dists{};
443 dists[0] =
444 std::abs(pnt[2] - min_point[2] + coords[2] * _step_sizes[2]); // bottom
445 dists[5] = std::abs(pnt[2] - min_point[2] +
446 (coords[2] + 1) * _step_sizes[2]); // top
447
448 dists[1] =
449 std::abs(pnt[1] - min_point[1] + coords[1] * _step_sizes[1]); // front
450 dists[3] = std::abs(pnt[1] - min_point[1] +
451 (coords[1] + 1) * _step_sizes[1]); // back
452
453 dists[4] =
454 std::abs(pnt[0] - min_point[0] + coords[0] * _step_sizes[0]); // left
455 dists[2] = std::abs(pnt[0] - min_point[0] +
456 (coords[0] + 1) * _step_sizes[0]); // right
457 return dists;
458}
459
460template <typename POINT>
461template <typename P>
463{
464 std::array<std::size_t, 3> coords(getGridCoords(pnt));
465 auto const& min_point{getMinPoint()};
466 auto const& max_point{getMaxPoint()};
467
468 double sqr_min_dist = (max_point - min_point).squaredNorm();
469 POINT* nearest_pnt(nullptr);
470
471 std::array<double, 6> const dists(getPointCellBorderDistances(pnt, coords));
472
473 if (calcNearestPointInGridCell(pnt, coords, sqr_min_dist, nearest_pnt))
474 {
475 double min_dist(std::sqrt(sqr_min_dist));
476 if (dists[0] >= min_dist && dists[1] >= min_dist &&
477 dists[2] >= min_dist && dists[3] >= min_dist &&
478 dists[4] >= min_dist && dists[5] >= min_dist)
479 {
480 return nearest_pnt;
481 }
482 }
483 else
484 {
485 // search in all border cells for at least one neighbor
486 double sqr_min_dist_tmp;
487 POINT* nearest_pnt_tmp(nullptr);
488 std::size_t offset(1);
489
490 while (nearest_pnt == nullptr)
491 {
492 std::array<std::size_t, 3> ijk{
493 {coords[0] < offset ? 0 : coords[0] - offset,
494 coords[1] < offset ? 0 : coords[1] - offset,
495 coords[2] < offset ? 0 : coords[2] - offset}};
496 for (; ijk[0] < coords[0] + offset; ijk[0]++)
497 {
498 for (; ijk[1] < coords[1] + offset; ijk[1]++)
499 {
500 for (; ijk[2] < coords[2] + offset; ijk[2]++)
501 {
502 // do not check the origin grid cell twice
503 if (ijk[0] == coords[0] && ijk[1] == coords[1] &&
504 ijk[2] == coords[2])
505 {
506 continue;
507 }
508 // check if temporary grid cell coordinates are valid
509 if (ijk[0] >= _n_steps[0] || ijk[1] >= _n_steps[1] ||
510 ijk[2] >= _n_steps[2])
511 {
512 continue;
513 }
514
516 pnt, ijk, sqr_min_dist_tmp, nearest_pnt_tmp))
517 {
518 if (sqr_min_dist_tmp < sqr_min_dist)
519 {
520 sqr_min_dist = sqr_min_dist_tmp;
521 nearest_pnt = nearest_pnt_tmp;
522 }
523 }
524 } // end k
525 } // end j
526 } // end i
527 offset++;
528 } // end while
529 } // end else
530
531 double const len(
532 (pnt.asEigenVector3d() - nearest_pnt->asEigenVector3d()).norm());
533 // search all other grid cells within the cube with the edge nodes
534 std::vector<std::vector<POINT*> const*> vecs_of_pnts(
536
537 const std::size_t n_vecs(vecs_of_pnts.size());
538 for (std::size_t j(0); j < n_vecs; j++)
539 {
540 std::vector<POINT*> const& pnts(*(vecs_of_pnts[j]));
541 const std::size_t n_pnts(pnts.size());
542 for (std::size_t k(0); k < n_pnts; k++)
543 {
544 const double sqr_dist(
545 (pnt.asEigenVector3d() - pnts[k]->asEigenVector3d())
546 .squaredNorm());
547 if (sqr_dist < sqr_min_dist)
548 {
549 sqr_min_dist = sqr_dist;
550 nearest_pnt = pnts[k];
551 }
552 }
553 }
554
555 return nearest_pnt;
556}
557
558template <typename POINT>
559void Grid<POINT>::initNumberOfSteps(std::size_t n_per_cell, std::size_t n_pnts,
560 std::array<double, 3> const& extensions)
561{
562 double const max_extension(
563 *std::max_element(extensions.cbegin(), extensions.cend()));
564
565 std::bitset<3> dim; // all bits set to zero
566 for (std::size_t k(0); k < 3; ++k)
567 {
568 // set dimension if the ratio kth-extension/max_extension >= 1e-4
569 if (extensions[k] >= 1e-4 * max_extension)
570 {
571 dim[k] = true;
572 }
573 }
574
575 // structured grid: n_cells = _n_steps[0] * _n_steps[1] * _n_steps[2]
576 // *** condition: n_pnts / n_cells < n_per_cell
577 // => n_pnts / n_per_cell < n_cells
578 // _n_steps[1] = _n_steps[0] * extensions[1]/extensions[0],
579 // _n_steps[2] = _n_steps[0] * extensions[2]/extensions[0],
580 // => n_cells = _n_steps[0]^3 * extensions[1]/extensions[0] *
581 // extensions[2]/extensions[0],
582 // => _n_steps[0] = cbrt(n_cells * extensions[0]^2 /
583 // (extensions[1]*extensions[2]))
584 auto sc_ceil = [](double v) { return static_cast<std::size_t>(ceil(v)); };
585
586 switch (dim.count())
587 {
588 case 3: // 3d case
589 _n_steps[0] = sc_ceil(
590 std::cbrt(n_pnts * (extensions[0] / extensions[1]) *
591 (extensions[0] / extensions[2]) / n_per_cell));
592 _n_steps[1] = sc_ceil(
593 _n_steps[0] * std::min(extensions[1] / extensions[0], 100.0));
594 _n_steps[2] = sc_ceil(
595 _n_steps[0] * std::min(extensions[2] / extensions[0], 100.0));
596 break;
597 case 2: // 2d cases
598 if (dim[0] && dim[1])
599 { // xy
600 _n_steps[0] = sc_ceil(std::sqrt(n_pnts * extensions[0] /
601 (n_per_cell * extensions[1])));
602 _n_steps[1] =
603 sc_ceil(_n_steps[0] *
604 std::min(extensions[1] / extensions[0], 100.0));
605 }
606 else if (dim[0] && dim[2])
607 { // xz
608 _n_steps[0] = sc_ceil(std::sqrt(n_pnts * extensions[0] /
609 (n_per_cell * extensions[2])));
610 _n_steps[2] =
611 sc_ceil(_n_steps[0] *
612 std::min(extensions[2] / extensions[0], 100.0));
613 }
614 else if (dim[1] && dim[2])
615 { // yz
616 _n_steps[1] = sc_ceil(std::sqrt(n_pnts * extensions[1] /
617 (n_per_cell * extensions[2])));
618 _n_steps[2] =
619 sc_ceil(std::min(extensions[2] / extensions[1], 100.0));
620 }
621 break;
622 case 1: // 1d cases
623 for (std::size_t k(0); k < 3; ++k)
624 {
625 if (dim[k])
626 {
627 _n_steps[k] =
628 sc_ceil(static_cast<double>(n_pnts) / n_per_cell);
629 }
630 }
631 }
632}
633
634template <typename POINT>
635template <typename P>
637 P const& pnt,
638 std::array<std::size_t, 3> const& coords,
639 double& sqr_min_dist,
640 POINT*& nearest_pnt) const
641{
642 const std::size_t grid_idx(coords[0] + coords[1] * _n_steps[0] +
643 coords[2] * _n_steps[0] * _n_steps[1]);
644 std::vector<typename std::add_pointer<
645 typename std::remove_pointer<POINT>::type>::type> const&
646 pnts(_grid_cell_nodes_map[grid_idx]);
647 if (pnts.empty())
648 {
649 return false;
650 }
651
652 const std::size_t n_pnts(pnts.size());
653 sqr_min_dist =
654 (pnts[0]->asEigenVector3d() - pnt.asEigenVector3d()).squaredNorm();
655 nearest_pnt = pnts[0];
656 for (std::size_t i(1); i < n_pnts; i++)
657 {
658 const double sqr_dist(
659 (pnts[i]->asEigenVector3d() - pnt.asEigenVector3d()).squaredNorm());
660 if (sqr_dist < sqr_min_dist)
661 {
662 sqr_min_dist = sqr_dist;
663 nearest_pnt = pnts[i];
664 }
665 }
666 return true;
667}
668
669template <typename POINT>
670template <typename P>
672 P const& pnt, double eps) const
673{
674 std::vector<std::vector<POINT*> const*> vec_pnts(
676
677 double const sqr_eps(eps * eps);
678
679 std::vector<std::size_t> pnts;
680 for (auto vec : vec_pnts)
681 {
682 for (auto const p : *vec)
683 {
684 if ((p->asEigenVector3d() - pnt.asEigenVector3d()).squaredNorm() <=
685 sqr_eps)
686 {
687 pnts.push_back(p->getID());
688 }
689 }
690 }
691
692 return pnts;
693}
694
695} // 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:462
std::array< std::size_t, 3 > getGridCoords(T const &pnt) const
Definition Grid.h:412
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:438
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:636
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:671
void initNumberOfSteps(std::size_t n_per_cell, std::size_t n_pnts, std::array< double, 3 > const &extensions)
Definition Grid.h:559
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