ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Grid3D.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #pragma once
9 
10 // Local
11 #include "CVMath.h"
12 #include "CVMiscTools.h"
13 #include "GenericCloud.h"
14 #include "GenericIndexedMesh.h"
16 #include "GenericTriangle.h"
17 
18 // System
19 #include <stdint.h>
20 
21 #include <cassert>
22 #include <cstdio>
23 #include <functional>
24 #include <vector>
25 
26 namespace cloudViewer {
27 
29 
31 template <class Type>
32 class Grid3D {
33 public:
35  using GridElement = Type;
36 
39  : m_innerSize(0, 0, 0),
40  m_margin(0),
41  m_rowSize(0),
42  m_sliceSize(0),
45  m_marginShift(0) {}
46 
48  inline const Tuple3ui& size() const { return m_innerSize; }
49 
51  inline bool isInitialized() const { return m_totalCellCount != 0; }
52 
54 
56  void clear() {
57  m_innerSize = Tuple3ui(0, 0, 0);
58  m_margin = 0;
59  m_innerCellCount = 0;
60  m_rowSize = 0;
61  m_sliceSize = 0;
62  m_totalCellCount = 0;
63  m_marginShift = 0;
64 
65  m_grid.clear();
66  }
67 
69 
77  bool init(unsigned di,
78  unsigned dj,
79  unsigned dk,
80  unsigned margin,
81  GridElement defaultCellValue = 0) {
82  m_innerSize = Tuple3ui(di, dj, dk);
83  m_margin = static_cast<int64_t>(margin);
85  (static_cast<uint64_t>(m_innerSize.x) * m_innerSize.y) *
86  m_innerSize.z;
87  m_rowSize = (m_innerSize.x + 2 * m_margin);
89  m_totalCellCount = static_cast<uint64_t>(
90  (m_innerSize.z + 2 * m_margin) * m_sliceSize);
92 
93  if (m_totalCellCount == 0) {
94  assert(false);
95  return false;
96  }
97 
98  // grid initialization
99  try {
100  m_grid.resize(m_totalCellCount, defaultCellValue);
101  } catch (const std::bad_alloc&) {
102  // not enough memory
103  m_totalCellCount = 0;
104  return false;
105  }
106 
107  return true;
108  }
109 
112  const CCVector3& gridMinCorner,
113  PointCoordinateType cellSize) const {
114  assert(cellSize > 0);
115 
116  // DGM: if we admit that cellLength > 0, then the 'floor' operator is
117  // useless (int cast = truncation)
118  Tuple3i cellPos(
119  static_cast<int>(/*floor*/ (P.x - gridMinCorner.x) / cellSize),
120  static_cast<int>(/*floor*/ (P.y - gridMinCorner.y) / cellSize),
121  static_cast<int>(/*floor*/ (P.z - gridMinCorner.z) / cellSize));
122 
123  return cellPos;
124  }
125 
126  // Internal structure used by 'intersectWith'
127  struct CellToTest {
131  int cellSize;
132  };
133 
136  PointCoordinateType cellLength,
137  const CCVector3& gridMinCorner,
138  GridElement intersectValue = 0,
139  GenericProgressCallback* progressCb = nullptr) {
140  auto setIntersectValue = [&](const Tuple3i& cellPos,
141  unsigned triIndex) {
142  this->setValue(cellPos, intersectValue);
143  };
144 
145  return intersectWith(mesh, cellLength, gridMinCorner, setIntersectValue,
146  progressCb);
147  }
148 
152  std::function<void(const Tuple3i&, unsigned)>;
153 
156  PointCoordinateType cellLength,
157  const CCVector3& gridMinCorner,
159  GenericProgressCallback* progressCb = nullptr) {
160  if (!mesh || !isInitialized()) {
161  assert(false);
162  return false;
163  }
164 
165  // cell dimension
166  CCVector3 halfCellDimensions(cellLength / 2, cellLength / 2,
167  cellLength / 2);
168 
169  std::vector<CellToTest> cellsToTest(1); // initial size must be > 0
170  unsigned cellsToTestCount = 0;
171 
172  // number of triangles
173  unsigned numberOfTriangles = mesh->size();
174 
175  // progress notification
176  NormalizedProgress nProgress(progressCb, numberOfTriangles);
177  if (progressCb) {
178  if (progressCb->textCanBeEdited()) {
179  char buffer[32];
180  snprintf(buffer, 32, "Triangles: %u", numberOfTriangles);
181  progressCb->setInfo(buffer);
182  progressCb->setMethodTitle("Intersect Grid/Mesh");
183  }
184  progressCb->update(0);
185  progressCb->start();
186  }
187 
188  // for each triangle: look for intersecting cells
189  mesh->placeIteratorAtBeginning();
190  for (unsigned n = 0; n < numberOfTriangles; ++n) {
191  // get the positions (in the grid) of each vertex
192  const GenericTriangle* T = mesh->_getNextTriangle();
193 
194  // current triangle vertices
195  const CCVector3* triPoints[3]{T->_getA(), T->_getB(), T->_getC()};
196 
197  CCVector3 AB = (*triPoints[1]) - (*triPoints[0]);
198  CCVector3 BC = (*triPoints[2]) - (*triPoints[1]);
199  CCVector3 CA = (*triPoints[0]) - (*triPoints[2]);
200 
201  // make sure that the triangle is not degenerate!!!
202  if (GreaterThanSquareEpsilon(AB.norm2()) &&
205  Tuple3i cellPos[3]{computeCellPos(*(triPoints[0]),
206  gridMinCorner, cellLength),
207  computeCellPos(*(triPoints[1]),
208  gridMinCorner, cellLength),
209  computeCellPos(*(triPoints[2]),
210  gridMinCorner, cellLength)};
211 
212  // compute the triangle bounding-box
213  Tuple3i minPos, maxPos;
214  for (int k = 0; k < 3; k++) {
215  minPos.u[k] = std::min(
216  cellPos[0].u[k],
217  std::min(cellPos[1].u[k], cellPos[2].u[k]));
218  maxPos.u[k] = std::max(
219  cellPos[0].u[k],
220  std::max(cellPos[1].u[k], cellPos[2].u[k]));
221  }
222 
223  // first cell
224  assert(cellsToTest.capacity() != 0);
225  cellsToTestCount = 1;
226  CellToTest* _currentCell =
227  &cellsToTest[0 /*cellsToTestCount-1*/];
228 
229  // compute the triangle normal
230  CCVector3 N = AB.cross(BC);
231 
232  // max distance (in terms of number of cells) between the
233  // vertices
234  Tuple3i delta = maxPos - minPos + Tuple3i(1, 1, 1);
235  int maxSize = 0;
236  {
237  maxSize = std::max(delta.x, delta.y);
238  maxSize = std::max(maxSize, delta.z);
239  }
240 
241  // we deduce the smallest bounding cell
242  static const double LOG_2 = log(2.0);
243  _currentCell->cellSize =
244  (1 << (maxSize > 1 ? static_cast<unsigned char>(ceil(
245  log(static_cast<double>(
246  maxSize)) /
247  LOG_2))
248  : 0));
249 
250  if (_currentCell->cellSize > 1) {
251  // center the virtual cell on the triangle
252  for (int k = 0; k < 3; k++) {
253  _currentCell->pos.u[k] = std::max(
254  0, minPos.u[k] + (delta.u[k] -
255  _currentCell->cellSize) /
256  2);
257  }
258  } else {
259  _currentCell->pos = minPos;
260  }
261 
262  // now we can (recursively) find the intersecting cells
263  while (cellsToTestCount != 0) {
264  _currentCell = &cellsToTest[--cellsToTestCount];
265 
266  // new cells may be written over the actual one
267  // so we need to remember its position!
268  Tuple3i currentCellPos = _currentCell->pos;
269 
270  // if we have reached the maximum subdivision level
271  if (_currentCell->cellSize == 1) {
272  // compute the (absolute) cell center
273  AB = gridMinCorner +
274  CCVector3::fromArray(currentCellPos.u) *
275  cellLength +
276  halfCellDimensions;
277 
278  // check that the triangle does intersect the cell (box)
279  if (CCMiscTools::TriBoxOverlap(AB, halfCellDimensions,
280  triPoints)) {
281  if ((currentCellPos.x >= 0 &&
282  currentCellPos.x <
283  static_cast<int>(size().x)) &&
284  (currentCellPos.y >= 0 &&
285  currentCellPos.y <
286  static_cast<int>(size().y)) &&
287  (currentCellPos.z >= 0 &&
288  currentCellPos.z <
289  static_cast<int>(size().z))) {
290  action(currentCellPos, n);
291  }
292  }
293  } else {
294  int halfCellSize = (_currentCell->cellSize >> 1);
295 
296  // compute the position of each neighbor cell relatively
297  // to the triangle (3*3*3 = 27, including the cell
298  // itself)
299  char pointsPosition[27];
300  {
301  char* _pointsPosition = pointsPosition;
302  CCVector3 distanceToMinBorder =
303  gridMinCorner - (*triPoints[0]);
304  for (int i = 0; i < 3; ++i) {
305  AB.x = distanceToMinBorder.x +
306  static_cast<PointCoordinateType>(
307  currentCellPos.x +
308  i * halfCellSize) *
309  cellLength;
310  for (int j = 0; j < 3; ++j) {
311  AB.y = distanceToMinBorder.y +
312  static_cast<PointCoordinateType>(
313  currentCellPos.y +
314  j * halfCellSize) *
315  cellLength;
316  for (int k = 0; k < 3; ++k) {
317  AB.z = distanceToMinBorder.z +
318  static_cast<PointCoordinateType>(
319  currentCellPos.z +
320  k * halfCellSize) *
321  cellLength;
322 
323  // determine on which side the triangle
324  // is
325  *_pointsPosition++ /*pointsPosition[i*9+j*3+k]*/
326  = (AB.dot(N) < 0 ? -1 : 1);
327  }
328  }
329  }
330  }
331 
332  // if necessary we enlarge the queue
333  if (cellsToTestCount + 27 > cellsToTest.capacity()) {
334  try {
335  cellsToTest.resize(
336  std::max(cellsToTest.capacity() + 27,
337  2 * cellsToTest.capacity()));
338  } catch (const std::bad_alloc&) {
339  // out of memory
340  return false;
341  }
342  }
343 
344  // the first new cell will be written over the actual
345  // one
346  CellToTest* _newCell = &cellsToTest[cellsToTestCount];
347  _newCell->cellSize = halfCellSize;
348 
349  // we look at the position of the 8 sub-cells relatively
350  // to the triangle
351  for (int i = 0; i < 2; ++i) {
352  _newCell->pos.x =
353  currentCellPos.x + i * halfCellSize;
354  // quick test to determine if the cube is
355  // potentially intersecting the triangle's bbox
356  if (static_cast<int>(_newCell->pos.x) +
357  halfCellSize >=
358  minPos.x &&
359  static_cast<int>(_newCell->pos.x) <= maxPos.x) {
360  for (int j = 0; j < 2; ++j) {
361  _newCell->pos.y =
362  currentCellPos.y + j * halfCellSize;
363  if (static_cast<int>(_newCell->pos.y) +
364  halfCellSize >=
365  minPos.y &&
366  static_cast<int>(_newCell->pos.y) <=
367  maxPos.y) {
368  for (int k = 0; k < 2; ++k) {
369  _newCell->pos.z = currentCellPos.z +
370  k * halfCellSize;
371  if (static_cast<int>(
372  _newCell->pos.z) +
373  halfCellSize >=
374  minPos.z &&
375  static_cast<int>(
376  _newCell->pos.z) <=
377  maxPos.z) {
378  const char* _pointsPosition =
379  pointsPosition +
380  (i * 9 + j * 3 + k);
381  char sum = _pointsPosition[0] +
382  _pointsPosition[1] +
383  _pointsPosition[3] +
384  _pointsPosition[4] +
385  _pointsPosition[9] +
386  _pointsPosition[10] +
387  _pointsPosition[12] +
388  _pointsPosition[13];
389 
390  // if not all the vertices of
391  // this sub-cube are on the same
392  // side, then the triangle may
393  // intersect the sub-cube
394  if (sum > -8 && sum < 8) {
395  // we make newCell point on
396  // next cell in array
397  cellsToTest
398  [++cellsToTestCount] =
399  *_newCell;
400  _newCell =
401  &cellsToTest
402  [cellsToTestCount];
403  }
404  }
405  }
406  }
407  }
408  }
409  }
410  }
411  }
412  }
413 
414  if (progressCb && !nProgress.oneStep()) {
415  return false;
416  }
417  }
418 
419  return true;
420  }
421 
424  PointCoordinateType cellLength,
425  const CCVector3& gridMinCorner,
426  GridElement intersectValue = 0,
427  GenericProgressCallback* progressCb = nullptr) {
428  if (!cloud || !isInitialized()) {
429  assert(false);
430  return false;
431  }
432 
433  // cell dimension
434  CCVector3 halfCellDimensions(cellLength / 2, cellLength / 2,
435  cellLength / 2);
436 
437  // number of points
438  unsigned numberOfPoints = cloud->size();
439 
440  // progress notification
441  NormalizedProgress nProgress(progressCb, numberOfPoints);
442  if (progressCb) {
443  if (progressCb->textCanBeEdited()) {
444  char buffer[32];
445  snprintf(buffer, 32, "Points: %u", numberOfPoints);
446  progressCb->setInfo(buffer);
447  progressCb->setMethodTitle("Intersect Grid/Cloud");
448  }
449  progressCb->update(0);
450  progressCb->start();
451  }
452 
453  // for each point: look for the intersecting cell
454  cloud->placeIteratorAtBeginning();
455  for (unsigned n = 0; n < numberOfPoints; ++n) {
456  Tuple3i cellPos = computeCellPos(*cloud->getNextPoint(),
457  gridMinCorner, cellLength);
458 
459  if ((cellPos.x >= 0 && cellPos.x < static_cast<int>(size().x)) &&
460  (cellPos.y >= 0 && cellPos.y < static_cast<int>(size().y)) &&
461  (cellPos.z >= 0 && cellPos.z < static_cast<int>(size().z))) {
462  setValue(cellPos, intersectValue);
463  }
464 
465  if (progressCb && !nProgress.oneStep()) {
466  // cancel by user
467  return false;
468  }
469  }
470 
471  return true;
472  }
473 
475 
480  inline void setValue(int i, int j, int k, GridElement value) {
481  m_grid[pos2index(i, j, k)] = value;
482  }
483 
485 
488  inline void setValue(const Tuple3i& cellPos, GridElement value) {
489  m_grid[pos2index(cellPos.x, cellPos.y, cellPos.z)] = value;
490  }
491 
493 
498  inline const GridElement& getValue(int i, int j, int k) const {
499  return m_grid[pos2index(i, j, k)];
500  }
502 
507  inline GridElement& getValue(int i, int j, int k) {
508  return m_grid[pos2index(i, j, k)];
509  }
510 
512 
515  const GridElement& getValue(Tuple3i& cellPos) const {
516  return m_grid[pos2index(cellPos.x, cellPos.y, cellPos.z)];
517  }
519 
523  return m_grid[pos2index(cellPos.x, cellPos.y, cellPos.z)];
524  }
525 
527  inline GridElement* data() { return m_grid.data(); }
529  inline const GridElement* data() const { return m_grid.data(); }
530 
532  inline uint64_t innerCellCount() const { return m_innerCellCount; }
534  inline uint64_t totalCellCount() const { return m_totalCellCount; }
535 
536 protected:
538  inline int64_t pos2index(int i, int j, int k) const {
539  return static_cast<int64_t>(i) + (j * m_rowSize) + (k * m_sliceSize) +
541  }
542 
544  std::vector<GridElement> m_grid;
545 
549  int64_t m_margin;
551  int64_t m_rowSize;
553  int64_t m_sliceSize;
559  int64_t m_marginShift;
560 };
561 
562 } // namespace cloudViewer
Tuple3Tpl< unsigned int > Tuple3ui
Tuple of 3 unsigned int values.
Definition: CVGeom.h:219
Tuple3Tpl< int > Tuple3i
Tuple of 3 int values.
Definition: CVGeom.h:217
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Type dot(const Vector3Tpl &v) const
Dot product.
Definition: CVGeom.h:408
Type norm2() const
Returns vector square norm.
Definition: CVGeom.h:417
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
Definition: CVGeom.h:412
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
static bool TriBoxOverlap(const CCVector3 &boxcenter, const CCVector3 &boxhalfsize, const CCVector3 *triverts[3])
Ovelap test between a 3D box and a triangle.
virtual void placeIteratorAtBeginning()=0
Sets the cloud iterator at the beginning.
virtual const CCVector3 * getNextPoint()=0
Returns the next point (relatively to the global iterator position)
virtual unsigned size() const =0
Returns the number of points.
A generic mesh with index-based vertex access.
virtual unsigned size() const =0
Returns the number of triangles.
virtual GenericTriangle * _getNextTriangle()=0
Returns the next triangle (relatively to the global iterator position)
virtual void placeIteratorAtBeginning()=0
Places the mesh iterator at the beginning.
A generic triangle interface.
virtual const CCVector3 * _getA() const =0
Returns the first vertex (A)
virtual const CCVector3 * _getC() const =0
Returns the third vertex (C)
virtual const CCVector3 * _getB() const =0
Returns the second vertex (B)
Simple 3D grid structure.
Definition: Grid3D.h:32
uint64_t totalCellCount() const
Returns the total number of cell count (with margin)
Definition: Grid3D.h:534
int64_t pos2index(int i, int j, int k) const
Converts a 3D position to an absolute index.
Definition: Grid3D.h:538
const GridElement * data() const
Gives access to the internal grid data (with margin) (const version)
Definition: Grid3D.h:529
int64_t m_rowSize
1D row size (with margin)
Definition: Grid3D.h:551
Tuple3ui m_innerSize
Dimensions of the grid (without margin)
Definition: Grid3D.h:547
bool intersectWith(GenericIndexedMesh *mesh, PointCoordinateType cellLength, const CCVector3 &gridMinCorner, GridElement intersectValue=0, GenericProgressCallback *progressCb=nullptr)
Intersects this grid with a mesh.
Definition: Grid3D.h:135
std::function< void(const Tuple3i &, unsigned)> genericCellTriIntersectionAction
Definition: Grid3D.h:152
const GridElement & getValue(int i, int j, int k) const
Returns the value of a given cell (const version)
Definition: Grid3D.h:498
Type GridElement
Cell type.
Definition: Grid3D.h:35
GridElement & getValue(int i, int j, int k)
Returns the value of a given cell.
Definition: Grid3D.h:507
Grid3D()
Default constructor.
Definition: Grid3D.h:38
int64_t m_sliceSize
2D slice size (with margin)
Definition: Grid3D.h:553
bool intersectWith(GenericIndexedMesh *mesh, PointCoordinateType cellLength, const CCVector3 &gridMinCorner, genericCellTriIntersectionAction action, GenericProgressCallback *progressCb=nullptr)
Intersects this grid with a mesh (generic form)
Definition: Grid3D.h:155
GridElement * data()
Gives access to the internal grid data (with margin)
Definition: Grid3D.h:527
std::vector< GridElement > m_grid
Grid data.
Definition: Grid3D.h:544
Tuple3i computeCellPos(const CCVector3 &P, const CCVector3 &gridMinCorner, PointCoordinateType cellSize) const
Computes the (grid) cell position that contains a given point.
Definition: Grid3D.h:111
uint64_t m_totalCellCount
3D grid size with margin
Definition: Grid3D.h:557
void setValue(const Tuple3i &cellPos, GridElement value)
Sets the value of a given cell.
Definition: Grid3D.h:488
bool isInitialized() const
Returns whether the grid has been initialized or not.
Definition: Grid3D.h:51
void setValue(int i, int j, int k, GridElement value)
Sets the value of a given cell.
Definition: Grid3D.h:480
const GridElement & getValue(Tuple3i &cellPos) const
Returns the value of a given cell const version)
Definition: Grid3D.h:515
int64_t m_marginShift
First index of real data (i.e. after marin)
Definition: Grid3D.h:559
GridElement & getValue(Tuple3i &cellPos)
Returns the value of a given cell.
Definition: Grid3D.h:522
uint64_t innerCellCount() const
Returns the number of cell count (whithout margin)
Definition: Grid3D.h:532
void clear()
Clears the grid.
Definition: Grid3D.h:56
const Tuple3ui & size() const
Returns the grid dimensions.
Definition: Grid3D.h:48
bool intersectWith(GenericCloud *cloud, PointCoordinateType cellLength, const CCVector3 &gridMinCorner, GridElement intersectValue=0, GenericProgressCallback *progressCb=nullptr)
Intersects this grid with a cloud.
Definition: Grid3D.h:423
uint64_t m_innerCellCount
3D grid size without margin
Definition: Grid3D.h:555
bool init(unsigned di, unsigned dj, unsigned dk, unsigned margin, GridElement defaultCellValue=0)
Initializes the grid.
Definition: Grid3D.h:77
int64_t m_margin
Margin.
Definition: Grid3D.h:549
bool oneStep()
Increments total progress value of a single unit.
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
Generic file read and write utility for python interface.
bool GreaterThanSquareEpsilon(double x)
Definition: CVMath.h:52
cloudViewer::NormalizedProgress * nProgress
Tuple3i pos
Cell position.
Definition: Grid3D.h:129