ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
DgmOctree.cpp
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 #include <DgmOctree.h>
9 
10 // local
11 #include <CVMiscTools.h>
13 #include <ParallelSort.h>
14 #include <RayAndBox.h>
15 #include <ReferenceCloud.h>
16 #include <ScalarField.h>
17 
18 // system
19 #include <cstdio>
20 #include <set>
21 
22 // DGM: tests in progress
23 // #define COMPUTE_NN_SEARCH_STATISTICS
24 // #define ADAPTATIVE_BINARY_SEARCH
25 
26 #ifdef CV_CORE_LIB_USES_QT_CONCURRENT
27 #ifndef CV_DEBUG
28 // enables multi-threading handling
29 #define ENABLE_MT_OCTREE
30 
31 #include <QThreadPool>
32 #include <QtConcurrentMap>
33 #include <QtCore>
34 #endif
35 #endif
36 
37 using namespace cloudViewer;
38 
39 /**********************************/
40 /* PRE COMPUTED VALUES AND TABLES */
41 /**********************************/
42 
44 static const double LOG_NAT_2 = log(2.0);
45 
50  // we compute all possible values
51  for (unsigned char level = 0; level <= DgmOctree::MAX_OCTREE_LEVEL;
52  ++level) {
53  values[level] =
55  }
56  }
57 
59  unsigned char values[DgmOctree::MAX_OCTREE_LEVEL + 1];
60 };
62 
67 
70  static const int VALUE_COUNT = cloudViewer::DgmOctree::MAX_OCTREE_LENGTH;
71 
74  // we compute all possible values for cell codes
75  //(along a unique dimension, the other ones are just shifted)
76  for (int value = 0; value < VALUE_COUNT; ++value) {
77  int mask = VALUE_COUNT;
79  for (unsigned char k = 0;
81  mask >>= 1;
82  code <<= 3;
83  if (value & mask) {
84  code |= 1;
85  }
86  }
87  values[value] = code;
88  }
89 
90  // we compute all possible masks as well! (all dimensions)
91  // cloudViewer::DgmOctree::CellCode baseMask = (1 << (3 *
92  // cloudViewer::DgmOctree::MAX_OCTREE_LEVEL)); for (int level =
93  // cloudViewer::DgmOctree::MAX_OCTREE_LEVEL; level >= 0; --level)
94  //{
95  // masks[level] = baseMask - 1;
96  // baseMask >>= 3;
97  // }
98  }
99 
101  cloudViewer::DgmOctree::CellCode values[VALUE_COUNT];
102 
104  // cloudViewer::DgmOctree::CellCode
105  // masks[cloudViewer::DgmOctree::MAX_OCTREE_LEVEL + 1];
106 };
108 
109 /**********************************/
110 /* STATIC ACCESSORS */
111 /**********************************/
112 
113 unsigned char DgmOctree::GET_BIT_SHIFT(unsigned char level) {
114  // return (3 * (cloudViewer::DgmOctree::MAX_OCTREE_LEVEL - level));
116 }
117 
118 int DgmOctree::OCTREE_LENGTH(int level) { return (1 << level); }
119 
121  unsigned char level) {
122  assert(cellPos.x >= 0 &&
124  cellPos.y >= 0 &&
126  cellPos.z >= 0 && cellPos.z < MonoDimensionalCellCodes::VALUE_COUNT);
127 
128  const unsigned char dec = MAX_OCTREE_LEVEL - level;
129 
130  return (PRE_COMPUTED_POS_CODES.values[cellPos.x << dec] |
131  (PRE_COMPUTED_POS_CODES.values[cellPos.y << dec] << 1) |
132  (PRE_COMPUTED_POS_CODES.values[cellPos.z << dec] << 2)
133 
134  ) >>
135  GET_BIT_SHIFT(level);
136 }
137 
138 #ifndef OCTREE_CODES_64_BITS
140  unsigned char level) {
141  assert(cellPos.x >= 0 &&
143  cellPos.y >= 0 &&
145  cellPos.z >= 0 && cellPos.z < MonoDimensionalCellCodes::VALUE_COUNT);
146 
147  const unsigned char dec = MAX_OCTREE_LEVEL - level;
148 
149  return (PRE_COMPUTED_POS_CODES.values[cellPos.x << dec] |
150  (PRE_COMPUTED_POS_CODES.values[cellPos.y << dec] << 1) |
151  (PRE_COMPUTED_POS_CODES.values[cellPos.z << dec] << 2)
152 
153  ) >>
154  GET_BIT_SHIFT(level);
155 }
156 #endif
157 
159  return PRE_COMPUTED_POS_CODES.values[pos];
160 }
161 
163 #ifdef ENABLE_MT_OCTREE
164  return true;
165 #else
166  return false;
167 #endif
168 }
169 
170 /**********************************/
171 /* EVERYTHING ELSE! */
172 /**********************************/
173 
175  : m_theAssociatedCloud(cloud),
176  m_numberOfProjectedPoints(0),
177  m_nearestPow2(0) {
178  clear();
179 
180  assert(m_theAssociatedCloud);
181 }
182 
184  // reset internal tables
186 
188  m_nearestPow2 = 0;
190 
191  memset(m_fillIndexes, 0, sizeof(int) * (MAX_OCTREE_LEVEL + 1) * 6);
192  memset(m_cellSize, 0, sizeof(PointCoordinateType) * (MAX_OCTREE_LEVEL + 2));
194 }
195 
197  if (!m_thePointsAndTheirCellCodes.empty()) clear();
198 
200 
201  return genericBuild(progressCb);
202 }
203 
204 int DgmOctree::build(const CCVector3& octreeMin,
205  const CCVector3& octreeMax,
206  const CCVector3* pointsMinFilter /*=0*/,
207  const CCVector3* pointsMaxFilter /*=0*/,
208  GenericProgressCallback* progressCb /*=0*/) {
209  if (!m_thePointsAndTheirCellCodes.empty()) clear();
210 
211  m_dimMin = octreeMin;
212  m_dimMax = octreeMax;
213 
214  // the user can specify boundaries for points different than the octree box!
215  m_pointsMin = (pointsMinFilter ? *pointsMinFilter : m_dimMin);
216  m_pointsMax = (pointsMaxFilter ? *pointsMaxFilter : m_dimMax);
217 
218  return genericBuild(progressCb);
219 }
220 
222  unsigned pointCount =
224  if (pointCount == 0) {
225  // no cloud/point?!
226  return -1;
227  }
228 
229  // allocate memory
230  try {
232  pointCount); // resize + operator[] is faster than reserve +
233  // push_back!
234  } catch (... /*const std::bad_alloc&*/) // out of memory
235  {
236  return -1;
237  }
238 
240  m_nearestPow2 = 0;
241 
242  // update the pre-computed 'cell size per level of subdivision' array
244 
245  // progress notification (optional)
246  if (progressCb) {
247  if (progressCb->textCanBeEdited()) {
248  progressCb->setMethodTitle("Build Octree");
249  char infosBuffer[256];
250  sprintf(infosBuffer, "Projecting %u points\nMax. depth: %i",
251  pointCount, MAX_OCTREE_LEVEL);
252  progressCb->setInfo(infosBuffer);
253  }
254  progressCb->update(0);
255  progressCb->start();
256  }
257  NormalizedProgress nprogress(
258  progressCb, pointCount,
259  90); // first phase: 90% (we keep 10% for sort)
260 
261  // fill indexes table (we'll fill the max. level, then deduce the others
262  // from this one)
263  int* fillIndexesAtMaxLevel = m_fillIndexes + (MAX_OCTREE_LEVEL * 6);
264 
265  // for all points
266  cellsContainer::iterator it = m_thePointsAndTheirCellCodes.begin();
267  for (unsigned i = 0; i < pointCount; i++) {
268  const CCVector3* P = m_theAssociatedCloud->getPoint(i);
269 
270  // does the point falls in the 'accepted points' box?
271  //(potentially different from the octree box - see DgmOctree::build)
272  if ((P->x >= m_pointsMin[0]) && (P->x <= m_pointsMax[0]) &&
273  (P->y >= m_pointsMin[1]) && (P->y <= m_pointsMax[1]) &&
274  (P->z >= m_pointsMin[2]) && (P->z <= m_pointsMax[2])) {
275  // compute the position of the cell that includes this point
276  Tuple3i cellPos;
278 
279  // clipping X
280  if (cellPos.x < 0)
281  cellPos.x = 0;
282  else if (cellPos.x >= MAX_OCTREE_LENGTH)
283  cellPos.x = MAX_OCTREE_LENGTH - 1;
284  // clipping Y
285  if (cellPos.y < 0)
286  cellPos.y = 0;
287  else if (cellPos.y >= MAX_OCTREE_LENGTH)
288  cellPos.y = MAX_OCTREE_LENGTH - 1;
289  // clipping Z
290  if (cellPos.z < 0)
291  cellPos.z = 0;
292  else if (cellPos.z >= MAX_OCTREE_LENGTH)
293  cellPos.z = MAX_OCTREE_LENGTH - 1;
294 
295  it->theIndex = i;
296  it->theCode = GenerateTruncatedCellCode(cellPos, MAX_OCTREE_LEVEL);
297 
299  if (fillIndexesAtMaxLevel[0] > cellPos.x)
300  fillIndexesAtMaxLevel[0] = cellPos.x;
301  else if (fillIndexesAtMaxLevel[3] < cellPos.x)
302  fillIndexesAtMaxLevel[3] = cellPos.x;
303 
304  if (fillIndexesAtMaxLevel[1] > cellPos.y)
305  fillIndexesAtMaxLevel[1] = cellPos.y;
306  else if (fillIndexesAtMaxLevel[4] < cellPos.y)
307  fillIndexesAtMaxLevel[4] = cellPos.y;
308 
309  if (fillIndexesAtMaxLevel[2] > cellPos.z)
310  fillIndexesAtMaxLevel[2] = cellPos.z;
311  else if (fillIndexesAtMaxLevel[5] < cellPos.z)
312  fillIndexesAtMaxLevel[5] = cellPos.z;
313  } else {
314  fillIndexesAtMaxLevel[0] = fillIndexesAtMaxLevel[3] = cellPos.x;
315  fillIndexesAtMaxLevel[1] = fillIndexesAtMaxLevel[4] = cellPos.y;
316  fillIndexesAtMaxLevel[2] = fillIndexesAtMaxLevel[5] = cellPos.z;
317  }
318 
319  ++it;
321  }
322 
323  if (!nprogress.oneStep()) {
326  if (progressCb) {
327  progressCb->stop();
328  }
329  return 0;
330  }
331  }
332 
333  // we deduce the lower levels 'fill indexes' from the highest level
334  {
335  for (int k = MAX_OCTREE_LEVEL - 1; k >= 0; k--) {
336  int* fillIndexes = m_fillIndexes + (k * 6);
337  for (int dim = 0; dim < 6; ++dim) {
338  fillIndexes[dim] = (fillIndexes[dim + 6] >> 1);
339  }
340  }
341  }
342 
343  if (m_numberOfProjectedPoints < pointCount)
345  m_numberOfProjectedPoints); // smaller --> should always be ok
346 
347  if (progressCb && progressCb->textCanBeEdited()) {
348  progressCb->setInfo("Sorting cells...");
349  }
350 
351  // we sort the 'cells' by ascending code order
354 
355  // update the pre-computed 'number of cells per level of subdivision' array
357 
358  // end of process notification
359  if (progressCb) {
360  if (progressCb->textCanBeEdited()) {
361  char buffer[256];
362  if (m_numberOfProjectedPoints == pointCount) {
363  sprintf(buffer,
364  "[Octree::build] Octree successfully built... %u "
365  "points (ok)!",
367  } else {
368  if (m_numberOfProjectedPoints == 0)
369  sprintf(buffer,
370  "[Octree::build] Warning : no point projected in "
371  "the Octree!");
372  else
373  sprintf(buffer,
374  "[Octree::build] Warning: some points have been "
375  "filtered out (%u/%u)",
376  pointCount - m_numberOfProjectedPoints, pointCount);
377  }
378  progressCb->setInfo(buffer);
379  }
380 
381  // DGM: the dialog may close itself once we set the progress to 100%
382  // (hiding the above information!)
383  progressCb->update(100.0f);
384  progressCb->stop();
385  }
386 
387  m_nearestPow2 =
388  (1 << static_cast<int>(
389  log(static_cast<double>(m_numberOfProjectedPoints - 1)) /
390  LOG_NAT_2));
391 
392  return static_cast<int>(m_numberOfProjectedPoints);
393 }
394 
396  if (!m_theAssociatedCloud) return;
397 
401 
403 }
404 
406  // update the cell dimension for each subdivision level
407  m_cellSize[0] = m_dimMax.x - m_dimMin.x;
408 
409  unsigned long long d = 1;
410  for (int k = 1; k <= MAX_OCTREE_LEVEL; k++) {
411  d <<= 1;
412  m_cellSize[k] = m_cellSize[0] / d;
413  }
414 }
415 
417  // level 0 is just the octree bounding-box
418  for (unsigned char i = 0; i <= MAX_OCTREE_LEVEL; ++i) {
420  }
421 }
422 
423 void DgmOctree::computeCellsStatistics(unsigned char level) {
424  assert(level <= MAX_OCTREE_LEVEL);
425 
426  // empty octree case?!
427  if (m_thePointsAndTheirCellCodes.empty()) {
428  // DGM: we make as if there were 1 point to avoid some degenerated
429  // cases!
430  m_cellCount[level] = 1;
431  m_maxCellPopulation[level] = 1;
432  m_averageCellPopulation[level] = 1.0;
433  m_stdDevCellPopulation[level] = 0.0;
434  return;
435  }
436 
437  // level '0' specific case
438  if (level == 0) {
439  m_cellCount[level] = 1;
440  m_maxCellPopulation[level] =
441  static_cast<unsigned>(m_thePointsAndTheirCellCodes.size());
442  m_averageCellPopulation[level] =
443  static_cast<double>(m_thePointsAndTheirCellCodes.size());
444  m_stdDevCellPopulation[level] = 0.0;
445  return;
446  }
447 
448  // binary shift for cell code truncation
449  unsigned char bitDec = GET_BIT_SHIFT(level);
450 
451  // iterator on octree elements
452  cellsContainer::const_iterator p = m_thePointsAndTheirCellCodes.begin();
453 
454  // we init scan with first element
455  CellCode predCode = (p->theCode >> bitDec);
456  unsigned counter = 0;
457  unsigned cellCounter = 0;
458  unsigned maxCellPop = 0;
459  double sum = 0.0, sum2 = 0.0;
460 
461  for (; p != m_thePointsAndTheirCellCodes.end(); ++p) {
462  CellCode currentCode = (p->theCode >> bitDec);
463  if (predCode != currentCode) {
464  sum += static_cast<double>(cellCounter);
465  sum2 += static_cast<double>(cellCounter) *
466  static_cast<double>(cellCounter);
467 
468  if (maxCellPop < cellCounter) maxCellPop = cellCounter;
469 
470  // new cell
471  predCode = currentCode;
472  cellCounter = 0;
473  ++counter;
474  }
475  ++cellCounter;
476  }
477 
478  // don't forget last cell!
479  sum += static_cast<double>(cellCounter);
480  sum2 += static_cast<double>(cellCounter) * static_cast<double>(cellCounter);
481  if (maxCellPop < cellCounter) maxCellPop = cellCounter;
482  ++counter;
483 
484  assert(counter > 0);
485  m_cellCount[level] = counter;
486  m_maxCellPopulation[level] = maxCellPop;
487  m_averageCellPopulation[level] = sum / static_cast<double>(counter);
488  m_stdDevCellPopulation[level] = sqrt(
489  sum2 / static_cast<double>(counter) -
491 }
492 
493 void DgmOctree::getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) const {
494  bbMin = m_dimMin;
495  bbMax = m_dimMax;
496 }
497 
499  unsigned char level,
500  Tuple3i& cellPos,
501  bool isCodeTruncated) const {
502  // binary shift for cell code truncation
503  if (!isCodeTruncated) {
504  code >>= GET_BIT_SHIFT(level);
505  }
506 
507  cellPos = Tuple3i(0, 0, 0);
508 
509  int bitMask = 1;
510  for (unsigned char k = 0; k < level; ++k) {
511  if (code & 4) cellPos.z |= bitMask;
512  if (code & 2) cellPos.y |= bitMask;
513  if (code & 1) cellPos.x |= bitMask;
514 
515  code >>= 3;
516  bitMask <<= 1;
517  }
518 }
519 
521  unsigned char level,
522  CCVector3& cellMin,
523  CCVector3& cellMax,
524  bool isCodeTruncated) const {
525  Tuple3i cellPos;
526  getCellPos(code, level, cellPos, isCodeTruncated);
527 
528  const PointCoordinateType& cs = getCellSize(level);
529 
530  cellMin.x = m_dimMin[0] + cs * cellPos.x;
531  cellMin.y = m_dimMin[1] + cs * cellPos.y;
532  cellMin.z = m_dimMin[2] + cs * cellPos.z;
533 
534  cellMax = cellMin + CCVector3(cs, cs, cs);
535 }
536 
538  unsigned char level,
539  ReferenceCloud* subset,
540  bool isCodeTruncated /*=false*/,
541  bool clearOutputCloud /* = true*/) const {
542  unsigned char bitDec = GET_BIT_SHIFT(level);
543  if (!isCodeTruncated) {
544  cellCode >>= bitDec;
545  }
546 
547  unsigned cellIndex = getCellIndex(cellCode, bitDec);
548  // check that cell exists!
549  if (cellIndex < m_numberOfProjectedPoints) {
550  return getPointsInCellByCellIndex(subset, cellIndex, level,
551  clearOutputCloud);
552  } else if (clearOutputCloud) {
553  subset->clear();
554  }
555 
556  return true;
557 }
558 
559 unsigned DgmOctree::getCellIndex(CellCode truncatedCellCode,
560  unsigned char bitDec) const {
561  // inspired from the algorithm proposed by MATT PULVER (see
562  // http://eigenjoy.com/2011/01/21/worlds-fastest-binary-search/)
563  // DGM: it's not faster, but the code is simpler ;)
564  unsigned i = 0;
565  unsigned b = m_nearestPow2;
566 
567  for (; b; b >>= 1) {
568  unsigned j = i | b;
569  if (j < m_numberOfProjectedPoints) {
570  CellCode middleCode =
571  (m_thePointsAndTheirCellCodes[j].theCode >> bitDec);
572  if (middleCode < truncatedCellCode) {
573  // what we are looking for is on the right
574  i = j;
575  } else if (middleCode == truncatedCellCode) {
576  // we must check that it's the first element equal to input code
577  if (j == 0 || (m_thePointsAndTheirCellCodes[j - 1].theCode >>
578  bitDec) != truncatedCellCode) {
579  // what we are looking for is right here
580  return j;
581  }
582  // otherwise what we are looking for is on the left!
583  }
584  }
585  }
586 
587  return (m_thePointsAndTheirCellCodes[i].theCode >> bitDec) ==
588  truncatedCellCode
589  ? i
591 }
592 
593 // optimized version with profiling
594 #ifdef COMPUTE_NN_SEARCH_STATISTICS
595 static double s_jumps = 0.0;
596 static double s_binarySearchCount = 0.0;
597 #endif
598 
599 #ifdef ADAPTATIVE_BINARY_SEARCH
600 unsigned DgmOctree::getCellIndex(CellCode truncatedCellCode,
601  unsigned char bitDec,
602  unsigned begin,
603  unsigned end) const {
604  assert(truncatedCellCode != INVALID_CELL_CODE);
605  assert(end >= begin);
606  assert(end < m_numberOfProjectedPoints);
607 
608 #ifdef COMPUTE_NN_SEARCH_STATISTICS
609  s_binarySearchCount += 1;
610 #endif
611 
612  // if query cell code is lower than or equal to the first octree cell code,
613  // then it's either the good one or there's no match
614  CellCode beginCode =
615  (m_thePointsAndTheirCellCodes[begin].theCode >> bitDec);
616  if (truncatedCellCode < beginCode)
618  else if (truncatedCellCode == beginCode)
619  return begin;
620 
621  // if query cell code is higher than the last octree cell code, then there's
622  // no match
623  CellCode endCode = (m_thePointsAndTheirCellCodes[end].theCode >> bitDec);
624  if (truncatedCellCode > endCode) return m_numberOfProjectedPoints;
625 
626  while (true) {
627  float centralPoint =
628  0.5f +
629  0.75f * (static_cast<float>(truncatedCellCode - beginCode) /
630  (-0.5f)); // 0.75 = speed coef (empirical)
631  unsigned middle = begin + static_cast<unsigned>(centralPoint *
632  float(end - begin));
633  CellCode middleCode =
634  (m_thePointsAndTheirCellCodes[middle].theCode >> bitDec);
635 
636  if (middleCode < truncatedCellCode) {
637  // no more cell in-between?
638  if (middle == begin) return m_numberOfProjectedPoints;
639 
640  begin = middle;
641  beginCode = middleCode;
642  } else if (middleCode > truncatedCellCode) {
643  // no more cell in-between?
644  if (middle == begin) return m_numberOfProjectedPoints;
645 
646  end = middle;
647  endCode = middleCode;
648  } else {
649  // if the previous point doesn't correspond, then we have just found
650  // the first good one!
651  if ((m_thePointsAndTheirCellCodes[middle - 1].theCode >> bitDec) !=
652  truncatedCellCode)
653  return middle;
654  end = middle;
655  endCode = middleCode;
656  }
657 
658 #ifdef COMPUTE_NN_SEARCH_STATISTICS
659  s_jumps += 1.0;
660 #endif
661  }
662 
663  // we shouldn't get there!
665 }
666 
667 #else
668 
669 unsigned DgmOctree::getCellIndex(CellCode truncatedCellCode,
670  unsigned char bitDec,
671  unsigned begin,
672  unsigned end) const {
673  assert(truncatedCellCode != INVALID_CELL_CODE);
674  assert(end >= begin && end < m_numberOfProjectedPoints);
675 
676 #ifdef COMPUTE_NN_SEARCH_STATISTICS
677  s_binarySearchCount += 1;
678 #endif
679 
680  // inspired from the algorithm proposed by MATT PULVER (see
681  // http://eigenjoy.com/2011/01/21/worlds-fastest-binary-search/)
682  // DGM: it's not faster, but the code is simpler ;)
683  unsigned i = 0;
684  unsigned count = end - begin + 1;
685  unsigned b = (1 << static_cast<int>(log(static_cast<double>(count - 1)) /
686  LOG_NAT_2));
687  for (; b; b >>= 1) {
688  unsigned j = i | b;
689  if (j < count) {
690  CellCode middleCode =
691  (m_thePointsAndTheirCellCodes[begin + j].theCode >> bitDec);
692  if (middleCode < truncatedCellCode) {
693  // what we are looking for is on the right
694  i = j;
695  } else if (middleCode == truncatedCellCode) {
696  // we must check that it's the first element equal to input code
697  if (j == 0 ||
698  (m_thePointsAndTheirCellCodes[begin + j - 1].theCode >>
699  bitDec) != truncatedCellCode) {
700  // what we are looking for is right here
701  return j + begin;
702  }
703  // otheriwse what we are looking for is on the left!
704  }
705  }
706 
707 #ifdef COMPUTE_NN_SEARCH_STATISTICS
708  s_jumps += 1.0;
709 #endif
710  }
711 
712  i += begin;
713 
714  return (m_thePointsAndTheirCellCodes[i].theCode >> bitDec) ==
715  truncatedCellCode
716  ? i
718 }
719 #endif
720 
722  const CCVector3* queryPoint,
723  ReferenceCloud* Yk,
724  unsigned maxNumberOfNeighbors,
725  unsigned char level,
726  double& maxSquareDist,
727  double maxSearchDist /*=0*/,
728  int* finalNeighbourhoodSize /*=nullptr*/) const {
729  assert(queryPoint);
731  nNSS.queryPoint = *queryPoint;
732  nNSS.level = level;
733  nNSS.minNumberOfNeighbors = maxNumberOfNeighbors;
734  bool inbounds = false;
736  nNSS.level, inbounds);
737  nNSS.alreadyVisitedNeighbourhoodSize = inbounds ? 0 : 1;
738 
739  computeCellCenter(nNSS.cellPos, level, nNSS.cellCenter);
740  nNSS.maxSearchSquareDistd =
741  (maxSearchDist > 0 ? maxSearchDist * maxSearchDist : 0);
742 
743  // special case: N=1
744  if (maxNumberOfNeighbors == 1) {
745  maxSquareDist = findTheNearestNeighborStartingFromCell(nNSS);
746 
747  if (finalNeighbourhoodSize) {
748  *finalNeighbourhoodSize = nNSS.alreadyVisitedNeighbourhoodSize;
749  }
750 
751  if (maxSquareDist >= 0) {
753  return 1;
754  } else {
755  return 0;
756  }
757  }
758 
759  // general case: N>1
760  unsigned nnFound = findNearestNeighborsStartingFromCell(nNSS);
761  if (nnFound) {
762  // nnFound can be superior to maxNumberOfNeighbors
763  // so we only keep the 'maxNumberOfNeighbors' firsts
764  nnFound = std::min(nnFound, maxNumberOfNeighbors);
765 
766  for (unsigned j = 0; j < nnFound; ++j)
767  Yk->addPointIndex(nNSS.pointsInNeighbourhood[j].pointIndex);
768 
769  maxSquareDist = nNSS.pointsInNeighbourhood.back().squareDistd;
770  } else {
771  maxSquareDist = -1.0;
772  }
773 
774  if (finalNeighbourhoodSize) {
775  *finalNeighbourhoodSize = nNSS.alreadyVisitedNeighbourhoodSize;
776  }
777 
778  return nnFound;
779 }
780 
782  unsigned char level,
783  int* cellDists) const {
784  const int* fillIndexes = m_fillIndexes + 6 * level;
785 
786  int* _cellDists = cellDists;
787  *_cellDists++ = cellPos.x - fillIndexes[0];
788  *_cellDists++ = fillIndexes[3] - cellPos.x;
789  *_cellDists++ = cellPos.y - fillIndexes[1];
790  *_cellDists++ = fillIndexes[4] - cellPos.y;
791  *_cellDists++ = cellPos.z - fillIndexes[2];
792  *_cellDists++ = fillIndexes[5] - cellPos.z;
793 }
794 
796  unsigned char level,
797  int neighbourhoodLength,
798  int* limits) const {
799  const int* fillIndexes = m_fillIndexes + 6 * level;
800 
801  int* _limits = limits;
802  for (int dim = 0; dim < 3; ++dim) {
803  // min dim.
804  {
805  int a = cellPos.u[dim] - fillIndexes[dim];
806  if (a < -neighbourhoodLength)
807  a = -neighbourhoodLength;
808  else if (a > neighbourhoodLength)
809  a = neighbourhoodLength;
810  *_limits++ = a;
811  }
812 
813  // max dim.
814  {
815  int b = fillIndexes[3 + dim] - cellPos.u[dim];
816  if (b < -neighbourhoodLength)
817  b = -neighbourhoodLength;
818  else if (b > neighbourhoodLength)
819  b = neighbourhoodLength;
820  *_limits++ = b;
821  }
822  }
823 }
824 
826  const Tuple3i& cellPos,
827  cellIndexesContainer& neighborCellsIndexes,
828  int neighbourhoodLength,
829  unsigned char level) const {
830  assert(neighbourhoodLength > 0);
831 
832  // get distance form cell to octree neighbourhood borders
833  int limits[6];
834  getCellDistanceFromBorders(cellPos, level, neighbourhoodLength, limits);
835 
836  // limits are expressed in terms of cells at the CURRENT 'level'!
837  const int& iMin = limits[0];
838  const int& iMax = limits[1];
839  const int& jMin = limits[2];
840  const int& jMax = limits[3];
841  const int& kMin = limits[4];
842  const int& kMax = limits[5];
843 
844  // binary shift for cell code truncation
845  const unsigned char bitDec = GET_BIT_SHIFT(level);
846 
847  for (int i = -iMin; i <= iMax; i++) {
848  bool iBorder =
849  (abs(i) ==
850  neighbourhoodLength); // test: are we on a plane of equation
851  // 'X = +/-neighbourhoodLength'?
852  CellCode c0 = GenerateCellCodeForDim(cellPos.x + i);
853 
854  for (int j = -jMin; j <= jMax; j++) {
855  CellCode c1 = c0 | (GenerateCellCodeForDim(cellPos.y + j) << 1);
856 
857  if (iBorder ||
858  (abs(j) == neighbourhoodLength)) // test: are we already on one
859  // of the X or Y borders?
860  {
861  for (int k = -kMin; k <= kMax; k++) {
862  CellCode c2 =
863  c1 | (GenerateCellCodeForDim(cellPos.z + k) << 2);
864 
865  unsigned index = getCellIndex(c2, bitDec);
866  if (index < m_numberOfProjectedPoints) {
867  neighborCellsIndexes.push_back(index);
868  }
869  }
870 
871  } else // otherwise we are inside the neighbourhood
872  {
873  if (kMin ==
874  neighbourhoodLength) // test: does the plane of equation 'Z
875  // = -neighbourhoodLength' is inside
876  // the octree box?
877  {
878  CellCode c2 = c1 | (GenerateCellCodeForDim(
879  cellPos.z - neighbourhoodLength)
880  << 2);
881 
882  unsigned index = getCellIndex(c2, bitDec);
883  if (index < m_numberOfProjectedPoints) {
884  neighborCellsIndexes.push_back(index);
885  }
886  }
887 
888  if (kMax ==
889  neighbourhoodLength) // test: does the plane of equation 'Z
890  // = +neighbourhoodLength' is inside
891  // the octree box?
892  {
893  CellCode c2 = c1 + (GenerateCellCodeForDim(cellPos.z + kMax)
894  << 2);
895 
896  unsigned index = getCellIndex(c2, bitDec);
897  if (index < m_numberOfProjectedPoints) {
898  neighborCellsIndexes.push_back(index);
899  }
900  }
901  }
902  }
903  }
904 }
905 
908  int neighbourhoodLength,
909  bool getOnlyPointsWithValidScalar /*=false*/) const {
910  assert(neighbourhoodLength >= nNSS.alreadyVisitedNeighbourhoodSize);
911 
912  // get distance form cell to octree neighbourhood borders
913  int limits[6];
914  getCellDistanceFromBorders(nNSS.cellPos, nNSS.level, neighbourhoodLength,
915  limits);
916 
917  // limits are expressed in terms of cells at the CURRENT 'level'!
918  const int& iMin = limits[0];
919  const int& iMax = limits[1];
920  const int& jMin = limits[2];
921  const int& jMax = limits[3];
922  const int& kMin = limits[4];
923  const int& kMax = limits[5];
924 
925  // binary shift for cell code truncation
926  const unsigned char bitDec = GET_BIT_SHIFT(nNSS.level);
927 
928  for (int i = -iMin; i <= iMax; i++) {
929  bool iBorder =
930  (abs(i) ==
931  neighbourhoodLength); // test: are we on a plane of equation
932  // 'X = +/-neighbourhoodLength'?
933  CellCode c0 = GenerateCellCodeForDim(nNSS.cellPos.x + i);
934 
935  for (int j = -jMin; j <= jMax; j++) {
936  CellCode c1 =
937  c0 | (GenerateCellCodeForDim(nNSS.cellPos.y + j) << 1);
938 
939  // if i or j is on the boundary
940  if (iBorder ||
941  (abs(j) == neighbourhoodLength)) // test: are we already on one
942  // of the X or Y borders?
943  {
944  for (int k = -kMin; k <= kMax; k++) {
945  CellCode c2 =
946  c1 |
947  (GenerateCellCodeForDim(nNSS.cellPos.z + k) << 2);
948 
949  unsigned index = getCellIndex(c2, bitDec);
950  if (index < m_numberOfProjectedPoints) {
951  // we increase 'pointsInNeighbourCells' capacity with
952  // average cell size
953  try {
954  nNSS.pointsInNeighbourhood.reserve(
955  nNSS.pointsInNeighbourhood.size() +
956  static_cast<unsigned>(
958  [nNSS.level])));
959  } catch (
960  ... /*const std::bad_alloc&*/) // out of memory
961  {
962  // DGM TODO: Shall we stop? shall we try to go on,
963  // as we are not sure that we will actually need
964  // this much points?
965  assert(false);
966  }
967  for (cellsContainer::const_iterator p =
969  index;
970  (p != m_thePointsAndTheirCellCodes.end()) &&
971  ((p->theCode >> bitDec) == c2);
972  ++p) {
973  if (!getOnlyPointsWithValidScalar ||
976  ->getPointScalarValue(
977  p->theIndex))) {
978  nNSS.pointsInNeighbourhood.emplace_back(
980  ->getPointPersistentPtr(
981  p->theIndex),
982  p->theIndex);
983  }
984  }
985  }
986  }
987 
988  } else // otherwise we are inside the neighbourhood
989  {
990  if (kMin ==
991  neighbourhoodLength) // test: does the plane of equation 'Z
992  // = -neighbourhoodLength' is inside
993  // the octree box?
994  {
995  CellCode c2 =
996  c1 | (GenerateCellCodeForDim(nNSS.cellPos.z -
997  neighbourhoodLength)
998  << 2);
999 
1000  unsigned index = getCellIndex(c2, bitDec);
1001  if (index < m_numberOfProjectedPoints) {
1002  // we increase 'nNSS.pointsInNeighbourhood' capacity
1003  // with average cell size
1004  try {
1005  nNSS.pointsInNeighbourhood.reserve(
1006  nNSS.pointsInNeighbourhood.size() +
1007  static_cast<unsigned>(
1009  [nNSS.level])));
1010  } catch (
1011  ... /*const std::bad_alloc&*/) // out of memory
1012  {
1013  // DGM TODO: Shall we stop? shall we try to go on,
1014  // as we are not sure that we will actually need
1015  // this much points?
1016  assert(false);
1017  }
1018  for (cellsContainer::const_iterator p =
1020  index;
1021  (p != m_thePointsAndTheirCellCodes.end()) &&
1022  ((p->theCode >> bitDec) == c2);
1023  ++p) {
1024  if (!getOnlyPointsWithValidScalar ||
1027  ->getPointScalarValue(
1028  p->theIndex))) {
1029  nNSS.pointsInNeighbourhood.emplace_back(
1031  ->getPointPersistentPtr(
1032  p->theIndex),
1033  p->theIndex);
1034  }
1035  }
1036  }
1037  }
1038 
1039  if (kMax ==
1040  neighbourhoodLength) // test: does the plane of equation 'Z
1041  // = +neighbourhoodLength' is inside
1042  // the octree box? (note that
1043  // neighbourhoodLength > 0)
1044  {
1045  CellCode c2 =
1046  c1 | (GenerateCellCodeForDim(nNSS.cellPos.z +
1047  neighbourhoodLength)
1048  << 2);
1049 
1050  unsigned index = getCellIndex(c2, bitDec);
1051  if (index < m_numberOfProjectedPoints) {
1052  // we increase 'nNSS.pointsInNeighbourhood' capacity
1053  // with average cell size
1054  try {
1055  nNSS.pointsInNeighbourhood.reserve(
1056  nNSS.pointsInNeighbourhood.size() +
1057  static_cast<unsigned>(
1059  [nNSS.level])));
1060  } catch (
1061  ... /*const std::bad_alloc&*/) // out of memory
1062  {
1063  // DGM TODO: Shall we stop? shall we try to go on,
1064  // as we are not sure that we will actually need
1065  // this much points?
1066  assert(false);
1067  }
1068  for (cellsContainer::const_iterator p =
1070  index;
1071  (p != m_thePointsAndTheirCellCodes.end()) &&
1072  ((p->theCode >> bitDec) == c2);
1073  ++p) {
1074  if (!getOnlyPointsWithValidScalar ||
1077  ->getPointScalarValue(
1078  p->theIndex))) {
1079  nNSS.pointsInNeighbourhood.emplace_back(
1081  ->getPointPersistentPtr(
1082  p->theIndex),
1083  p->theIndex);
1084  }
1085  }
1086  }
1087  }
1088  }
1089  }
1090  }
1091 }
1092 
1093 #ifdef TEST_CELLS_FOR_SPHERICAL_NN
1095  NearestNeighboursSphericalSearchStruct& nNSS,
1096  int minNeighbourhoodLength,
1097  int maxNeighbourhoodLength) const {
1098  assert(minNeighbourhoodLength >= nNSS.alreadyVisitedNeighbourhoodSize);
1099 
1100  // binary shift for cell code truncation
1101  unsigned char bitDec = GET_BIT_SHIFT(nNSS.level);
1102  CellDescriptor cellDesc;
1103 
1104  if (minNeighbourhoodLength == 0) // special case
1105  {
1106  // we don't look if the cell is inside the octree as it is generally the
1107  // case
1108  CellCode truncatedCellCode =
1109  GenerateTruncatedCellCode(nNSS.cellPos, nNSS.level);
1110  unsigned index = getCellIndex(truncatedCellCode, bitDec);
1111  if (index < m_numberOfProjectedPoints) {
1112  // add cell descriptor to cells list
1113  cellDesc.center = CCVector3(nNSS.cellCenter);
1114  cellDesc.index = 0;
1115  nNSS.cellsInNeighbourhood.push_back(cellDesc);
1116 
1117  for (cellsContainer::const_iterator p =
1118  m_thePointsAndTheirCellCodes.begin() + index;
1119  (p != m_thePointsAndTheirCellCodes.end()) &&
1120  ((p->theCode >> bitDec) == truncatedCellCode);
1121  ++p) {
1122  PointDescriptor newPoint(
1124  p->theIndex),
1125  p->theIndex);
1126  nNSS.pointsInSphericalNeighbourhood.push_back(newPoint);
1127  }
1128  }
1129  if (maxNeighbourhoodLength == 0) return;
1130  ++minNeighbourhoodLength;
1131  }
1132 
1133  // get distance form cell to octree neighbourhood borders
1134  int limits[6];
1135  if (!getCellDistanceFromBorders(nNSS.cellPos, nNSS.level,
1136  maxNeighbourhoodLength, limits))
1137  return;
1138 
1139  int& iMinAbs = limits[0];
1140  int& iMaxAbs = limits[1];
1141  int& jMinAbs = limits[2];
1142  int& jMaxAbs = limits[3];
1143  int& kMinAbs = limits[4];
1144  int& kMaxAbs = limits[5];
1145 
1146  unsigned old_index = 0;
1147  CellCode old_c2 = 0;
1148  Tuple3i currentCellPos;
1149 
1150  // first part: i in [-maxNL,-minNL] and (j,k) in [-maxNL,maxNL]
1151  if (iMinAbs >= minNeighbourhoodLength) {
1152  for (int v0 = nNSS.cellPos.x - iMinAbs;
1153  v0 <= nNSS.cellPos.x - minNeighbourhoodLength; ++v0) {
1155  currentCellPos.x = v0;
1156  for (int v1 = nNSS.cellPos.y - jMinAbs;
1157  v1 <= nNSS.cellPos.y + jMaxAbs; ++v1) {
1158  CellCode c1 = c0 | (GenerateCellCodeForDim(v1) << 1);
1159  currentCellPos.y = v1;
1160  for (int v2 = nNSS.cellPos.z - kMinAbs;
1161  v2 <= nNSS.cellPos.z + kMaxAbs; ++v2) {
1162  CellCode c2 = c1 | (GenerateCellCodeForDim(v2) << 2);
1163 
1164  // look for corresponding cell
1165  unsigned index =
1166  (old_c2 < c2
1167  ? getCellIndex(
1168  c2, bitDec, old_index,
1170  : getCellIndex(c2, bitDec, 0, old_index));
1171  if (index < m_numberOfProjectedPoints) {
1172  // add cell descriptor to cells list
1173  currentCellPos.z = v2;
1174  computeCellCenter(currentCellPos, nNSS.level,
1175  cellDesc.center);
1176  cellDesc.index =
1177  nNSS.pointsInSphericalNeighbourhood.size();
1178  nNSS.cellsInNeighbourhood.push_back(cellDesc);
1179 
1180  for (cellsContainer::const_iterator p =
1182  index;
1183  (p != m_thePointsAndTheirCellCodes.end()) &&
1184  ((p->theCode >> bitDec) == c2);
1185  ++p) {
1186  PointDescriptor newPoint(
1188  p->theIndex),
1189  p->theIndex);
1190  nNSS.pointsInSphericalNeighbourhood.push_back(
1191  newPoint);
1192  }
1193 
1194  old_index = index;
1195  old_c2 = c2;
1196  }
1197  }
1198  }
1199  }
1200  iMinAbs = minNeighbourhoodLength - 1; // minNeighbourhoodLength>1
1201  }
1202 
1203  // second part: i in [minNL,maxNL] and (j,k) in [-maxNL,maxNL]
1204  if (iMaxAbs >= minNeighbourhoodLength) {
1205  for (int v0 = nNSS.cellPos.x + minNeighbourhoodLength;
1206  v0 <= nNSS.cellPos.x + iMaxAbs; ++v0) {
1208  currentCellPos.x = v0;
1209  for (int v1 = nNSS.cellPos.y - jMinAbs;
1210  v1 <= nNSS.cellPos.y + jMaxAbs; ++v1) {
1211  CellCode c1 = c0 | (GenerateCellCodeForDim(v1) << 1);
1212  currentCellPos.y = v1;
1213  for (int v2 = nNSS.cellPos.z - kMinAbs;
1214  v2 <= nNSS.cellPos.z + kMaxAbs; ++v2) {
1215  CellCode c2 = c1 | (GenerateCellCodeForDim(v2) << 2);
1216 
1217  // look for corresponding cell
1218  unsigned index =
1219  (old_c2 < c2
1220  ? getCellIndex(
1221  c2, bitDec, old_index,
1223  : getCellIndex(c2, bitDec, 0, old_index));
1224  if (index < m_numberOfProjectedPoints) {
1225  // add cell descriptor to cells list
1226  currentCellPos.z = v2;
1227  computeCellCenter(currentCellPos, nNSS.level,
1228  cellDesc.center);
1229  cellDesc.index =
1230  nNSS.pointsInSphericalNeighbourhood.size();
1231  nNSS.cellsInNeighbourhood.push_back(cellDesc);
1232 
1233  for (cellsContainer::const_iterator p =
1235  index;
1236  (p != m_thePointsAndTheirCellCodes.end()) &&
1237  ((p->theCode >> bitDec) == c2);
1238  ++p) {
1239  PointDescriptor newPoint(
1241  p->theIndex),
1242  p->theIndex);
1243  nNSS.pointsInSphericalNeighbourhood.push_back(
1244  newPoint);
1245  }
1246 
1247  old_index = index;
1248  old_c2 = c2;
1249  }
1250  }
1251  }
1252  }
1253  iMaxAbs = minNeighbourhoodLength - 1; // minNeighbourhoodLength>1
1254  }
1255 
1256  // third part: j in [-maxNL,-minNL] and (i,k) in [-maxNL,maxNL]
1257  if (jMinAbs >= minNeighbourhoodLength) {
1258  for (int v1 = nNSS.cellPos.y - jMinAbs;
1259  v1 <= nNSS.cellPos.y - minNeighbourhoodLength; ++v1) {
1260  CellCode c1 = (GenerateCellCodeForDim(v1) << 1);
1261  currentCellPos.y = v1;
1262  for (int v0 = nNSS.cellPos.x - iMinAbs;
1263  v0 <= nNSS.cellPos.x + iMaxAbs; ++v0) {
1264  CellCode c0 = c1 | GenerateCellCodeForDim(v0);
1265  currentCellPos.x = v0;
1266  for (int v2 = nNSS.cellPos.z - kMinAbs;
1267  v2 <= nNSS.cellPos.z + kMaxAbs; ++v2) {
1268  CellCode c2 = c0 | (GenerateCellCodeForDim(v2) << 2);
1269 
1270  // look for corresponding cell
1271  unsigned index =
1272  (old_c2 < c2
1273  ? getCellIndex(
1274  c2, bitDec, old_index,
1276  : getCellIndex(c2, bitDec, 0, old_index));
1277  if (index < m_numberOfProjectedPoints) {
1278  // add cell descriptor to cells list
1279  currentCellPos.z = v2;
1280  computeCellCenter(currentCellPos, nNSS.level,
1281  cellDesc.center);
1282  cellDesc.index =
1283  nNSS.pointsInSphericalNeighbourhood.size();
1284  nNSS.cellsInNeighbourhood.push_back(cellDesc);
1285 
1286  for (cellsContainer::const_iterator p =
1288  index;
1289  (p != m_thePointsAndTheirCellCodes.end()) &&
1290  ((p->theCode >> bitDec) == c2);
1291  ++p) {
1292  PointDescriptor newPoint(
1294  p->theIndex),
1295  p->theIndex);
1296  nNSS.pointsInSphericalNeighbourhood.push_back(
1297  newPoint);
1298  }
1299 
1300  old_index = index;
1301  old_c2 = c2;
1302  }
1303  }
1304  }
1305  }
1306  jMinAbs = minNeighbourhoodLength - 1; // minNeighbourhoodLength>1
1307  }
1308 
1309  // fourth part: j in [minNL,maxNL] and (i,k) in [-maxNL,maxNL]
1310  if (jMaxAbs >= minNeighbourhoodLength) {
1311  for (int v1 = nNSS.cellPos.y + minNeighbourhoodLength;
1312  v1 <= nNSS.cellPos.y + jMaxAbs; ++v1) {
1313  CellCode c1 = (GenerateCellCodeForDim(v1) << 1);
1314  currentCellPos.y = v1;
1315  for (int v0 = nNSS.cellPos.x - iMinAbs;
1316  v0 <= nNSS.cellPos.x + iMaxAbs; ++v0) {
1317  CellCode c0 = c1 | GenerateCellCodeForDim(v0);
1318  currentCellPos.x = v0;
1319  for (int v2 = nNSS.cellPos.z - kMinAbs;
1320  v2 <= nNSS.cellPos.z + kMaxAbs; ++v2) {
1321  CellCode c2 = c0 | (GenerateCellCodeForDim(v2) << 2);
1322 
1323  // look for corresponding cell
1324  unsigned index =
1325  (old_c2 < c2
1326  ? getCellIndex(
1327  c2, bitDec, old_index,
1329  : getCellIndex(c2, bitDec, 0, old_index));
1330  if (index < m_numberOfProjectedPoints) {
1331  // add cell descriptor to cells list
1332  currentCellPos.z = v2;
1333  computeCellCenter(currentCellPos, nNSS.level,
1334  cellDesc.center);
1335  cellDesc.index =
1336  nNSS.pointsInSphericalNeighbourhood.size();
1337  nNSS.cellsInNeighbourhood.push_back(cellDesc);
1338 
1339  for (cellsContainer::const_iterator p =
1341  index;
1342  (p != m_thePointsAndTheirCellCodes.end()) &&
1343  ((p->theCode >> bitDec) == c2);
1344  ++p) {
1345  PointDescriptor newPoint(
1347  p->theIndex),
1348  p->theIndex);
1349  nNSS.pointsInSphericalNeighbourhood.push_back(
1350  newPoint);
1351  }
1352 
1353  old_index = index;
1354  old_c2 = c2;
1355  }
1356  }
1357  }
1358  }
1359  jMaxAbs = minNeighbourhoodLength - 1; // minNeighbourhoodLength>1
1360  }
1361 
1362  // fifth part: k in [-maxNL,-minNL] and (i,k) in [-maxNL,maxNL]
1363  if (kMinAbs >= minNeighbourhoodLength) {
1364  for (int v2 = nNSS.cellPos.z - kMinAbs;
1365  v2 <= nNSS.cellPos.z - minNeighbourhoodLength; ++v2) {
1366  CellCode c2 = (GenerateCellCodeForDim(v2) << 2);
1367  currentCellPos.z = v2;
1368  for (int v0 = nNSS.cellPos.x - iMinAbs;
1369  v0 <= nNSS.cellPos.x + iMaxAbs; ++v0) {
1370  CellCode c0 = c2 | GenerateCellCodeForDim(v0);
1371  currentCellPos.x = v0;
1372  for (int v1 = nNSS.cellPos.y - jMinAbs;
1373  v1 <= nNSS.cellPos.y + jMaxAbs; ++v1) {
1374  CellCode c1 = c0 | (GenerateCellCodeForDim(v1) << 1);
1375  // look for corresponding cell
1376  unsigned index =
1377  (old_c2 < c1
1378  ? getCellIndex(
1379  c1, bitDec, old_index,
1381  : getCellIndex(c1, bitDec, 0, old_index));
1382  if (index < m_numberOfProjectedPoints) {
1383  // add cell descriptor to cells list
1384  currentCellPos.y = v1;
1385  computeCellCenter(currentCellPos, nNSS.level,
1386  cellDesc.center);
1387  cellDesc.index =
1388  nNSS.pointsInSphericalNeighbourhood.size();
1389  nNSS.cellsInNeighbourhood.push_back(cellDesc);
1390 
1391  for (cellsContainer::const_iterator p =
1393  index;
1394  (p != m_thePointsAndTheirCellCodes.end()) &&
1395  ((p->theCode >> bitDec) == c1);
1396  ++p) {
1397  PointDescriptor newPoint(
1399  p->theIndex),
1400  p->theIndex);
1401  nNSS.pointsInSphericalNeighbourhood.push_back(
1402  newPoint);
1403  }
1404 
1405  old_index = index;
1406  old_c2 = c1;
1407  }
1408  }
1409  }
1410  }
1411  // kMinAbs = minNeighbourhoodLength-1; //minNeighbourhoodLength>1
1412  }
1413 
1414  // sixth and last part: k in [minNL,maxNL] and (i,k) in [-maxNL,maxNL]
1415  if (kMaxAbs >= minNeighbourhoodLength) {
1416  for (int v2 = nNSS.cellPos.z + minNeighbourhoodLength;
1417  v2 <= nNSS.cellPos.z + kMaxAbs; ++v2) {
1418  CellCode c2 = (GenerateCellCodeForDim(v2) << 2);
1419  currentCellPos.z = v2;
1420  for (int v0 = nNSS.cellPos.x - iMinAbs;
1421  v0 <= nNSS.cellPos.x + iMaxAbs; ++v0) {
1422  CellCode c0 = c2 | GenerateCellCodeForDim(v0);
1423  currentCellPos.x = v0;
1424  for (int v1 = nNSS.cellPos.y - jMinAbs;
1425  v1 <= nNSS.cellPos.y + jMaxAbs; ++v1) {
1426  CellCode c1 = c0 | (GenerateCellCodeForDim(v1) << 1);
1427  // look for corresponding cell
1428  unsigned index =
1429  (old_c2 < c1
1430  ? getCellIndex(
1431  c1, bitDec, old_index,
1433  : getCellIndex(c1, bitDec, 0, old_index));
1434  if (index < m_numberOfProjectedPoints) {
1435  // add cell descriptor to cells list
1436  currentCellPos.y = v1;
1437  computeCellCenter(currentCellPos, nNSS.level,
1438  cellDesc.center);
1439  cellDesc.index =
1440  nNSS.pointsInSphericalNeighbourhood.size();
1441  nNSS.cellsInNeighbourhood.push_back(cellDesc);
1442 
1443  for (cellsContainer::const_iterator p =
1445  index;
1446  (p != m_thePointsAndTheirCellCodes.end()) &&
1447  ((p->theCode >> bitDec) == c1);
1448  ++p) {
1449  PointDescriptor newPoint(
1451  p->theIndex),
1452  p->theIndex);
1453  nNSS.pointsInSphericalNeighbourhood.push_back(
1454  newPoint);
1455  }
1456 
1457  old_index = index;
1458  old_c2 = c1;
1459  }
1460  }
1461  }
1462  }
1463  // kMaxAbs = minNeighbourhoodLength-1; //minNeighbourhoodLength>1
1464  }
1465 }
1466 #endif
1467 
1469  NearestNeighboursSearchStruct& nNSS) const {
1470  // binary shift for cell code truncation
1471  unsigned char bitDec = GET_BIT_SHIFT(nNSS.level);
1472 
1473  // cell size at the current level of subdivision
1474  const PointCoordinateType& cs = getCellSize(nNSS.level);
1475 
1476  // already visited cells (relative distance to the cell that includes the
1477  // query point)
1478  int visitedCellDistance = nNSS.alreadyVisitedNeighbourhoodSize;
1479  // minimum (a priori) relative distance to get eligible points (see
1480  // 'eligibleDist' below)
1481  int eligibleCellDistance = visitedCellDistance;
1482 
1483  // if we have not already looked for the first cell (the one including the
1484  // query point)
1485  if (visitedCellDistance == 0) {
1486  //'visitedCellDistance == 0' means that no cell has ever been processed!
1487  // No cell should be inside 'minimalCellsSetToVisit'
1488  assert(nNSS.minimalCellsSetToVisit.empty());
1489 
1490  // check for existence of an 'including' cell
1491  CellCode truncatedCellCode =
1493  unsigned index = (truncatedCellCode == INVALID_CELL_CODE
1495  : getCellIndex(truncatedCellCode, bitDec));
1496 
1497  visitedCellDistance = 1;
1498 
1499  // it this cell does exist...
1500  if (index < m_numberOfProjectedPoints) {
1501  // we add it to the 'cells to visit' set
1502  nNSS.minimalCellsSetToVisit.push_back(index);
1503  eligibleCellDistance = 1;
1504  }
1505  // otherwise, we may be very far from the nearest octree cell
1506  //(let's try to get there asap)
1507  else {
1508  // fill indexes for current level
1509  const int* _fillIndexes = m_fillIndexes + 6 * nNSS.level;
1510  int diagonalDistance = 0;
1511  for (int dim = 0; dim < 3; ++dim) {
1512  // distance to min border of octree along each axis
1513  int distToBorder = *_fillIndexes - nNSS.cellPos.u[dim];
1514  // if its negative, lets look the other side
1515  if (distToBorder < 0) {
1516  // distance to max border of octree along each axis
1517  distToBorder = nNSS.cellPos.u[dim] - _fillIndexes[3];
1518  }
1519 
1520  if (distToBorder > 0) {
1521  visitedCellDistance =
1522  std::max(distToBorder, visitedCellDistance);
1523  diagonalDistance += distToBorder * distToBorder;
1524  }
1525 
1526  // next dimension
1527  ++_fillIndexes;
1528  }
1529 
1530  // the nearest octree cell
1531  diagonalDistance = static_cast<int>(
1532  ceil(sqrt(static_cast<float>(diagonalDistance))));
1533  eligibleCellDistance = std::max(diagonalDistance, 1);
1534 
1535  if (nNSS.maxSearchSquareDistd > 0) {
1536  // Distance to the nearest point
1537  double minDist =
1538  static_cast<double>(eligibleCellDistance - 1) * cs;
1539  // if we are already outside of the search limit, we can quit
1540  if (minDist * minDist > nNSS.maxSearchSquareDistd) {
1541  return -1.0;
1542  }
1543  }
1544  }
1545 
1546  // update
1547  nNSS.alreadyVisitedNeighbourhoodSize = visitedCellDistance;
1548  }
1549 
1550  // for each dimension, we look for the min distance between the query point
1551  // and the cell border. This distance (minDistToBorder) corresponds to the
1552  // maximal radius of a sphere centered on the query point and totally
1553  // included inside the cell
1555  nNSS.queryPoint, cs, nNSS.cellCenter);
1556 
1557  // cells for which we have already computed the distances from their points
1558  // to the query point
1559  unsigned alreadyProcessedCells = 0;
1560 
1561  // Min (squared) distance of neighbours
1562  double minSquareDist = -1.0;
1563 
1564  while (true) {
1565  // if we do have found points but that were too far to be eligible
1566  if (minSquareDist > 0) {
1567  // what would be the correct neighbourhood size to be sure of it?
1568  int newEligibleCellDistance = static_cast<int>(ceil(
1569  (static_cast<PointCoordinateType>(sqrt(minSquareDist)) -
1570  minDistToBorder) /
1571  cs));
1572  eligibleCellDistance =
1573  std::max(newEligibleCellDistance, eligibleCellDistance);
1574  }
1575 
1576  // we get the (new) cells around the current neighbourhood
1577  while (nNSS.alreadyVisitedNeighbourhoodSize <
1578  eligibleCellDistance) // DGM: warning,
1579  // alreadyVisitedNeighbourhoodSize == 1
1580  // means that we have only visited the
1581  // first cell (distance=0)
1582  {
1585  nNSS.level);
1587  }
1588 
1589  // we compute distances for the new points
1590  DgmOctree::cellIndexesContainer::const_iterator q;
1591  for (q = nNSS.minimalCellsSetToVisit.begin() + alreadyProcessedCells;
1592  q != nNSS.minimalCellsSetToVisit.end(); ++q) {
1593  // current cell index (== index of its first point)
1594  unsigned m = *q;
1595 
1596  // we scan the whole cell to see if it contains a closer point
1597  cellsContainer::const_iterator p =
1598  m_thePointsAndTheirCellCodes.begin() + m;
1599  CellCode code = (p->theCode >> bitDec);
1600  while (m < m_numberOfProjectedPoints &&
1601  (p->theCode >> bitDec) == code) {
1602  // square distance to query point
1603  double dist2 = (*m_theAssociatedCloud->getPointPersistentPtr(
1604  p->theIndex) -
1605  nNSS.queryPoint)
1606  .norm2d();
1607  // we keep track of the closest one
1608  if (dist2 < minSquareDist || minSquareDist < 0) {
1609  nNSS.theNearestPointIndex = p->theIndex;
1610  minSquareDist = dist2;
1611  if (dist2 == 0) // no need to process any further
1612  break;
1613  }
1614  ++m;
1615  ++p;
1616  }
1617  }
1618  alreadyProcessedCells =
1619  static_cast<unsigned>(nNSS.minimalCellsSetToVisit.size());
1620 
1621  // equivalent spherical neighbourhood radius (as we are actually looking
1622  // to 'square' neighbourhoods, we must check that the nearest points
1623  // inside such neighbourhoods are indeed near enough to fall inside the
1624  // biggest included sphere). Otherwise we must look further.
1625  double eligibleDist =
1626  static_cast<double>(eligibleCellDistance - 1) * cs +
1627  minDistToBorder;
1628  double squareEligibleDist = eligibleDist * eligibleDist;
1629 
1630  // if we have found an eligible point
1631  if (minSquareDist >= 0 && minSquareDist <= squareEligibleDist) {
1632  if (nNSS.maxSearchSquareDistd <= 0 ||
1633  minSquareDist <= nNSS.maxSearchSquareDistd)
1634  return minSquareDist;
1635  else
1636  return -1.0;
1637  } else {
1638  // no eligible point? Maybe we are already too far?
1639  if (nNSS.maxSearchSquareDistd > 0 &&
1640  squareEligibleDist >= nNSS.maxSearchSquareDistd)
1641  return -1.0;
1642  }
1643 
1644  // default strategy: increase neighbourhood size of +1 (for next step)
1645  ++eligibleCellDistance;
1646  }
1647 
1648  // we should never get here!
1649  assert(false);
1650 
1651  return -1.0;
1652 }
1653 
1654 // search for at least "minNumberOfNeighbors" points around a query point
1657  bool getOnlyPointsWithValidScalar /*=false*/) const {
1658  // binary shift for cell code truncation
1659  unsigned char bitDec = GET_BIT_SHIFT(nNSS.level);
1660 
1661  // cell size at the current level of subdivision
1662  const PointCoordinateType& cs = getCellSize(nNSS.level);
1663 
1664  // already visited cells (relative distance to the cell that includes the
1665  // query point)
1666  int visitedCellDistance = nNSS.alreadyVisitedNeighbourhoodSize;
1667  // minimum (a priori) relative distance to get eligible points (see
1668  // 'eligibleDist' below)
1669  int eligibleCellDistance = visitedCellDistance;
1670 
1671  // shall we look inside the first cell (the one including the query point)?
1672  if (visitedCellDistance == 0) {
1673  // visitedCellDistance == 0 means that no cell has ever been processed!
1674  // No point should be inside 'pointsInNeighbourhood'
1675  assert(nNSS.pointsInNeighbourhood.empty());
1676 
1677  // check for existence of 'including' cell
1678  CellCode truncatedCellCode =
1680  unsigned index = (truncatedCellCode == INVALID_CELL_CODE
1682  : getCellIndex(truncatedCellCode, bitDec));
1683 
1684  visitedCellDistance = 1;
1685 
1686  // it this cell does exist...
1687  if (index < m_numberOfProjectedPoints) {
1688  // we grab the points inside
1689  cellsContainer::const_iterator p =
1690  m_thePointsAndTheirCellCodes.begin() + index;
1691  while (p != m_thePointsAndTheirCellCodes.end() &&
1692  (p->theCode >> bitDec) == truncatedCellCode) {
1693  if (!getOnlyPointsWithValidScalar ||
1696  p->theIndex))) {
1697  nNSS.pointsInNeighbourhood.emplace_back(
1699  p->theIndex),
1700  p->theIndex);
1701  ++p;
1702  }
1703  }
1704 
1705  eligibleCellDistance = 1;
1706  }
1707  // otherwise, we may be very far from the nearest octree cell
1708  //(let's try to get there asap)
1709  else {
1710  // fill indexes for current level
1711  const int* _fillIndexes = m_fillIndexes + 6 * nNSS.level;
1712  int diagonalDistance = 0;
1713  for (int dim = 0; dim < 3; ++dim) {
1714  // distance to min border of octree along each axis
1715  int distToBorder = *_fillIndexes - nNSS.cellPos.u[dim];
1716  // if its negative, lets look the other side
1717  if (distToBorder < 0) {
1718  // distance to max border of octree along each axis
1719  distToBorder = nNSS.cellPos.u[dim] - _fillIndexes[3];
1720  }
1721 
1722  if (distToBorder > 0) {
1723  visitedCellDistance =
1724  std::max(distToBorder, visitedCellDistance);
1725  diagonalDistance += distToBorder * distToBorder;
1726  }
1727 
1728  // next dimension
1729  ++_fillIndexes;
1730  }
1731 
1732  // the nearest octree cell
1733  diagonalDistance = static_cast<int>(
1734  ceil(sqrt(static_cast<float>(diagonalDistance))));
1735  eligibleCellDistance = std::max(diagonalDistance, 1);
1736 
1737  if (nNSS.maxSearchSquareDistd > 0) {
1738  // Distance of the nearest point
1739  double minDist =
1740  static_cast<double>(eligibleCellDistance - 1) * cs;
1741  // if we are already outside of the search limit, we can quit
1742  if (minDist * minDist > nNSS.maxSearchSquareDistd) {
1743  return 0;
1744  }
1745  }
1746  }
1747  }
1748 
1749  // for each dimension, we look for the min distance between the query point
1750  // and the cell border. This distance (minDistToBorder) corresponds to the
1751  // maximal radius of a sphere centered on the query point and totally
1752  // included inside the cell
1754  nNSS.queryPoint, cs, nNSS.cellCenter);
1755 
1756  // eligible points found
1757  unsigned eligiblePoints = 0;
1758 
1759  // points for which we have already computed the distance to the query point
1760  unsigned alreadyProcessedPoints = 0;
1761 
1762  // Min (squared) distance of non eligible points
1763  double minSquareDist = -1.0;
1764 
1765  // while we don't have enough 'nearest neighbours'
1766  while (eligiblePoints < nNSS.minNumberOfNeighbors) {
1767  // if we do have found points but that were too far to be eligible
1768  if (minSquareDist > 0) {
1769  // what would be the correct neighbourhood size to be sure of it?
1770  int newEligibleCellDistance = static_cast<int>(ceil(
1771  (static_cast<PointCoordinateType>(sqrt(minSquareDist)) -
1772  minDistToBorder) /
1773  cs));
1774  eligibleCellDistance =
1775  std::max(newEligibleCellDistance, eligibleCellDistance);
1776  }
1777 
1778  // we get the (new) points lying in the added area
1779  while (visitedCellDistance <
1780  eligibleCellDistance) // DGM: warning, visitedCellDistance == 1
1781  // means that we have only visited the
1782  // first cell (distance=0)
1783  {
1784  getPointsInNeighbourCellsAround(nNSS, visitedCellDistance,
1785  getOnlyPointsWithValidScalar);
1786  ++visitedCellDistance;
1787  }
1788 
1789  // we compute distances for the new points
1790  NeighboursSet::iterator q;
1791  for (q = nNSS.pointsInNeighbourhood.begin() + alreadyProcessedPoints;
1792  q != nNSS.pointsInNeighbourhood.end(); ++q)
1793  q->squareDistd = (*q->point - nNSS.queryPoint).norm2d();
1794  alreadyProcessedPoints =
1795  static_cast<unsigned>(nNSS.pointsInNeighbourhood.size());
1796 
1797  // equivalent spherical neighbourhood radius (as we are actually looking
1798  // to 'square' neighbourhoods, we must check that the nearest points
1799  // inside such neighbourhoods are indeed near enough to fall inside the
1800  // biggest included sphere). Otherwise we must look further.
1801  double eligibleDist =
1802  static_cast<double>(eligibleCellDistance - 1) * cs +
1803  minDistToBorder;
1804  double squareEligibleDist = eligibleDist * eligibleDist;
1805 
1806  // let's test all the previous 'not yet eligible' points and the new
1807  // ones
1808  unsigned j = eligiblePoints;
1809  for (q = nNSS.pointsInNeighbourhood.begin() + eligiblePoints;
1810  q != nNSS.pointsInNeighbourhood.end(); ++q, ++j) {
1811  // if the point is eligible
1812  if (q->squareDistd <= squareEligibleDist) {
1813  if (eligiblePoints < j)
1814  std::swap(nNSS.pointsInNeighbourhood[eligiblePoints],
1815  nNSS.pointsInNeighbourhood[j]);
1816  ++eligiblePoints;
1817  }
1818  // otherwise we track the nearest one
1819  else if (q->squareDistd < minSquareDist || j == eligiblePoints) {
1820  minSquareDist = q->squareDistd;
1821  }
1822  }
1823 
1824  // Maybe we are already too far?
1825  if (nNSS.maxSearchSquareDistd > 0 &&
1826  squareEligibleDist >= nNSS.maxSearchSquareDistd)
1827  break;
1828 
1829  // default strategy: increase neighbourhood size of +1 (for next step)
1830  ++eligibleCellDistance;
1831  }
1832 
1833  // update the neighbourhood size (for next call, if the query point lies in
1834  // the same cell)
1835  nNSS.alreadyVisitedNeighbourhoodSize = visitedCellDistance;
1836 
1837  // we sort the eligible points
1838  std::sort(nNSS.pointsInNeighbourhood.begin(),
1839  nNSS.pointsInNeighbourhood.begin() + eligiblePoints,
1841 
1842  // we return the number of eligible points found
1843  return eligiblePoints;
1844 }
1845 
1847  const CCVector3& sphereCenter,
1848  PointCoordinateType radius,
1849  NeighboursSet& neighbours,
1850  unsigned char level /*=0*/) const {
1851  // cell size
1852  const PointCoordinateType& cs = getCellSize(level);
1853  PointCoordinateType halfCellSize = cs / 2;
1854 
1855  // squared radius
1856  double squareRadius =
1857  static_cast<double>(radius) * static_cast<double>(radius);
1858  // constant value for cell/sphere inclusion test
1859  double maxDiagFactor = squareRadius + (0.75 * cs + SQRT_3 * radius) * cs;
1860 
1861  // we are going to test all the cells that may intersect the sphere
1862  CCVector3 corner = sphereCenter - CCVector3(radius, radius, radius);
1863  Tuple3i cornerPos;
1864  getTheCellPosWhichIncludesThePoint(&corner, cornerPos, level);
1865 
1866  // don't need to look outside the octree limits!
1867  cornerPos.x = std::max<int>(cornerPos.x, 0);
1868  cornerPos.y = std::max<int>(cornerPos.y, 0);
1869  cornerPos.z = std::max<int>(cornerPos.z, 0);
1870 
1871  // corresponding cell limits
1872  CCVector3 boxMin(m_dimMin[0] + cs * cornerPos.x,
1873  m_dimMin[1] + cs * cornerPos.y,
1874  m_dimMin[2] + cs * cornerPos.z);
1875 
1876  // max number of cells for this dimension
1877  int maxCellCount = OCTREE_LENGTH(level);
1878  // binary shift for cell code truncation
1879  unsigned char bitDec = GET_BIT_SHIFT(level);
1880 
1881  CCVector3 cellMin = boxMin;
1882  Tuple3i cellPos(cornerPos.x, 0, 0);
1883  while (cellMin.x < sphereCenter.x + radius && cellPos.x < maxCellCount) {
1884  CCVector3 cellCenter(cellMin.x + halfCellSize, 0, 0);
1885 
1886  cellMin.y = boxMin.y;
1887  cellPos.y = cornerPos.y;
1888  while (cellMin.y < sphereCenter.y + radius &&
1889  cellPos.y < maxCellCount) {
1890  cellCenter.y = cellMin.y + halfCellSize;
1891 
1892  cellMin.z = boxMin.z;
1893  cellPos.z = cornerPos.z;
1894  while (cellMin.z < sphereCenter.z + radius &&
1895  cellPos.z < maxCellCount) {
1896  cellCenter.z = cellMin.z + halfCellSize;
1897  // test this cell
1898  // 1st test: is it close enough to the sphere center?
1899  if ((cellCenter - sphereCenter).norm2d() <=
1900  maxDiagFactor) // otherwise cell is totally outside
1901  {
1902  // 2nd test: does this cell exists?
1903  CellCode truncatedCellCode =
1904  GenerateTruncatedCellCode(cellPos, level);
1905  unsigned cellIndex =
1906  getCellIndex(truncatedCellCode, bitDec);
1907 
1908  // if yes get the corresponding points
1909  if (cellIndex < m_numberOfProjectedPoints) {
1910  // we look for the first index in
1911  // 'm_thePointsAndTheirCellCodes' corresponding to this
1912  // cell
1913  cellsContainer::const_iterator p =
1915  cellIndex;
1916  CellCode searchCode = (p->theCode >> bitDec);
1917 
1918  // while the (partial) cell code matches this cell
1919  for (; (p != m_thePointsAndTheirCellCodes.end()) &&
1920  ((p->theCode >> bitDec) == searchCode);
1921  ++p) {
1922  const CCVector3* P =
1923  m_theAssociatedCloud->getPoint(p->theIndex);
1924  double d2 = (*P - sphereCenter).norm2d();
1925  // we keep the points falling inside the sphere
1926  if (d2 <= squareRadius) {
1927  neighbours.emplace_back(P, p->theIndex, d2);
1928  }
1929  }
1930  }
1931  }
1932 
1933  // next cell
1934  cellMin.z += cs;
1935  ++cellPos.z;
1936  }
1937 
1938  // next cell
1939  cellMin.y += cs;
1940  ++cellPos.y;
1941  }
1942 
1943  // next cell
1944  cellMin.x += cs;
1945  ++cellPos.x;
1946  }
1947 
1948  return static_cast<int>(neighbours.size());
1949 }
1950 
1952  BoxNeighbourhood& params) const {
1953  // cell size
1954  const PointCoordinateType& cs = getCellSize(params.level);
1955 
1956  // we are going to test all the cells that may intersect this box
1957  // first we extract the box... bounding box ;)
1958  CCVector3 minCorner, maxCorner;
1959  if (params.axes) {
1960  // normalize axes (just in case)
1961  params.axes[0].normalize();
1962  params.axes[1].normalize();
1963  params.axes[2].normalize();
1964 
1965  const PointCoordinateType& dx = params.dimensions.x;
1966  const PointCoordinateType& dy = params.dimensions.y;
1967  const PointCoordinateType& dz = params.dimensions.z;
1968 
1969  CCVector3 corners[8] = {CCVector3(-dx / 2, -dy / 2, -dz / 2),
1970  CCVector3(-dx / 2, -dy / 2, dz / 2),
1971  CCVector3(-dx / 2, dy / 2, dz / 2),
1972  CCVector3(-dx / 2, dy / 2, -dz / 2),
1973  CCVector3(dx / 2, -dy / 2, -dz / 2),
1974  CCVector3(dx / 2, -dy / 2, dz / 2),
1975  CCVector3(dx / 2, dy / 2, dz / 2),
1976  CCVector3(dx / 2, dy / 2, -dz / 2)};
1977 
1978  // position of the box vertices in the octree coordinate system
1979  for (unsigned char i = 0; i < 8; ++i) {
1980  corners[i] = corners[i].x * params.axes[0] +
1981  corners[i].y * params.axes[1] +
1982  corners[i].z * params.axes[2];
1983  if (i) {
1984  if (corners[i].x < minCorner.x)
1985  minCorner.x = corners[i].x;
1986  else if (corners[i].x > maxCorner.x)
1987  maxCorner.x = corners[i].x;
1988 
1989  if (corners[i].y < minCorner.y)
1990  minCorner.y = corners[i].y;
1991  else if (corners[i].y > maxCorner.y)
1992  maxCorner.y = corners[i].y;
1993 
1994  if (corners[i].z < minCorner.z)
1995  minCorner.z = corners[i].z;
1996  else if (corners[i].z > maxCorner.z)
1997  maxCorner.z = corners[i].z;
1998  } else {
1999  minCorner = maxCorner = corners[0];
2000  }
2001  }
2002 
2003  // up to now min and max corners where centered on (0,0,0)
2004  minCorner = params.center + minCorner;
2005  maxCorner = params.center + maxCorner;
2006  } else {
2007  minCorner = params.center - params.dimensions / 2;
2008  maxCorner = params.center + params.dimensions / 2;
2009  }
2010 
2011  Tuple3i minCornerPos;
2012  getTheCellPosWhichIncludesThePoint(&minCorner, minCornerPos, params.level);
2013  Tuple3i maxCornerPos;
2014  getTheCellPosWhichIncludesThePoint(&maxCorner, maxCornerPos, params.level);
2015 
2016  const int* minFillIndexes = getMinFillIndexes(params.level);
2017  const int* maxFillIndexes = getMaxFillIndexes(params.level);
2018 
2019  // don't need to look outside the octree limits!
2020  minCornerPos.x = std::max<int>(minCornerPos.x, minFillIndexes[0]);
2021  minCornerPos.y = std::max<int>(minCornerPos.y, minFillIndexes[1]);
2022  minCornerPos.z = std::max<int>(minCornerPos.z, minFillIndexes[2]);
2023 
2024  maxCornerPos.x = std::min<int>(maxCornerPos.x, maxFillIndexes[0]);
2025  maxCornerPos.y = std::min<int>(maxCornerPos.y, maxFillIndexes[1]);
2026  maxCornerPos.z = std::min<int>(maxCornerPos.z, maxFillIndexes[2]);
2027 
2028  // half cell diagonal
2029  CCVector3 boxHalfDimensions = params.dimensions / 2;
2030  CCVector3 maxHalfDist = boxHalfDimensions;
2031  if (params.axes) {
2032  PointCoordinateType halfDiag =
2033  static_cast<PointCoordinateType>(cs * sqrt(3.0) / 2.0);
2034  maxHalfDist += CCVector3(halfDiag, halfDiag, halfDiag);
2035  }
2036 
2037  // binary shift for cell code truncation
2038  unsigned char bitDec = GET_BIT_SHIFT(params.level);
2039 
2040  for (int i = minCornerPos.x; i <= maxCornerPos.x; ++i) {
2041  for (int j = minCornerPos.y; j <= maxCornerPos.y; ++j) {
2042  for (int k = minCornerPos.z; k <= maxCornerPos.z; ++k) {
2043  // additional inclusion test
2044  if (params.axes) {
2045  CCVector3 cellCenter =
2046  m_dimMin +
2047  CCVector3(
2048  static_cast<PointCoordinateType>(i + 0.5),
2049  static_cast<PointCoordinateType>(j + 0.5),
2050  static_cast<PointCoordinateType>(k + 0.5)) *
2051  cs;
2052 
2053  // project the cell center in the box C.S.
2054  CCVector3 Q = cellCenter - params.center;
2055  Q = CCVector3(params.axes[0].dot(Q), params.axes[1].dot(Q),
2056  params.axes[2].dot(Q));
2057 
2058  // rough inclusion test
2059  if (std::abs(Q.x) > maxHalfDist.x ||
2060  std::abs(Q.y) > maxHalfDist.y ||
2061  std::abs(Q.z) > maxHalfDist.z) {
2062  // skip this cell
2063  continue;
2064  }
2065  }
2066 
2067  // test if this cell exists
2068  Tuple3i cellPos(i, j, k);
2069  CellCode truncatedCellCode =
2070  GenerateTruncatedCellCode(cellPos, params.level);
2071  unsigned cellIndex = getCellIndex(truncatedCellCode, bitDec);
2072 
2073  // if yes, we can test the corresponding points
2074  if (cellIndex < m_numberOfProjectedPoints) {
2075  // we look for the first index in
2076  // 'm_thePointsAndTheirCellCodes' corresponding to this cell
2077  cellsContainer::const_iterator p =
2078  m_thePointsAndTheirCellCodes.begin() + cellIndex;
2079  CellCode searchCode = (p->theCode >> bitDec);
2080 
2081  // while the (partial) cell code matches this cell
2082  for (; (p != m_thePointsAndTheirCellCodes.end()) &&
2083  ((p->theCode >> bitDec) == searchCode);
2084  ++p) {
2085  const CCVector3* P =
2086  m_theAssociatedCloud->getPoint(p->theIndex);
2087  CCVector3 Q = *P - params.center;
2088 
2089  if (params.axes) {
2090  // project the point in the box C.S.
2091  Q = CCVector3(params.axes[0].dot(Q),
2092  params.axes[1].dot(Q),
2093  params.axes[2].dot(Q));
2094  }
2095 
2096  // we keep the points that fall inside the box
2097  if (std::abs(Q.x) <= boxHalfDimensions.x &&
2098  std::abs(Q.y) <= boxHalfDimensions.y &&
2099  std::abs(Q.z) <= boxHalfDimensions.z) {
2100  params.neighbours.emplace_back(P, p->theIndex, 0);
2101  }
2102  }
2103  }
2104  }
2105  }
2106  }
2107 
2108  return params.neighbours.size();
2109 }
2110 
2113  // cell size
2114  const PointCoordinateType& cs = getCellSize(params.level);
2115  PointCoordinateType halfCellSize = cs / 2;
2116 
2117  // squared radius
2118  double squareRadius = static_cast<double>(params.radius) *
2119  static_cast<double>(params.radius);
2120  // constant value for cell/sphere inclusion test
2121  double maxDiagFactor =
2122  squareRadius + (0.75 * cs + SQRT_3 * params.radius) * cs;
2123  PointCoordinateType maxLengthFactor =
2124  params.maxHalfLength +
2125  static_cast<PointCoordinateType>(cs * SQRT_3 / 2);
2126  PointCoordinateType minLengthFactor =
2127  params.onlyPositiveDir ? 0 : -maxLengthFactor;
2128 
2129  PointCoordinateType minHalfLength =
2130  params.onlyPositiveDir ? 0 : -params.maxHalfLength;
2131 
2132  // we are going to test all the cells that may intersect this cylinder
2133  // dumb bounding-box estimation: place two spheres at the ends of the
2134  // cylinder
2135  CCVector3 minCorner;
2136  CCVector3 maxCorner;
2137  {
2138  CCVector3 C1 = params.center + params.dir * params.maxHalfLength;
2139  CCVector3 C2 = params.center + params.dir * minHalfLength;
2140  CCVector3 corner1 =
2141  C1 - CCVector3(params.radius, params.radius, params.radius);
2142  CCVector3 corner2 =
2143  C1 + CCVector3(params.radius, params.radius, params.radius);
2144  CCVector3 corner3 =
2145  C2 - CCVector3(params.radius, params.radius, params.radius);
2146  CCVector3 corner4 =
2147  C2 + CCVector3(params.radius, params.radius, params.radius);
2148 
2149  minCorner.x = std::min(std::min(corner1.x, corner2.x),
2150  std::min(corner3.x, corner4.x));
2151  minCorner.y = std::min(std::min(corner1.y, corner2.y),
2152  std::min(corner3.y, corner4.y));
2153  minCorner.z = std::min(std::min(corner1.z, corner2.z),
2154  std::min(corner3.z, corner4.z));
2155 
2156  maxCorner.x = std::max(std::max(corner1.x, corner2.x),
2157  std::max(corner3.x, corner4.x));
2158  maxCorner.y = std::max(std::max(corner1.y, corner2.y),
2159  std::max(corner3.y, corner4.y));
2160  maxCorner.z = std::max(std::max(corner1.z, corner2.z),
2161  std::max(corner3.z, corner4.z));
2162  }
2163 
2164  Tuple3i cornerPos;
2165  getTheCellPosWhichIncludesThePoint(&minCorner, cornerPos, params.level);
2166 
2167  const int* minFillIndexes = getMinFillIndexes(params.level);
2168  const int* maxFillIndexes = getMaxFillIndexes(params.level);
2169 
2170  // don't need to look outside the octree limits!
2171  cornerPos.x = std::max<int>(cornerPos.x, minFillIndexes[0]);
2172  cornerPos.y = std::max<int>(cornerPos.y, minFillIndexes[1]);
2173  cornerPos.z = std::max<int>(cornerPos.z, minFillIndexes[2]);
2174 
2175  // corresponding cell limits
2176  CCVector3 boxMin(
2177  m_dimMin[0] + cs * static_cast<PointCoordinateType>(cornerPos.x),
2178  m_dimMin[1] + cs * static_cast<PointCoordinateType>(cornerPos.y),
2179  m_dimMin[2] + cs * static_cast<PointCoordinateType>(cornerPos.z));
2180 
2181  // binary shift for cell code truncation
2182  unsigned char bitDec = GET_BIT_SHIFT(params.level);
2183 
2184  CCVector3 cellMin = boxMin;
2185  Tuple3i cellPos(cornerPos.x, 0, 0);
2186  while (cellMin.x < maxCorner.x && cellPos.x <= maxFillIndexes[0]) {
2187  CCVector3 cellCenter(cellMin.x + halfCellSize, 0, 0);
2188 
2189  cellMin.y = boxMin.y;
2190  cellPos.y = cornerPos.y;
2191  while (cellMin.y < maxCorner.y && cellPos.y <= maxFillIndexes[1]) {
2192  cellCenter.y = cellMin.y + halfCellSize;
2193 
2194  cellMin.z = boxMin.z;
2195  cellPos.z = cornerPos.z;
2196  while (cellMin.z < maxCorner.z && cellPos.z <= maxFillIndexes[2]) {
2197  cellCenter.z = cellMin.z + halfCellSize;
2198  // test this cell
2199  // 1st test: is it close enough to the cylinder axis?
2200  CCVector3 OC = (cellCenter - params.center);
2201  PointCoordinateType dot = OC.dot(params.dir);
2202  double d2 = (OC - params.dir * dot).norm2d();
2203  if (d2 <= maxDiagFactor && dot <= maxLengthFactor &&
2204  dot >= minLengthFactor) // otherwise cell is totally
2205  // outside
2206  {
2207  // 2nd test: does this cell exists?
2208  CellCode truncatedCellCode =
2209  GenerateTruncatedCellCode(cellPos, params.level);
2210  unsigned cellIndex =
2211  getCellIndex(truncatedCellCode, bitDec);
2212 
2213  // if yes get the corresponding points
2214  if (cellIndex < m_numberOfProjectedPoints) {
2215  // we look for the first index in
2216  // 'm_thePointsAndTheirCellCodes' corresponding to this
2217  // cell
2218  cellsContainer::const_iterator p =
2220  cellIndex;
2221  CellCode searchCode = (p->theCode >> bitDec);
2222 
2223  // while the (partial) cell code matches this cell
2224  for (; (p != m_thePointsAndTheirCellCodes.end()) &&
2225  ((p->theCode >> bitDec) == searchCode);
2226  ++p) {
2227  const CCVector3* P =
2228  m_theAssociatedCloud->getPoint(p->theIndex);
2229 
2230  // we keep the points falling inside the sphere
2231  CCVector3 OP = (*P - params.center);
2232  dot = OP.dot(params.dir);
2233  d2 = (OP - params.dir * dot).norm2d();
2234  if (d2 <= squareRadius && dot >= minHalfLength &&
2235  dot <= params.maxHalfLength) {
2236  params.neighbours.emplace_back(
2237  P, p->theIndex,
2238  dot); // we save the distance
2239  // relatively to the center
2240  // projected on the axis!
2241  }
2242  }
2243  }
2244  }
2245 
2246  // next cell
2247  cellMin.z += cs;
2248  ++cellPos.z;
2249  }
2250 
2251  // next cell
2252  cellMin.y += cs;
2253  ++cellPos.y;
2254  }
2255 
2256  // next cell
2257  cellMin.x += cs;
2258  ++cellPos.x;
2259  }
2260 
2261  return params.neighbours.size();
2262 }
2263 
2266  // cell size
2267  const PointCoordinateType& cs = getCellSize(params.level);
2268  PointCoordinateType halfCellSize = cs / 2;
2269 
2270  // squared radius
2271  double squareRadius = static_cast<double>(params.radius) *
2272  static_cast<double>(params.radius);
2273  // constant value for cell/sphere inclusion test
2274  double maxDiagFactor =
2275  squareRadius + (0.75 * cs + SQRT_3 * params.radius) * cs;
2276  PointCoordinateType maxLengthFactor =
2277  params.maxHalfLength +
2278  static_cast<PointCoordinateType>(cs * SQRT_3 / 2);
2279  PointCoordinateType minLengthFactor =
2280  params.onlyPositiveDir ? 0 : -maxLengthFactor;
2281 
2282  // increase the search cylinder's height
2283  params.currentHalfLength += params.radius;
2284  // no need to chop the max cylinder if the parts are too small!
2285  //(takes also into account any 'overflow' above maxHalfLength ;)
2286  if (params.maxHalfLength - params.currentHalfLength < params.radius / 2)
2287  params.currentHalfLength = params.maxHalfLength;
2288 
2289  PointCoordinateType currentHalfLengthMinus =
2290  params.onlyPositiveDir ? 0 : -params.currentHalfLength;
2291 
2292  // first process potential candidates from the previous pass
2293  {
2294  for (std::size_t k = 0; k < params.potentialCandidates.size();
2295  /*++k*/) {
2296  // potentialCandidates[k].squareDist = 'dot'!
2297  if (params.potentialCandidates[k].squareDistd >=
2298  currentHalfLengthMinus &&
2299  params.potentialCandidates[k].squareDistd <=
2300  params.currentHalfLength) {
2301  params.neighbours.push_back(params.potentialCandidates[k]);
2302  // and remove it from the potential list
2303  std::swap(params.potentialCandidates[k],
2304  params.potentialCandidates.back());
2305  params.potentialCandidates.pop_back();
2306  } else {
2307  ++k;
2308  }
2309  }
2310  }
2311 
2312  // we are going to test all the cells that may intersect this cylinder
2313  // dumb bounding-box estimation: place two spheres at the ends of the
2314  // cylinder
2315  CCVector3 minCorner;
2316  CCVector3 maxCorner;
2317  {
2318  CCVector3 C1 = params.center + params.dir * params.currentHalfLength;
2319  CCVector3 C2 = params.center + params.dir * currentHalfLengthMinus;
2320  CCVector3 corner1 =
2321  C1 - CCVector3(params.radius, params.radius, params.radius);
2322  CCVector3 corner2 =
2323  C1 + CCVector3(params.radius, params.radius, params.radius);
2324  CCVector3 corner3 =
2325  C2 - CCVector3(params.radius, params.radius, params.radius);
2326  CCVector3 corner4 =
2327  C2 + CCVector3(params.radius, params.radius, params.radius);
2328 
2329  minCorner.x = std::min(std::min(corner1.x, corner2.x),
2330  std::min(corner3.x, corner4.x));
2331  minCorner.y = std::min(std::min(corner1.y, corner2.y),
2332  std::min(corner3.y, corner4.y));
2333  minCorner.z = std::min(std::min(corner1.z, corner2.z),
2334  std::min(corner3.z, corner4.z));
2335 
2336  maxCorner.x = std::max(std::max(corner1.x, corner2.x),
2337  std::max(corner3.x, corner4.x));
2338  maxCorner.y = std::max(std::max(corner1.y, corner2.y),
2339  std::max(corner3.y, corner4.y));
2340  maxCorner.z = std::max(std::max(corner1.z, corner2.z),
2341  std::max(corner3.z, corner4.z));
2342  }
2343 
2344  Tuple3i cornerPos;
2345  getTheCellPosWhichIncludesThePoint(&minCorner, cornerPos, params.level);
2346 
2347  const int* minFillIndexes = getMinFillIndexes(params.level);
2348  const int* maxFillIndexes = getMaxFillIndexes(params.level);
2349 
2350  // don't need to look outside the octree limits!
2351  cornerPos.x = std::max<int>(cornerPos.x, minFillIndexes[0]);
2352  cornerPos.y = std::max<int>(cornerPos.y, minFillIndexes[1]);
2353  cornerPos.z = std::max<int>(cornerPos.z, minFillIndexes[2]);
2354 
2355  // corresponding cell limits
2356  CCVector3 boxMin(
2357  m_dimMin[0] + cs * static_cast<PointCoordinateType>(cornerPos.x),
2358  m_dimMin[1] + cs * static_cast<PointCoordinateType>(cornerPos.y),
2359  m_dimMin[2] + cs * static_cast<PointCoordinateType>(cornerPos.z));
2360 
2361  // binary shift for cell code truncation
2362  unsigned char bitDec = GET_BIT_SHIFT(params.level);
2363 
2364  Tuple3i cellPos(cornerPos.x, 0, 0);
2365  CCVector3 cellMin = boxMin;
2366  while (cellMin.x < maxCorner.x && cellPos.x <= maxFillIndexes[0]) {
2367  CCVector3 cellCenter(cellMin.x + halfCellSize, 0, 0);
2368 
2369  cellMin.y = boxMin.y;
2370  cellPos.y = cornerPos.y;
2371  while (cellMin.y < maxCorner.y && cellPos.y <= maxFillIndexes[1]) {
2372  cellCenter.y = cellMin.y + halfCellSize;
2373 
2374  cellMin.z = boxMin.z;
2375  cellPos.z = cornerPos.z;
2376  while (cellMin.z < maxCorner.z && cellPos.z <= maxFillIndexes[2]) {
2377  cellCenter.z = cellMin.z + halfCellSize;
2378 
2379  // don't test already tested cells!
2380  if (cellPos.x < params.prevMinCornerPos.x ||
2381  cellPos.x >= params.prevMaxCornerPos.x ||
2382  cellPos.y < params.prevMinCornerPos.y ||
2383  cellPos.y >= params.prevMaxCornerPos.y ||
2384  cellPos.z < params.prevMinCornerPos.z ||
2385  cellPos.z >= params.prevMaxCornerPos.z) {
2386  // test this cell
2387  // 1st test: is it close enough to the cylinder axis?
2388  CCVector3 OC = (cellCenter - params.center);
2389  PointCoordinateType dot = OC.dot(params.dir);
2390  double d2 = (OC - params.dir * dot).norm2d();
2391  if (d2 <= maxDiagFactor && dot <= maxLengthFactor &&
2392  dot >= minLengthFactor) // otherwise cell is totally
2393  // outside
2394  {
2395  // 2nd test: does this cell exists?
2396  CellCode truncatedCellCode = GenerateTruncatedCellCode(
2397  cellPos, params.level);
2398  unsigned cellIndex =
2399  getCellIndex(truncatedCellCode, bitDec);
2400 
2401  // if yes get the corresponding points
2402  if (cellIndex < m_numberOfProjectedPoints) {
2403  // we look for the first index in
2404  // 'm_thePointsAndTheirCellCodes' corresponding to
2405  // this cell
2406  cellsContainer::const_iterator p =
2408  cellIndex;
2409  CellCode searchCode = (p->theCode >> bitDec);
2410 
2411  // while the (partial) cell code matches this cell
2412  for (; (p != m_thePointsAndTheirCellCodes.end()) &&
2413  ((p->theCode >> bitDec) == searchCode);
2414  ++p) {
2415  const CCVector3* P =
2417  p->theIndex);
2418 
2419  // we keep the points falling inside the sphere
2420  CCVector3 OP = (*P - params.center);
2421  dot = OP.dot(params.dir);
2422  d2 = (OP - params.dir * dot).norm2d();
2423  if (d2 <= squareRadius) {
2424  // potential candidate?
2425  if (dot >= currentHalfLengthMinus &&
2426  dot <= params.currentHalfLength) {
2427  params.neighbours.emplace_back(
2428  P, p->theIndex,
2429  dot); // we save the distance
2430  // relatively to the
2431  // center projected on
2432  // the axis!
2433  } else if (params.currentHalfLength <
2434  params.maxHalfLength) {
2435  // we still keep it in the 'potential
2436  // candidates' list
2437  params.potentialCandidates.emplace_back(
2438  P, p->theIndex,
2439  dot); // we save the distance
2440  // relatively to the
2441  // center projected on
2442  // the axis!
2443  }
2444  }
2445  }
2446  }
2447  }
2448  }
2449 
2450  // next cell
2451  cellMin.z += cs;
2452  ++cellPos.z;
2453  }
2454 
2455  // next cell
2456  cellMin.y += cs;
2457  ++cellPos.y;
2458  }
2459 
2460  // next cell
2461  cellMin.x += cs;
2462  ++cellPos.x;
2463  }
2464 
2465  params.prevMinCornerPos = cornerPos;
2466  params.prevMaxCornerPos = cellPos;
2467 
2468  return params.neighbours.size();
2469 }
2470 
2471 #ifdef COMPUTE_NN_SEARCH_STATISTICS
2472 static double s_skippedPoints = 0.0;
2473 static double s_testedPoints = 0.0;
2474 #endif
2475 
2476 // search for all neighbors inside a sphere
2477 // warning: there may be more points at the end of nNSS.pointsInNeighbourhood
2478 // than the actual nearest neighbors!
2481  double radius,
2482  bool sortValues) const {
2483 #ifdef TEST_CELLS_FOR_SPHERICAL_NN
2484  if (!nNSS.ready) {
2485  // current level cell size
2486  const PointCoordinateType& cs = getCellSize(nNSS.level);
2487 
2488  // we deduce the minimum cell neighbourhood size (integer) that includes
2489  // the search sphere for ANY point in the cell
2490  int minNeighbourhoodSize =
2491  static_cast<int>(ceil(radius / cs + SQRT_3 / 2.0));
2492 
2493  nNSS.cellsInNeighbourhood.reserve(minNeighbourhoodSize *
2494  minNeighbourhoodSize *
2495  minNeighbourhoodSize);
2496 
2497  if (nNSS.alreadyVisitedNeighbourhoodSize == 1 &&
2498  nNSS.cellsInNeighbourhood.empty() &&
2499  !nNSS.pointsInNeighbourhood.empty()) {
2500  // in this case, we assume the points already in
2501  // 'pointsInNeighbourhood' are the 1st cell points
2502  nNSS.cellsInNeighbourhood.push_back(
2503  CellDescriptor(nNSS.cellCenter, 0));
2504  nNSS.pointsInSphericalNeighbourhood = nNSS.pointsInNeighbourhood;
2505  }
2506 
2509  minNeighbourhoodSize);
2510 
2511  if (nNSS.pointsInNeighbourhood.size() <
2512  nNSS.pointsInSphericalNeighbourhood.size())
2513  nNSS.pointsInNeighbourhood.resize(
2514  nNSS.pointsInSphericalNeighbourhood.size());
2515 
2516  // don't forget to update the visited neighbourhood size!
2517  nNSS.alreadyVisitedNeighbourhoodSize = minNeighbourhoodSize + 1;
2518 
2519  nNSS.ready = true;
2520  }
2521 #else
2522  // current level cell size
2523  const PointCoordinateType& cs = getCellSize(nNSS.level);
2524 
2525  // we compute the minimal distance between the query point and all cell
2526  // borders
2527  const PointCoordinateType minDistToBorder = ComputeMinDistanceToCellBorder(
2528  nNSS.queryPoint, cs, nNSS.cellCenter);
2529 
2530  // we deduce the minimum cell neighbourhood size (integer) that includes the
2531  // search sphere
2532  const int minNeighbourhoodSize =
2533  1 +
2534  (radius > minDistToBorder
2535  ? static_cast<int>(ceil((radius - minDistToBorder) / cs))
2536  : 0);
2537 
2538  // if we don't have visited such a neighbourhood...
2539  if (nNSS.alreadyVisitedNeighbourhoodSize < minNeighbourhoodSize) {
2540  //... let's look for the corresponding points
2541  for (int i = nNSS.alreadyVisitedNeighbourhoodSize;
2542  i < minNeighbourhoodSize; ++i)
2544 
2545  // don't forget to update the visited neighbourhood size!
2546  nNSS.alreadyVisitedNeighbourhoodSize = minNeighbourhoodSize;
2547  }
2548 #endif
2549 
2550  // squared distances comparison is faster!
2551  const double squareRadius = radius * radius;
2552  unsigned numberOfEligiblePoints = 0;
2553 
2554 #ifdef TEST_CELLS_FOR_SPHERICAL_NN
2555  // cell limit relatively to sphere tight bounding box
2556  // const PointCoordinateType& half_cs=getCellSize(nNSS.level+1); //half cell
2557  // size at current level CCVector3 limitMin =
2558  // nNSS.queryPoint-CCVector3(radius,radius,radius)-CCVector3(half_cs,half_cs,half_cs);
2559  // CCVector3 limitMax =
2560  // nNSS.queryPoint+CCVector3(radius,radius,radius)+CCVector3(half_cs,half_cs,half_cs);
2561 
2562  // cell by cell scan
2563  for (NeighbourCellsSet::iterator c = nNSS.cellsInNeighbourhood.begin();
2564  c != nNSS.cellsInNeighbourhood.end(); ++c) {
2565  // we check the cell bounding box
2566  /*if (limitMax.x < c->center.x || limitMin.x > c->center.x
2567  || limitMax.y < c->center.y || limitMin.y > c->center.y
2568  || limitMax.z < c->center.z || limitMin.z > c->center.z)
2569  continue; //sphere totally outside the cell
2570  //*/
2571 
2572  // distance between the new cell center and the sphere center
2573  PointCoordinateType d2 = (c->center - nNSS.queryPoint).norm2();
2574 
2575  // is this cell totally out of the sphere?
2576  if (d2 <= nNSS.minOutD2) {
2577  NeighboursSet::iterator p =
2578  nNSS.pointsInSphericalNeighbourhood.begin() + c->index;
2579  unsigned count =
2580  ((c + 1) != nNSS.cellsInNeighbourhood.end()
2581  ? (c + 1)->index
2582  : nNSS.pointsInSphericalNeighbourhood.size()) -
2583  c->index;
2584  if (!sortValues &&
2585  d2 <= nNSS.maxInD2) // totally inside? (+ we can skip distances
2586  // computation)
2587  {
2588  //... we had them to the 'eligible points' part of the container
2589  std::copy(p, p + count,
2590  nNSS.pointsInNeighbourhood.begin() +
2591  numberOfEligiblePoints);
2592  numberOfEligiblePoints += count;
2593 #ifdef COMPUTE_NN_SEARCH_STATISTICS
2594  s_skippedPoints += static_cast<double>(count);
2595 #endif
2596  } else {
2597  for (unsigned j = 0; j < count; ++j, ++p) {
2598  p->squareDist = (*p->point - nNSS.queryPoint).norm2();
2599 #ifdef COMPUTE_NN_SEARCH_STATISTICS
2600  s_testedPoints += 1.0;
2601 #endif
2602  // if the distance is inferior to the sphere radius...
2603  if (p->squareDistd <= squareRadius) {
2604  //... we had it to the 'eligible points' part of the
2605  // container
2606  nNSS.pointsInNeighbourhood[numberOfEligiblePoints++] =
2607  *p;
2608  }
2609  }
2610  }
2611  }
2612  // else cell is totally outside
2613  {
2614 #ifdef COMPUTE_NN_SEARCH_STATISTICS
2615  unsigned count =
2616  ((c + 1) != nNSS.cellsInNeighbourhood.end()
2617  ? (c + 1)->index
2618  : nNSS.pointsInSphericalNeighbourhood.size()) -
2619  c->index;
2620  s_skippedPoints += static_cast<double>(count);
2621 #endif
2622  }
2623  }
2624 
2625 #else // TEST_CELLS_FOR_SPHERICAL_NN
2626 
2627  // point by point scan
2628  std::size_t i = 0;
2629 
2630  for (PointDescriptor& pDescr : nNSS.pointsInNeighbourhood) {
2631  pDescr.squareDistd = (*pDescr.point - nNSS.queryPoint).norm2d();
2632 
2633  // if the distance is inferior to the sphere radius...
2634  if (pDescr.squareDistd <= squareRadius) {
2635  //... we add it to the 'eligible points' part of the container
2636  if (i > numberOfEligiblePoints) {
2638  nNSS.pointsInNeighbourhood[numberOfEligiblePoints]);
2639  }
2640 
2641  ++numberOfEligiblePoints;
2642 
2643 #ifdef COMPUTE_NN_SEARCH_STATISTICS
2644  s_testedPoints += 1.0;
2645 #endif
2646  }
2647 
2648  ++i;
2649  }
2650 
2651 #endif
2652 
2653  // eventually (if requested) we sort the eligible points
2654  if (sortValues && numberOfEligiblePoints > 0) {
2655  std::sort(nNSS.pointsInNeighbourhood.begin(),
2656  nNSS.pointsInNeighbourhood.begin() + numberOfEligiblePoints,
2658  }
2659 
2660  // return the number of eligible points
2661  return numberOfEligiblePoints;
2662 }
2663 
2665  PointCoordinateType radius) const {
2666  static const PointCoordinateType c_neighbourhoodSizeExtractionFactor =
2667  static_cast<PointCoordinateType>(2.5);
2668  PointCoordinateType aim = std::max<PointCoordinateType>(
2669  0, radius / c_neighbourhoodSizeExtractionFactor);
2670 
2671  int level = 1;
2672  PointCoordinateType minValue = getCellSize(1) - aim;
2673  minValue *= minValue;
2674  for (int i = 2; i <= MAX_OCTREE_LEVEL; ++i) {
2675  // we need two points per cell ideally
2676  if (m_averageCellPopulation[i] < 1.5) break;
2677 
2678  // The level with cell size as near as possible to the aim
2679  PointCoordinateType cellSizeDelta = getCellSize(i) - aim;
2680  cellSizeDelta *= cellSizeDelta;
2681 
2682  if (cellSizeDelta < minValue) {
2683  level = i;
2684  minValue = cellSizeDelta;
2685  }
2686  }
2687 
2688  return static_cast<unsigned char>(level);
2689 }
2690 
2692  const DgmOctree* theOtherOctree) const {
2693  unsigned ptsA = getNumberOfProjectedPoints();
2694  unsigned ptsB = theOtherOctree->getNumberOfProjectedPoints();
2695 
2696  unsigned char maxOctreeLevel = MAX_OCTREE_LEVEL;
2697 
2698  if (std::min(ptsA, ptsB) < 16)
2699  maxOctreeLevel =
2700  std::min(maxOctreeLevel,
2701  static_cast<unsigned char>(5)); // very small clouds
2702  else if (std::max(ptsA, ptsB) < 2000000)
2703  maxOctreeLevel = std::min(
2704  maxOctreeLevel,
2705  static_cast<unsigned char>(10)); // average size clouds
2706 
2707  double estimatedTime[MAX_OCTREE_LEVEL]{};
2708  unsigned char bestLevel = 1;
2709 
2710  for (int i = 1; i < maxOctreeLevel; ++i) // warning: i >= 1
2711  {
2712  int diffA = 0;
2713  int diffB = 0;
2714  int cellsA = 0;
2715  int cellsB = 0;
2716 
2717  bool success = diff(i, m_thePointsAndTheirCellCodes,
2718  theOtherOctree->m_thePointsAndTheirCellCodes, diffA,
2719  diffB, cellsA, cellsB);
2720 
2721  if (!success) {
2722  continue;
2723  }
2724 
2725  // we use a linear model for prediction
2726  estimatedTime[i] =
2727  ((static_cast<double>(ptsA) * ptsB) / cellsB) * 0.001 + diffA;
2728 
2729  if (estimatedTime[i] < estimatedTime[bestLevel]) {
2730  bestLevel = i;
2731  }
2732  }
2733 
2734  return bestLevel;
2735 }
2736 
2738  unsigned indicativeNumberOfPointsPerCell) const {
2739  for (unsigned char level = MAX_OCTREE_LEVEL; level > 0; --level) {
2740  if (m_averageCellPopulation[level] >
2741  indicativeNumberOfPointsPerCell) // density can only increase. If
2742  // it's above the target, no need
2743  // to look further
2744  {
2745  // we take the closest match between this level and the previous one
2746  if (level == MAX_OCTREE_LEVEL ||
2747  (m_averageCellPopulation[level] -
2748  indicativeNumberOfPointsPerCell <=
2749  indicativeNumberOfPointsPerCell -
2751  [level +
2752  1])) // by definition
2753  // "m_averageCellPopulation[level + 1]
2754  // <= indicativeNumberOfPointsPerCell"
2755  {
2756  return level;
2757  } else {
2758  return level + 1;
2759  }
2760  }
2761  }
2762 
2763  return 1;
2764 }
2765 
2767  unsigned indicativeNumberOfCells) const {
2768  // we look for the level giviing the number of points per cell as close to
2769  // the query
2770  unsigned char bestLevel = 1;
2771  // number of cells for this level
2772  int n = getCellNumber(bestLevel);
2773  // error relatively to the query
2774  int oldd = abs(n - static_cast<int>(indicativeNumberOfCells));
2775 
2776  n = getCellNumber(bestLevel + 1);
2777  int d = abs(n - static_cast<int>(indicativeNumberOfCells));
2778 
2779  while (d < oldd && bestLevel < MAX_OCTREE_LEVEL) {
2780  ++bestLevel;
2781  oldd = d;
2782  n = getCellNumber(bestLevel + 1);
2783  d = abs(n - static_cast<int>(indicativeNumberOfCells));
2784  }
2785 
2786  return bestLevel;
2787 }
2788 
2789 double DgmOctree::computeMeanOctreeDensity(unsigned char level) const {
2790  return static_cast<double>(m_numberOfProjectedPoints) /
2791  static_cast<double>(getCellNumber(level));
2792 }
2793 
2794 bool DgmOctree::getCellCodesAndIndexes(unsigned char level,
2795  cellsContainer& vec,
2796  bool truncatedCodes /*=false*/) const {
2797  try {
2798  // binary shift for cell code truncation
2799  unsigned char bitDec = GET_BIT_SHIFT(level);
2800 
2801  cellsContainer::const_iterator p = m_thePointsAndTheirCellCodes.begin();
2802 
2803  CellCode predCode =
2804  (p->theCode >> bitDec) +
2805  1; // pred value must be different than the first element's
2806 
2807  for (unsigned i = 0; i < m_numberOfProjectedPoints; ++i, ++p) {
2808  CellCode currentCode = (p->theCode >> bitDec);
2809 
2810  if (predCode != currentCode)
2811  vec.emplace_back(i, truncatedCodes ? currentCode : p->theCode);
2812 
2813  predCode = currentCode;
2814  }
2815  } catch (const std::bad_alloc&) {
2816  // not enough memory
2817  return false;
2818  }
2819  return true;
2820 }
2821 
2822 bool DgmOctree::getCellCodes(unsigned char level,
2823  cellCodesContainer& vec,
2824  bool truncatedCodes /*=false*/) const {
2825  try {
2826  // binary shift for cell code truncation
2827  unsigned char bitDec = GET_BIT_SHIFT(level);
2828 
2829  cellsContainer::const_iterator p = m_thePointsAndTheirCellCodes.begin();
2830 
2831  CellCode predCode =
2832  (p->theCode >> bitDec) +
2833  1; // pred value must be different than the first element's
2834 
2835  for (unsigned i = 0; i < m_numberOfProjectedPoints; ++i, ++p) {
2836  CellCode currentCode = (p->theCode >> bitDec);
2837 
2838  if (predCode != currentCode)
2839  vec.push_back(truncatedCodes ? currentCode : p->theCode);
2840 
2841  predCode = currentCode;
2842  }
2843  } catch (const std::bad_alloc&) {
2844  // not enough memory
2845  return false;
2846  }
2847  return true;
2848 }
2849 
2850 bool DgmOctree::getCellIndexes(unsigned char level,
2851  cellIndexesContainer& vec) const {
2852  try {
2853  vec.resize(m_cellCount[level]);
2854  } catch (const std::bad_alloc&) {
2855  // not enough memory
2856  return false;
2857  }
2858 
2859  // binary shift for cell code truncation
2860  unsigned char bitDec = GET_BIT_SHIFT(level);
2861 
2862  cellsContainer::const_iterator p = m_thePointsAndTheirCellCodes.begin();
2863 
2864  CellCode predCode =
2865  (p->theCode >> bitDec) +
2866  1; // pred value must be different than the first element's
2867 
2868  for (unsigned i = 0, j = 0; i < m_numberOfProjectedPoints; ++i, ++p) {
2869  CellCode currentCode = (p->theCode >> bitDec);
2870 
2871  if (predCode != currentCode) vec[j++] = i;
2872 
2873  predCode = currentCode;
2874  }
2875 
2876  return true;
2877 }
2878 
2880  ReferenceCloud* cloud,
2881  unsigned cellIndex,
2882  unsigned char level,
2883  bool clearOutputCloud /* = true*/) const {
2884  assert(cloud && cloud->getAssociatedCloud() == m_theAssociatedCloud);
2885 
2886  // binary shift for cell code truncation
2887  unsigned char bitDec = GET_BIT_SHIFT(level);
2888 
2889  // we look for the first index in 'm_thePointsAndTheirCellCodes'
2890  // corresponding to this cell
2891  cellsContainer::const_iterator p =
2892  m_thePointsAndTheirCellCodes.begin() + cellIndex;
2893  CellCode searchCode = (p->theCode >> bitDec);
2894 
2895  if (clearOutputCloud) {
2896  cloud->clear();
2897  }
2898 
2899  // while the (partial) cell code matches this cell
2900  while ((p != m_thePointsAndTheirCellCodes.end()) &&
2901  ((p->theCode >> bitDec) == searchCode)) {
2902  if (!cloud->addPointIndex(p->theIndex)) return false;
2903  ++p;
2904  }
2905 
2906  return true;
2907 }
2908 
2910  cellCodesContainer& cellCodes,
2911  unsigned char level,
2912  ReferenceCloud* subset,
2913  bool areCodesTruncated /*=false*/) const {
2914  assert(subset);
2915 
2916  // binary shift for cell code truncation
2917  unsigned char bitDec1 = GET_BIT_SHIFT(level); // shift for this octree
2918  // codes
2919  unsigned char bitDec2 =
2920  (areCodesTruncated ? 0 : bitDec1); // shift for the input codes
2921 
2922  cellsContainer::const_iterator p = m_thePointsAndTheirCellCodes.begin();
2923  CellCode toExtractCode,
2924  currentCode =
2925  (p->theCode >> bitDec1); // pred value must be different
2926  // than the first element's
2927 
2928  subset->clear();
2929 
2930  cellCodesContainer::const_iterator q = cellCodes.begin();
2931  unsigned ind_p = 0;
2932  while (ind_p < m_numberOfProjectedPoints) {
2933  // we skip codes while the searched code is below the current one
2934  while (((toExtractCode = (*q >> bitDec2)) < currentCode) &&
2935  (q != cellCodes.end()))
2936  ++q;
2937 
2938  if (q == cellCodes.end()) break;
2939 
2940  // now we skip current codes to catch the search one!
2941  while ((ind_p < m_numberOfProjectedPoints) &&
2942  (currentCode <= toExtractCode)) {
2943  if (currentCode == toExtractCode)
2944  subset->addPointIndex(p->theIndex);
2945 
2946  ++p;
2947  if (++ind_p < m_numberOfProjectedPoints)
2948  currentCode = p->theCode >> bitDec1;
2949  }
2950  }
2951 
2952  return subset;
2953 }
2954 
2956  const cellCodesContainer& codesB,
2957  cellCodesContainer& diffA,
2958  cellCodesContainer& diffB) const {
2959  if (codesA.empty() && codesB.empty()) return;
2960 
2961  cellCodesContainer::const_iterator pA = codesA.begin();
2962  cellCodesContainer::const_iterator pB = codesB.begin();
2963 
2964  // cell codes should already be sorted!
2965  while (pA != codesA.end() && pB != codesB.end()) {
2966  if (*pA < *pB)
2967  diffA.push_back(*pA++);
2968  else if (*pA > *pB)
2969  diffB.push_back(*pB++);
2970  else {
2971  ++pA;
2972  ++pB;
2973  }
2974  }
2975 
2976  while (pA != codesA.end()) diffA.push_back(*pA++);
2977  while (pB != codesB.end()) diffB.push_back(*pB++);
2978 }
2979 
2980 bool DgmOctree::diff(unsigned char octreeLevel,
2981  const cellsContainer& codesA,
2982  const cellsContainer& codesB,
2983  int& diffA,
2984  int& diffB,
2985  int& cellsA,
2986  int& cellsB) const {
2987  diffA = 0;
2988  diffB = 0;
2989  cellsA = 0;
2990  cellsB = 0;
2991 
2992  if (codesA.empty() && codesB.empty()) {
2993  return false;
2994  }
2995 
2996  cellsContainer::const_iterator pA = codesA.begin();
2997  cellsContainer::const_iterator pB = codesB.begin();
2998 
2999  // binary shift for cell code truncation
3000  unsigned char bitDec = GET_BIT_SHIFT(octreeLevel);
3001 
3002  CellCode predCodeA = pA->theCode >> bitDec;
3003  CellCode predCodeB = pB->theCode >> bitDec;
3004 
3005  CellCode currentCodeA = 0;
3006  CellCode currentCodeB = 0;
3007 
3008  // cell codes should already be sorted!
3009  while ((pA != codesA.end()) && (pB != codesB.end())) {
3010  if (predCodeA < predCodeB) {
3011  ++diffA;
3012  ++cellsA;
3013  while ((pA != codesA.end()) &&
3014  ((currentCodeA = (pA->theCode >> bitDec)) == predCodeA))
3015  ++pA;
3016  predCodeA = currentCodeA;
3017  } else if (predCodeA > predCodeB) {
3018  ++diffB;
3019  ++cellsB;
3020  while ((pB != codesB.end()) &&
3021  ((currentCodeB = (pB->theCode >> bitDec)) == predCodeB))
3022  ++pB;
3023  predCodeB = currentCodeB;
3024  } else {
3025  while ((pA != codesA.end()) &&
3026  ((currentCodeA = (pA->theCode >> bitDec)) == predCodeA))
3027  ++pA;
3028  predCodeA = currentCodeA;
3029  ++cellsA;
3030  while ((pB != codesB.end()) &&
3031  ((currentCodeB = (pB->theCode >> bitDec)) == predCodeB))
3032  ++pB;
3033  predCodeB = currentCodeB;
3034  ++cellsB;
3035  }
3036  }
3037 
3038  while (pA != codesA.end()) {
3039  ++diffA;
3040  ++cellsA;
3041  while ((pA != codesA.end()) &&
3042  ((currentCodeA = (pA->theCode >> bitDec)) == predCodeA))
3043  ++pA;
3044  predCodeA = currentCodeA;
3045  }
3046  while (pB != codesB.end()) {
3047  ++diffB;
3048  ++cellsB;
3049  while ((pB != codesB.end()) &&
3050  ((currentCodeB = (pB->theCode >> bitDec)) == predCodeB))
3051  ++pB;
3052  predCodeB = currentCodeB;
3053  }
3054 
3055  return true;
3056 }
3057 
3058 int DgmOctree::extractCCs(unsigned char level,
3059  bool sixConnexity,
3060  GenericProgressCallback* progressCb) const {
3061  std::vector<CellCode> cellCodes;
3062  getCellCodes(level, cellCodes);
3063  return extractCCs(cellCodes, level, sixConnexity, progressCb);
3064 }
3065 
3067 #ifdef OCTREE_CODES_64_BITS
3068  using IndexType = unsigned long long;
3069 #else
3070  using IndexType = unsigned;
3071 #endif
3072 
3075 
3078 
3080  IndexAndCodeExt() : theIndex(0), theCode(0) {}
3081 
3083 
3087  static bool indexComp(const IndexAndCodeExt& a,
3088  const IndexAndCodeExt& b) throw() {
3089  return a.theIndex < b.theIndex;
3090  }
3091 };
3092 
3094  unsigned char level,
3095  bool sixConnexity,
3096  GenericProgressCallback* progressCb) const {
3097  std::size_t numberOfCells = cellCodes.size();
3098  if (numberOfCells == 0) // no cells!
3099  return -1;
3100 
3101  // filled octree cells
3102  std::vector<IndexAndCodeExt> ccCells;
3103  try {
3104  ccCells.resize(numberOfCells);
3105  } catch (const std::bad_alloc&) {
3106  // not enough memory
3107  return -2;
3108  }
3109 
3110  // we compute the position of each cell (grid coordinates)
3111  Tuple3i indexMin, indexMax;
3112  {
3113  // binary shift for cell code truncation
3114  unsigned char bitDec = GET_BIT_SHIFT(level);
3115 
3116  for (std::size_t i = 0; i < numberOfCells; i++) {
3117  ccCells[i].theCode = (cellCodes[i] >> bitDec);
3118 
3119  Tuple3i cellPos;
3120  getCellPos(ccCells[i].theCode, level, cellPos, true);
3121 
3122  // we look for the actual min and max dimensions of the input cells
3123  // set (which may not be the whole set of octree cells!)
3124  if (i != 0) {
3125  for (unsigned char k = 0; k < 3; k++) {
3126  if (cellPos.u[k] < indexMin.u[k])
3127  indexMin.u[k] = cellPos.u[k];
3128  else if (cellPos.u[k] > indexMax.u[k])
3129  indexMax.u[k] = cellPos.u[k];
3130  }
3131  } else {
3132  indexMin.x = indexMax.x = cellPos.x;
3133  indexMin.y = indexMax.y = cellPos.y;
3134  indexMin.z = indexMax.z = cellPos.z;
3135  }
3136 
3137  // Warning: the cells will have to be sorted inside a slice
3138  // afterwards!
3139  ccCells[i].theIndex =
3140  (static_cast<IndexAndCodeExt::IndexType>(cellPos.x)) +
3141  (static_cast<IndexAndCodeExt::IndexType>(cellPos.y)
3142  << level) +
3143  (static_cast<IndexAndCodeExt::IndexType>(cellPos.z)
3144  << (2 * level));
3145  }
3146  }
3147 
3148  // we deduce the size of the grid that totally includes input cells
3149  Tuple3i gridSize = indexMax - indexMin + Tuple3i(1, 1, 1);
3150 
3151  // we sort the cells
3152  ParallelSort(ccCells.begin(), ccCells.end(),
3153  IndexAndCodeExt::indexComp); // ascending index code order
3154 
3155  const int& di = gridSize.x;
3156  const int& dj = gridSize.y;
3157  const int& step = gridSize.z;
3158 
3159  // relative neighbos positions (either 6 or 26 total - but we only use half
3160  // of it)
3161  unsigned char neighborsInCurrentSlice = 0, neighborsInPrecedingSlice = 0;
3162  int currentSliceNeighborsShifts[4],
3163  precedingSliceNeighborsShifts[9]; // maximum size to simplify
3164  // code...
3165 
3166  if (sixConnexity) // 6-connexity
3167  {
3168  neighborsInCurrentSlice = 2;
3169  currentSliceNeighborsShifts[0] = -(di + 2);
3170  currentSliceNeighborsShifts[1] = -1;
3171 
3172  neighborsInPrecedingSlice = 1;
3173  precedingSliceNeighborsShifts[0] = 0;
3174  } else // 26-connexity
3175  {
3176  neighborsInCurrentSlice = 4;
3177  currentSliceNeighborsShifts[0] = -1 - (di + 2);
3178  currentSliceNeighborsShifts[1] = -(di + 2);
3179  currentSliceNeighborsShifts[2] = 1 - (di + 2);
3180  currentSliceNeighborsShifts[3] = -1;
3181 
3182  neighborsInPrecedingSlice = 9;
3183  precedingSliceNeighborsShifts[0] = -1 - (di + 2);
3184  precedingSliceNeighborsShifts[1] = -(di + 2);
3185  precedingSliceNeighborsShifts[2] = 1 - (di + 2);
3186  precedingSliceNeighborsShifts[3] = -1;
3187  precedingSliceNeighborsShifts[4] = 0;
3188  precedingSliceNeighborsShifts[5] = 1;
3189  precedingSliceNeighborsShifts[6] = -1 + (di + 2);
3190  precedingSliceNeighborsShifts[7] = (di + 2);
3191  precedingSliceNeighborsShifts[8] = 1 + (di + 2);
3192  }
3193 
3194  // shared structures (to avoid repeated allocations)
3195  std::vector<int> neighboursVal, neighboursMin;
3196  try {
3197  neighboursVal.reserve(neighborsInCurrentSlice +
3198  neighborsInPrecedingSlice);
3199  neighboursMin.reserve(neighborsInCurrentSlice +
3200  neighborsInPrecedingSlice);
3201  } catch (const std::bad_alloc&) {
3202  // not enough memory
3203  return -2;
3204  }
3205 
3206  // temporary virtual 'slices'
3207  int sliceSize =
3208  (di + 2) * (dj + 2); // add a margin to avoid "boundary effects"
3209  std::vector<int> slice;
3210  std::vector<int> oldSlice;
3211  // equivalence table between 'on the fly' labels
3212  std::vector<int> equivalentLabels;
3213  std::vector<int> cellIndexToLabel;
3214 
3215  try {
3216  slice.resize(sliceSize);
3217  oldSlice.resize(sliceSize, 0); // previous slice is empty by default
3218  equivalentLabels.resize(numberOfCells + 2, 0);
3219  cellIndexToLabel.resize(numberOfCells, 0);
3220  } catch (const std::bad_alloc&) {
3221  // not enough memory
3222  return -2;
3223  }
3224 
3225  // progress notification
3226  if (progressCb) {
3227  if (progressCb->textCanBeEdited()) {
3228  progressCb->setMethodTitle("Components Labeling");
3229  char buffer[256];
3230  sprintf(buffer, "Box: [%i*%i*%i]", gridSize.x, gridSize.y,
3231  gridSize.z);
3232  progressCb->setInfo(buffer);
3233  }
3234  progressCb->update(0);
3235  progressCb->start();
3236  }
3237 
3238  // current label
3239  std::size_t currentLabel = 1;
3240 
3241  // process each slice
3242  {
3243  unsigned counter = 0;
3244  const IndexAndCodeExt::IndexType gridCoordMask =
3245  (static_cast<IndexAndCodeExt::IndexType>(1) << level) - 1;
3246  std::vector<IndexAndCodeExt>::const_iterator _ccCells = ccCells.begin();
3247  NormalizedProgress nprogress(progressCb, step);
3248 
3249  for (int k = indexMin.z; k < indexMin.z + step; k++) {
3250  // initialize the 'current' slice
3251  std::fill(slice.begin(), slice.end(), 0);
3252 
3253  // for each cell of the slice
3254  while (counter < numberOfCells &&
3255  static_cast<int>(_ccCells->theIndex >> (level << 1)) == k) {
3256  int iind = static_cast<int>(_ccCells->theIndex & gridCoordMask);
3257  int jind = static_cast<int>((_ccCells->theIndex >> level) &
3258  gridCoordMask);
3259  int cellIndex = (iind - indexMin.x + 1) +
3260  (jind - indexMin.y + 1) * (di + 2);
3261  ++_ccCells;
3262 
3263  // we look if the cell has neighbors inside the slice
3264  int* _slice = &(slice[cellIndex]);
3265  {
3266  for (unsigned char n = 0; n < neighborsInCurrentSlice;
3267  n++) {
3268  assert(cellIndex + currentSliceNeighborsShifts[n] <
3269  sliceSize);
3270  const int& neighborLabel =
3271  _slice[currentSliceNeighborsShifts[n]];
3272  if (neighborLabel > 1)
3273  neighboursVal.push_back(neighborLabel);
3274  }
3275  }
3276 
3277  // and in the previous slice
3278  const int* _oldSlice = &(oldSlice[cellIndex]);
3279  {
3280  for (unsigned char n = 0; n < neighborsInPrecedingSlice;
3281  n++) {
3282  assert(cellIndex + precedingSliceNeighborsShifts[n] <
3283  sliceSize);
3284  const int& neighborLabel =
3285  _oldSlice[precedingSliceNeighborsShifts[n]];
3286  if (neighborLabel > 1)
3287  neighboursVal.push_back(neighborLabel);
3288  }
3289  }
3290 
3291  // number of neighbors for current cell
3292  std::size_t p = neighboursVal.size();
3293 
3294  if (p == 0) // no neighbor
3295  {
3296  *_slice = static_cast<int>(
3297  ++currentLabel); // we create a new label
3298  } else if (p == 1) // 1 neighbor
3299  {
3300  *_slice = neighboursVal.back(); // we'll use its label
3301  neighboursVal.pop_back();
3302  } else // more than 1 neighbor?
3303  {
3304  // we get the smallest label
3305  ParallelSort(neighboursVal.begin(), neighboursVal.end());
3306 
3307  int smallestLabel = neighboursVal[0];
3308 
3309  // if they are not the same
3310  if (smallestLabel != neighboursVal.back()) {
3311  int lastLabel = 0;
3312  neighboursMin.clear();
3313  // we get the smallest equivalent label for each
3314  // neighbor's branch
3315  {
3316  for (std::size_t n = 0; n < p; n++) {
3317  // ... we start from its C.C. index
3318  int label = neighboursVal[n];
3319  // if it's not the same as the previous
3320  // neighbor's
3321  if (label != lastLabel) {
3322  // we update the 'before' value
3323  assert(label <
3324  static_cast<int>(numberOfCells) + 2);
3325  lastLabel = label;
3326 
3327  // we look for its real equivalent value
3328  while (equivalentLabels[label] > 1) {
3329  label = equivalentLabels[label];
3330  assert(label <
3331  static_cast<int>(numberOfCells) +
3332  2);
3333  }
3334 
3335  neighboursMin.push_back(label);
3336  }
3337  }
3338  }
3339 
3340  // get the smallest one
3341  ParallelSort(neighboursMin.begin(),
3342  neighboursMin.end());
3343 
3344  smallestLabel = neighboursMin.front();
3345 
3346  // update the equivalence table by the way
3347  // for all other branches
3348  lastLabel = smallestLabel;
3349  {
3350  for (std::size_t n = 1; n < neighboursMin.size();
3351  n++) {
3352  int label = neighboursMin[n];
3353  assert(label <
3354  static_cast<int>(numberOfCells) + 2);
3355  // we don't process it if it's the same label as
3356  // the previous neighbor
3357  if (label != lastLabel) {
3358  equivalentLabels[label] = smallestLabel;
3359  lastLabel = label;
3360  }
3361  }
3362  }
3363  }
3364 
3365  // update current cell label
3366  *_slice = smallestLabel;
3367  neighboursVal.clear();
3368  }
3369 
3370  cellIndexToLabel[counter++] = *_slice;
3371  }
3372 
3373  if (counter == numberOfCells) break;
3374 
3375  std::swap(slice, oldSlice);
3376 
3377  nprogress.oneStep();
3378  }
3379  }
3380 
3381  // release some memory
3382  slice.resize(0);
3383  oldSlice.resize(0);
3384 
3385  if (progressCb) {
3386  progressCb->stop();
3387  }
3388 
3389  if (currentLabel < 2) {
3390  // No component found
3391  return -3;
3392  }
3393 
3394  // path compression (http://en.wikipedia.org/wiki/Union_find)
3395  assert(currentLabel < numberOfCells + 2);
3396  {
3397  for (std::size_t i = 2; i <= currentLabel; i++) {
3398  int label = equivalentLabels[i];
3399  assert(label < static_cast<int>(numberOfCells) + 2);
3400  while (equivalentLabels[label] > 1) // equivalentLabels[0] == 0 !!!
3401  {
3402  label = equivalentLabels[label];
3403  assert(label < static_cast<int>(numberOfCells) + 2);
3404  }
3405  equivalentLabels[i] = label;
3406  }
3407  }
3408 
3409  // update leaves
3410  {
3411  for (std::size_t i = 0; i < numberOfCells; i++) {
3412  int label = cellIndexToLabel[i];
3413  assert(label < static_cast<int>(numberOfCells) + 2);
3414  if (equivalentLabels[label] > 1)
3415  cellIndexToLabel[i] = equivalentLabels[label];
3416  }
3417  }
3418 
3419  // hack: we use "equivalentLabels" to count how many components will have to
3420  // be created
3421  int numberOfComponents = 0;
3422  {
3423  std::fill(equivalentLabels.begin(), equivalentLabels.end(), 0);
3424 
3425  for (std::size_t i = 0; i < numberOfCells; i++) {
3426  assert(cellIndexToLabel[i] > 1 &&
3427  cellIndexToLabel[i] < static_cast<int>(numberOfCells) + 2);
3428  equivalentLabels[cellIndexToLabel[i]] = 1;
3429  }
3430 
3431  // we create (following) indexes for each components
3432  for (std::size_t i = 2; i < numberOfCells + 2; i++)
3433  if (equivalentLabels[i] == 1)
3434  equivalentLabels[i] =
3435  ++numberOfComponents; // labels start at '1'
3436  }
3437  assert(equivalentLabels[0] == 0);
3438  assert(equivalentLabels[1] == 0);
3439 
3440  // we flag each component's points with its label
3441  {
3442  if (progressCb) {
3443  if (progressCb->textCanBeEdited()) {
3444  char buffer[256];
3445  sprintf(buffer, "Components: %i", numberOfComponents);
3446  progressCb->setMethodTitle("Connected Components Extraction");
3447  progressCb->setInfo(buffer);
3448  }
3449  progressCb->update(0);
3450  progressCb->start();
3451  }
3452  NormalizedProgress nprogress(progressCb,
3453  static_cast<unsigned>(numberOfCells));
3454 
3456  for (std::size_t i = 0; i < numberOfCells; i++) {
3457  assert(cellIndexToLabel[i] < static_cast<int>(numberOfCells) + 2);
3458 
3459  const int& label = equivalentLabels[cellIndexToLabel[i]];
3460  assert(label > 0);
3461  getPointsInCell(ccCells[i].theCode, level, &Y, true);
3463  ScalarType d = static_cast<ScalarType>(label);
3464  for (unsigned j = 0; j < Y.size(); ++j) {
3466  Y.forwardIterator();
3467  }
3468 
3469  nprogress.oneStep();
3470  }
3471 
3472  if (progressCb) {
3473  progressCb->stop();
3474  }
3475  }
3476 
3477  return numberOfComponents;
3478 }
3479 
3480 /*** Octree-based cloud traversal mechanism ***/
3481 
3483  : parentOctree(_parentOctree),
3484  truncatedCode(0),
3485  index(0),
3486  points(nullptr),
3487  level(0) {
3490  } else {
3491  assert(false);
3492  }
3493 }
3494 
3496  : parentOctree(cell.parentOctree),
3497  level(cell.level),
3498  truncatedCode(cell.truncatedCode),
3499  index(cell.index),
3500  points(nullptr) {
3501  // copy constructor shouldn't be used (we can't properly share the 'points'
3502  // reference)
3503  assert(false);
3504 }
3505 
3507  if (points) delete points;
3508 }
3509 
3510 #ifdef ENABLE_MT_OCTREE
3511 
3512 /*** FOR THE MULTI THREADING WRAPPER ***/
3513 struct octreeCellDesc {
3514  DgmOctree::CellCode truncatedCode;
3515  unsigned i1, i2;
3516  unsigned char level;
3517 };
3518 
3519 static DgmOctree* s_octree_MT = nullptr;
3520 static DgmOctree::octreeCellFunc s_func_MT = nullptr;
3521 static void** s_userParams_MT = nullptr;
3522 static GenericProgressCallback* s_progressCb_MT = nullptr;
3523 static NormalizedProgress* s_normProgressCb_MT = nullptr;
3524 static bool s_cellFunc_MT_success = true;
3525 
3526 void LaunchOctreeCellFunc_MT(const octreeCellDesc& desc) {
3527  // skip cell if process is aborted/has failed
3528  if (!s_cellFunc_MT_success) {
3529  return;
3530  }
3531 
3532  const DgmOctree::cellsContainer& pointsAndCodes =
3533  s_octree_MT->pointsAndTheirCellCodes();
3534 
3535  // cell descriptor
3536  DgmOctree::octreeCell cell(s_octree_MT);
3537  cell.level = desc.level;
3538  cell.index = desc.i1;
3539  cell.truncatedCode = desc.truncatedCode;
3540  if (cell.points->reserve(desc.i2 - desc.i1 + 1)) {
3541  for (unsigned i = desc.i1; i <= desc.i2; ++i) {
3542  cell.points->addPointIndex(pointsAndCodes[i].theIndex);
3543  }
3544 
3545  s_cellFunc_MT_success &=
3546  (*s_func_MT)(cell, s_userParams_MT, s_normProgressCb_MT);
3547  } else {
3548  s_cellFunc_MT_success = false;
3549  }
3550 
3551  if (!s_cellFunc_MT_success) {
3552  // TODO: display a message to make clear that the cancel order has been
3553  // acknowledged!
3554  if (s_progressCb_MT) {
3555  if (s_progressCb_MT->textCanBeEdited()) {
3556  s_progressCb_MT->setInfo("Cancelling...");
3557  }
3558  }
3559 
3560  // if (s_normProgressCb_MT)
3561  //{
3562  // if (!s_normProgressCb_MT->oneStep())
3563  // {
3564  // s_cellFunc_MT_success = false;
3565  // return;
3566  // }
3567  // }
3568  }
3569 }
3570 
3571 #endif
3572 
3574  unsigned char level,
3575  octreeCellFunc func,
3576  void** additionalParameters,
3577  bool multiThread /*=false*/,
3578  GenericProgressCallback* progressCb /*=0*/,
3579  const char* functionTitle /*=0*/,
3580  int maxThreadCount /*=0*/) {
3581  if (m_thePointsAndTheirCellCodes.empty()) return 0;
3582 
3583 #ifdef ENABLE_MT_OCTREE
3584 
3585  // cells that will be processed by QtConcurrent::map
3586  const unsigned cellsNumber = getCellNumber(level);
3587  std::vector<octreeCellDesc> cells;
3588 
3589  if (multiThread) {
3590  try {
3591  cells.reserve(cellsNumber);
3592  } catch (const std::bad_alloc&) {
3593  // not enough memory
3594  // we use standard way (DGM TODO: we should warn the user!)
3595  multiThread = false;
3596  }
3597  }
3598 
3599  if (!multiThread)
3600 #endif
3601  {
3602  // we get the maximum cell population for this level
3603  unsigned maxCellPopulation = m_maxCellPopulation[level];
3604 
3605  // cell descriptor (initialize it with first cell/point)
3606  octreeCell cell(this);
3607  if (!cell.points->reserve(maxCellPopulation)) // not enough memory
3608  return 0;
3609  cell.level = level;
3610  cell.index = 0;
3611 
3612  // binary shift for cell code truncation
3613  unsigned char bitDec = GET_BIT_SHIFT(level);
3614 
3615  // iterator on cell codes
3616  cellsContainer::const_iterator p = m_thePointsAndTheirCellCodes.begin();
3617 
3618  // init with first cell
3619  cell.truncatedCode = (p->theCode >> bitDec);
3620  cell.points->addPointIndex(p->theIndex); // can't fail (see above)
3621  ++p;
3622 
3623  // number of cells for this level
3624  unsigned cellCount = getCellNumber(level);
3625 
3626  // progress bar
3627  if (progressCb) {
3628  if (progressCb->textCanBeEdited()) {
3629  if (functionTitle) {
3630  progressCb->setMethodTitle(functionTitle);
3631  }
3632  char buffer[512];
3633  sprintf(buffer,
3634  "Octree level %i\nCells: %u\nMean population: %3.2f "
3635  "(+/-%3.2f)\nMax population: %u",
3636  level, cellCount, m_averageCellPopulation[level],
3637  m_stdDevCellPopulation[level],
3638  m_maxCellPopulation[level]);
3639  progressCb->setInfo(buffer);
3640  }
3641  progressCb->update(0);
3642  progressCb->start();
3643  }
3644  NormalizedProgress nprogress(progressCb, m_theAssociatedCloud->size());
3645 
3646  bool result = true;
3647 
3648 #ifdef COMPUTE_NN_SEARCH_STATISTICS
3649  s_skippedPoints = 0;
3650  s_testedPoints = 0;
3651  s_jumps = 0.0;
3652  s_binarySearchCount = 0.0;
3653 #endif
3654 
3655  // for each point
3656  for (; p != m_thePointsAndTheirCellCodes.end(); ++p) {
3657  // check if it belongs to the current cell
3658  CellCode nextCode = (p->theCode >> bitDec);
3659  if (nextCode != cell.truncatedCode) {
3660  // if not, we call the user function on the previous cell
3661  result = (*func)(cell, additionalParameters, &nprogress);
3662 
3663  if (!result) break;
3664 
3665  // and we start a new cell
3666  cell.index += cell.points->size();
3667  cell.points->clear();
3668  cell.truncatedCode = nextCode;
3669  }
3670 
3671  cell.points->addPointIndex(p->theIndex); // can't fail (see above)
3672  }
3673 
3674  // don't forget last cell!
3675  if (result) result = (*func)(cell, additionalParameters, &nprogress);
3676 
3677 #ifdef COMPUTE_NN_SEARCH_STATISTICS
3678  FILE* fp = fopen("octree_log.txt", "at");
3679  if (fp) {
3680  fprintf(fp, "Function: %s\n",
3681  functionTitle ? functionTitle : "unknown");
3682  fprintf(fp, "Tested: %f (%3.1f %%)\n", s_testedPoints,
3683  100.0 * s_testedPoints /
3684  std::max(1.0, s_testedPoints + s_skippedPoints));
3685  fprintf(fp, "skipped: %f (%3.1f %%)\n", s_skippedPoints,
3686  100.0 * s_skippedPoints /
3687  std::max(1.0, s_testedPoints + s_skippedPoints));
3688  fprintf(fp, "Binary search count: %.0f\n", s_binarySearchCount);
3689  if (s_binarySearchCount > 0.0)
3690  fprintf(fp, "Mean jumps: %f\n", s_jumps / s_binarySearchCount);
3691  fprintf(fp, "\n");
3692  fclose(fp);
3693  }
3694 #endif
3695 
3696  // if something went wrong, we return 0
3697  return (result ? cellCount : 0);
3698  }
3699 #ifdef ENABLE_MT_OCTREE
3700  else {
3701  assert(cells.capacity() == cellsNumber);
3702 
3703  // binary shift for cell code truncation
3704  unsigned char bitDec = GET_BIT_SHIFT(level);
3705 
3706  // iterator on cell codes
3707  cellsContainer::const_iterator p = m_thePointsAndTheirCellCodes.begin();
3708 
3709  // cell descriptor (init. with first point/cell)
3710  octreeCellDesc cellDesc;
3711  cellDesc.i1 = 0;
3712  cellDesc.i2 = 0;
3713  cellDesc.level = level;
3714  cellDesc.truncatedCode = (p->theCode >> bitDec);
3715  ++p;
3716 
3717  // sweep through the octree
3718  for (; p != m_thePointsAndTheirCellCodes.end(); ++p) {
3719  CellCode nextCode = (p->theCode >> bitDec);
3720 
3721  if (nextCode != cellDesc.truncatedCode) {
3722  cells.push_back(cellDesc);
3723  cellDesc.i1 = cellDesc.i2 + 1;
3724  }
3725 
3726  cellDesc.truncatedCode = nextCode;
3727  ++cellDesc.i2;
3728  }
3729  // don't forget the last cell!
3730  cells.push_back(cellDesc);
3731 
3732  // static wrap
3733  s_octree_MT = this;
3734  s_func_MT = func;
3735  s_userParams_MT = additionalParameters;
3736  s_cellFunc_MT_success = true;
3737  s_progressCb_MT = progressCb;
3738  if (s_normProgressCb_MT) {
3739  delete s_normProgressCb_MT;
3740  s_normProgressCb_MT = 0;
3741  }
3742 
3743  // progress notification
3744  if (progressCb) {
3745  if (progressCb->textCanBeEdited()) {
3746  if (functionTitle) {
3747  progressCb->setMethodTitle(functionTitle);
3748  }
3749  char buffer[512];
3750  sprintf(buffer,
3751  "Octree level %i\nCells: %i\nAverage population: %3.2f "
3752  "(+/-%3.2f)\nMax population: %u",
3753  level, static_cast<int>(cells.size()),
3754  m_averageCellPopulation[level],
3755  m_stdDevCellPopulation[level],
3756  m_maxCellPopulation[level]);
3757  progressCb->setInfo(buffer);
3758  }
3759  progressCb->update(0);
3760  s_normProgressCb_MT = new NormalizedProgress(
3761  progressCb, m_theAssociatedCloud->size());
3762  progressCb->start();
3763  }
3764 
3765 #ifdef COMPUTE_NN_SEARCH_STATISTICS
3766  s_skippedPoints = 0;
3767  s_testedPoints = 0;
3768  s_jumps = 0.0;
3769  s_binarySearchCount = 0.0;
3770 #endif
3771 
3772  if (maxThreadCount == 0) {
3773  maxThreadCount = QThread::idealThreadCount();
3774  }
3775  QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
3776  QtConcurrent::blockingMap(cells, LaunchOctreeCellFunc_MT);
3777 
3778 #ifdef COMPUTE_NN_SEARCH_STATISTICS
3779  FILE* fp = fopen("octree_log.txt", "at");
3780  if (fp) {
3781  fprintf(fp, "Function: %s\n",
3782  functionTitle ? functionTitle : "unknown");
3783  fprintf(fp, "Tested: %f (%3.1f %%)\n", s_testedPoints,
3784  100.0 * s_testedPoints /
3785  std::max(1.0, s_testedPoints + s_skippedPoints));
3786  fprintf(fp, "skipped: %f (%3.1f %%)\n", s_skippedPoints,
3787  100.0 * s_skippedPoints /
3788  std::max(1.0, s_testedPoints + s_skippedPoints));
3789  fprintf(fp, "Binary search count: %.0f\n", s_binarySearchCount);
3790  if (s_binarySearchCount > 0.0)
3791  fprintf(fp, "Mean jumps: %f\n", s_jumps / s_binarySearchCount);
3792  fprintf(fp, "\n");
3793  fclose(fp);
3794  }
3795 #endif
3796 
3797  s_octree_MT = nullptr;
3798  s_func_MT = nullptr;
3799  s_userParams_MT = nullptr;
3800 
3801  if (progressCb) {
3802  progressCb->stop();
3803  if (s_normProgressCb_MT) delete s_normProgressCb_MT;
3804  s_normProgressCb_MT = nullptr;
3805  s_progressCb_MT = nullptr;
3806  }
3807 
3808  // if something went wrong, we clear everything and return 0!
3809  if (!s_cellFunc_MT_success) cells.clear();
3810 
3811  return static_cast<unsigned>(cells.size());
3812  }
3813 #endif
3814 }
3815 
3816 // Down-top traversal (for standard and mutli-threaded versions)
3817 #define ENABLE_DOWN_TOP_TRAVERSAL
3818 #define ENABLE_DOWN_TOP_TRAVERSAL_MT
3819 
3821  unsigned char startingLevel,
3822  octreeCellFunc func,
3823  void** additionalParameters,
3824  unsigned minNumberOfPointsPerCell,
3825  unsigned maxNumberOfPointsPerCell,
3826  bool multiThread /* = true*/,
3827  GenericProgressCallback* progressCb /*=0*/,
3828  const char* functionTitle /*=0*/,
3829  int maxThreadCount /*=0*/) {
3830  if (m_thePointsAndTheirCellCodes.empty()) return 0;
3831 
3832  const unsigned cellsNumber = getCellNumber(startingLevel);
3833 
3834 #ifdef ENABLE_MT_OCTREE
3835 
3836  // cells that will be processed by QtConcurrent::map
3837  std::vector<octreeCellDesc> cells;
3838  if (multiThread) {
3839  try {
3840  cells.reserve(cellsNumber); // at least!
3841  } catch (const std::bad_alloc&) {
3842  // not enough memory?
3843  // we use standard way (DGM TODO: we should warn the user!)
3844  multiThread = false;
3845  }
3846  }
3847 
3848  if (!multiThread)
3849 #endif
3850  {
3851  // we get the maximum cell population for this level
3852  unsigned maxCellPopulation = m_maxCellPopulation[startingLevel];
3853 
3854  // cell descriptor
3855  octreeCell cell(this);
3856  if (!cell.points->reserve(maxCellPopulation)) // not enough memory
3857  return 0;
3858  cell.level = startingLevel;
3859  cell.index = 0;
3860 
3861  // progress notification
3862  if (progressCb) {
3863  if (progressCb->textCanBeEdited()) {
3864  if (functionTitle) {
3865  progressCb->setMethodTitle(functionTitle);
3866  }
3867  char buffer[1024];
3868  sprintf(buffer,
3869  "Octree levels %i - %i\nCells: %i - %i\nAverage "
3870  "population: %3.2f (+/-%3.2f) - %3.2f (+/-%3.2f)\nMax "
3871  "population: %u - %u",
3872  startingLevel, MAX_OCTREE_LEVEL,
3873  getCellNumber(startingLevel),
3875  m_averageCellPopulation[startingLevel],
3876  m_stdDevCellPopulation[startingLevel],
3879  m_maxCellPopulation[startingLevel],
3881  progressCb->setInfo(buffer);
3882  }
3883  progressCb->update(0);
3884  progressCb->start();
3885  }
3886 #ifndef ENABLE_DOWN_TOP_TRAVERSAL
3887  NormalizedProgress nprogress(progressCb, m_theAssociatedCloud->size());
3888 #endif
3889 
3890  // binary shift for cell code truncation at current level
3891  unsigned char currentBitDec = GET_BIT_SHIFT(startingLevel);
3892 
3893 #ifdef ENABLE_DOWN_TOP_TRAVERSAL
3894  bool firstSubCell = true;
3895 #else
3896  unsigned char shallowSteps = 0;
3897 #endif
3898 
3899  // pointer on the current octree element
3900  cellsContainer::const_iterator startingElement =
3902 
3903  bool result = true;
3904 
3905  // let's sweep through the octree
3906  while (cell.index < m_numberOfProjectedPoints) {
3907  // new cell
3908  cell.truncatedCode = (startingElement->theCode >> currentBitDec);
3909  // we can already 'add' (virtually) the first point to the current
3910  // cell description struct
3911  unsigned elements = 1;
3912 
3913  // progress notification
3914 #ifndef ENABLE_DOWN_TOP_TRAVERSAL
3915  // if (cell.level == startingLevel)
3916  //{
3917  // if (!nprogress.oneStep())
3918  // {
3919  // result=false;
3920  // break;
3921  // }
3922  // }
3923 #else
3924  // in this mode, we can't update progress notification regularly...
3925  if (progressCb) {
3926  progressCb->update(
3927  100.0f * static_cast<float>(cell.index) /
3928  static_cast<float>(m_numberOfProjectedPoints));
3929  if (progressCb->isCancelRequested()) {
3930  result = false;
3931  break;
3932  }
3933  }
3934 #endif
3935 
3936  // let's test the following points
3937  for (cellsContainer::const_iterator p = startingElement + 1;
3938  p != m_thePointsAndTheirCellCodes.end(); ++p) {
3939  // next point code (at current level of subdivision)
3940  CellCode currentTruncatedCode = (p->theCode >> currentBitDec);
3941  // same code? Then it belongs to the same cell
3942  if (currentTruncatedCode == cell.truncatedCode) {
3943  // if we have reached the user specified limit
3944  if (elements == maxNumberOfPointsPerCell) {
3945  bool keepGoing = true;
3946 
3947  // we should go deeper in the octree (as long as the
3948  // current element belongs to the same cell as the first
3949  // cell element - in which case the cell will still be
3950  // too big)
3951  while (cell.level < MAX_OCTREE_LEVEL) {
3952  // next level
3953  ++cell.level;
3954  currentBitDec -= 3;
3955  cell.truncatedCode =
3956  (startingElement->theCode >> currentBitDec);
3957 
3958  // not the same cell anymore?
3959  if (cell.truncatedCode !=
3960  (p->theCode >> currentBitDec)) {
3961  // we must re-check all the previous inserted
3962  // points at this new level to determine the end
3963  // of this new cell
3964  p = startingElement;
3965  elements = 1;
3966  while (((++p)->theCode >> currentBitDec) ==
3967  cell.truncatedCode)
3968  ++elements;
3969 
3970  // and we must stop point collection here
3971  keepGoing = false;
3972 
3973 #ifdef ENABLE_DOWN_TOP_TRAVERSAL
3974  // in this case, the next cell won't be the
3975  // first sub-cell!
3976  firstSubCell = false;
3977 #endif
3978  break;
3979  }
3980  }
3981 
3982  // we should stop point collection here
3983  if (!keepGoing) break;
3984  }
3985 
3986  // otherwise we 'add' the point to the cell descriptor
3987  ++elements;
3988  } else // code is different --> not the same cell anymore
3989  {
3990 #ifndef ENABLE_DOWN_TOP_TRAVERSAL
3991  // we may have to go shallower ... as long as the parent
3992  // cell is different
3993  assert(shallowSteps == 0);
3994  CellCode cellTruncatedCode = cell.truncatedCode;
3995  while (cell.level > startingLevel + shallowSteps) {
3996  cellTruncatedCode >>= 3;
3997  currentTruncatedCode >>= 3;
3998  // this cell and the following share the same parent
3999  if (cellTruncatedCode == currentTruncatedCode) break;
4000  ++shallowSteps;
4001  }
4002 
4003  // we must stop point collection here
4004  break;
4005 #else
4006  // we are at the end of the cell
4007  bool keepGoing = false;
4008  // can we continue collecting points?
4009  if (cell.level > startingLevel) {
4010  // this cell and the following share the same parent?
4011  if ((cell.truncatedCode >> 3) ==
4012  (currentTruncatedCode >> 3)) {
4013  // if this cell is the first one, and we don't have
4014  // enough points we can simply proceed with its
4015  // parent cell
4016  if (firstSubCell &&
4017  elements < minNumberOfPointsPerCell) {
4018  // previous level
4019  --cell.level;
4020  currentBitDec += 3;
4021  cell.truncatedCode >>= 3;
4022 
4023  // we 'add' the point to the cell descriptor
4024  ++elements;
4025  // and we can continue collecting points
4026  keepGoing = true;
4027  }
4028 
4029  // as this cell and the next one share the same
4030  // parent, the next cell won't be the first
4031  // sub-cell!
4032  firstSubCell = false;
4033  } else {
4034  // as this cell and the next one have different
4035  // parents, the next cell is the first sub-cell!
4036  firstSubCell = true;
4037  }
4038  } else {
4039  // at the ceiling level, all cells are considered as
4040  // 'frist' sub-cells
4041  firstSubCell = true;
4042  }
4043 
4044  // we must stop point collection here
4045  if (!keepGoing) break;
4046 #endif
4047  }
4048  }
4049 
4050  // we can now really 'add' the points to the cell descriptor
4051  cell.points->clear();
4052  // DGM: already done earlier
4053  /*if (!cell.points->reserve(elements)) //not enough memory
4054  {
4055  result=false;
4056  break;
4057  }
4058  */
4059  for (unsigned i = 0; i < elements; ++i) {
4060  cell.points->addPointIndex((startingElement++)->theIndex);
4061  }
4062 
4063  // call user method on current cell
4064  result = (*func)(cell, additionalParameters,
4065 #ifndef ENABLE_DOWN_TOP_TRAVERSAL
4066  &nProgress
4067 #else
4068  nullptr
4069 #endif
4070  );
4071 
4072  if (!result) break;
4073 
4074  // proceed to next cell
4075  cell.index += elements;
4076 
4077 #ifndef ENABLE_DOWN_TOP_TRAVERSAL
4078  if (shallowSteps) {
4079  // we should go shallower
4080  assert(cell.level - shallowSteps >= startingLevel);
4081  cell.level -= shallowSteps;
4082  currentBitDec += 3 * shallowSteps;
4083  shallowSteps = 0;
4084  }
4085 #endif
4086  }
4087 
4088  if (progressCb) {
4089  progressCb->stop();
4090  }
4091 
4092  // if something went wrong, we return 0
4093  return (result ? cellsNumber : 0);
4094  }
4095 #ifdef ENABLE_MT_OCTREE
4096  else {
4097  assert(cells.capacity() == cellsNumber);
4098 
4099  // cell descriptor (init. with first point/cell)
4100  octreeCellDesc cellDesc;
4101  cellDesc.i1 = 0;
4102  cellDesc.i2 = 0;
4103  cellDesc.level = startingLevel;
4104 
4105  // binary shift for cell code truncation at current level
4106  unsigned char currentBitDec = GET_BIT_SHIFT(startingLevel);
4107 
4108 #ifdef ENABLE_DOWN_TOP_TRAVERSAL_MT
4109  bool firstSubCell = true;
4110 #else
4111  unsigned char shallowSteps = 0;
4112 #endif
4113  // pointer on the current octree element
4114  cellsContainer::const_iterator startingElement =
4116 
4117  // we compute some statistics on the fly
4118  unsigned long long popSum = 0;
4119  unsigned long long popSum2 = 0;
4120  unsigned long long maxPop = 0;
4121 
4122  // let's sweep through the octree
4123  while (cellDesc.i1 < m_numberOfProjectedPoints) {
4124  // new cell
4125  cellDesc.truncatedCode =
4126  (startingElement->theCode >> currentBitDec);
4127  // we can already 'add' (virtually) the first point to the current
4128  // cell description struct
4129  unsigned elements = 1;
4130 
4131  // let's test the following points
4132  for (cellsContainer::const_iterator p = startingElement + 1;
4133  p != m_thePointsAndTheirCellCodes.end(); ++p) {
4134  // next point code (at current level of subdivision)
4135  CellCode currentTruncatedCode = (p->theCode >> currentBitDec);
4136  // same code? Then it belongs to the same cell
4137  if (currentTruncatedCode == cellDesc.truncatedCode) {
4138  // if we have reached the user specified limit
4139  if (elements == maxNumberOfPointsPerCell) {
4140  bool keepGoing = true;
4141 
4142  // we should go deeper in the octree (as long as the
4143  // current element belongs to the same cell as the first
4144  // cell element - in which case the cell will still be
4145  // too big)
4146  while (cellDesc.level < MAX_OCTREE_LEVEL) {
4147  // next level
4148  ++cellDesc.level;
4149  currentBitDec -= 3;
4150  cellDesc.truncatedCode =
4151  (startingElement->theCode >> currentBitDec);
4152 
4153  // not the same cell anymore?
4154  if (cellDesc.truncatedCode !=
4155  (p->theCode >> currentBitDec)) {
4156  // we must re-check all the previously inserted
4157  // points at this new level to determine the end
4158  // of this new cell
4159  p = startingElement;
4160  elements = 1;
4161  while (((++p)->theCode >> currentBitDec) ==
4162  cellDesc.truncatedCode)
4163  ++elements;
4164 
4165  // and we must stop point collection here
4166  keepGoing = false;
4167 
4168 #ifdef ENABLE_DOWN_TOP_TRAVERSAL_MT
4169  // in this case, the next cell won't be the
4170  // first sub-cell!
4171  firstSubCell = false;
4172 #endif
4173  break;
4174  }
4175  }
4176 
4177  // we should stop point collection here
4178  if (!keepGoing) break;
4179  }
4180 
4181  // otherwise we 'add' the point to the cell descriptor
4182  ++elements;
4183  } else // code is different --> not the same cell anymore
4184  {
4185 #ifndef ENABLE_DOWN_TOP_TRAVERSAL_MT
4186  // we may have to go shallower ... as long as the parent
4187  // cell is different
4188  assert(shallowSteps == 0);
4189  CellCode cellTruncatedCode = cellDesc.truncatedCode;
4190  while (cellDesc.level > startingLevel + shallowSteps) {
4191  cellTruncatedCode >>= 3;
4192  currentTruncatedCode >>= 3;
4193  // this cell and the following share the same parent
4194  if (cellTruncatedCode == currentTruncatedCode) break;
4195  ++shallowSteps;
4196  }
4197 
4198  // we must stop point collection here
4199  break;
4200 #else
4201  // we are at the end of the cell
4202  bool keepGoing = false;
4203  // can we continue collecting points?
4204  if (cellDesc.level > startingLevel) {
4205  // this cell and the following share the same parent?
4206  if ((cellDesc.truncatedCode >> 3) ==
4207  (currentTruncatedCode >> 3)) {
4208  // if this cell is the first one, and we don't have
4209  // enough points we can simply proceed with its
4210  // parent cell
4211  if (firstSubCell &&
4212  elements < minNumberOfPointsPerCell) {
4213  // previous level
4214  --cellDesc.level;
4215  currentBitDec += 3;
4216  cellDesc.truncatedCode >>= 3;
4217 
4218  // we 'add' the point to the cell descriptor
4219  ++elements;
4220  // and we can continue collecting points
4221  keepGoing = true;
4222  }
4223 
4224  // as this cell and the next one share the same
4225  // parent, the next cell won't be the first
4226  // sub-cell!
4227  firstSubCell = false;
4228  } else {
4229  // as this cell and the next one have different
4230  // parents, the next cell is the first sub-cell!
4231  firstSubCell = true;
4232  }
4233  } else {
4234  // at the ceiling level, all cells are considered as
4235  // 'frist' sub-cells
4236  firstSubCell = true;
4237  }
4238 
4239  // we must stop point collection here
4240  if (!keepGoing) break;
4241 #endif
4242  }
4243  }
4244 
4245  // we can now 'add' this cell to the list
4246  cellDesc.i2 = cellDesc.i1 + (elements - 1);
4247  cells.push_back(cellDesc);
4248  popSum += static_cast<unsigned long long>(elements);
4249  popSum2 += static_cast<unsigned long long>(elements * elements);
4250  if (maxPop < elements) maxPop = elements;
4251 
4252  // proceed to next cell
4253  cellDesc.i1 += elements;
4254  startingElement += elements;
4255 
4256 #ifndef ENABLE_DOWN_TOP_TRAVERSAL_MT
4257  if (shallowSteps) {
4258  // we should go shallower
4259  assert(cellDesc.level - shallowSteps >= startingLevel);
4260  cellDesc.level -= shallowSteps;
4261  currentBitDec += 3 * shallowSteps;
4262  shallowSteps = 0;
4263  }
4264 #endif
4265  }
4266 
4267  // statistics
4268  double mean = static_cast<double>(popSum) / cells.size();
4269  double stddev = sqrt(static_cast<double>(popSum2 - popSum * popSum)) /
4270  cells.size();
4271 
4272  // static wrap
4273  s_octree_MT = this;
4274  s_func_MT = func;
4275  s_userParams_MT = additionalParameters;
4276  s_cellFunc_MT_success = true;
4277  if (s_normProgressCb_MT) delete s_normProgressCb_MT;
4278  s_normProgressCb_MT = nullptr;
4279 
4280  // progress notification
4281  if (progressCb) {
4282  if (progressCb->textCanBeEdited()) {
4283  if (functionTitle) {
4284  progressCb->setMethodTitle(functionTitle);
4285  }
4286  char buffer[1024];
4287  sprintf(buffer,
4288  "Octree levels %i - %i\nCells: %i\nAverage population: "
4289  "%3.2f (+/-%3.2f)\nMax population: %llu",
4290  startingLevel, MAX_OCTREE_LEVEL,
4291  static_cast<int>(cells.size()), mean, stddev, maxPop);
4292  progressCb->setInfo(buffer);
4293  }
4294  if (s_normProgressCb_MT) {
4295  delete s_normProgressCb_MT;
4296  }
4297  s_normProgressCb_MT = new NormalizedProgress(
4298  progressCb, static_cast<unsigned>(cells.size()));
4299  progressCb->update(0);
4300  progressCb->start();
4301  }
4302 
4303 #ifdef COMPUTE_NN_SEARCH_STATISTICS
4304  s_skippedPoints = 0;
4305  s_testedPoints = 0;
4306  s_jumps = 0.0;
4307  s_binarySearchCount = 0.0;
4308 #endif
4309 
4310  if (maxThreadCount == 0) {
4311  maxThreadCount = QThread::idealThreadCount();
4312  }
4313  QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
4314  QtConcurrent::blockingMap(cells, LaunchOctreeCellFunc_MT);
4315 
4316 #ifdef COMPUTE_NN_SEARCH_STATISTICS
4317  FILE* fp = fopen("octree_log.txt", "at");
4318  if (fp) {
4319  fprintf(fp, "Function: %s\n",
4320  functionTitle ? functionTitle : "unknown");
4321  fprintf(fp, "Tested: %f (%3.1f %%)\n", s_testedPoints,
4322  100.0 * s_testedPoints /
4323  std::max(1.0, s_testedPoints + s_skippedPoints));
4324  fprintf(fp, "skipped: %f (%3.1f %%)\n", s_skippedPoints,
4325  100.0 * s_skippedPoints /
4326  std::max(1.0, s_testedPoints + s_skippedPoints));
4327  fprintf(fp, "Binary search count: %.0f\n", s_binarySearchCount);
4328  if (s_binarySearchCount > 0.0)
4329  fprintf(fp, "Mean jumps: %f\n", s_jumps / s_binarySearchCount);
4330  fprintf(fp, "\n");
4331  fclose(fp);
4332  }
4333 #endif
4334 
4335  s_octree_MT = nullptr;
4336  s_func_MT = nullptr;
4337  s_userParams_MT = nullptr;
4338 
4339  if (progressCb) {
4340  progressCb->stop();
4341  if (s_normProgressCb_MT) delete s_normProgressCb_MT;
4342  s_normProgressCb_MT = nullptr;
4343  }
4344 
4345  // if something went wrong, we clear everything and return 0!
4346  if (!s_cellFunc_MT_success) cells.resize(0);
4347 
4348  return static_cast<unsigned>(cells.size());
4349  }
4350 #endif
4351 }
4352 
4353 bool DgmOctree::rayCast(const CCVector3& rayAxis,
4354  const CCVector3& rayOrigin,
4355  double maxRadiusOrFov,
4356  bool isFOV,
4357  RayCastProcess process,
4358  std::vector<PointDescriptor>& output) const {
4359  if (m_thePointsAndTheirCellCodes.empty()) {
4360  // nothing to do
4361  assert(false);
4362  return false;
4363  }
4364 
4365  CCVector3 margin(0, 0, 0);
4366  double maxSqRadius = 0;
4367  if (!isFOV) {
4368  margin = CCVector3(1, 1, 1) *
4369  static_cast<PointCoordinateType>(maxRadiusOrFov);
4370  maxSqRadius = maxRadiusOrFov * maxRadiusOrFov;
4371  }
4372 
4373  // first test with the total bounding box
4374  Ray<PointCoordinateType> ray(rayAxis, rayOrigin);
4375  if (!AABB<PointCoordinateType>(m_dimMin - margin, m_dimMax + margin)
4376  .intersects(ray)) {
4377  // no intersection
4378  output.resize(0);
4379  return true;
4380  }
4381 
4382  // no need to go too deep
4383  const unsigned char maxLevel = findBestLevelForAGivenPopulationPerCell(10);
4384 
4385  // starting level of subdivision
4386  unsigned char level = 1;
4387  // binary shift for cell code truncation at current level
4388  unsigned char currentBitDec = GET_BIT_SHIFT(level);
4389  // current cell code
4390  CellCode currentCode = INVALID_CELL_CODE;
4391  // whether the current cell should be skipped or not
4392  bool skipThisCell = false;
4393  // smallest FOV (i.e. nearest point)
4394  double smallestOrderDist = -1.0;
4395 
4396 #ifdef CV_DEBUG
4398 #endif
4399 
4400  // ray with origin expressed in the local coordinate system!
4401  Ray<PointCoordinateType> rayLocal(rayAxis, rayOrigin - m_dimMin);
4402 
4403  // let's sweep through the octree
4404  for (cellsContainer::const_iterator it =
4406  it != m_thePointsAndTheirCellCodes.end(); ++it) {
4407  CellCode truncatedCode = (it->theCode >> currentBitDec);
4408 
4409  // new cell?
4410  if (truncatedCode != (currentCode >> currentBitDec)) {
4411  // look for the biggest 'parent' cell that englobes this cell and
4412  // the previous one (if any)
4413  while (level > 1) {
4414  unsigned char bitDec = GET_BIT_SHIFT(level - 1);
4415  if ((it->theCode >> bitDec) == (currentCode >> bitDec)) {
4416  // same parent cell, we can stop here
4417  break;
4418  }
4419  --level;
4420  }
4421 
4422  currentCode = it->theCode;
4423 
4424  // now try to go deeper with the new cell
4425  while (level < maxLevel) {
4426  Tuple3i cellPos;
4427  getCellPos(it->theCode, level, cellPos, false);
4428 
4429  // first test with the total bounding box
4430  const PointCoordinateType& halfCellSize =
4431  getCellSize(level) / 2;
4432  CCVector3 cellCenter((2 * cellPos.x + 1) * halfCellSize,
4433  (2 * cellPos.y + 1) * halfCellSize,
4434  (2 * cellPos.z + 1) * halfCellSize);
4435 
4436  CCVector3 halfCell =
4437  CCVector3(halfCellSize, halfCellSize, halfCellSize);
4438 
4439  if (isFOV) {
4440  double radialSqDist, sqDistToOrigin;
4441  rayLocal.squareDistances(cellCenter, radialSqDist,
4442  sqDistToOrigin);
4443 
4444  double dx = sqrt(sqDistToOrigin);
4445  double dy = std::max<double>(
4446  0, sqrt(radialSqDist) - SQRT_3 * halfCellSize);
4447  double fov_rad = atan2(dy, dx);
4448 
4449  skipThisCell = (fov_rad > maxRadiusOrFov);
4450  } else {
4451  skipThisCell = !AABB<PointCoordinateType>(
4452  cellCenter - halfCell - margin,
4453  cellCenter + halfCell + margin)
4454  .intersects(rayLocal);
4455  }
4456 
4457  if (skipThisCell)
4458  break;
4459  else
4460  ++level;
4461  }
4462  currentBitDec = GET_BIT_SHIFT(level);
4463  }
4464 
4465 #ifdef CV_DEBUG
4466  m_theAssociatedCloud->setPointScalarValue(it->theIndex, level);
4467 #endif
4468 
4469  if (!skipThisCell) {
4470  // test the point
4471  const CCVector3* P = m_theAssociatedCloud->getPoint(it->theIndex);
4472 
4473  double radialSqDist = ray.radialSquareDistance(*P);
4474  double orderDist = -1.0;
4475  bool isElligible = false;
4476 
4477  if (isFOV) {
4478  double sqDist = ray.squareDistanceToOrigin(*P);
4479  double fov_rad = atan2(sqrt(radialSqDist), sqrt(sqDist));
4480  isElligible = (fov_rad <= maxRadiusOrFov);
4481  orderDist = fov_rad;
4482 #ifdef CV_DEBUG
4483  // m_theAssociatedCloud->setPointScalarValue(it->theIndex,
4484  // fov_rad);
4485  // m_theAssociatedCloud->setPointScalarValue(it->theIndex,
4486  // sqrt(sqDist));
4487 #endif
4488  } else {
4489  isElligible = (radialSqDist <= maxSqRadius);
4490 #ifdef CV_DEBUG
4491  // m_theAssociatedCloud->setPointScalarValue(it->theIndex,
4492  // sqrt(radialSqDist));
4493 #endif
4494  }
4495 
4496  if (isElligible) {
4497  try {
4498  switch (process) {
4499  case RC_NEAREST_POINT:
4500 
4501  if (orderDist < 0) {
4502  // to give better chances to points that are
4503  // closer to the viewer)
4504  double sqDist = ray.squareDistanceToOrigin(*P);
4505  orderDist = sqrt(radialSqDist * sqDist);
4506  }
4507 
4508  // keep only the 'nearest' point
4509  if (output.empty()) {
4510  output.resize(1,
4511  PointDescriptor(P, it->theIndex,
4512  radialSqDist));
4513  smallestOrderDist = orderDist;
4514  } else {
4515  if (orderDist < smallestOrderDist) {
4516  output.back() = PointDescriptor(
4517  P, it->theIndex, radialSqDist);
4518  smallestOrderDist = orderDist;
4519  }
4520  }
4521  break;
4522 
4523  case RC_CLOSE_POINTS:
4524 
4525  // store all the points that are close enough to the
4526  // ray
4527  output.emplace_back(P, it->theIndex, radialSqDist);
4528  break;
4529 
4530  default:
4531  assert(false);
4532  return false;
4533  }
4534  } catch (const std::bad_alloc&) {
4535  // not enough memory
4536  return false;
4537  }
4538  }
4539  }
4540  }
4541 
4542  return true;
4543 }
constexpr double SQRT_3
Square root of 3.
Definition: CVConst.h:29
Tuple3Tpl< int > Tuple3i
Tuple of 3 int values.
Definition: CVGeom.h:217
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int count
int points
#define ParallelSort
Definition: ParallelSort.h:33
cmdLineReadable * params[]
core::Tensor result
Definition: VtkUtils.cpp:76
bool copy
Definition: VtkUtils.cpp:74
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
static void MakeMinAndMaxCubical(CCVector3 &dimMin, CCVector3 &dimMax, double enlargeFactor=0.01)
Transforms a 3D box into a 3D cube.
Definition: CVMiscTools.cpp:88
The octree structure used throughout the library.
Definition: DgmOctree.h:39
const int * getMaxFillIndexes(unsigned char level) const
Definition: DgmOctree.h:485
static const CellCode INVALID_CELL_CODE
Invalid cell code.
Definition: DgmOctree.h:89
static bool MultiThreadSupport()
Definition: DgmOctree.cpp:162
unsigned CellCode
Type of the code of an octree cell.
Definition: DgmOctree.h:78
void getNeighborCellsAround(const Tuple3i &cellPos, cellIndexesContainer &neighborCellsIndexes, int neighbourhoodLength, unsigned char level) const
Definition: DgmOctree.cpp:825
static const int MAX_OCTREE_LENGTH
Max octree length at last level of subdivision (number of cells)
Definition: DgmOctree.h:84
GenericIndexedCloudPersist * m_theAssociatedCloud
Associated cloud.
Definition: DgmOctree.h:1233
int m_fillIndexes[(MAX_OCTREE_LEVEL+1) *6]
Definition: DgmOctree.h:1258
unsigned char findBestLevelForAGivenCellNumber(unsigned indicativeNumberOfCells) const
Definition: DgmOctree.cpp:2766
std::vector< unsigned int > cellIndexesContainer
Octree cell indexes container.
Definition: DgmOctree.h:95
void diff(const cellCodesContainer &codesA, const cellCodesContainer &codesB, cellCodesContainer &diffA, cellCodesContainer &diffB) const
Definition: DgmOctree.cpp:2955
void getCellPos(CellCode code, unsigned char level, Tuple3i &cellPos, bool isCodeTruncated) const
Definition: DgmOctree.cpp:498
int genericBuild(GenericProgressCallback *progressCb=nullptr)
Generic method to build the octree structure.
Definition: DgmOctree.cpp:221
unsigned findPointNeighbourhood(const CCVector3 *_queryPoint, ReferenceCloud *Yk, unsigned maxNumberOfNeighbors, unsigned char level, double &maxSquareDist, double maxSearchDist=0, int *finalNeighbourhoodSize=nullptr) const
Finds the nearest neighbours around a query point.
Definition: DgmOctree.cpp:721
void getPointsInNeighbourCellsAround(NearestNeighboursSearchStruct &nNSS, int neighbourhoodLength, bool getOnlyPointsWithValidScalar=false) const
Gets point in the neighbourhing cells of a specific cell.
Definition: DgmOctree.cpp:906
double findTheNearestNeighborStartingFromCell(NearestNeighboursSearchStruct &nNSS) const
Definition: DgmOctree.cpp:1468
static const int MAX_OCTREE_LEVEL
Max octree subdivision level.
Definition: DgmOctree.h:67
bool getPointsInCellByCellIndex(ReferenceCloud *cloud, unsigned cellIndex, unsigned char level, bool clearOutputCloud=true) const
Returns the points lying in a specific cell.
Definition: DgmOctree.cpp:2879
unsigned findNearestNeighborsStartingFromCell(NearestNeighboursSearchStruct &nNSS, bool getOnlyPointsWithValidScalar=false) const
Definition: DgmOctree.cpp:1655
const int * getMinFillIndexes(unsigned char level) const
Definition: DgmOctree.h:474
static PointCoordinateType ComputeMinDistanceToCellBorder(const CCVector3 &queryPoint, PointCoordinateType cs, const CCVector3 &cellCenter)
Definition: DgmOctree.h:1057
unsigned char findBestLevelForComparisonWithOctree(const DgmOctree *theOtherOctree) const
Definition: DgmOctree.cpp:2691
bool getCellIndexes(unsigned char level, cellIndexesContainer &vec) const
Definition: DgmOctree.cpp:2850
static int OCTREE_LENGTH(int level)
Definition: DgmOctree.cpp:118
CCVector3 m_dimMax
Max coordinates of the octree bounding-box.
Definition: DgmOctree.h:1245
unsigned char findBestLevelForAGivenNeighbourhoodSizeExtraction(PointCoordinateType radius) const
Definition: DgmOctree.cpp:2664
bool getPointsInCell(CellCode cellCode, unsigned char level, ReferenceCloud *subset, bool isCodeTruncated=false, bool clearOutputCloud=true) const
Returns the points lying in a specific cell.
Definition: DgmOctree.cpp:537
int findNeighborsInASphereStartingFromCell(NearestNeighboursSearchStruct &nNSS, double radius, bool sortValues=true) const
Advanced form of the nearest neighbours search algorithm (in a sphere)
Definition: DgmOctree.cpp:2479
std::size_t getPointsInBoxNeighbourhood(BoxNeighbourhood &params) const
Returns the points falling inside a box.
Definition: DgmOctree.cpp:1951
bool(*)(const octreeCell &, void **, NormalizedProgress *) octreeCellFunc
Definition: DgmOctree.h:395
virtual void clear()
Clears the octree.
Definition: DgmOctree.cpp:183
void getTheCellPosWhichIncludesThePoint(const CCVector3 *thePoint, Tuple3i &cellPos) const
Definition: DgmOctree.h:779
static CellCode GenerateTruncatedCellCode(const Tuple3i &cellPos, unsigned char level)
Definition: DgmOctree.cpp:120
unsigned getNumberOfProjectedPoints() const
Returns the number of points projected into the octree.
Definition: DgmOctree.h:446
PointCoordinateType m_cellSize[MAX_OCTREE_LEVEL+2]
Cell dimensions for all subdivision levels.
Definition: DgmOctree.h:1255
const PointCoordinateType & getCellSize(unsigned char level) const
Returns the octree cells length for a given level of subdivision.
Definition: DgmOctree.h:494
std::vector< CellCode > cellCodesContainer
Octree cell codes container.
Definition: DgmOctree.h:92
int getPointsInSphericalNeighbourhood(const CCVector3 &sphereCenter, PointCoordinateType radius, NeighboursSet &neighbours, unsigned char level) const
Returns the points falling inside a sphere.
Definition: DgmOctree.cpp:1846
unsigned m_numberOfProjectedPoints
Number of points projected in the octree.
Definition: DgmOctree.h:1236
int extractCCs(const cellCodesContainer &cellCodes, unsigned char level, bool sixConnexity, GenericProgressCallback *progressCb=nullptr) const
Definition: DgmOctree.cpp:3093
void computeCellCenter(CellCode code, unsigned char level, CCVector3 &center, bool isCodeTruncated=false) const
Definition: DgmOctree.h:862
DgmOctree(GenericIndexedCloudPersist *cloud)
DgmOctree constructor.
Definition: DgmOctree.cpp:174
double m_averageCellPopulation[MAX_OCTREE_LEVEL+1]
Average cell population per level of subdivision.
Definition: DgmOctree.h:1264
unsigned executeFunctionForAllCellsAtLevel(unsigned char level, octreeCellFunc func, void **additionalParameters, bool multiThread=false, GenericProgressCallback *progressCb=nullptr, const char *functionTitle=nullptr, int maxThreadCount=0)
Definition: DgmOctree.cpp:3573
unsigned m_cellCount[MAX_OCTREE_LEVEL+1]
Number of cells per level of subdivision.
Definition: DgmOctree.h:1260
unsigned getCellIndex(CellCode truncatedCellCode, unsigned char bitDec) const
Returns the index of a given cell represented by its code.
Definition: DgmOctree.cpp:559
void computeCellLimits(CellCode code, unsigned char level, CCVector3 &cellMin, CCVector3 &cellMax, bool isCodeTruncated=false) const
Returns the spatial limits of a given cell.
Definition: DgmOctree.cpp:520
RayCastProcess
Ray casting processes.
Definition: DgmOctree.h:1179
int build(GenericProgressCallback *progressCb=nullptr)
Builds the structure.
Definition: DgmOctree.cpp:196
void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax) const
Returns the octree bounding box.
Definition: DgmOctree.cpp:493
std::vector< IndexAndCode > cellsContainer
Container of 'IndexAndCode' structures.
Definition: DgmOctree.h:351
CCVector3 m_dimMin
Min coordinates of the octree bounding-box.
Definition: DgmOctree.h:1243
void updateMinAndMaxTables()
Updates the tables containing octree limits and boundaries.
Definition: DgmOctree.cpp:395
ReferenceCloud * getPointsInCellsWithSortedCellCodes(cellCodesContainer &cellCodes, unsigned char level, ReferenceCloud *subset, bool areCodesTruncated=false) const
Returns the points lying in multiple cells.
Definition: DgmOctree.cpp:2909
unsigned m_maxCellPopulation[MAX_OCTREE_LEVEL+1]
Max cell population per level of subdivision.
Definition: DgmOctree.h:1262
const cellsContainer & pointsAndTheirCellCodes() const
Returns the octree 'structure'.
Definition: DgmOctree.h:1196
bool getCellCodes(unsigned char level, cellCodesContainer &vec, bool truncatedCodes=false) const
Definition: DgmOctree.cpp:2822
double m_stdDevCellPopulation[MAX_OCTREE_LEVEL+1]
Std. dev. of cell population per level of subdivision.
Definition: DgmOctree.h:1266
unsigned executeFunctionForAllCellsStartingAtLevel(unsigned char startingLevel, octreeCellFunc func, void **additionalParameters, unsigned minNumberOfPointsPerCell, unsigned maxNumberOfPointsPerCell, bool multiThread=true, GenericProgressCallback *progressCb=nullptr, const char *functionTitle=nullptr, int maxThreadCount=0)
Definition: DgmOctree.cpp:3820
bool rayCast(const CCVector3 &rayAxis, const CCVector3 &rayOrigin, double maxRadiusOrFov, bool isFOV, RayCastProcess process, std::vector< PointDescriptor > &output) const
Ray casting algorithm.
Definition: DgmOctree.cpp:4353
cellsContainer m_thePointsAndTheirCellCodes
The coded octree structure.
Definition: DgmOctree.h:1230
const unsigned & getCellNumber(unsigned char level) const
Returns the number of cells for a given level of subdivision.
Definition: DgmOctree.h:1038
static unsigned char GET_BIT_SHIFT(unsigned char level)
Returns the binary shift for a given level of subdivision.
Definition: DgmOctree.cpp:113
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
Definition: DgmOctree.h:133
double computeMeanOctreeDensity(unsigned char level) const
Definition: DgmOctree.cpp:2789
std::size_t getPointsInCylindricalNeighbourhoodProgressive(ProgressiveCylindricalNeighbourhood &params) const
Same as getPointsInCylindricalNeighbourhood with progressive approach.
Definition: DgmOctree.cpp:2264
std::size_t getPointsInCylindricalNeighbourhood(CylindricalNeighbourhood &params) const
Returns the points falling inside a cylinder.
Definition: DgmOctree.cpp:2111
unsigned char findBestLevelForAGivenPopulationPerCell(unsigned indicativeNumberOfPointsPerCell) const
Definition: DgmOctree.cpp:2737
bool getCellCodesAndIndexes(unsigned char level, cellsContainer &vec, bool truncatedCodes=false) const
Definition: DgmOctree.cpp:2794
void getCellDistanceFromBorders(const Tuple3i &cellPos, unsigned char level, int *cellDists) const
Definition: DgmOctree.cpp:781
void computeCellsStatistics(unsigned char level)
Computes statistics about cells for a given level of subdivision.
Definition: DgmOctree.cpp:423
virtual void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax)=0
Returns the cloud bounding box.
virtual bool enableScalarField()=0
Enables the scalar field associated to the cloud.
virtual unsigned size() const =0
Returns the number of points.
virtual ScalarType getPointScalarValue(unsigned pointIndex) const =0
Returns the ith point associated scalar value.
virtual void setPointScalarValue(unsigned pointIndex, ScalarType value)=0
Sets the ith point associated scalar value.
A generic 3D point cloud with index-based and presistent access to points.
virtual const CCVector3 * getPointPersistentPtr(unsigned index)=0
Returns the ith point as a persistent pointer.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
virtual void stop()=0
Notifies the fact that the process has ended.
virtual void setInfo(const char *infoStr)=0
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle)=0
Notifies the algorithm title.
virtual bool textCanBeEdited() const
Returns whether the dialog title and info can be updated or not.
virtual void update(float percent)=0
Notifies the algorithm progress.
virtual bool isCancelRequested()=0
Checks if the process should be canceled.
bool oneStep()
Increments total progress value of a single unit.
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
void placeIteratorAtBeginning() override
Sets the cloud iterator at the beginning.
virtual GenericIndexedCloudPersist * getAssociatedCloud()
Returns the associated (source) cloud.
unsigned size() const override
Returns the number of points.
virtual void clear(bool releaseMemory=false)
Clears the cloud.
virtual void setCurrentPointScalarValue(ScalarType value)
Sets the current point associated scalar value.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
virtual void forwardIterator()
Forwards the local element iterator.
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
Definition: ScalarField.h:61
static DgmOctree::CellCode GenerateCellCodeForDim(int pos)
Definition: DgmOctree.cpp:158
static const double LOG_NAT_2
Const value: ln(2)
Definition: DgmOctree.cpp:44
static BitShiftValues PRE_COMPUTED_BIT_SHIFT_VALUES
Definition: DgmOctree.cpp:61
static MonoDimensionalCellCodes PRE_COMPUTED_POS_CODES
Definition: DgmOctree.cpp:107
__host__ __device__ float dot(float2 a, float2 b)
Definition: cutil_math.h:1119
int min(int a, int b)
Definition: cutil_math.h:53
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
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.
constexpr Rgbaf middle(0.50f, 0.50f, 0.50f, 1.00f)
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
double stddev(std::vector< double > const &func)
Definition: qAutoSeg.cpp:86
cloudViewer::NormalizedProgress * nProgress
unsigned char octreeLevel
Simple axis aligned box structure.
Definition: RayAndBox.h:50
bool intersects(const Ray< T > &r, T *t0=0, T *t1=0) const
Definition: RayAndBox.h:69
Pre-computed bit shift values (one for each level)
Definition: DgmOctree.cpp:47
unsigned char values[DgmOctree::MAX_OCTREE_LEVEL+1]
Values.
Definition: DgmOctree.cpp:59
BitShiftValues()
Default initialization.
Definition: DgmOctree.cpp:49
IndexAndCodeExt()
Default constructor.
Definition: DgmOctree.cpp:3080
unsigned IndexType
Definition: DgmOctree.cpp:3070
IndexType theIndex
index
Definition: DgmOctree.cpp:3074
DgmOctree::CellCode theCode
cell code
Definition: DgmOctree.cpp:3077
static bool indexComp(const IndexAndCodeExt &a, const IndexAndCodeExt &b)
Compares two IndexAndCodeExt instances based on their index.
Definition: DgmOctree.cpp:3087
static const int VALUE_COUNT
Total number of positions/values.
Definition: DgmOctree.cpp:70
cloudViewer::DgmOctree::CellCode values[VALUE_COUNT]
Mono-dimensional cell codes.
Definition: DgmOctree.cpp:101
MonoDimensionalCellCodes()
Default initialization.
Definition: DgmOctree.cpp:73
Simple Ray structure.
Definition: RayAndBox.h:14
double squareDistanceToOrigin(const Vector3Tpl< T > &P) const
Definition: RayAndBox.h:30
double radialSquareDistance(const Vector3Tpl< T > &P) const
Definition: RayAndBox.h:25
void squareDistances(const Vector3Tpl< T > &P, double &radial, double &toOrigin) const
Definition: RayAndBox.h:35
Input/output parameters structure for getPointsInBoxNeighbourhood.
Definition: DgmOctree.h:727
Structure used during nearest neighbour search.
Definition: DgmOctree.h:136
static bool codeComp(const IndexAndCode &a, const IndexAndCode &b)
Compares two IndexAndCode instances based on their code.
Definition: DgmOctree.h:333
Container of in/out parameters for nearest neighbour(s) search.
Definition: DgmOctree.h:161
unsigned char level
Level of subdivision of the octree at which to start the search.
Definition: DgmOctree.h:171
double maxSearchSquareDistd
Maximum neihgbours distance.
Definition: DgmOctree.h:196
Tuple3i cellPos
Position in the octree of the cell including the query point.
Definition: DgmOctree.h:184
CCVector3 cellCenter
Coordinates of the center of the cell including the query point.
Definition: DgmOctree.h:189
unsigned minNumberOfNeighbors
Minimal number of neighbours to find.
Definition: DgmOctree.h:177
Structure used during nearest neighbour search.
Definition: DgmOctree.h:101
const CCVector3 * point
Point.
Definition: DgmOctree.h:103
double squareDistd
Point associated distance value.
Definition: DgmOctree.h:107
static bool distComp(const PointDescriptor &a, const PointDescriptor &b)
Comparison operator.
Definition: DgmOctree.h:126
Octree cell descriptor.
Definition: DgmOctree.h:354
virtual ~octreeCell()
Default destructor.
Definition: DgmOctree.cpp:3506
octreeCell(const DgmOctree *parentOctree)
Default constructor.
Definition: DgmOctree.cpp:3482
ReferenceCloud * points
Set of points lying inside this cell.
Definition: DgmOctree.h:365
unsigned index
Cell index in octree structure (see m_thePointsAndTheirCellCodes)
Definition: DgmOctree.h:363
const DgmOctree * parentOctree
Octree to which the cell belongs.
Definition: DgmOctree.h:359
unsigned char level
Cell level of subdivision.
Definition: DgmOctree.h:367
CellCode truncatedCode
Truncated cell code.
Definition: DgmOctree.h:361