ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
DistanceComputationTools.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 
9 
10 // local
11 #include <CVPointCloud.h>
14 #include <LocalModel.h>
15 #include <Polyline.h>
16 #include <ReferenceCloud.h>
18 #include <ScalarField.h>
19 #include <ScalarFieldTools.h>
20 #include <SimpleTriangle.h>
21 #include <SquareMatrix.h>
22 
23 // system
24 #include <algorithm>
25 #include <cassert>
26 
27 #ifdef CV_CORE_LIB_USES_QT_CONCURRENT
28 #ifndef CV_DEBUG
29 // enables multi-threading handling
30 #define ENABLE_CLOUD2MESH_DIST_MT
31 
32 #include <QtConcurrentMap>
33 #include <QtCore>
34 #endif
35 #endif
36 
37 namespace cloudViewer {
38 
40 struct TriangleList {
42  std::vector<unsigned> indexes;
43 
45 
47  inline bool push(unsigned index) {
48  try {
49  indexes.push_back(index);
50  } catch (const std::bad_alloc&) {
51  return false;
52  }
53  return true;
54  }
55 };
56 
60 public:
67 
72 
75 
78  : octree(nullptr),
79  mesh(nullptr),
80  distanceTransform(nullptr),
81  minFillIndexes(0, 0, 0),
82  maxFillIndexes(0, 0, 0) {}
83 
86  if (perCellTriangleList.isInitialized()) {
87  TriangleList** data = perCellTriangleList.data();
88  for (std::size_t i = 0; i < perCellTriangleList.totalCellCount();
89  ++i, ++data) {
90  if (*data) delete (*data);
91  }
92  }
93 
94  if (distanceTransform) {
95  delete distanceTransform;
96  distanceTransform = nullptr;
97  }
98  }
99 };
100 } // namespace cloudViewer
101 
102 using namespace cloudViewer;
103 
105 #ifdef ENABLE_CLOUD2MESH_DIST_MT
106  return true;
107 #else
108  return false;
109 #endif
110 }
111 
113  GenericIndexedCloudPersist* comparedCloud,
114  GenericIndexedCloudPersist* referenceCloud,
116  GenericProgressCallback* progressCb /*=0*/,
117  DgmOctree* compOctree /*=0*/,
118  DgmOctree* refOctree /*=0*/) {
119  assert(comparedCloud && referenceCloud);
120 
121  if (params.CPSet && params.maxSearchDist > 0) {
122  // we can't use a 'max search distance' criterion if the "Closest Point
123  // Set" is requested
124  assert(false);
125  return DISTANCE_COMPUTATION_RESULTS::
126  ERROR_CANT_USE_MAX_SEARCH_DIST_AND_CLOSEST_POINT_SET;
127  }
128 
129  // we spatially 'synchronize' the octrees
130  DgmOctree *comparedOctree = compOctree, *referenceOctree = refOctree;
131  SOReturnCode soCode = synchronizeOctrees(comparedCloud, referenceCloud,
132  comparedOctree, referenceOctree,
133  params.maxSearchDist, progressCb);
134 
135  if (soCode != SYNCHRONIZED && soCode != DISJOINT) {
136  // not enough memory (or invalid input)
137  return DISTANCE_COMPUTATION_RESULTS::ERROR_SYNCHRONIZE_OCTREES_FAILURE;
138  }
139 
140  // we 'enable' a scalar field (if it is not already done) to store
141  // resulting distances
142  if (!comparedCloud->enableScalarField()) {
143  // not enough memory
144  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
145  }
146 
147  // internally we don't use the maxSearchDist parameters as is, but the
148  // square of it
149  double maxSearchSquareDistd =
150  params.maxSearchDist <= 0
151  ? 0
152  : static_cast<double>(params.maxSearchDist) *
153  params.maxSearchDist;
154 
155  // closest point set
156  if (params.CPSet) {
157  assert(maxSearchSquareDistd <= 0);
158 
159  if (!params.CPSet->resize(comparedCloud->size())) {
160  // not enough memory
161  if (comparedOctree && !compOctree) delete comparedOctree;
162  if (referenceOctree && !refOctree) delete referenceOctree;
163  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
164  }
165  }
166 
167  // by default we reset any former value stored in the 'enabled' scalar field
168  const ScalarType resetValue =
169  maxSearchSquareDistd <= 0 ? NAN_VALUE : params.maxSearchDist;
170  if (params.resetFormerDistances) {
171  for (unsigned i = 0; i < comparedCloud->size(); ++i) {
172  comparedCloud->setPointScalarValue(i, resetValue);
173  }
174  }
175 
176  // specific case: a max search distance has been defined and octrees are
177  // totally disjoint
178  if (maxSearchSquareDistd > 0 && soCode == DISJOINT) {
179  // nothing to do! (all points are farther than 'maxSearchDist'
180  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
181  }
182 
183  // if necessary we try to guess the best octree level for distances
184  // computation
185  if (params.octreeLevel == 0 &&
186  referenceOctree) // DGM: referenceOctree can be 0 if the input entities
187  // bounding-boxes are disjoint!
188  {
189  params.octreeLevel =
190  comparedOctree->findBestLevelForComparisonWithOctree(
191  referenceOctree);
192  }
193 
194  // whether to compute split distances or not
195  bool computeSplitDistances = false;
196  {
197  for (int i = 0; i < 3; ++i) {
198  if (params.splitDistances[i] &&
199  params.splitDistances[i]->currentSize() ==
200  comparedCloud->size()) {
201  computeSplitDistances = true;
202  params.splitDistances[i]->fill(NAN_VALUE);
203  }
204  }
205  }
206 
207  // additional parameters
208  void* additionalParameters[] = {
209  reinterpret_cast<void*>(referenceCloud),
210  reinterpret_cast<void*>(referenceOctree),
211  reinterpret_cast<void*>(&params),
212  reinterpret_cast<void*>(&maxSearchSquareDistd),
213  reinterpret_cast<void*>(&computeSplitDistances)};
214 
215  int result = DISTANCE_COMPUTATION_RESULTS::SUCCESS;
216 
217  if (comparedOctree->executeFunctionForAllCellsAtLevel(
218  params.octreeLevel,
219  params.localModel == NO_MODEL
222  additionalParameters, params.multiThread, progressCb,
223  "Cloud-Cloud Distance", params.maxThreadCount) == 0) {
224  // something went wrong
225  result = DISTANCE_COMPUTATION_RESULTS::
226  ERROR_EXECUTE_FUNCTION_FOR_ALL_CELLS_AT_LEVEL_FAILURE;
227  }
228 
229  if (comparedOctree && !compOctree) {
230  delete comparedOctree;
231  comparedOctree = nullptr;
232  }
233  if (referenceOctree && !refOctree) {
234  delete referenceOctree;
235  referenceOctree = nullptr;
236  }
237 
238  return result;
239 }
240 
243  GenericIndexedCloudPersist* comparedCloud,
244  GenericIndexedCloudPersist* referenceCloud,
245  DgmOctree*& comparedOctree,
246  DgmOctree*& referenceOctree,
247  PointCoordinateType maxDist,
248  GenericProgressCallback* progressCb /*=0*/) {
249  assert(comparedCloud && referenceCloud);
250 
251  unsigned nA = comparedCloud->size();
252  unsigned nB = referenceCloud->size();
253 
254  if (nA == 0 || nB == 0) return EMPTY_CLOUD;
255 
256  // we compute the bounding box of BOTH clouds
257  CCVector3 minsA, minsB, maxsA, maxsB;
258  comparedCloud->getBoundingBox(minsA, maxsA);
259  referenceCloud->getBoundingBox(minsB, maxsB);
260 
261  // we compute the union of both bounding-boxes
262  CCVector3 maxD, minD;
263  {
264  for (unsigned char k = 0; k < 3; k++) {
265  minD.u[k] = std::min(minsA.u[k], minsB.u[k]);
266  maxD.u[k] = std::max(maxsA.u[k], maxsB.u[k]);
267  }
268  }
269 
270  if (maxDist > 0) {
271  // we reduce the bounding box to the intersection of both bounding-boxes
272  // enlarged by 'maxDist'
273  for (unsigned char k = 0; k < 3; k++) {
274  minD.u[k] = std::max(minD.u[k],
275  std::max(minsA.u[k], minsB.u[k]) - maxDist);
276  maxD.u[k] = std::min(maxD.u[k],
277  std::min(maxsA.u[k], maxsB.u[k]) + maxDist);
278  if (minD.u[k] > maxD.u[k]) {
279  return DISJOINT;
280  }
281  }
282  }
283 
284  CCVector3 minPoints = minD;
285  CCVector3 maxPoints = maxD;
286 
287  // we make this bounding-box cubical (+1% growth to avoid round-off issues)
288  CCMiscTools::MakeMinAndMaxCubical(minD, maxD, 0.01);
289 
290  // then we (re)compute octree A if necessary
291  bool needToRecalculateOctreeA = true;
292  if (comparedOctree && comparedOctree->getNumberOfProjectedPoints() != 0) {
293  needToRecalculateOctreeA = false;
294  for (unsigned char k = 0; k < 3; k++) {
295  if (maxD.u[k] != comparedOctree->getOctreeMaxs().u[k] ||
296  minD.u[k] != comparedOctree->getOctreeMins().u[k]) {
297  needToRecalculateOctreeA = true;
298  break;
299  }
300  }
301  }
302 
303  bool octreeACreated = false;
304  if (needToRecalculateOctreeA) {
305  if (comparedOctree) {
306  comparedOctree->clear();
307  } else {
308  comparedOctree = new DgmOctree(comparedCloud);
309  octreeACreated = true;
310  }
311 
312  if (comparedOctree->build(minD, maxD, &minPoints, &maxPoints,
313  progressCb) < 1) {
314  if (octreeACreated) {
315  delete comparedOctree;
316  comparedOctree = nullptr;
317  }
318  return OUT_OF_MEMORY;
319  }
320  }
321 
322  // and we (re)compute octree B as well if necessary
323  bool needToRecalculateOctreeB = true;
324  if (referenceOctree && referenceOctree->getNumberOfProjectedPoints() != 0) {
325  needToRecalculateOctreeB = false;
326  for (unsigned char k = 0; k < 3; k++) {
327  if (maxD.u[k] != referenceOctree->getOctreeMaxs().u[k] ||
328  minD.u[k] != referenceOctree->getOctreeMins().u[k]) {
329  needToRecalculateOctreeB = true;
330  break;
331  }
332  }
333  }
334 
335  if (needToRecalculateOctreeB) {
336  bool octreeBCreated = false;
337  if (referenceOctree) {
338  referenceOctree->clear();
339  } else {
340  referenceOctree = new DgmOctree(referenceCloud);
341  octreeBCreated = true;
342  }
343 
344  if (referenceOctree->build(minD, maxD, &minPoints, &maxPoints,
345  progressCb) < 1) {
346  if (octreeACreated) {
347  delete comparedOctree;
348  comparedOctree = nullptr;
349  }
350  if (octreeBCreated) {
351  delete referenceOctree;
352  referenceOctree = nullptr;
353  }
354  return OUT_OF_MEMORY;
355  }
356  }
357 
358  // we check that both octrees are ok
359  assert(comparedOctree && referenceOctree);
360  assert(comparedOctree->getNumberOfProjectedPoints() != 0 &&
361  referenceOctree->getNumberOfProjectedPoints() != 0);
362  return SYNCHRONIZED;
363 }
364 
365 // Description of expected 'additionalParameters'
366 // [0] -> (GenericIndexedCloudPersist*) reference cloud
367 // [1] -> (Octree*): reference cloud octree
368 // [2] -> (Cloud2CloudDistancesComputationParams*): parameters
369 // [3] -> (ScalarType*): max search distance (squared)
371  const DgmOctree::octreeCell& cell,
372  void** additionalParameters,
373  NormalizedProgress* nProgress /*=0*/) {
374  // additional parameters
375  const GenericIndexedCloudPersist* referenceCloud =
376  reinterpret_cast<GenericIndexedCloudPersist*>(
377  additionalParameters[0]);
378  const DgmOctree* referenceOctree =
379  reinterpret_cast<DgmOctree*>(additionalParameters[1]);
381  reinterpret_cast<Cloud2CloudDistancesComputationParams*>(
382  additionalParameters[2]);
383  const double* maxSearchSquareDistd =
384  reinterpret_cast<double*>(additionalParameters[3]);
385  bool computeSplitDistances =
386  *reinterpret_cast<bool*>(additionalParameters[4]);
387 
388  // structure for the nearest neighbor search
390  nNSS.level = cell.level;
392  nNSS.theNearestPointIndex = 0;
393  nNSS.maxSearchSquareDistd = *maxSearchSquareDistd;
394 
395  // we can already compute the position of the 'equivalent' cell in the
396  // reference octree
397  referenceOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
398  true);
399  // and we deduce its center
400  referenceOctree->computeCellCenter(nNSS.cellPos, cell.level,
401  nNSS.cellCenter);
402 
403  // for each point of the current cell (compared octree) we look for its
404  // nearest neighbour in the reference cloud
405  unsigned pointCount = cell.points->size();
406  for (unsigned i = 0; i < pointCount; i++) {
407  cell.points->getPoint(i, nNSS.queryPoint);
408 
409  if (params->CPSet ||
410  referenceCloud->testVisibility(nNSS.queryPoint) ==
411  POINT_VISIBLE) // to build the closest point set up we must
412  // process the point whatever its visibility
413  // is!
414  {
415  double squareDist =
417  nNSS);
418  if (squareDist >= 0) {
419  ScalarType dist = static_cast<ScalarType>(sqrt(squareDist));
420  cell.points->setPointScalarValue(i, dist);
421 
422  if (params->CPSet) {
423  params->CPSet->setPointIndex(
424  cell.points->getPointGlobalIndex(i),
425  nNSS.theNearestPointIndex);
426  }
427 
428  if (computeSplitDistances) {
429  CCVector3 P;
430  referenceCloud->getPoint(nNSS.theNearestPointIndex, P);
431 
432  unsigned index = cell.points->getPointGlobalIndex(i);
433  if (params->splitDistances[0])
434  params->splitDistances[0]->setValue(
435  index, static_cast<ScalarType>(
436  nNSS.queryPoint.x - P.x));
437  if (params->splitDistances[1])
438  params->splitDistances[1]->setValue(
439  index, static_cast<ScalarType>(
440  nNSS.queryPoint.y - P.y));
441  if (params->splitDistances[2])
442  params->splitDistances[2]->setValue(
443  index, static_cast<ScalarType>(
444  nNSS.queryPoint.z - P.z));
445  }
446  } else {
447  assert(!params->CPSet);
448  }
449  } else {
451  }
452 
453  if (nProgress && !nProgress->oneStep()) {
454  return false;
455  }
456  }
457 
458  return true;
459 }
460 
461 // Description of expected 'additionalParameters'
462 // [0] -> (GenericIndexedCloudPersist*) reference cloud
463 // [1] -> (Octree*): reference cloud octree
464 // [2] -> (Cloud2CloudDistancesComputationParams*): parameters
465 // [3] -> (ScalarType*): max search distance (squared)
467  const DgmOctree::octreeCell& cell,
468  void** additionalParameters,
469  NormalizedProgress* nProgress /*=0*/) {
470  // additional parameters
471  GenericIndexedCloudPersist* referenceCloud =
472  reinterpret_cast<GenericIndexedCloudPersist*>(
473  additionalParameters[0]);
474  const DgmOctree* referenceOctree =
475  reinterpret_cast<DgmOctree*>(additionalParameters[1]);
477  reinterpret_cast<Cloud2CloudDistancesComputationParams*>(
478  additionalParameters[2]);
479  const double* maxSearchSquareDistd =
480  reinterpret_cast<double*>(additionalParameters[3]);
481  bool computeSplitDistances =
482  *reinterpret_cast<bool*>(additionalParameters[4]);
483 
484  assert(params && params->localModel != NO_MODEL);
485 
486  // structure for the nearest neighbor search
488  nNSS.level = cell.level;
490  nNSS.theNearestPointIndex = 0;
491  nNSS.maxSearchSquareDistd = *maxSearchSquareDistd;
492  // we already compute the position of the 'equivalent' cell in the reference
493  // octree
494  referenceOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
495  true);
496  // and we deduce its center
497  referenceOctree->computeCellCenter(nNSS.cellPos, cell.level,
498  nNSS.cellCenter);
499 
500  // structures for determining the nearest neighbours of the 'nearest
501  // neighbour' (to compute the local model) either inside a sphere or the k
502  // nearest
504  nNSS_Model.level = cell.level;
505  if (params->useSphericalSearchForLocalModel) {
506  nNSS_Model.prepare(
507  static_cast<PointCoordinateType>(params->radiusForLocalModel),
508  cell.parentOctree->getCellSize(cell.level));
509  } else {
510  nNSS_Model.minNumberOfNeighbors = params->kNNForLocalModel;
511  }
512 
513  // already computed models
514  std::vector<const LocalModel*> models;
515 
516  // for each point of the current cell (compared octree) we look for its
517  // nearest neighbour in the reference cloud
518  unsigned pointCount = cell.points->size();
519  for (unsigned i = 0; i < pointCount; ++i) {
520  // distance of the current point
521  ScalarType distPt = NAN_VALUE;
522 
523  cell.points->getPoint(i, nNSS.queryPoint);
524  if (params->CPSet ||
525  referenceCloud->testVisibility(nNSS.queryPoint) ==
526  POINT_VISIBLE) // to build the closest point set up we must
527  // process the point whatever its visibility
528  // is!
529  {
530  // first, we look for the nearest point to "_queryPoint" in the
531  // reference cloud
532  double squareDistToNearestPoint =
534  nNSS);
535 
536  // if it exists
537  if (squareDistToNearestPoint >= 0) {
538  ScalarType distToNearestPoint =
539  static_cast<ScalarType>(sqrt(squareDistToNearestPoint));
540 
541  CCVector3 nearestPoint;
542  referenceCloud->getPoint(nNSS.theNearestPointIndex,
543  nearestPoint);
544 
545  // local model for the 'nearest point'
546  const LocalModel* lm = nullptr;
547 
548  if (params->reuseExistingLocalModels) {
549  // we look if the nearest point is close to existing models
550  for (std::vector<const LocalModel*>::const_iterator it =
551  models.begin();
552  it != models.end(); ++it) {
553  // we take the first model that 'includes' the nearest
554  // point
555  if (((*it)->getCenter() - nearestPoint).norm2() <=
556  (*it)->getSquareSize()) {
557  lm = *it;
558  break;
559  }
560  }
561  }
562 
563  // create new local model
564  if (!lm) {
565  nNSS_Model.queryPoint = nearestPoint;
566 
567  // update cell pos information (as the nearestPoint may not
568  // be inside the same cell as the actual query point!)
569  {
570  bool inbounds = false;
571  Tuple3i cellPos;
572  referenceOctree->getTheCellPosWhichIncludesThePoint(
573  &nearestPoint, cellPos, cell.level, inbounds);
574  // if the cell is different or the structure has not yet
575  // been initialized, we reset it!
576  if (cellPos.x != nNSS_Model.cellPos.x ||
577  cellPos.y != nNSS_Model.cellPos.y ||
578  cellPos.z != nNSS_Model.cellPos.z) {
579  nNSS_Model.cellPos = cellPos;
580  referenceOctree->computeCellCenter(
581  nNSS_Model.cellPos, nNSS_Model.level,
582  nNSS_Model.cellCenter);
583  assert(inbounds);
584  nNSS_Model.minimalCellsSetToVisit.clear();
585  nNSS_Model.pointsInNeighbourhood.clear();
587  inbounds ? 0 : 1;
588  // nNSS_Model.theNearestPointIndex = 0;
589  }
590  }
591  // let's grab the nearest neighbours of the 'nearest point'
592  unsigned kNN = 0;
593  if (params->useSphericalSearchForLocalModel) {
594  // we only need to sort neighbours if we want to use the
595  // 'reuseExistingLocalModels' optimization warning:
596  // there may be more points at the end of
597  // nNSS.pointsInNeighbourhood than the actual nearest
598  // neighbors (kNN)!
599  kNN = referenceOctree
601  nNSS_Model,
602  static_cast<PointCoordinateType>(
603  params->radiusForLocalModel),
604  params->reuseExistingLocalModels);
605  } else {
606  kNN = referenceOctree
608  nNSS_Model);
609  kNN = std::min(kNN, params->kNNForLocalModel);
610  }
611 
612  // if there's enough neighbours
613  if (kNN >= CV_LOCAL_MODEL_MIN_SIZE[params->localModel]) {
614  DgmOctreeReferenceCloud neighboursCloud(
615  &nNSS_Model.pointsInNeighbourhood, kNN);
616  Neighbourhood Z(&neighboursCloud);
617 
618  // Neighbours are sorted, so the farthest is at the end.
619  // It also gives us an approximation of the model 'size'
620  const double& maxSquareDist =
621  nNSS_Model.pointsInNeighbourhood[kNN - 1]
622  .squareDistd;
623  if (maxSquareDist >
624  0) // DGM: with duplicate points, all neighbors can
625  // be at the same place :(
626  {
627  lm = LocalModel::New(
628  params->localModel, Z, nearestPoint,
629  static_cast<PointCoordinateType>(
630  maxSquareDist));
631  if (lm && params->reuseExistingLocalModels) {
632  // we add the model to the 'existing models'
633  // list
634  try {
635  models.push_back(lm);
636  } catch (const std::bad_alloc&) {
637  // not enough memory!
638  while (!models.empty()) {
639  delete models.back();
640  models.pop_back();
641  }
642  return false;
643  }
644  }
645  }
646  // neighbours->clear();
647  }
648  }
649 
650  // if we have a local model
651  if (lm) {
652  CCVector3 nearestModelPoint;
653  ScalarType distToModel =
655  &nNSS.queryPoint,
656  computeSplitDistances ? &nearestModelPoint
657  : nullptr);
658 
659  // we take the best estimation between the nearest neighbor
660  // and the model! this way we only reduce any potential
661  // noise (that would be due to sampling) instead of 'adding'
662  // noise if the model is badly shaped
663  if (distToNearestPoint <= distToModel) {
664  distPt = distToNearestPoint;
665  } else {
666  distPt = distToModel;
667  nearestPoint = nearestModelPoint;
668  }
669 
670  if (!params->reuseExistingLocalModels) {
671  // we don't need the local model anymore!
672  delete lm;
673  lm = nullptr;
674  }
675 
676  if (computeSplitDistances) {
677  unsigned index = cell.points->getPointGlobalIndex(i);
678  if (params->splitDistances[0])
679  params->splitDistances[0]->setValue(
680  index,
681  static_cast<ScalarType>(nNSS.queryPoint.x -
682  nearestPoint.x));
683  if (params->splitDistances[1])
684  params->splitDistances[1]->setValue(
685  index,
686  static_cast<ScalarType>(nNSS.queryPoint.y -
687  nearestPoint.y));
688  if (params->splitDistances[2])
689  params->splitDistances[2]->setValue(
690  index,
691  static_cast<ScalarType>(nNSS.queryPoint.z -
692  nearestPoint.z));
693  }
694  } else {
695  distPt = distToNearestPoint;
696  }
697  } else if (nNSS.maxSearchSquareDistd > 0) {
698  distPt = static_cast<ScalarType>(
699  sqrt(nNSS.maxSearchSquareDistd));
700  }
701 
702  if (params->CPSet) {
703  params->CPSet->setPointIndex(
704  cell.points->getPointGlobalIndex(i),
705  nNSS.theNearestPointIndex);
706  }
707  }
708 
709  cell.points->setPointScalarValue(i, distPt);
710 
711  if (nProgress && !nProgress->oneStep()) {
712  return false;
713  }
714  }
715 
716  // clear all models for this cell
717  while (!models.empty()) {
718  delete models.back();
719  models.pop_back();
720  }
721 
722  return true;
723 }
724 
725 // Internal structure used by
726 // DistanceComputationTools::computeCloud2MeshDistances
727 struct CellToTest {
728  // Warning: put the non aligned members (< 4 bytes) at the end to avoid too
729  // much alignment padding!
730 
732  Tuple3i pos; // 12 bytes
734  int cellSize; // 4 bytes
736  unsigned char level; // 1 byte (+ 3 for alignment)
737 
738  // Total //20 bytes
739 };
740 
742  OctreeAndMeshIntersection* intersection,
743  unsigned char octreeLevel,
744  GenericProgressCallback* progressCb /*=0*/) {
745  if (!intersection) {
746  assert(false);
747  return DISTANCE_COMPUTATION_RESULTS::
748  ERROR_INVALID_OCTREE_AND_MESH_INTERSECTION;
749  }
750 
751  DgmOctree* octree = intersection->octree;
752  GenericIndexedMesh* mesh = intersection->mesh;
753  if (!octree || !mesh) {
754  assert(false);
755  return DISTANCE_COMPUTATION_RESULTS::
756  ERROR_INVALID_OCTREE_AND_MESH_INTERSECTION;
757  }
758 
759  // cell dimension
761  CCVector3 halfCellDimensions(cellLength / 2, cellLength / 2,
762  cellLength / 2);
763 
764  std::vector<CellToTest> cellsToTest(1); // initial size must be > 0
765  unsigned cellsToTestCount = 0;
766 
767  // get octree box
768  const CCVector3& minBB = octree->getOctreeMins();
769  // and the number of triangles
770  unsigned numberOfTriangles = mesh->size();
771 
772  // for progress notification
773  NormalizedProgress nProgress(progressCb, numberOfTriangles);
774  if (progressCb) {
775  if (progressCb->textCanBeEdited()) {
776  char buffer[64];
777  sprintf(buffer, "Triangles: %u", numberOfTriangles);
778  progressCb->setInfo(buffer);
779  progressCb->setMethodTitle("Intersect Grid/Mesh");
780  }
781  progressCb->update(0);
782  progressCb->start();
783  }
784 
785  // For each triangle: look for intersecting cells
786  mesh->placeIteratorAtBeginning();
787  int result = 0;
788  for (unsigned n = 0; n < numberOfTriangles; ++n) {
789  // get the positions (in the grid) of each vertex
790  const GenericTriangle* T = mesh->_getNextTriangle();
791  const CCVector3* triPoints[3] = {T->_getA(), T->_getB(), T->_getC()};
792 
793  CCVector3 AB = (*triPoints[1]) - (*triPoints[0]);
794  CCVector3 BC = (*triPoints[2]) - (*triPoints[1]);
795  CCVector3 CA = (*triPoints[0]) - (*triPoints[2]);
796 
797  // be sure that the triangle is not degenerate!!!
798  if (GreaterThanEpsilon(AB.norm2()) && GreaterThanEpsilon(BC.norm2()) &&
799  GreaterThanEpsilon(CA.norm2())) {
800  Tuple3i cellPos[3];
801  octree->getTheCellPosWhichIncludesThePoint(triPoints[0], cellPos[0],
802  octreeLevel);
803  octree->getTheCellPosWhichIncludesThePoint(triPoints[1], cellPos[1],
804  octreeLevel);
805  octree->getTheCellPosWhichIncludesThePoint(triPoints[2], cellPos[2],
806  octreeLevel);
807 
808  // compute the triangle bounding-box
809  Tuple3i minPos, maxPos;
810  for (int k = 0; k < 3; k++) {
811  minPos.u[k] =
812  std::min(cellPos[0].u[k],
813  std::min(cellPos[1].u[k], cellPos[2].u[k]));
814  maxPos.u[k] =
815  std::max(cellPos[0].u[k],
816  std::max(cellPos[1].u[k], cellPos[2].u[k]));
817  }
818 
819  // first cell
820  assert(cellsToTest.capacity() != 0);
821  cellsToTestCount = 1;
822  CellToTest* _currentCell = &cellsToTest[0 /*cellsToTestCount-1*/];
823 
824  _currentCell->pos = minPos;
825  CCVector3 distanceToOctreeMinBorder = minBB - (*triPoints[0]);
826 
827  // compute the triangle normal
828  CCVector3 N = AB.cross(BC);
829 
830  // max distance (in terms of cell) between the vertices
831  int maxSize = 0;
832  {
833  Tuple3i delta = maxPos - minPos + Tuple3i(1, 1, 1);
834  maxSize = std::max(delta.x, delta.y);
835  maxSize = std::max(maxSize, delta.z);
836  }
837 
838  // we deduce the smallest bounding 'octree' cell
839  //(not a real octree cell in fact as its starting position is
840  // anywhere in the grid and it can even 'outbounds' the grid, i.e.
841  // currentCell.pos.u[k]+currentCell.cellSize > octreeLength)
842  static const double LOG_2 = log(2.0);
843  _currentCell->level =
844  octreeLevel -
845  (maxSize > 1 ? static_cast<unsigned char>(ceil(
846  log(static_cast<double>(maxSize)) /
847  LOG_2))
848  : 0);
849  _currentCell->cellSize = (1 << (octreeLevel - _currentCell->level));
850 
851  // now we can (recursively) find the intersecting cells
852  while (cellsToTestCount != 0) {
853  _currentCell = &cellsToTest[--cellsToTestCount];
854 
855  // new cells may be written over the actual one
856  // so we need to remember its position!
857  Tuple3i currentCellPos = _currentCell->pos;
858 
859  // if we have reached the maximal subdivision level
860  if (_currentCell->level == octreeLevel) {
861  // compute the (absolute) cell center
862  octree->computeCellCenter(currentCellPos, octreeLevel, AB);
863 
864  // check that the triangle do intersects the cell (box)
865  if (CCMiscTools::TriBoxOverlap(AB, halfCellDimensions,
866  triPoints)) {
867  if ((currentCellPos.x >=
868  intersection->minFillIndexes.x &&
869  currentCellPos.x <=
870  intersection->maxFillIndexes.x) &&
871  (currentCellPos.y >=
872  intersection->minFillIndexes.y &&
873  currentCellPos.y <=
874  intersection->maxFillIndexes.y) &&
875  (currentCellPos.z >=
876  intersection->minFillIndexes.z &&
877  currentCellPos.z <=
878  intersection->maxFillIndexes.z)) {
879  Tuple3i cellPos = currentCellPos -
880  intersection->minFillIndexes;
881 
882  if (intersection->perCellTriangleList
883  .isInitialized()) {
884  TriangleList*& triList =
885  intersection->perCellTriangleList
886  .getValue(cellPos);
887  if (!triList) {
888  triList = new TriangleList();
889  // triList->cellCode =
890  // DgmOctree::GenerateTruncatedCellCode(currentCellPos,
891  // octreeLevel);
892  }
893 
894  // add the triangle to the current 'intersecting
895  // triangles' list
896  triList->push(n);
897  }
898 
899  if (intersection->distanceTransform) {
900  intersection->distanceTransform->setValue(
901  cellPos, 1);
902  }
903  }
904  }
905  } else {
906  int halfCellSize = (_currentCell->cellSize >> 1);
907 
908  // compute the position of each cell 'neighbors' relatively
909  // to the triangle (3*3*3 = 27, including the cell itself)
910  char pointsPosition[27];
911  {
912  char* _pointsPosition = pointsPosition;
913  for (int i = 0; i < 3; ++i) {
914  AB.x = distanceToOctreeMinBorder.x +
915  static_cast<PointCoordinateType>(
916  currentCellPos.x +
917  i * halfCellSize) *
918  cellLength;
919  for (int j = 0; j < 3; ++j) {
920  AB.y = distanceToOctreeMinBorder.y +
921  static_cast<PointCoordinateType>(
922  currentCellPos.y +
923  j * halfCellSize) *
924  cellLength;
925  for (int k = 0; k < 3; ++k) {
926  AB.z = distanceToOctreeMinBorder.z +
927  static_cast<PointCoordinateType>(
928  currentCellPos.z +
929  k * halfCellSize) *
930  cellLength;
931 
932  // determine on which side the triangle is
933  *_pointsPosition++ /*pointsPosition[i*9+j*3+k]*/
934  = (AB.dot(N) < 0 ? -1 : 1);
935  }
936  }
937  }
938  }
939 
940  // if necessary we enlarge the queue
941  if (cellsToTestCount + 27 > cellsToTest.capacity()) {
942  try {
943  cellsToTest.resize(
944  std::max(cellsToTest.capacity() + 27,
945  2 * cellsToTest.capacity()));
946  } catch (const std::bad_alloc&) {
947  // out of memory
948  return DISTANCE_COMPUTATION_RESULTS::
949  ERROR_OUT_OF_MEMORY;
950  }
951  }
952 
953  // the first new cell will be written over the actual one
954  CellToTest* _newCell = &cellsToTest[cellsToTestCount];
955  _newCell->level++;
956  _newCell->cellSize = halfCellSize;
957 
958  // we look at the position of the 8 sub-cubes relatively to
959  // the triangle
960  for (int i = 0; i < 2; ++i) {
961  _newCell->pos.x = currentCellPos.x + i * halfCellSize;
962  // quick test to determine if the cube is potentially
963  // intersecting the triangle's bbox
964  if (static_cast<int>(_newCell->pos.x) + halfCellSize >=
965  minPos.x &&
966  static_cast<int>(_newCell->pos.x) <= maxPos.x) {
967  for (int j = 0; j < 2; ++j) {
968  _newCell->pos.y =
969  currentCellPos.y + j * halfCellSize;
970  if (static_cast<int>(_newCell->pos.y) +
971  halfCellSize >=
972  minPos.y &&
973  static_cast<int>(_newCell->pos.y) <=
974  maxPos.y) {
975  for (int k = 0; k < 2; ++k) {
976  _newCell->pos.z = currentCellPos.z +
977  k * halfCellSize;
978  if (static_cast<int>(_newCell->pos.z) +
979  halfCellSize >=
980  minPos.z &&
981  static_cast<int>(_newCell->pos.z) <=
982  maxPos.z) {
983  const char* _pointsPosition =
984  pointsPosition +
985  (i * 9 + j * 3 + k);
986  char sum = _pointsPosition[0] +
987  _pointsPosition[1] +
988  _pointsPosition[3] +
989  _pointsPosition[4] +
990  _pointsPosition[9] +
991  _pointsPosition[10] +
992  _pointsPosition[12] +
993  _pointsPosition[13];
994 
995  // if all the sub-cube vertices are
996  // not on the same side, then the
997  // triangle may intersect the cell
998  if (abs(sum) < 8) {
999  // we make newCell point on next
1000  // cell in array (we copy
1001  // current info by the way)
1002  cellsToTest
1003  [++cellsToTestCount] =
1004  *_newCell;
1005  _newCell =
1006  &cellsToTest
1007  [cellsToTestCount];
1008  }
1009  }
1010  }
1011  }
1012  }
1013  }
1014  }
1015  }
1016  }
1017  }
1018 
1019  if (progressCb && !nProgress.oneStep()) {
1020  // cancel by user
1021  result = -2;
1022  break;
1023  }
1024  }
1025 
1026  return result;
1027 }
1028 
1031  ReferenceCloud& Yk,
1032  unsigned& remainingPoints,
1034  std::vector<unsigned>& trianglesToTest,
1035  std::size_t& trianglesToTestCount,
1036  std::vector<ScalarType>& minDists,
1037  ScalarType maxRadius,
1039  Cloud2MeshDistancesComputationParams& params) {
1040  assert(mesh);
1041  assert(remainingPoints <= Yk.size());
1042  assert(trianglesToTestCount <= trianglesToTest.size());
1043 
1044  bool firstComparisonDone = (trianglesToTestCount != 0);
1045 
1046  CCVector3 nearestPoint;
1047  CCVector3* _nearestPoint = params.CPSet ? &nearestPoint : nullptr;
1048 
1049  // for each triangle
1050  while (trianglesToTestCount != 0) {
1051  // we query the vertex coordinates
1053  mesh->getTriangleVertices(trianglesToTest[--trianglesToTestCount],
1054  tri.A, tri.B, tri.C);
1055 
1056  // for each point inside the current cell
1057  if (params.signedDistances) {
1058  // we have to use absolute distances
1059  for (unsigned j = 0; j < remainingPoints; ++j) {
1060  // compute the distance to the triangle
1061  ScalarType dPTri =
1063  Yk.getPoint(j), &tri, true, _nearestPoint);
1064  // keep it if it's smaller
1065  ScalarType min_d = Yk.getPointScalarValue(j);
1066  if (!ScalarField::ValidValue(min_d) ||
1067  min_d * min_d > dPTri * dPTri) {
1068  Yk.setPointScalarValue(j,
1069  params.flipNormals ? -dPTri : dPTri);
1070  if (params.CPSet) {
1071  // Closest Point Set: save the nearest point as well
1072  assert(_nearestPoint);
1073  *const_cast<CCVector3*>(params.CPSet->getPoint(
1074  Yk.getPointGlobalIndex(j))) = *_nearestPoint;
1075  }
1076  }
1077  }
1078  } else // squared distances
1079  {
1080  for (unsigned j = 0; j < remainingPoints; ++j) {
1081  // compute the (SQUARED) distance to the triangle
1082  ScalarType dPTri =
1084  Yk.getPoint(j), &tri, false, _nearestPoint);
1085  // keep it if it's smaller
1086  ScalarType min_d = Yk.getPointScalarValue(j);
1087  if (!ScalarField::ValidValue(min_d) || dPTri < min_d) {
1088  Yk.setPointScalarValue(j, dPTri);
1089  if (params.CPSet) {
1090  // Closest Point Set: save the nearest point as well
1091  assert(_nearestPoint);
1092  *const_cast<CCVector3*>(params.CPSet->getPoint(
1093  Yk.getPointGlobalIndex(j))) = *_nearestPoint;
1094  }
1095  }
1096  }
1097  }
1098  }
1099 
1100  // we can 'remove' all the eligible points at the current neighborhood
1101  // radius
1102  if (firstComparisonDone) {
1104  for (unsigned j = 0; j < remainingPoints; ++j) {
1105  // eligibility distance
1106  ScalarType eligibleDist = minDists[j] + maxRadius;
1107  ScalarType dPTri = Yk.getCurrentPointScalarValue();
1108  if (params.signedDistances) {
1109  // need to get the square distance in all cases
1110  dPTri *= dPTri;
1111  }
1112  if (dPTri <= eligibleDist * eligibleDist) {
1113  // remove this point
1115  // and do the same for the 'minDists' array! (see
1116  // ReferenceCloud::removeCurrentPointGlobalIndex)
1117  assert(remainingPoints != 0);
1118  minDists[j] = minDists[--remainingPoints];
1119  // minDists.pop_back();
1120  --j;
1121  } else {
1122  Yk.forwardIterator();
1123  }
1124  }
1125  }
1126 
1127  return true;
1128 }
1129 
1130 int ComputeMaxNeighborhoodLength(ScalarType maxSearchDist,
1131  PointCoordinateType cellSize) {
1132  return static_cast<int>(
1133  ceil(maxSearchDist / cellSize +
1134  static_cast<ScalarType>((sqrt(2.0) - 1.0) / 2)));
1135 }
1136 
1137 #ifdef ENABLE_CLOUD2MESH_DIST_MT
1138 
1139 /*** MULTI THREADING WRAPPER ***/
1140 static DgmOctree* s_octree_MT = nullptr;
1141 static NormalizedProgress* s_normProgressCb_MT = nullptr;
1142 static OctreeAndMeshIntersection* s_intersection_MT = nullptr;
1143 static bool s_cellFunc_MT_success = true;
1144 static cloudViewer::DistanceComputationTools::
1145  Cloud2MeshDistancesComputationParams s_params_MT;
1146 
1147 //'processTriangles' mechanism (based on bit mask)
1148 static std::vector<std::vector<bool>*> s_bitArrayPool_MT;
1149 static bool s_useBitArrays_MT = true;
1150 static QMutex s_currentBitMaskMutex;
1151 
1152 void cloudMeshDistCellFunc_MT(const DgmOctree::IndexAndCode& desc) {
1153  if (!s_cellFunc_MT_success) {
1154  // skip this cell if the process is aborted / has failed
1155  return;
1156  }
1157 
1158  if (s_normProgressCb_MT && !s_normProgressCb_MT->oneStep()) {
1159  s_cellFunc_MT_success = false;
1160  return;
1161  }
1162 
1163  ReferenceCloud Yk(s_octree_MT->associatedCloud());
1164  s_octree_MT->getPointsInCellByCellIndex(&Yk, desc.theIndex,
1165  s_params_MT.octreeLevel);
1166 
1167  // min distance array
1168  unsigned remainingPoints = Yk.size();
1169 
1170  std::vector<ScalarType> minDists;
1171  try {
1172  minDists.resize(remainingPoints);
1173  } catch (const std::bad_alloc&) {
1174  // not enough memory
1175  s_cellFunc_MT_success = false;
1176  return;
1177  }
1178 
1179  // get cell pos
1180  Tuple3i startPos;
1181  s_octree_MT->getCellPos(desc.theCode, s_params_MT.octreeLevel, startPos,
1182  true);
1183 
1184  // get the distance to the nearest and farthest boundaries
1185  Tuple3i signedDistToLowerBorder, signedDistToUpperBorder;
1186  signedDistToLowerBorder = startPos - s_intersection_MT->minFillIndexes;
1187  signedDistToUpperBorder = s_intersection_MT->maxFillIndexes - startPos;
1188 
1189  Tuple3i minDistToGridBoundaries, maxDistToGridBoundaries;
1190  for (unsigned k = 0; k < 3; ++k) {
1191  minDistToGridBoundaries.u[k] =
1192  std::max(std::max(-signedDistToLowerBorder.u[k], 0),
1193  std::max(0, -signedDistToUpperBorder.u[k]));
1194  maxDistToGridBoundaries.u[k] =
1195  std::max(std::max(signedDistToLowerBorder.u[k], 0),
1196  std::max(0, signedDistToUpperBorder.u[k]));
1197  }
1198  int minDistToBoundaries = std::max(
1199  minDistToGridBoundaries.x,
1200  std::max(minDistToGridBoundaries.y, minDistToGridBoundaries.z));
1201  int maxDistToBoundaries = std::max(
1202  maxDistToGridBoundaries.x,
1203  std::max(maxDistToGridBoundaries.y, maxDistToGridBoundaries.z));
1204  int maxIntDist = maxDistToBoundaries;
1205 
1206  if (s_params_MT.maxSearchDist > 0) {
1207  // no need to look farther than 'maxNeighbourhoodLength'
1208  int maxNeighbourhoodLength = ComputeMaxNeighborhoodLength(
1209  s_params_MT.maxSearchDist,
1210  s_octree_MT->getCellSize(s_params_MT.octreeLevel));
1211  if (maxNeighbourhoodLength < maxIntDist)
1212  maxIntDist = maxNeighbourhoodLength;
1213 
1214  ScalarType maxDistance = s_params_MT.maxSearchDist;
1215  if (!s_params_MT.signedDistances) {
1216  // we compute squared distances when not in 'signed' mode!
1217  maxDistance = s_params_MT.maxSearchDist * s_params_MT.maxSearchDist;
1218  }
1219 
1220  for (unsigned j = 0; j < remainingPoints; ++j)
1221  Yk.setPointScalarValue(j, maxDistance);
1222  }
1223 
1224  // determine the cell center
1225  CCVector3 cellCenter;
1226  s_octree_MT->computeCellCenter(startPos, s_params_MT.octreeLevel,
1227  cellCenter);
1228 
1229  // express 'startPos' relatively to the grid borders
1230  startPos -= s_intersection_MT->minFillIndexes;
1231 
1232  // octree cell size
1233  const PointCoordinateType& cellLength =
1234  s_octree_MT->getCellSize(s_params_MT.octreeLevel);
1235 
1236  // useful variables
1237  std::vector<unsigned> trianglesToTest;
1238  std::size_t trianglesToTestCount = 0;
1239  std::size_t trianglesToTestCapacity = 0;
1240 
1241  // bit mask for efficient comparisons
1242  std::vector<bool>* bitArray = nullptr;
1243  if (s_useBitArrays_MT) {
1244  const unsigned numTri = s_intersection_MT->mesh->size();
1245  s_currentBitMaskMutex.lock();
1246  if (s_bitArrayPool_MT.empty()) {
1247  bitArray = new std::vector<bool>(numTri);
1248  } else {
1249  bitArray = s_bitArrayPool_MT.back();
1250  s_bitArrayPool_MT.pop_back();
1251  }
1252  s_currentBitMaskMutex.unlock();
1253  bitArray->assign(numTri, false);
1254  }
1255 
1256  // for each point, we pre-compute its distance to the nearest cell border
1257  //(will be handy later)
1258  Yk.placeIteratorAtBeginning();
1259  for (unsigned j = 0; j < remainingPoints; ++j) {
1260  // coordinates of the current point
1261  const CCVector3* tempPt = Yk.getCurrentPointCoordinates();
1262  // distance du bord le plus proche = taille de la cellule - distance la
1263  // plus grande par rapport au centre de la cellule
1265  *tempPt, cellLength, cellCenter);
1266  Yk.forwardIterator();
1267  }
1268 
1269  // let's find the nearest triangles for each point in the neighborhood 'Yk'
1270  ScalarType maxRadius = 0;
1271  for (int dist = minDistToBoundaries;
1272  remainingPoints != 0 && dist <= maxIntDist;
1273  ++dist, maxRadius += static_cast<ScalarType>(cellLength)) {
1274  // test the neighbor cells at distance = 'dist'
1275  // a,b,c,d,e,f are the extents of this neighborhood
1276  // for the 6 main directions -X,+X,-Y,+Y,-Z,+Z
1277  int a = std::min(dist, signedDistToLowerBorder.x);
1278  int b = std::min(dist, signedDistToUpperBorder.x);
1279  int c = std::min(dist, signedDistToLowerBorder.y);
1280  int d = std::min(dist, signedDistToUpperBorder.y);
1281  int e = std::min(dist, signedDistToLowerBorder.z);
1282  int f = std::min(dist, signedDistToUpperBorder.z);
1283 
1284  for (int i = -a; i <= b; ++i) {
1285  bool imax = (abs(i) == dist);
1286  Tuple3i cellPos(startPos.x + i, 0, 0);
1287 
1288  for (int j = -c; j <= d; j++) {
1289  cellPos.y = startPos.y + j;
1290 
1291  // if i or j is 'maximal'
1292  if (imax || abs(j) == dist) {
1293  // we must be on the border of the neighborhood
1294  for (int k = -e; k <= f; k++) {
1295  // are there any triangles near this cell?
1296  cellPos.z = startPos.z + k;
1297  TriangleList* triList =
1298  s_intersection_MT->perCellTriangleList.getValue(
1299  cellPos);
1300  if (triList) {
1301  if (trianglesToTestCount + triList->indexes.size() >
1302  trianglesToTestCapacity) {
1303  trianglesToTestCapacity = std::max(
1304  trianglesToTestCount +
1305  triList->indexes.size(),
1306  2 * trianglesToTestCount);
1307  trianglesToTest.resize(trianglesToTestCapacity);
1308  }
1309  // let's test all the triangles that intersect this
1310  // cell
1311  for (unsigned p = 0; p < triList->indexes.size();
1312  ++p) {
1313  if (bitArray) {
1314  const unsigned indexTri =
1315  triList->indexes[p];
1316  // if the triangles has not been processed
1317  // yet
1318  if (!(*bitArray)[indexTri]) {
1319  trianglesToTest
1320  [trianglesToTestCount++] =
1321  indexTri;
1322  (*bitArray)[indexTri] = true;
1323  }
1324  } else {
1325  trianglesToTest[trianglesToTestCount++] =
1326  triList->indexes[p];
1327  }
1328  }
1329  }
1330  }
1331  } else // we must go the cube border
1332  {
1333  if (e == dist) //'negative' side
1334  {
1335  // are there any triangles near this cell?
1336  cellPos.z = startPos.z - e;
1337  TriangleList* triList =
1338  s_intersection_MT->perCellTriangleList.getValue(
1339  cellPos);
1340  if (triList) {
1341  if (trianglesToTestCount + triList->indexes.size() >
1342  trianglesToTestCapacity) {
1343  trianglesToTestCapacity = std::max(
1344  trianglesToTestCount +
1345  triList->indexes.size(),
1346  2 * trianglesToTestCount);
1347  trianglesToTest.resize(trianglesToTestCapacity);
1348  }
1349  // let's test all the triangles that intersect this
1350  // cell
1351  for (unsigned p = 0; p < triList->indexes.size();
1352  ++p) {
1353  if (bitArray) {
1354  const unsigned& indexTri =
1355  triList->indexes[p];
1356  // if the triangles has not been processed
1357  // yet
1358  if (!(*bitArray)[indexTri]) {
1359  trianglesToTest
1360  [trianglesToTestCount++] =
1361  indexTri;
1362  (*bitArray)[indexTri] = true;
1363  }
1364  } else {
1365  trianglesToTest[trianglesToTestCount++] =
1366  triList->indexes[p];
1367  }
1368  }
1369  }
1370  }
1371 
1372  if (f == dist && dist > 0) //'positive' side
1373  {
1374  // are there any triangles near this cell?
1375  cellPos.z = startPos.z + f;
1376  TriangleList* triList =
1377  s_intersection_MT->perCellTriangleList.getValue(
1378  cellPos);
1379  if (triList) {
1380  if (trianglesToTestCount + triList->indexes.size() >
1381  trianglesToTestCapacity) {
1382  trianglesToTestCapacity = std::max(
1383  trianglesToTestCount +
1384  triList->indexes.size(),
1385  2 * trianglesToTestCount);
1386  trianglesToTest.resize(trianglesToTestCapacity);
1387  }
1388  // let's test all the triangles that intersect this
1389  // cell
1390  for (unsigned p = 0; p < triList->indexes.size();
1391  ++p) {
1392  if (bitArray) {
1393  const unsigned& indexTri =
1394  triList->indexes[p];
1395  // if the triangles has not been processed
1396  // yet
1397  if (!(*bitArray)[indexTri]) {
1398  trianglesToTest
1399  [trianglesToTestCount++] =
1400  indexTri;
1401  (*bitArray)[indexTri] = true;
1402  }
1403  } else {
1404  trianglesToTest[trianglesToTestCount++] =
1405  triList->indexes[p];
1406  }
1407  }
1408  }
1409  }
1410  }
1411  }
1412  }
1413 
1414  ComparePointsAndTriangles(Yk, remainingPoints, s_intersection_MT->mesh,
1415  trianglesToTest, trianglesToTestCount,
1416  minDists, maxRadius, s_params_MT);
1417  }
1418 
1419  // Save the bit mask
1420  if (bitArray) {
1421  s_currentBitMaskMutex.lock();
1422  s_bitArrayPool_MT.push_back(bitArray);
1423  s_currentBitMaskMutex.unlock();
1424  }
1425 }
1426 
1427 #endif
1428 
1430  OctreeAndMeshIntersection* intersection,
1432  GenericProgressCallback* progressCb /*=0*/) {
1433  assert(intersection);
1434  assert(!params.signedDistances ||
1435  !intersection->distanceTransform); // signed distances are not
1436  // compatible with Distance
1437  // Transform acceleration
1438  assert(!params.multiThread ||
1439  params.maxSearchDist <= 0); // maxSearchDist is not compatible with
1440  // parallel processing
1441 
1442  DgmOctree* octree = intersection->octree;
1443  if (!octree) {
1444  // invalid input
1445  assert(false);
1446  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_OCTREE;
1447  }
1448 
1449  // Closest Point Set
1450  if (params.CPSet) {
1451  assert(params.maxSearchDist <= 0);
1452  assert(params.useDistanceMap == false);
1453 
1454  // reserve memory for the Closest Point Set
1455  if (!params.CPSet->resize(octree->associatedCloud()->size())) {
1456  // not enough memory
1457  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
1458  }
1459  }
1460 
1461 #ifdef ENABLE_CLOUD2MESH_DIST_MT
1462  if (!params.multiThread)
1463 #endif
1464  {
1465  GenericIndexedMesh* mesh = intersection->mesh;
1466 
1467  // dimension of an octree cell
1468  PointCoordinateType cellLength =
1469  octree->getCellSize(params.octreeLevel);
1470 
1471  // get the cell indexes at level "octreeLevel"
1472  DgmOctree::cellsContainer cellCodesAndIndexes;
1473  if (!octree->getCellCodesAndIndexes(params.octreeLevel,
1474  cellCodesAndIndexes, true)) {
1475  // not enough memory
1476  return DISTANCE_COMPUTATION_RESULTS::
1477  ERROR_GET_CELL_CODES_AND_INDEXES_FAILURE;
1478  }
1479 
1480  unsigned numberOfCells =
1481  static_cast<unsigned>(cellCodesAndIndexes.size());
1482  bool boundedSearch = (params.maxSearchDist > 0);
1483 
1484  DgmOctree::cellsContainer::const_iterator pCodeAndIndex =
1485  cellCodesAndIndexes.begin();
1487 
1488  // if we only need approximate distances
1489  if (intersection->distanceTransform) {
1490  // for each cell
1491  for (unsigned i = 0; i < numberOfCells; ++i, ++pCodeAndIndex) {
1492  octree->getPointsInCellByCellIndex(&Yk, pCodeAndIndex->theIndex,
1493  params.octreeLevel);
1494 
1495  // get the cell pos
1496  Tuple3i cellPos;
1497  octree->getCellPos(pCodeAndIndex->theCode, params.octreeLevel,
1498  cellPos, true);
1499  cellPos -= intersection->minFillIndexes;
1500 
1501  // get the Distance Transform distance
1502  unsigned squareDist =
1503  intersection->distanceTransform->getValue(cellPos);
1504 
1505  // assign the distance to all points inside this cell
1506  ScalarType maxRadius =
1507  sqrt(static_cast<ScalarType>(squareDist)) * cellLength;
1508 
1509  if (boundedSearch && maxRadius > params.maxSearchDist) {
1510  maxRadius = params.maxSearchDist;
1511  }
1512 
1513  unsigned count = Yk.size();
1514  for (unsigned j = 0; j < count; ++j) {
1515  Yk.setPointScalarValue(j, maxRadius);
1516  }
1517 
1518  // Yk.clear(); //useless
1519  }
1520 
1521  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
1522  }
1523 
1524  // otherwise we have to compute the distance from each point to its
1525  // nearest triangle
1526 
1527  // Progress callback
1528  NormalizedProgress nProgress(progressCb, numberOfCells);
1529  if (progressCb) {
1530  if (progressCb->textCanBeEdited()) {
1531  char buffer[256];
1532  sprintf(buffer, "Cells: %u", numberOfCells);
1533  progressCb->setInfo(buffer);
1534  progressCb->setMethodTitle(params.signedDistances
1535  ? "Compute signed distances"
1536  : "Compute distances");
1537  }
1538  progressCb->update(0);
1539  progressCb->start();
1540  }
1541 
1542  // variables
1543  std::vector<unsigned> trianglesToTest;
1544  std::size_t trianglesToTestCount = 0;
1545  std::size_t trianglesToTestCapacity = 0;
1546  unsigned numberOfTriangles = mesh->size();
1547 
1548  // acceleration structure
1549  std::vector<unsigned> processTriangles;
1550  try {
1551  processTriangles.resize(numberOfTriangles, 0);
1552  } catch (const std::bad_alloc&) {
1553  // otherwise, no big deal, we can do without it!
1554  }
1555 
1556  // min distance array ('persistent' version to save some memory)
1557  std::vector<ScalarType> minDists;
1558 
1559  // maximal neighbors search distance (if maxSearchDist is defined)
1560  int maxNeighbourhoodLength = 0;
1561  if (boundedSearch) {
1562  maxNeighbourhoodLength = ComputeMaxNeighborhoodLength(
1563  params.maxSearchDist, cellLength);
1564  }
1565 
1566  // for each cell
1567  for (unsigned cellIndex = 1; cellIndex <= numberOfCells;
1568  ++cellIndex, ++pCodeAndIndex) // cellIndex = unique ID for the
1569  // current cell
1570  {
1571  octree->getPointsInCellByCellIndex(&Yk, pCodeAndIndex->theIndex,
1572  params.octreeLevel);
1573 
1574  // get cell pos
1575  Tuple3i startPos;
1576  octree->getCellPos(pCodeAndIndex->theCode, params.octreeLevel,
1577  startPos, true);
1578 
1579  // get the distance to the nearest and farthest boundaries
1580  Tuple3i signedDistToLowerBorder, signedDistToUpperBorder;
1581  signedDistToLowerBorder = startPos - intersection->minFillIndexes;
1582  signedDistToUpperBorder = intersection->maxFillIndexes - startPos;
1583 
1584  Tuple3i minDistToGridBoundaries, maxDistToGridBoundaries;
1585  for (unsigned char k = 0; k < 3; ++k) {
1586  minDistToGridBoundaries.u[k] =
1587  std::max(std::max(-signedDistToLowerBorder.u[k], 0),
1588  std::max(0, -signedDistToUpperBorder.u[k]));
1589  maxDistToGridBoundaries.u[k] =
1590  std::max(std::max(signedDistToLowerBorder.u[k], 0),
1591  std::max(0, signedDistToUpperBorder.u[k]));
1592  }
1593  int minDistToBoundaries =
1594  std::max(minDistToGridBoundaries.x,
1595  std::max(minDistToGridBoundaries.y,
1596  minDistToGridBoundaries.z));
1597  int maxDistToBoundaries =
1598  std::max(maxDistToGridBoundaries.x,
1599  std::max(maxDistToGridBoundaries.y,
1600  maxDistToGridBoundaries.z));
1601  int maxIntDist = maxDistToBoundaries;
1602 
1603  // determine the cell center
1604  CCVector3 cellCenter;
1605  octree->computeCellCenter(startPos, params.octreeLevel, cellCenter);
1606 
1607  // express 'startPos' relatively to the grid borders
1608  startPos -= intersection->minFillIndexes;
1609 
1610  // minDists.clear(); //not necessary
1611  unsigned remainingPoints = Yk.size();
1612  if (minDists.size() < remainingPoints) {
1613  try {
1614  minDists.resize(remainingPoints);
1615  } catch (const std::bad_alloc&) // out of memory
1616  {
1617  // not enough memory
1618  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
1619  }
1620  }
1621 
1622  // for each point, we pre-compute its distance to the nearest cell
1623  // border (will be handy later)
1624  for (unsigned j = 0; j < remainingPoints; ++j) {
1625  const CCVector3* tempPt = Yk.getPointPersistentPtr(j);
1626  minDists[j] = static_cast<ScalarType>(
1628  *tempPt, cellLength, cellCenter));
1629  }
1630 
1631  if (boundedSearch) {
1632  // no need to look farther than 'maxNeighbourhoodLength'
1633  if (maxNeighbourhoodLength < maxIntDist)
1634  maxIntDist = maxNeighbourhoodLength;
1635 
1636  // we compute squared distances when not in 'signed' mode!
1637  ScalarType maxDistance = params.maxSearchDist;
1638  if (!params.signedDistances) {
1639  // we compute squared distances when not in 'signed' mode!
1640  maxDistance = params.maxSearchDist * params.maxSearchDist;
1641  }
1642 
1643  for (unsigned j = 0; j < remainingPoints; ++j)
1644  Yk.setPointScalarValue(j, maxDistance);
1645  }
1646 
1647  // let's find the nearest triangles for each point in the
1648  // neighborhood 'Yk'
1649  ScalarType maxRadius = 0;
1650  for (int dist = minDistToBoundaries;
1651  dist <= maxIntDist && remainingPoints != 0;
1652  ++dist, maxRadius += static_cast<ScalarType>(cellLength)) {
1653  // test the neighbor cells at distance = 'dist'
1654  // a,b,c,d,e,f are the extents of this neighborhood
1655  // for the 6 main directions -X,+X,-Y,+Y,-Z,+Z
1656  int a = std::min(dist, signedDistToLowerBorder.x);
1657  int b = std::min(dist, signedDistToUpperBorder.x);
1658  int c = std::min(dist, signedDistToLowerBorder.y);
1659  int d = std::min(dist, signedDistToUpperBorder.y);
1660  int e = std::min(dist, signedDistToLowerBorder.z);
1661  int f = std::min(dist, signedDistToUpperBorder.z);
1662 
1663  for (int i = -a; i <= b; i++) {
1664  bool imax = (abs(i) == dist);
1665  Tuple3i cellPos(startPos.x + i, 0, 0);
1666 
1667  for (int j = -c; j <= d; j++) {
1668  cellPos.y = startPos.y + j;
1669 
1670  // if i or j is 'maximal'
1671  if (imax || abs(j) == dist) {
1672  // we must be on the border of the neighborhood
1673  for (int k = -e; k <= f; k++) {
1674  // are there any triangles near this cell?
1675  cellPos.z = startPos.z + k;
1676  TriangleList* triList =
1677  intersection->perCellTriangleList
1678  .getValue(cellPos);
1679  if (triList) {
1680  if (trianglesToTestCount +
1681  triList->indexes.size() >
1682  trianglesToTestCapacity) {
1683  trianglesToTestCapacity = std::max(
1684  trianglesToTestCount +
1685  triList->indexes.size(),
1686  2 * trianglesToTestCount);
1687  trianglesToTest.resize(
1688  trianglesToTestCapacity);
1689  }
1690  // let's test all the triangles that
1691  // intersect this cell
1692  for (std::size_t p = 0;
1693  p < triList->indexes.size(); ++p) {
1694  if (!processTriangles.empty()) {
1695  unsigned indexTri =
1696  triList->indexes[p];
1697  // if the triangles has not been
1698  // processed yet
1699  if (processTriangles[indexTri] !=
1700  cellIndex) {
1701  trianglesToTest
1702  [trianglesToTestCount++] =
1703  indexTri;
1704  processTriangles[indexTri] =
1705  cellIndex;
1706  }
1707  } else {
1708  trianglesToTest
1709  [trianglesToTestCount++] =
1710  triList->indexes[p];
1711  }
1712  }
1713  }
1714  }
1715  } else // we must go the cube border
1716  {
1717  if (e == dist) //'negative' side
1718  {
1719  // are there any triangles near this cell?
1720  cellPos.z = startPos.z - e;
1721  TriangleList* triList =
1722  intersection->perCellTriangleList
1723  .getValue(cellPos);
1724  if (triList) {
1725  if (trianglesToTestCount +
1726  triList->indexes.size() >
1727  trianglesToTestCapacity) {
1728  trianglesToTestCapacity = std::max(
1729  trianglesToTestCount +
1730  triList->indexes.size(),
1731  2 * trianglesToTestCount);
1732  trianglesToTest.resize(
1733  trianglesToTestCapacity);
1734  }
1735  // let's test all the triangles that
1736  // intersect this cell
1737  for (std::size_t p = 0;
1738  p < triList->indexes.size(); ++p) {
1739  if (!processTriangles.empty()) {
1740  const unsigned& indexTri =
1741  triList->indexes[p];
1742  // if the triangles has not been
1743  // processed yet
1744  if (processTriangles[indexTri] !=
1745  cellIndex) {
1746  trianglesToTest
1747  [trianglesToTestCount++] =
1748  indexTri;
1749  processTriangles[indexTri] =
1750  cellIndex;
1751  }
1752  } else {
1753  trianglesToTest
1754  [trianglesToTestCount++] =
1755  triList->indexes[p];
1756  }
1757  }
1758  }
1759  }
1760 
1761  if (f == dist && dist > 0) //'positive' side
1762  {
1763  // are there any triangles near this cell?
1764  cellPos.z = startPos.z + f;
1765  TriangleList* triList =
1766  intersection->perCellTriangleList
1767  .getValue(cellPos);
1768  if (triList) {
1769  if (trianglesToTestCount +
1770  triList->indexes.size() >
1771  trianglesToTestCapacity) {
1772  trianglesToTestCapacity = std::max(
1773  trianglesToTestCount +
1774  triList->indexes.size(),
1775  2 * trianglesToTestCount);
1776  trianglesToTest.resize(
1777  trianglesToTestCapacity);
1778  }
1779  // let's test all the triangles that
1780  // intersect this cell
1781  for (std::size_t p = 0;
1782  p < triList->indexes.size(); ++p) {
1783  if (!processTriangles.empty()) {
1784  const unsigned& indexTri =
1785  triList->indexes[p];
1786  // if the triangles has not been
1787  // processed yet
1788  if (processTriangles[indexTri] !=
1789  cellIndex) {
1790  trianglesToTest
1791  [trianglesToTestCount++] =
1792  indexTri;
1793  processTriangles[indexTri] =
1794  cellIndex;
1795  }
1796  } else {
1797  trianglesToTest
1798  [trianglesToTestCount++] =
1799  triList->indexes[p];
1800  }
1801  }
1802  }
1803  }
1804  }
1805  }
1806  }
1807 
1808  ComparePointsAndTriangles(Yk, remainingPoints, mesh,
1809  trianglesToTest, trianglesToTestCount,
1810  minDists, maxRadius, params);
1811  }
1812 
1813  // Yk.clear(); //not necessary
1814 
1815  if (progressCb && !nProgress.oneStep()) {
1816  // process cancelled by the user
1817  break;
1818  }
1819  }
1820 
1821  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
1822  }
1823 #ifdef ENABLE_CLOUD2MESH_DIST_MT
1824  else {
1825  // extraction des indexes et codes des cellules du niveau "octreeLevel"
1826  DgmOctree::cellsContainer cellsDescs;
1827  octree->getCellCodesAndIndexes(params.octreeLevel, cellsDescs, true);
1828 
1829  unsigned numberOfCells = static_cast<unsigned>(cellsDescs.size());
1830 
1831  // Progress callback
1832  NormalizedProgress nProgress(progressCb, numberOfCells);
1833  if (progressCb) {
1834  if (progressCb->textCanBeEdited()) {
1835  char buffer[256];
1836  sprintf(buffer, "Cells: %u", numberOfCells);
1837  progressCb->setInfo(buffer);
1838  progressCb->setMethodTitle("Compute signed distances");
1839  }
1840  progressCb->update(0);
1841  progressCb->start();
1842  }
1843 
1844  s_octree_MT = octree;
1845  s_normProgressCb_MT = &nProgress;
1846  s_cellFunc_MT_success = true;
1847  s_params_MT = params;
1848  s_intersection_MT = intersection;
1849  // acceleration structure
1850  s_useBitArrays_MT = true;
1851 
1852  // Single thread emulation
1853  // for (unsigned i=0; i<numberOfCells; ++i)
1854  // cloudMeshDistCellFunc_MT(cellsDescs[i]);
1855 
1856  int maxThreadCount = params.maxThreadCount;
1857  if (maxThreadCount == 0) {
1858  maxThreadCount = QThread::idealThreadCount();
1859  }
1860  QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
1861  QtConcurrent::blockingMap(cellsDescs, cloudMeshDistCellFunc_MT);
1862 
1863  s_octree_MT = nullptr;
1864  s_normProgressCb_MT = nullptr;
1865  s_intersection_MT = nullptr;
1866 
1867  // clean acceleration structure
1868  while (!s_bitArrayPool_MT.empty()) {
1869  delete s_bitArrayPool_MT.back();
1870  s_bitArrayPool_MT.pop_back();
1871  }
1872 
1873  return (s_cellFunc_MT_success ? 0 : -2);
1874  }
1875 #endif
1876 }
1877 
1878 // convert all 'distances' (squared in fact) to their square root
1879 inline void applySqrtToPointDist(const CCVector3& aPoint,
1880  ScalarType& aScalarValue) {
1881  if (ScalarField::ValidValue(aScalarValue))
1882  aScalarValue = sqrt(aScalarValue);
1883 }
1884 
1888  : trianglesToTestCount(0),
1889  trianglesToTestCapacity(0),
1890  numberOfTriangles(0) {
1891  numberOfTriangles = mesh.size();
1892  try {
1893  processTriangles.resize(numberOfTriangles, 0);
1894  } catch (const std::bad_alloc&) {
1895  // otherwise, no big deal, we can do without it!
1896  }
1897  }
1898 
1899  // variables
1900  std::vector<unsigned> trianglesToTest;
1904 
1905  // optional acceleration structure
1906  std::vector<unsigned> processTriangles;
1907 };
1908 
1911  OctreeAndMeshIntersection* intersection,
1913  ReferenceCloud& Yk,
1914  unsigned cellIndex,
1915  const Tuple3i& cellPos,
1916  TrianglesToTest& ttt,
1917  bool boundedSearch,
1918  int maxNeighbourhoodLength) {
1919  // get the distance to the nearest and farthest boundaries
1920  Tuple3i localCellPos = cellPos - intersection->minFillIndexes;
1921  Tuple3i signedDistToLowerBorder = localCellPos;
1922  Tuple3i signedDistToUpperBorder = intersection->maxFillIndexes -
1923  intersection->minFillIndexes -
1924  localCellPos;
1925 
1926  Tuple3i minDistToGridBoundaries, maxDistToGridBoundaries;
1927  for (unsigned char k = 0; k < 3; ++k) {
1928  minDistToGridBoundaries.u[k] =
1929  std::max(std::max(-signedDistToLowerBorder.u[k], 0),
1930  std::max(0, -signedDistToUpperBorder.u[k]));
1931  maxDistToGridBoundaries.u[k] =
1932  std::max(std::max(signedDistToLowerBorder.u[k], 0),
1933  std::max(0, signedDistToUpperBorder.u[k]));
1934  }
1935  int minDistToBoundaries = std::max(
1936  minDistToGridBoundaries.x,
1937  std::max(minDistToGridBoundaries.y, minDistToGridBoundaries.z));
1938  int maxDistToBoundaries = std::max(
1939  maxDistToGridBoundaries.x,
1940  std::max(maxDistToGridBoundaries.y, maxDistToGridBoundaries.z));
1941 
1942  // determine the cell center
1943  CCVector3 cellCenter;
1944  DgmOctree* octree = intersection->octree;
1945  PointCoordinateType cellSize = octree->getCellSize(params.octreeLevel);
1946  octree->computeCellCenter(cellPos, params.octreeLevel, cellCenter);
1947 
1948  // express 'startPos' relatively to the inner grid borders
1949  Tuple3i startPos = localCellPos;
1950 
1951  // min distance array ('persistent' version to save some memory)
1952  std::vector<ScalarType> minDists;
1953  unsigned remainingPoints = Yk.size();
1954 
1955  try {
1956  minDists.resize(remainingPoints);
1957  } catch (const std::bad_alloc&) // out of memory
1958  {
1959  // not enough memory
1960  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
1961  ERROR_OUT_OF_MEMORY;
1962  }
1963 
1964  // for each point, we pre-compute its distance to the nearest cell border
1965  //(will be handy later)
1966  for (unsigned j = 0; j < remainingPoints; ++j) {
1967  const CCVector3* tempPt = Yk.getPointPersistentPtr(j);
1968  minDists[j] = static_cast<ScalarType>(
1970  cellCenter));
1971  }
1972 
1973  if (boundedSearch) {
1974  // no need to look farther than 'maxNeighbourhoodLength'
1975  if (maxNeighbourhoodLength < maxDistToBoundaries)
1976  maxDistToBoundaries = maxNeighbourhoodLength;
1977 
1978  // we compute squared distances when not in 'signed' mode!
1979  ScalarType maxDistance = params.maxSearchDist;
1980  if (!params.signedDistances) {
1981  // we compute squared distances when not in 'signed' mode!
1982  maxDistance = params.maxSearchDist * params.maxSearchDist;
1983  }
1984 
1985  for (unsigned j = 0; j < remainingPoints; ++j) {
1986  Yk.setPointScalarValue(j, maxDistance);
1987  }
1988  }
1989 
1990  const Tuple3ui& gridSize = intersection->perCellTriangleList.size();
1991 
1992  // let's find the nearest triangles for each point in the neighborhood 'Yk'
1993  ScalarType maxRadius = 0;
1994  for (int dist = minDistToBoundaries;
1995  dist <= maxDistToBoundaries && remainingPoints != 0;
1996  ++dist, maxRadius += static_cast<ScalarType>(cellSize)) {
1997  // test the neighbor cells at distance = 'dist'
1998  // a,b,c,d,e,f are the extents of this neighborhood
1999  // for the 6 main directions -X,+X,-Y,+Y,-Z,+Z
2000  int a = std::min(dist, signedDistToLowerBorder.x);
2001  int b = std::min(dist, signedDistToUpperBorder.x);
2002  int c = std::min(dist, signedDistToLowerBorder.y);
2003  int d = std::min(dist, signedDistToUpperBorder.y);
2004  int e = std::min(dist, signedDistToLowerBorder.z);
2005  int f = std::min(dist, signedDistToUpperBorder.z);
2006 
2007  for (int i = -a; i <= b; i++) {
2008  bool imax = (std::abs(i) == dist);
2009  Tuple3i localCellPos(startPos.x + i, 0, 0);
2010  if (localCellPos.x < 0 ||
2011  static_cast<unsigned>(localCellPos.x) >= gridSize.x)
2012  continue;
2013 
2014  for (int j = -c; j <= d; j++) {
2015  localCellPos.y = startPos.y + j;
2016  if (localCellPos.y < 0 ||
2017  static_cast<unsigned>(localCellPos.y) >= gridSize.y)
2018  continue;
2019 
2020  // if i or j is 'maximal'
2021  if (imax || std::abs(j) == dist) {
2022  // we must be on the border of the neighborhood
2023  for (int k = -e; k <= f; k++) {
2024  // are there any triangles near this cell?
2025  localCellPos.z = startPos.z + k;
2026  if (localCellPos.z < 0 ||
2027  static_cast<unsigned>(localCellPos.z) >= gridSize.z)
2028  continue;
2029  TriangleList* triList =
2030  intersection->perCellTriangleList.getValue(
2031  localCellPos);
2032  if (triList) {
2033  if (ttt.trianglesToTestCount +
2034  triList->indexes.size() >
2037  ttt.trianglesToTestCount +
2038  triList->indexes.size(),
2039  2 * ttt.trianglesToTestCount);
2040  ttt.trianglesToTest.resize(
2042  }
2043  // let's test all the triangles that intersect this
2044  // cell
2045  for (unsigned indexTri : triList->indexes) {
2046  if (!ttt.processTriangles.empty()) {
2047  // if the triangles has not been processed
2048  // yet
2049  if (ttt.processTriangles[indexTri] !=
2050  cellIndex) {
2051  ttt.trianglesToTest
2052  [ttt.trianglesToTestCount++] =
2053  indexTri;
2054  ttt.processTriangles[indexTri] =
2055  cellIndex;
2056  }
2057  } else {
2058  ttt.trianglesToTest
2059  [ttt.trianglesToTestCount++] =
2060  indexTri;
2061  }
2062  }
2063  }
2064  }
2065  } else // we must go the cube border
2066  {
2067  if (e == dist) //'negative' side
2068  {
2069  // are there any triangles near this cell?
2070  localCellPos.z = startPos.z - e;
2071  if (localCellPos.z < 0 ||
2072  static_cast<unsigned>(localCellPos.z) >= gridSize.z)
2073  continue;
2074  TriangleList* triList =
2075  intersection->perCellTriangleList.getValue(
2076  localCellPos);
2077  if (triList) {
2078  if (ttt.trianglesToTestCount +
2079  triList->indexes.size() >
2082  ttt.trianglesToTestCount +
2083  triList->indexes.size(),
2084  2 * ttt.trianglesToTestCount);
2085  ttt.trianglesToTest.resize(
2087  }
2088  // let's test all the triangles that intersect this
2089  // cell
2090  for (unsigned triIndex : triList->indexes) {
2091  if (!ttt.processTriangles.empty()) {
2092  // if the triangles has not been processed
2093  // yet
2094  if (ttt.processTriangles[triIndex] !=
2095  cellIndex) {
2096  ttt.trianglesToTest
2097  [ttt.trianglesToTestCount++] =
2098  triIndex;
2099  ttt.processTriangles[triIndex] =
2100  cellIndex;
2101  }
2102  } else {
2103  ttt.trianglesToTest
2104  [ttt.trianglesToTestCount++] =
2105  triIndex;
2106  }
2107  }
2108  }
2109  }
2110 
2111  if (f == dist && dist > 0) //'positive' side
2112  {
2113  // are there any triangles near this cell?
2114  localCellPos.z = startPos.z + f;
2115  if (localCellPos.z < 0 ||
2116  static_cast<unsigned>(localCellPos.z) >= gridSize.z)
2117  continue;
2118  TriangleList* triList =
2119  intersection->perCellTriangleList.getValue(
2120  localCellPos);
2121  if (triList) {
2122  if (ttt.trianglesToTestCount +
2123  triList->indexes.size() >
2126  ttt.trianglesToTestCount +
2127  triList->indexes.size(),
2128  2 * ttt.trianglesToTestCapacity);
2129  ttt.trianglesToTest.resize(
2131  }
2132  // let's test all the triangles that intersect this
2133  // cell
2134  for (unsigned triIndex : triList->indexes) {
2135  if (!ttt.processTriangles.empty()) {
2136  // if the triangles has not been processed
2137  // yet
2138  if (ttt.processTriangles[triIndex] !=
2139  cellIndex) {
2140  ttt.trianglesToTest
2141  [ttt.trianglesToTestCount++] =
2142  triIndex;
2143  ttt.processTriangles[triIndex] =
2144  cellIndex;
2145  }
2146  } else {
2147  ttt.trianglesToTest
2148  [ttt.trianglesToTestCount++] =
2149  triIndex;
2150  }
2151  }
2152  }
2153  }
2154  }
2155  }
2156  }
2157 
2158  if (false == ComparePointsAndTriangles(
2159  Yk, remainingPoints, intersection->mesh,
2161  minDists, maxRadius, params)) {
2162  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2163  ERROR_OUT_OF_MEMORY;
2164  }
2165  }
2166 
2167  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::SUCCESS;
2168 }
2169 
2171  const CCVector3& P,
2172  ScalarType& distance,
2173  OctreeAndMeshIntersection* intersection,
2175  distance = NAN_VALUE;
2176 
2177  if (!intersection) {
2178  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2179  ERROR_INVALID_OCTREE_AND_MESH_INTERSECTION;
2180  }
2181 
2182  if (intersection->distanceTransform) {
2183  // Distance Transform acceleration is not supported
2184  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2185  ERROR_INVALID_OCTREE_AND_MESH_INTERSECTION;
2186  }
2187 
2188  GenericIndexedMesh* mesh = intersection->mesh;
2189  if (!mesh) {
2190  // invalid input
2191  assert(false);
2192  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2193  ERROR_NULL_REFERENCEMESH;
2194  }
2195 
2196  DgmOctree* octree = intersection->octree;
2197  if (!octree) {
2198  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2199  ERROR_NULL_OCTREE;
2200  }
2201 
2202  bool boundedSearch = (params.maxSearchDist > 0);
2203 
2204  PointCloud cloud;
2205  if (!cloud.reserve(1)) {
2206  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2207  ERROR_OUT_OF_MEMORY;
2208  }
2209  cloud.addPoint(P);
2210  if (!cloud.enableScalarField()) {
2211  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2212  ERROR_OUT_OF_MEMORY;
2213  }
2214  cloud.setPointScalarValue(0, NAN_VALUE);
2215  ReferenceCloud Yk(&cloud);
2216  if (!Yk.reserve(1)) {
2217  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::
2218  ERROR_OUT_OF_MEMORY;
2219  }
2220  Yk.addPointIndex(0);
2221 
2222  TrianglesToTest ttt(*mesh);
2223 
2224  // maximal neighbors search distance (if maxSearchDist is defined)
2225  int maxNeighbourhoodLength = 0;
2226  if (boundedSearch) {
2227  PointCoordinateType cellLength =
2228  octree->getCellSize(params.octreeLevel);
2229  maxNeighbourhoodLength =
2230  ComputeMaxNeighborhoodLength(params.maxSearchDist, cellLength);
2231  }
2232 
2233  // get cell pos (global coordinates)
2234  Tuple3i cellPos;
2235  octree->getTheCellPosWhichIncludesThePoint(&P, cellPos, params.octreeLevel);
2236 
2238  intersection, params, Yk, 1, cellPos, ttt, boundedSearch,
2239  maxNeighbourhoodLength);
2240 
2241  if (result !=
2242  DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::SUCCESS) {
2243  // an error occurred
2244  return result;
2245  }
2246 
2247  distance = cloud.getPointScalarValue(0);
2248 
2249  return DistanceComputationTools::DISTANCE_COMPUTATION_RESULTS::SUCCESS;
2250 }
2251 
2253  const DgmOctree* octree,
2254  OctreeAndMeshIntersection* intersection,
2256  GenericProgressCallback* progressCb /*=nullptr*/) {
2257  assert(!params.signedDistances ||
2258  !intersection->distanceTransform); // signed distances are not
2259  // compatible with Distance
2260  // Transform acceleration
2261 
2262  if (!octree) {
2263  // invalid input
2264  assert(false);
2265  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_OCTREE;
2266  }
2267 
2268  if (!intersection) {
2269  return DISTANCE_COMPUTATION_RESULTS::
2270  ERROR_INVALID_OCTREE_AND_MESH_INTERSECTION;
2271  }
2272 
2273  if (!intersection->distanceTransform &&
2274  !intersection->perCellTriangleList.isInitialized()) {
2275  assert(false);
2276  return DISTANCE_COMPUTATION_RESULTS::
2277  ERROR_INVALID_OCTREE_AND_MESH_INTERSECTION;
2278  }
2279 
2280  if (params.multiThread &&
2281  !intersection->perCellTriangleList.isInitialized()) {
2282  // a grid/mesh intersection is mandatory for multithread mode
2283  return DISTANCE_COMPUTATION_RESULTS::
2284  ERROR_INVALID_OCTREE_AND_MESH_INTERSECTION;
2285  }
2286 
2287  if (params.useDistanceMap && !intersection->distanceTransform) {
2288  // a valid distance transform is mandatory to use it ;)
2289  return DISTANCE_COMPUTATION_RESULTS::
2290  ERROR_OCTREE_AND_MESH_INTERSECTION_MISMATCH;
2291  }
2292 
2293  // Closest Point Set
2294  if (params.CPSet) {
2295  assert(params.maxSearchDist <= 0);
2296  assert(params.useDistanceMap == false);
2297 
2298  // reserve memory for the Closest Point Set
2299  if (!params.CPSet->resize(octree->associatedCloud()->size())) {
2300  // not enough memory
2301  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
2302  }
2303  // reserve memory for an associated scalar field (the nearest triangle
2304  // index)
2305  if (!params.CPSet->enableScalarField()) {
2306  // not enough memory
2307  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
2308  }
2309  assert(params.CPSet->getCurrentInScalarField());
2310  params.CPSet->getCurrentInScalarField()->fill(0);
2311  }
2312 
2313 #ifdef ENABLE_CLOUD2MESH_DIST_MT
2314 
2315 #ifdef CV_CORE_LIB_USES_QT_CONCURRENT
2316  if (params.multiThread) {
2317  if (params.maxThreadCount == 0) {
2318  // retrieve the maximum number of threads
2319  params.maxThreadCount = QThread::idealThreadCount();
2320  }
2321  if (params.maxThreadCount == 1) {
2322  // if only one thread should/cloud be used, the direct approach is
2323  // more efficient
2324  params.multiThread = false;
2325  }
2326  }
2327 #endif // CV_CORE_LIB_USES_QT_CONCURRENT
2328 
2329  if (!params.multiThread)
2330 #endif // ENABLE_CLOUD2MESH_DIST_MT
2331  {
2332  GenericIndexedMesh* mesh = intersection->mesh;
2333  if (!mesh) {
2334  // invalid input
2335  assert(false);
2336  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_REFERENCEMESH;
2337  }
2338 
2339  // dimension of an octree cell
2340  PointCoordinateType cellSize = octree->getCellSize(params.octreeLevel);
2341 
2342  // get the cell indexes at level "octreeLevel"
2343  DgmOctree::cellsContainer cellCodesAndIndexes;
2344  if (!octree->getCellCodesAndIndexes(params.octreeLevel,
2345  cellCodesAndIndexes, true)) {
2346  // not enough memory
2347  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
2348  }
2349 
2350  unsigned numberOfCells =
2351  static_cast<unsigned>(cellCodesAndIndexes.size());
2352  bool boundedSearch = (params.maxSearchDist > 0);
2353 
2354  DgmOctree::cellsContainer::const_iterator pCodeAndIndex =
2355  cellCodesAndIndexes.begin();
2357 
2358  // if we only need approximate distances
2359  if (intersection->distanceTransform) {
2360  // for each cell
2361  for (unsigned i = 0; i < numberOfCells; ++i, ++pCodeAndIndex) {
2362  octree->getPointsInCellByCellIndex(&Yk, pCodeAndIndex->theIndex,
2363  params.octreeLevel);
2364 
2365  // get the cell pos
2366  Tuple3i cellPos;
2367  octree->getCellPos(pCodeAndIndex->theCode, params.octreeLevel,
2368  cellPos, true);
2369  cellPos -= intersection->minFillIndexes;
2370 
2371  // get the Distance Transform distance
2372  unsigned squareDist =
2373  intersection->distanceTransform->getValue(cellPos);
2374 
2375  // assign the distance to all points inside this cell
2376  ScalarType maxRadius =
2377  sqrt(static_cast<ScalarType>(squareDist)) * cellSize;
2378 
2379  if (boundedSearch && maxRadius > params.maxSearchDist) {
2380  maxRadius = params.maxSearchDist;
2381  }
2382 
2383  unsigned count = Yk.size();
2384  for (unsigned j = 0; j < count; ++j) {
2385  Yk.setPointScalarValue(j, maxRadius);
2386  }
2387 
2388  // Yk.clear(); //useless
2389  }
2390 
2391  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
2392  }
2393 
2394  // otherwise we have to compute the distance from each point to its
2395  // nearest triangle
2396 
2397  // Progress callback
2398  NormalizedProgress nProgress(progressCb, numberOfCells);
2399  if (progressCb) {
2400  if (progressCb->textCanBeEdited()) {
2401  char buffer[32];
2402  snprintf(buffer, 32, "Cells: %u", numberOfCells);
2403  progressCb->setInfo(buffer);
2404  progressCb->setMethodTitle(params.signedDistances
2405  ? "Compute signed distances"
2406  : "Compute distances");
2407  }
2408  progressCb->update(0);
2409  progressCb->start();
2410  }
2411 
2412  TrianglesToTest ttt(*mesh);
2413 
2414  // min distance array ('persistent' version to save some memory)
2415  std::vector<ScalarType> minDists;
2416 
2417  // maximal neighbors search distance (if maxSearchDist is defined)
2418  int maxNeighbourhoodLength = 0;
2419  if (boundedSearch) {
2420  maxNeighbourhoodLength = ComputeMaxNeighborhoodLength(
2421  params.maxSearchDist, cellSize);
2422  }
2423 
2424  // for each cell
2425  for (unsigned cellIndex = 1; cellIndex <= numberOfCells;
2426  ++cellIndex, ++pCodeAndIndex) // cellIndex = unique ID for the
2427  // current cell
2428  {
2430  &Yk, pCodeAndIndex->theIndex, params.octreeLevel)) {
2431  return DISTANCE_COMPUTATION_RESULTS::
2432  ERROR_EXECUTE_GET_POINTS_IN_CELL_BY_INDEX_FAILURE;
2433  }
2434 
2435  // get cell pos
2436  Tuple3i cellPos;
2437  octree->getCellPos(pCodeAndIndex->theCode, params.octreeLevel,
2438  cellPos, true);
2439 
2441  intersection, params, Yk, cellIndex, cellPos, ttt,
2442  boundedSearch, maxNeighbourhoodLength);
2443 
2444  if (result != DISTANCE_COMPUTATION_RESULTS::SUCCESS) {
2445  // an error occurred
2446  return result;
2447  }
2448 
2449  // Yk.clear(); //not necessary
2450 
2451 #if defined(CV_CORE_LIB_USES_QT_CONCURRENT)
2452  QCoreApplication::processEvents(
2453  QEventLoop::EventLoopExec); // to allow the GUI to refresh
2454  // itself
2455 #endif
2456  if (progressCb && !nProgress.oneStep()) {
2457  // process cancelled by the user
2458  return DISTANCE_COMPUTATION_RESULTS::CANCELED_BY_USER;
2459  }
2460  }
2461 
2462  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
2463  }
2464 #ifdef ENABLE_CLOUD2MESH_DIST_MT
2465  else {
2466  // extract indexes and codes for all cells at depth 'octreeLevel'
2467  DgmOctree::cellsContainer cellsDescs;
2468  if (!octree->getCellCodesAndIndexes(params.octreeLevel, cellsDescs,
2469  true)) {
2470  return DISTANCE_COMPUTATION_RESULTS::
2471  ERROR_GET_CELL_CODES_AND_INDEXES_FAILURE;
2472  }
2473 
2474  unsigned numberOfCells = static_cast<unsigned>(cellsDescs.size());
2475 
2476  // Progress callback
2477  NormalizedProgress nProgress(progressCb, numberOfCells);
2478  if (progressCb) {
2479  if (progressCb->textCanBeEdited()) {
2480  char buffer[32];
2481  snprintf(buffer, 32, "Cells: %u", numberOfCells);
2482  progressCb->setInfo(buffer);
2483  progressCb->setMethodTitle("Compute signed distances");
2484  }
2485  progressCb->update(0);
2486  progressCb->start();
2487  }
2488 
2489  // Note: Multi-threading implementation would go here
2490  // For now, we'll use single-threaded approach
2491  // TODO: Implement multi-threading support if needed
2492 
2493  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
2494  }
2495 #endif // ENABLE_CLOUD2MESH_DIST_MT
2496 }
2497 
2499  GenericIndexedCloudPersist* pointCloud,
2500  GenericIndexedMesh* mesh,
2502  GenericProgressCallback* progressCb /*=nullptr*/,
2503  DgmOctree* cloudOctree /*=nullptr*/) {
2504  // check the input
2505  // check the input
2506  if (!pointCloud) {
2507  assert(false);
2508  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
2509  }
2510 
2511  if (pointCloud->size() == 0) {
2512  assert(false);
2513  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
2514  }
2515 
2516  if (!mesh) {
2517  assert(false);
2518  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_REFERENCEMESH;
2519  }
2520 
2521  if (mesh->size() == 0) {
2522  assert(false);
2523  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_REFERENCEMESH;
2524  }
2525 
2526  if (params.signedDistances) {
2527  // signed distances are incompatible with approximate distances (with
2528  // Distance Transform)
2529  params.useDistanceMap = false;
2530  }
2531  if (params.CPSet) {
2532  // Closest Point Set determination is incompatible with distance map
2533  // approximation and max search distance
2534  params.useDistanceMap = false;
2535  params.maxSearchDist = 0;
2536  }
2537 
2538  // compute the (cubical) bounding box that contains both the cloud and the
2539  // mehs BBs
2540  CCVector3 cloudMinBB, cloudMaxBB;
2541  CCVector3 meshMinBB, meshMaxBB;
2542  CCVector3 minBB, maxBB;
2543  CCVector3 minCubifiedBB, maxCubifiedBB;
2544  {
2545  pointCloud->getBoundingBox(cloudMinBB, cloudMaxBB);
2546  mesh->getBoundingBox(meshMinBB, meshMaxBB);
2547 
2548  // max bounding-box (non-cubical)
2549  for (unsigned char k = 0; k < 3; ++k) {
2550  minBB.u[k] = std::min(meshMinBB.u[k], cloudMinBB.u[k]);
2551  maxBB.u[k] = std::max(meshMaxBB.u[k], cloudMaxBB.u[k]);
2552  }
2553 
2554  // max bounding-box (cubical)
2555  minCubifiedBB = minBB;
2556  maxCubifiedBB = maxBB;
2557  CCMiscTools::MakeMinAndMaxCubical(minCubifiedBB, maxCubifiedBB);
2558  }
2559 
2560  // compute the octree if necessary
2561  DgmOctree tempOctree(pointCloud);
2562  DgmOctree* octree = cloudOctree;
2563  bool rebuildTheOctree = false;
2564  if (!octree) {
2565  octree = &tempOctree;
2566  rebuildTheOctree = true;
2567  } else {
2568  // check the input octree dimensions
2569  const CCVector3& theOctreeMins = octree->getOctreeMins();
2570  const CCVector3& theOctreeMaxs = octree->getOctreeMaxs();
2571  for (unsigned char k = 0; k < 3; ++k) {
2572  if (theOctreeMins.u[k] != minCubifiedBB.u[k] ||
2573  theOctreeMaxs.u[k] != maxCubifiedBB.u[k]) {
2574  rebuildTheOctree = true;
2575  break;
2576  }
2577  }
2578  }
2579 
2580  if (rebuildTheOctree) {
2581  // build the octree
2582  if (octree->build(minCubifiedBB, maxCubifiedBB, &cloudMinBB,
2583  &cloudMaxBB, progressCb) <= 0) {
2584  return DISTANCE_COMPUTATION_RESULTS::ERROR_BUILD_OCTREE_FAILURE;
2585  }
2586  }
2587 
2588  OctreeAndMeshIntersection intersection;
2589  intersection.octree = octree;
2590  intersection.mesh = mesh;
2591 
2592  // we deduce the grid cell size very simply (as the bbox has been
2593  // "cubified")
2594  PointCoordinateType cellSize =
2595  (maxCubifiedBB.x - minCubifiedBB.x) / (1 << params.octreeLevel);
2596  // we compute grid occupancy ... and we deduce the grid dimensions
2597  Tuple3ui gridSize;
2598  {
2599  for (unsigned char k = 0; k < 3; ++k) {
2600  intersection.minFillIndexes.u[k] = static_cast<int>(
2601  floor((minBB.u[k] - minCubifiedBB.u[k]) / cellSize));
2602  intersection.maxFillIndexes.u[k] = static_cast<int>(
2603  floor((maxBB.u[k] - minCubifiedBB.u[k]) / cellSize));
2604  gridSize.u[k] =
2605  static_cast<unsigned>(intersection.maxFillIndexes.u[k] -
2606  intersection.minFillIndexes.u[k] + 1);
2607  }
2608  }
2609 
2610  // if the user (or the current cloud/mesh configuration) requires that we
2611  // use a Distance Transform
2612  if (params.useDistanceMap) {
2614  if (!intersection.distanceTransform ||
2615  !intersection.distanceTransform->initGrid(gridSize)) {
2616  return DISTANCE_COMPUTATION_RESULTS::
2617  ERROR_INIT_DISTANCE_TRANSFORM_GRID_FAILURE;
2618  }
2619  params.multiThread = false; // not necessary/supported
2620  } else {
2621  // we need to build the list of triangles intersecting each cell of the
2622  // 3D grid
2623  if (!intersection.perCellTriangleList.init(gridSize.x, gridSize.y,
2624  gridSize.z, 0, 0)) {
2625  return DISTANCE_COMPUTATION_RESULTS::
2626  ERROR_INIT_PER_CELL_TRIANGLE_LIST_FAILURE;
2627  }
2628  }
2629 
2630  // INTERSECT THE OCTREE WITH THE MESH
2631  int result = intersectMeshWithOctree(&intersection, params.octreeLevel,
2632  progressCb);
2633  if (result < 0) {
2634  return result;
2635  }
2636 
2637  // reset the output distances
2638  pointCloud->enableScalarField();
2640 
2641  // acceleration by approximating the distance
2642  if (params.useDistanceMap && intersection.distanceTransform) {
2643  intersection.distanceTransform->propagateDistance(progressCb);
2644  }
2645 
2646  // WE CAN EVENTUALLY COMPUTE THE DISTANCES!
2648  progressCb);
2649 
2650  // don't forget to compute the square root of the (squared) unsigned
2651  // distances
2652  if (result == DISTANCE_COMPUTATION_RESULTS::SUCCESS &&
2653  !params.signedDistances && !params.useDistanceMap) {
2654  pointCloud->forEach(applySqrtToPointDist);
2655  }
2656 
2657  if (result < 0) {
2658  return result;
2659  }
2660 
2661  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
2662 }
2663 
2664 // Inspired from documents and code by:
2665 // David Eberly
2666 // Geometric Tools, LLC
2667 // http://www.geometrictools.com/
2669  const CCVector3* P,
2670  const GenericTriangle* theTriangle,
2671  bool signedDist,
2672  CCVector3* nearestP /*=0*/) {
2673  assert(P && theTriangle);
2674 
2675  const CCVector3* A = theTriangle->_getA();
2676  const CCVector3* B = theTriangle->_getB();
2677  const CCVector3* C = theTriangle->_getC();
2678 
2679  // we do all computations with double precision, otherwise
2680  // some triangles with sharp angles will give very poor results.
2681  CCVector3d AP(P->x - A->x, P->y - A->y, P->z - A->z);
2682  CCVector3d AB(B->x - A->x, B->y - A->y, B->z - A->z);
2683  CCVector3d AC(C->x - A->x, C->y - A->y, C->z - A->z);
2684 
2685  double a00 = AB.dot(AB);
2686  double a01 = AB.dot(AC);
2687  double a11 = AC.dot(AC);
2688  double b0 = -AP.dot(AB);
2689  double b1 = -AP.dot(AC);
2690  double det = a00 * a11 - a01 * a01;
2691  double t0 = a01 * b1 - a11 * b0;
2692  double t1 = a01 * b0 - a00 * b1;
2693 
2694  if (t0 + t1 <= det) {
2695  if (t0 < 0) {
2696  if (t1 < 0) // region 4
2697  {
2698  if (b0 < 0) {
2699  t1 = 0;
2700  if (-b0 >= a00) // V0
2701  {
2702  t0 = 1.0;
2703  } else // E01
2704  {
2705  t0 = -b0 / a00;
2706  }
2707  } else {
2708  t0 = 0;
2709  if (b1 >= 0) // V0
2710  {
2711  t1 = 0;
2712  } else if (-b1 >= a11) // V2
2713  {
2714  t1 = 1.0;
2715  } else // E20
2716  {
2717  t1 = -b1 / a11;
2718  }
2719  }
2720  } else // region 3
2721  {
2722  t0 = 0;
2723  if (b1 >= 0) // V0
2724  {
2725  t1 = 0;
2726  } else if (-b1 >= a11) // V2
2727  {
2728  t1 = 1.0;
2729  } else // E20
2730  {
2731  t1 = -b1 / a11;
2732  }
2733  }
2734  } else if (t1 < 0) // region 5
2735  {
2736  t1 = 0;
2737  if (b0 >= 0) // V0
2738  {
2739  t0 = 0;
2740  } else if (-b0 >= a00) // V1
2741  {
2742  t0 = 1.0;
2743  } else // E01
2744  {
2745  t0 = -b0 / a00;
2746  }
2747  } else // region 0, interior
2748  {
2749  t0 /= det;
2750  t1 /= det;
2751  }
2752  } else {
2753  double tmp0, tmp1, numer, denom;
2754 
2755  if (t0 < 0) // region 2
2756  {
2757  tmp0 = a01 + b0;
2758  tmp1 = a11 + b1;
2759  if (tmp1 > tmp0) {
2760  numer = tmp1 - tmp0;
2761  denom = a00 - 2 * a01 + a11;
2762  if (numer >= denom) // V1
2763  {
2764  t0 = 1.0;
2765  t1 = 0;
2766  } else // E12
2767  {
2768  t0 = numer / denom;
2769  t1 = 1.0 - t0;
2770  }
2771  } else {
2772  t0 = 0;
2773  if (tmp1 <= 0) // V2
2774  {
2775  t1 = 1.0;
2776  } else if (b1 >= 0) // V0
2777  {
2778  t1 = 0;
2779  } else // E20
2780  {
2781  t1 = -b1 / a11;
2782  }
2783  }
2784  } else if (t1 < 0) // region 6
2785  {
2786  tmp0 = a01 + b1;
2787  tmp1 = a00 + b0;
2788  if (tmp1 > tmp0) {
2789  numer = tmp1 - tmp0;
2790  denom = a00 - 2 * a01 + a11;
2791  if (numer >= denom) // V2
2792  {
2793  t1 = 1.0;
2794  t0 = 0;
2795  } else // E12
2796  {
2797  t1 = numer / denom;
2798  t0 = 1.0 - t1;
2799  }
2800  } else {
2801  t1 = 0;
2802  if (tmp1 <= 0) // V1
2803  {
2804  t0 = 1.0;
2805  } else if (b0 >= 0) // V0
2806  {
2807  t0 = 0;
2808  } else // E01
2809  {
2810  t0 = -b0 / a00;
2811  }
2812  }
2813  } else // region 1
2814  {
2815  numer = a11 + b1 - a01 - b0;
2816  if (numer <= 0) // V2
2817  {
2818  t0 = 0;
2819  t1 = 1.0;
2820  } else {
2821  denom = a00 - 2 * a01 + a11;
2822  if (numer >= denom) // V1
2823  {
2824  t0 = 1.0;
2825  t1 = 0;
2826  } else // 12
2827  {
2828  t0 = numer / denom;
2829  t1 = 1.0 - t0;
2830  }
2831  }
2832  }
2833  }
2834 
2835  // point on the plane (relative to A)
2836  CCVector3d Q = t0 * AB + t1 * AC;
2837 
2838  if (nearestP) {
2839  // if requested we also output the nearest point
2840  *nearestP = *A + CCVector3::fromArray(Q.u);
2841  }
2842 
2843  double squareDist = (Q - AP).norm2();
2844  if (signedDist) {
2845  ScalarType d = static_cast<ScalarType>(sqrt(squareDist));
2846 
2847  // triangle normal
2848  CCVector3d N = AB.cross(AC);
2849 
2850  // we test the sign of the cross product of the triangle normal and the
2851  // vector AP
2852  return (AP.dot(N) < 0 ? -d : d);
2853  } else {
2854  return static_cast<ScalarType>(squareDist);
2855  }
2856 }
2857 
2859  const CCVector3* P, const PointCoordinateType* planeEquation) {
2860  // point to plane distance: d = (a0*x+a1*y+a2*z - a3) / sqrt(a0^2+a1^2+a2^2)
2861  assert(std::abs(CCVector3::vnorm(planeEquation) - PC_ONE) <=
2862  std::numeric_limits<PointCoordinateType>::epsilon());
2863 
2864  return static_cast<ScalarType>((
2865  CCVector3::vdot(P->u, planeEquation) -
2866  planeEquation[3]) /*/CCVector3::vnorm(planeEquation)*/); // norm
2867  // == 1.0!
2868 }
2869 
2871  const CCVector3* p, const CCVector3* start, const CCVector3* end) {
2872  assert(p && start && end);
2873  CCVector3 line = *end - *start;
2874  CCVector3 vec;
2875  ScalarType distSq;
2876  PointCoordinateType t = line.dot(*p - *start);
2877  PointCoordinateType normSq = line.norm2();
2878  if (normSq != 0) {
2879  t /= normSq;
2880  }
2881  if (t < 0) {
2882  vec = *p - *start;
2883  } else if (t > 1) {
2884  vec = *p - *end;
2885  } else {
2886  CCVector3 pProjectedOnLine = *start + t * line;
2887  vec = *p - pProjectedOnLine;
2888  }
2889 
2890  distSq = vec.norm2();
2891  return distSq;
2892 }
2893 
2894 // This algorithm is a modification of the distance computation between a point
2895 // and a cone from Barbier & Galin's Fast Distance Computation Between a Point
2896 // and Cylinders, Cones, Line Swept Spheres and Cone-Spheres. The modifications
2897 // from the paper are to compute the closest distance when the point is interior
2898 // to the cone. http://liris.cnrs.fr/Documents/Liris-1297.pdf
2901  const CCVector3& coneP1,
2902  const CCVector3& coneP2,
2903  const PointCoordinateType coneR1,
2904  const PointCoordinateType coneR2,
2905  bool signedDistances /*=true*/,
2906  bool solutionType /*=false*/,
2907  double* rms /*=nullptr*/) {
2908  if (!cloud) {
2909  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
2910  }
2911  unsigned count = cloud->size();
2912  if (count == 0) {
2913  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
2914  }
2915  if (!cloud->enableScalarField()) {
2916  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
2917  }
2918  if (coneR1 < coneR2) {
2919  return DISTANCE_COMPUTATION_RESULTS::ERROR_CONE_R1_LT_CONE_R2;
2920  }
2921  double dSumSq = 0.0;
2922 
2923  CCVector3 coneAxis = coneP2 - coneP1;
2924  double axisLength = coneAxis.normd();
2925  coneAxis.normalize();
2926  double delta = static_cast<double>(coneR2) - coneR1;
2927  double rr1 = static_cast<double>(coneR1) * coneR1;
2928  double rr2 = static_cast<double>(coneR2) * coneR2;
2929  double coneLength = sqrt((axisLength * axisLength) + (delta * delta));
2930  CCVector3d side{axisLength, delta, 0.0};
2931  side /= coneLength;
2932 
2933  for (unsigned i = 0; i < count; ++i) {
2934  const CCVector3* P = cloud->getPoint(i);
2935  CCVector3 n = *P - coneP1;
2936  double x = n.dot(coneAxis);
2937  double xx = x * x;
2938  double yy = (n.norm2d()) - xx;
2939  double y = 0;
2940  double d = 0;
2941  double rx = 0;
2942  double ry = 0;
2943  if (yy < 0) {
2944  yy = 0;
2945  }
2946  if (x <= 0) // Below the bottom point
2947  {
2948  if (yy < rr1) {
2949  if (!solutionType) {
2950  d = -x; // Below the bottom point within larger disk radius
2951  } else {
2952  d = 1; // Works
2953  }
2954  } else {
2955  if (!solutionType) {
2956  y = sqrt(yy) - coneR1;
2957  d = sqrt((y * y) + xx); // Below the bottom point not
2958  // within larger disk radius
2959  } else {
2960  d = 2; // Works
2961  }
2962  }
2963 
2964  } else // Above the bottom point
2965  {
2966  if (yy < rr2) // within smaller disk radius
2967  {
2968  if (x > axisLength) // outside cone within smaller disk
2969  {
2970  if (!solutionType) {
2971  d = x - axisLength; // Above the top point within top
2972  // disk radius
2973  } else {
2974  d = 3;
2975  }
2976  } else // inside cone within smaller disk
2977  {
2978  if (!solutionType) {
2979  y = sqrt(yy) - coneR1;
2980  ry = y * side[0] -
2981  x * side[1]; // rotated y value (distance from the
2982  // coneside axis)
2983  d = std::min(
2984  axisLength - x,
2985  x); // determine whether closer to either radii
2986  d = std::min(d, static_cast<double>(
2987  std::abs(ry))); // or to side
2988  d = -d; // negative inside
2989  } else {
2990  d = 4;
2991  }
2992  }
2993  } else // Outside smaller disk radius
2994  {
2995  y = sqrt(yy) - coneR1;
2996  {
2997  rx = y * side[1] +
2998  x * side[0]; // rotated x value (distance along the
2999  // coneside axis)
3000  if (rx < 0) {
3001  if (!solutionType) {
3002  d = sqrt(y * y + xx);
3003  } else {
3004  d = 7; // point projects onto the large cap
3005  }
3006  } else {
3007  ry = y * side[0] -
3008  x * side[1]; // rotated y value (distance from the
3009  // coneside axis)
3010  if (rx > coneLength) {
3011  if (!solutionType) {
3012  rx -= coneLength;
3013  d = sqrt(ry * ry + rx * rx);
3014  } else {
3015  d = 8; // point projects onto the small cap
3016  }
3017  } else {
3018  if (!solutionType) {
3019  d = ry; // point projects onto the side of cone
3020  if (ry < 0) { // point is interior to the cone
3021  d = std::min(axisLength - x,
3022  x); // determine whether
3023  // closer to either radii
3024  d = std::min(d,
3025  static_cast<double>(std::abs(
3026  ry))); // or to side
3027  d = -d; // negative inside
3028  }
3029  } else {
3030  d = 9;
3031  }
3032  }
3033  }
3034  }
3035  }
3036  }
3037  if (signedDistances) {
3038  cloud->setPointScalarValue(i, static_cast<ScalarType>(d));
3039  } else {
3040  cloud->setPointScalarValue(i, static_cast<ScalarType>(std::abs(d)));
3041  }
3042  dSumSq += d * d;
3043  }
3044  if (rms) {
3045  *rms = sqrt(dSumSq / count);
3046  }
3047  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3048 }
3049 
3050 // This algorithm is a modification of the distance computation between a point
3051 // and a cylinder from Barbier & Galin's Fast Distance Computation Between a
3052 // Point and Cylinders, Cones, Line Swept Spheres and Cone-Spheres. The
3053 // modifications from the paper are to compute the closest distance when the
3054 // point is interior to the capped cylinder.
3055 // http://liris.cnrs.fr/Documents/Liris-1297.pdf
3056 // solutionType 1 = exterior to the cylinder and within the bounds of the axis
3057 // solutionType 2 = interior to the cylinder and either closer to an end-cap or
3058 // the cylinder wall solutionType 3 = beyond the bounds of the cylinder's axis
3059 // and radius solutionType 4 = beyond the bounds of the cylinder's axis but
3060 // within the bounds of it's radius
3063  const CCVector3& cylinderP1,
3064  const CCVector3& cylinderP2,
3065  const PointCoordinateType cylinderRadius,
3066  bool signedDistances /*=true*/,
3067  bool solutionType /*=false*/,
3068  double* rms /*=0*/) {
3069  if (!cloud) {
3070  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3071  }
3072  unsigned count = cloud->size();
3073  if (count == 0) {
3074  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3075  }
3076  if (!cloud->enableScalarField()) {
3077  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
3078  }
3079  double dSumSq = 0.0;
3080 
3081  CCVector3 cylinderCenter = (cylinderP1 + cylinderP2) / 2.;
3082 
3083  CCVector3 cylinderAxis = cylinderP2 - cylinderP1;
3084  double h = cylinderAxis.normd() / 2.;
3085  cylinderAxis.normalize();
3086  double cylinderRadius2 =
3087  static_cast<double>(cylinderRadius) * cylinderRadius;
3088 
3089  for (unsigned i = 0; i < count; ++i) {
3090  const CCVector3* P = cloud->getPoint(i);
3091  CCVector3 n = *P - cylinderCenter;
3092 
3093  double x = std::abs(n.dot(cylinderAxis));
3094  double xx = x * x;
3095  double yy = (n.norm2d()) - xx;
3096  double y = 0;
3097  double d = 0;
3098  if (x <= h) {
3099  if (yy >= cylinderRadius2) {
3100  if (!solutionType) {
3101  d = sqrt(yy) -
3102  cylinderRadius; // exterior to the cylinder and within
3103  // the bounds of the axis
3104  } else {
3105  d = 1;
3106  }
3107  } else {
3108  if (!solutionType) {
3109  d = -std::min(
3110  std::abs(sqrt(yy) - cylinderRadius),
3111  std::abs(h - x)); // interior to the cylinder and
3112  // either closer to an end-cap or
3113  // the cylinder wall
3114  } else {
3115  d = 2;
3116  }
3117  }
3118  } else {
3119  if (yy >= cylinderRadius2) {
3120  if (!solutionType) {
3121  y = sqrt(yy);
3122  d = sqrt(
3123  ((y - cylinderRadius) * (y - cylinderRadius)) +
3124  ((x - h) * (x - h))); // beyond the bounds of the
3125  // cylinder's axis and radius
3126  } else {
3127  d = 3;
3128  }
3129  } else {
3130  if (!solutionType) {
3131  d = x - h; // beyond the bounds of the cylinder's axis but
3132  // within the bounds of it's radius
3133  } else {
3134  d = 4;
3135  }
3136  }
3137  }
3138 
3139  if (signedDistances) {
3140  cloud->setPointScalarValue(i, static_cast<ScalarType>(d));
3141  } else {
3142  cloud->setPointScalarValue(i, static_cast<ScalarType>(std::abs(d)));
3143  }
3144  dSumSq += d * d;
3145  }
3146  if (rms) {
3147  *rms = sqrt(dSumSq / count);
3148  }
3149 
3150  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3151 }
3152 
3155  const CCVector3& sphereCenter,
3156  const PointCoordinateType sphereRadius,
3157  bool signedDistances /*=true*/,
3158  double* rms /*=0*/) {
3159  if (!cloud) {
3160  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3161  }
3162  unsigned count = cloud->size();
3163  if (count == 0) {
3164  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3165  }
3166  if (!cloud->enableScalarField()) {
3167  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
3168  }
3169  double dSumSq = 0.0;
3170  for (unsigned i = 0; i < count; ++i) {
3171  const CCVector3* P = cloud->getPoint(i);
3172  double d = (*P - sphereCenter).normd() - sphereRadius;
3173  if (signedDistances) {
3174  cloud->setPointScalarValue(i, static_cast<ScalarType>(d));
3175  } else {
3176  cloud->setPointScalarValue(i, static_cast<ScalarType>(std::abs(d)));
3177  }
3178  dSumSq += d * d;
3179  }
3180  if (rms) {
3181  *rms = sqrt(dSumSq / count);
3182  }
3183 
3184  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3185 }
3186 
3189  const PointCoordinateType* planeEquation,
3190  bool signedDistances /*=true*/,
3191  double* rms /*=0*/) {
3192  assert(cloud && planeEquation);
3193  if (!cloud) {
3194  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3195  }
3196  if (!planeEquation) {
3197  return DISTANCE_COMPUTATION_RESULTS::NULL_PLANE_EQUATION;
3198  }
3199  unsigned count = cloud->size();
3200  if (count == 0) {
3201  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3202  }
3203  if (!cloud->enableScalarField()) {
3204  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
3205  }
3206 
3207  // point to plane distance: d = std::abs(a0*x+a1*y+a2*z-a3) /
3208  // sqrt(a0^2+a1^2+a2^2) <-- "norm" but the norm should always be equal
3209  // to 1.0!
3210  PointCoordinateType norm2 = CCVector3::vnorm2(planeEquation);
3211  assert(std::abs(sqrt(norm2) - PC_ONE) <=
3212  std::numeric_limits<PointCoordinateType>::epsilon());
3213  if (LessThanEpsilon(norm2)) {
3214  return DISTANCE_COMPUTATION_RESULTS::ERROR_PLANE_NORMAL_LT_ZERO;
3215  }
3216  double dSumSq = 0.0;
3217  for (unsigned i = 0; i < count; ++i) {
3218  const CCVector3* P = cloud->getPoint(i);
3219  double d = static_cast<double>(CCVector3::vdotd(P->u, planeEquation) -
3220  planeEquation[3]);
3221  if (signedDistances) {
3222  cloud->setPointScalarValue(i, static_cast<ScalarType>(d));
3223  } else {
3224  cloud->setPointScalarValue(i, static_cast<ScalarType>(std::abs(d)));
3225  }
3226  dSumSq += d * d;
3227  }
3228  if (rms) {
3229  *rms = sqrt(dSumSq / count);
3230  }
3231 
3232  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3233 }
3234 
3237  PointCoordinateType widthX,
3238  PointCoordinateType widthY,
3239  const SquareMatrix& rotationTransform,
3240  const CCVector3& center,
3241  bool signedDist /*=true*/,
3242  double* rms /*= nullptr*/) {
3243  // p3---------------------p2
3244  // ^ |
3245  // |e1 |
3246  // | |
3247  // | e0 |
3248  // p0-------------------->p1
3249  // Rect(s,t)=p0 + s*0e + t*e1
3250  // for s,t in {0,1}
3251  assert(cloud);
3252  if (!cloud) {
3253  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3254  }
3255  unsigned count = cloud->size();
3256  if (count == 0) {
3257  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3258  }
3259  if (!cloud->enableScalarField()) {
3260  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
3261  }
3262  if (widthX <= 0 || widthY <= 0) {
3263  return DISTANCE_COMPUTATION_RESULTS::ERROR_INVALID_PRIMITIVE_DIMENSIONS;
3264  }
3265 
3266  CCVector3 widthXVec(widthX, 0, 0);
3267  CCVector3 widthYVec(0, widthY, 0);
3268  CCVector3 normalVector(0, 0, 1);
3269 
3270  widthXVec = rotationTransform * widthXVec;
3271  widthYVec = rotationTransform * widthYVec;
3272  normalVector = rotationTransform * normalVector;
3273  PointCoordinateType planeDistance = center.dot(normalVector);
3274  PointCoordinateType dSumSq = 0;
3275  CCVector3 rectangleP0 = center - (widthXVec / 2) - (widthYVec / 2);
3276  CCVector3 rectangleP1 = center + (widthXVec / 2) - (widthYVec / 2);
3277  CCVector3 rectangleP3 = center - (widthXVec / 2) + (widthYVec / 2);
3278  CCVector3 e0 = rectangleP1 - rectangleP0;
3279  CCVector3 e1 = rectangleP3 - rectangleP0;
3280 
3281  for (unsigned i = 0; i < count; ++i) {
3282  const CCVector3* pe = cloud->getPoint(i);
3283  CCVector3 dist = (*pe - rectangleP0);
3284  PointCoordinateType s = e0.dot(dist);
3285  if (s > 0) {
3286  PointCoordinateType dot0 = e0.norm2();
3287  if (s < dot0) {
3288  dist -= (s / dot0) * e0;
3289  } else {
3290  dist -= e0;
3291  }
3292  }
3293 
3294  PointCoordinateType t = e1.dot(dist);
3295  if (t > 0) {
3296  PointCoordinateType dot1 = e1.norm2();
3297  if (t < dot1) {
3298  dist -= (t / dot1) * e1;
3299  } else {
3300  dist -= e1;
3301  }
3302  }
3303  PointCoordinateType d = static_cast<PointCoordinateType>(dist.normd());
3304  dSumSq += d * d;
3305  if (signedDist && pe->dot(normalVector) - planeDistance < 0) {
3306  d = -d;
3307  }
3308  cloud->setPointScalarValue(i, static_cast<ScalarType>(d));
3309  }
3310  if (rms) {
3311  *rms = sqrt(dSumSq / count);
3312  }
3313  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3314 }
3315 
3318  const CCVector3& boxDimensions,
3319  const SquareMatrix& rotationTransform,
3320  const CCVector3& boxCenter,
3321  bool signedDist /*=true*/,
3322  double* rms /*= nullptr*/) {
3323  assert(cloud);
3324  if (!cloud) {
3325  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3326  }
3327  unsigned count = cloud->size();
3328  if (count == 0) {
3329  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3330  }
3331  if (!cloud->enableScalarField()) {
3332  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
3333  }
3334  if (boxDimensions.x <= 0 || boxDimensions.y <= 0 || boxDimensions.z <= 0) {
3335  return DISTANCE_COMPUTATION_RESULTS::ERROR_INVALID_PRIMITIVE_DIMENSIONS;
3336  }
3337  // box half lengths hu hv and hw
3338  const PointCoordinateType hu = boxDimensions.x / 2;
3339  const PointCoordinateType hv = boxDimensions.y / 2;
3340  const PointCoordinateType hw = boxDimensions.z / 2;
3341  // box coordinates unit vectors u,v, and w
3342  CCVector3 u(1, 0, 0);
3343  CCVector3 v(0, 1, 0);
3344  CCVector3 w(0, 0, 1);
3345  u = rotationTransform * u;
3346  v = rotationTransform * v;
3347  w = rotationTransform * w;
3348  PointCoordinateType dSumSq = 0;
3349  for (unsigned i = 0; i < count; ++i) {
3350  const CCVector3* p = cloud->getPoint(i);
3351  CCVector3 pointCenterDifference = (*p - boxCenter);
3352  CCVector3 p_inBoxCoords(pointCenterDifference.dot(u),
3353  pointCenterDifference.dot(v),
3354  pointCenterDifference.dot(w));
3355  bool insideBox = false;
3356  if (p_inBoxCoords.x > -hu && p_inBoxCoords.x < hu &&
3357  p_inBoxCoords.y > -hv && p_inBoxCoords.y < hv &&
3358  p_inBoxCoords.z > -hw && p_inBoxCoords.z < hw) {
3359  insideBox = true;
3360  }
3361 
3362  CCVector3 dist(0, 0, 0);
3363  if (p_inBoxCoords.x < -hu) {
3364  dist.x = -(p_inBoxCoords.x + hu);
3365  } else if (p_inBoxCoords.x > hu) {
3366  dist.x = p_inBoxCoords.x - hu;
3367  } else if (insideBox) {
3368  dist.x = std::abs(p_inBoxCoords.x) - hu;
3369  }
3370 
3371  if (p_inBoxCoords.y < -hv) {
3372  dist.y = -(p_inBoxCoords.y + hv);
3373  } else if (p_inBoxCoords.y > hv) {
3374  dist.y = p_inBoxCoords.y - hv;
3375  } else if (insideBox) {
3376  dist.y = std::abs(p_inBoxCoords.y) - hv;
3377  }
3378 
3379  if (p_inBoxCoords.z < -hw) {
3380  dist.z = -(p_inBoxCoords.z + hw);
3381  } else if (p_inBoxCoords.z > hw) {
3382  dist.z = p_inBoxCoords.z - hw;
3383  } else if (insideBox) {
3384  dist.z = std::abs(p_inBoxCoords.z) - hw;
3385  }
3386 
3387  if (insideBox) // take min distance inside box
3388  {
3389  if (dist.x >= dist.y && dist.x >= dist.z) {
3390  dist.y = 0;
3391  dist.z = 0;
3392  } else if (dist.y >= dist.x && dist.y >= dist.z) {
3393  dist.x = 0;
3394  dist.z = 0;
3395  } else if (dist.z >= dist.x && dist.z >= dist.y) {
3396  dist.x = 0;
3397  dist.y = 0;
3398  }
3399  }
3400  PointCoordinateType d = static_cast<PointCoordinateType>(dist.normd());
3401  dSumSq += d * d;
3402  if (signedDist && insideBox) {
3403  d = -d;
3404  }
3405  cloud->setPointScalarValue(i, static_cast<ScalarType>(d));
3406  }
3407  if (rms) {
3408  *rms = sqrt(dSumSq / count);
3409  }
3410  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3411 }
3412 
3415  const CCVector3& discCenter,
3416  const PointCoordinateType discRadius,
3417  const SquareMatrix& rotationTransform,
3418  bool signedDistances /*=true*/,
3419  double* rms /*=nullptr*/) {
3420  if (discRadius < 0) {
3421  return DISTANCE_COMPUTATION_RESULTS::INVALID_INPUT;
3422  }
3423  if (!cloud) {
3424  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3425  }
3426  unsigned count = cloud->size();
3427  if (count == 0) {
3428  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3429  }
3430  if (!cloud->enableScalarField()) {
3431  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
3432  }
3433 
3434  CCVector3 normal(0, 0, 1);
3435  normal = rotationTransform * normal;
3436  double dSumSq = 0.0;
3437  for (unsigned i = 0; i < count; ++i) {
3438  const CCVector3* P = cloud->getPoint(i);
3439  // Project the point onto the plane which contains the disc
3440  ScalarType dPlane = (*P - discCenter).dot(normal);
3441  CCVector3 pProj = *P - normal * dPlane;
3442  ScalarType rProj = (pProj - discCenter).norm();
3443 
3444  double d = 0.0;
3445  // Is the projection inside or outside the disc?
3446  if (rProj <= discRadius) {
3447  d = dPlane; // The distance is simply the distance to the plane
3448  } else {
3449  CCVector3 pEdge =
3450  discCenter + discRadius * (pProj - discCenter) /
3451  rProj; // safe as rProj can not be
3452  // null at this point
3453  d = (*P - pEdge).normd(); // The distance is the distance between
3454  // the point and the border of the disc
3455  d = (*P - pEdge).dot(normal) > 0 ? d : -d;
3456  }
3457  if (signedDistances) {
3458  cloud->setPointScalarValue(i, static_cast<ScalarType>(d));
3459  } else {
3460  cloud->setPointScalarValue(i, static_cast<ScalarType>(std::abs(d)));
3461  }
3462  dSumSq += d * d;
3463  }
3464  if (rms) {
3465  *rms = sqrt(dSumSq / count);
3466  }
3467 
3468  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3469 }
3470 
3473  const Polyline* polyline,
3474  double* rms /*= nullptr*/) {
3475  assert(cloud);
3476  if (!cloud) {
3477  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3478  }
3479  unsigned count = cloud->size();
3480  if (count == 0) {
3481  return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3482  }
3483  if (!cloud->enableScalarField()) {
3484  return DISTANCE_COMPUTATION_RESULTS::ERROR_ENABLE_SCALAR_FIELD_FAILURE;
3485  }
3486  ScalarType d = 0;
3487  ScalarType dSumSq = 0;
3488  for (unsigned i = 0; i < count; ++i) {
3489  ScalarType distSq = NAN_VALUE;
3490  const CCVector3* p = cloud->getPoint(i);
3491  for (unsigned j = 0; j < polyline->size() - 1; j++) {
3492  const CCVector3* start = polyline->getPoint(j);
3493  const CCVector3* end = polyline->getPoint(j + 1);
3494  PointCoordinateType startXMinusA = start->x - p->x;
3495  PointCoordinateType startYMinusB = start->y - p->y;
3496  PointCoordinateType startZMinusC = start->z - p->z;
3497  PointCoordinateType endXMinusA = end->x - p->x;
3498  PointCoordinateType endYMinusB = end->y - p->y;
3499  PointCoordinateType endZMinusC = end->z - p->z;
3500  PointCoordinateType startXMinusASq = startXMinusA * startXMinusA;
3501  PointCoordinateType startYMinusBSq = startYMinusB * startYMinusB;
3502  PointCoordinateType startZMinusCSq = startZMinusC * startZMinusC;
3503  PointCoordinateType endXMinusASq = endXMinusA * endXMinusA;
3504  PointCoordinateType endYMinusBSq = endYMinusB * endYMinusB;
3505  PointCoordinateType endZMinusCSq = endZMinusC * endZMinusC;
3506 
3507  // Rejection test
3508  if (((startXMinusASq >= distSq) && (endXMinusASq >= distSq) &&
3509  GreaterThanEpsilon(startXMinusA * endXMinusA)) ||
3510  ((startYMinusBSq >= distSq) && (endYMinusBSq >= distSq) &&
3511  GreaterThanEpsilon(startYMinusB * endYMinusB)) ||
3512  ((startZMinusCSq >= distSq) && (endZMinusCSq >= distSq) &&
3513  GreaterThanEpsilon(startZMinusC * endZMinusC))) {
3514  continue;
3515  }
3516  if (std::isnan(distSq)) {
3517  distSq = computePoint2LineSegmentDistSquared(p, start, end);
3518  } else {
3520  p, start, end));
3521  }
3522  }
3523  d = sqrt(distSq);
3524  dSumSq += distSq;
3525  cloud->setPointScalarValue(i, d);
3526  }
3527 
3528  if (rms) {
3529  *rms = sqrt(dSumSq / count);
3530  }
3531  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3532 }
3533 
3535  GenericCloud* cloud, const PointCoordinateType* planeEquation) {
3536  assert(cloud && planeEquation);
3537 
3538  // point count
3539  unsigned count = cloud->size();
3540  if (count == 0) return 0;
3541 
3542  // point to plane distance: d = std::abs(a0*x+a1*y+a2*z-a3) /
3543  // sqrt(a0^2+a1^2+a2^2) <-- "norm" but the norm should always be equal
3544  // to 1.0!
3545  PointCoordinateType norm2 = CCVector3::vnorm2(planeEquation);
3546  if (LessThanEpsilon(norm2)) return NAN_VALUE;
3547  assert(std::abs(sqrt(norm2) - PC_ONE) <=
3548  std::numeric_limits<PointCoordinateType>::epsilon());
3549 
3550  double dSumSq = 0.0;
3551 
3552  // compute deviations
3553  cloud->placeIteratorAtBeginning();
3554  for (unsigned i = 0; i < count; ++i) {
3555  const CCVector3* P = cloud->getNextPoint();
3556  double d =
3557  static_cast<double>(CCVector3::vdot(P->u, planeEquation) -
3558  planeEquation[3]) /*/norm*/; // norm == 1.0
3559 
3560  dSumSq += d * d;
3561  }
3562 
3563  return static_cast<ScalarType>(sqrt(dSumSq / count));
3564 }
3565 
3567  GenericCloud* cloud,
3568  const PointCoordinateType* planeEquation,
3569  float percent) {
3570  assert(cloud && planeEquation);
3571  assert(percent < 1.0f);
3572 
3573  // point count
3574  unsigned count = cloud->size();
3575  if (count == 0) return 0;
3576 
3577  // point to plane distance: d = std::abs(a0*x+a1*y+a2*z-a3) /
3578  // sqrt(a0^2+a1^2+a2^2) <-- "norm" but the norm should always be equal
3579  // to 1.0!
3580  PointCoordinateType norm2 = CCVector3::vnorm2(planeEquation);
3581  if (LessThanEpsilon(norm2)) return NAN_VALUE;
3582  assert(std::abs(sqrt(norm2) - PC_ONE) <=
3583  std::numeric_limits<PointCoordinateType>::epsilon());
3584 
3585  // we search the max @ 'percent'% (to avoid outliers)
3586  std::vector<PointCoordinateType> tail;
3587  std::size_t tailSize =
3588  static_cast<std::size_t>(ceil(static_cast<float>(count) * percent));
3589  tail.resize(tailSize);
3590 
3591  // compute deviations
3592  cloud->placeIteratorAtBeginning();
3593  std::size_t pos = 0;
3594  for (unsigned i = 0; i < count; ++i) {
3595  const CCVector3* P = cloud->getNextPoint();
3597  std::abs(CCVector3::vdot(P->u, planeEquation) -
3598  planeEquation[3]) /*/norm*/; // norm == 1.0
3599 
3600  if (pos < tailSize) {
3601  tail[pos++] = d;
3602  } else if (tail.back() < d) {
3603  tail.back() = d;
3604  }
3605 
3606  // search the max element of the tail
3607  std::size_t maxPos = pos - 1;
3608  if (maxPos != 0) {
3609  std::size_t maxIndex = maxPos;
3610  for (std::size_t j = 0; j < maxPos; ++j)
3611  if (tail[j] < tail[maxIndex]) maxIndex = j;
3612  // and put it to the back!
3613  if (maxPos != maxIndex) std::swap(tail[maxIndex], tail[maxPos]);
3614  }
3615  }
3616 
3617  return static_cast<ScalarType>(tail.back());
3618 }
3619 
3621  GenericCloud* cloud, const PointCoordinateType* planeEquation) {
3622  assert(cloud && planeEquation);
3623 
3624  // point count
3625  unsigned count = cloud->size();
3626  if (count == 0) return 0;
3627 
3628  // point to plane distance: d = std::abs(a0*x+a1*y+a2*z-a3) /
3629  // sqrt(a0^2+a1^2+a2^2) <-- "norm" but the norm should always be equal
3630  // to 1.0!
3631  PointCoordinateType norm2 = CCVector3::vnorm2(planeEquation);
3632  if (LessThanEpsilon(norm2)) return NAN_VALUE;
3633  assert(std::abs(sqrt(norm2) - PC_ONE) <=
3634  std::numeric_limits<PointCoordinateType>::epsilon());
3635 
3636  // we search the max distance
3637  PointCoordinateType maxDist = 0;
3638 
3639  cloud->placeIteratorAtBeginning();
3640  for (unsigned i = 0; i < count; ++i) {
3641  const CCVector3* P = cloud->getNextPoint();
3643  std::abs(CCVector3::vdot(P->u, planeEquation) -
3644  planeEquation[3]) /*/norm*/; // norm == 1.0
3645  maxDist = std::max(d, maxDist);
3646  }
3647 
3648  return static_cast<ScalarType>(maxDist);
3649 }
3650 
3653  const PointCoordinateType* planeEquation,
3654  ERROR_MEASURES measureType) {
3655  switch (measureType) {
3656  case RMS:
3658  computeCloud2PlaneDistanceRMS(cloud, planeEquation);
3659 
3660  case MAX_DIST_68_PERCENT:
3662  ComputeCloud2PlaneRobustMax(cloud, planeEquation, 0.32f);
3663  case MAX_DIST_95_PERCENT:
3665  ComputeCloud2PlaneRobustMax(cloud, planeEquation, 0.05f);
3666  case MAX_DIST_99_PERCENT:
3668  ComputeCloud2PlaneRobustMax(cloud, planeEquation, 0.01f);
3669 
3670  case MAX_DIST:
3672  ComputeCloud2PlaneMaxDistance(cloud, planeEquation);
3673 
3674  default:
3675  assert(false);
3676  return NAN_VALUE;
3677  }
3678 }
3679 
3682  unsigned seedPointIndex,
3683  unsigned char octreeLevel,
3684  GenericProgressCallback* progressCb) {
3685  assert(cloud);
3686 
3687  unsigned n = cloud->size();
3688  if (n == 0 || seedPointIndex >= n) return false;
3689 
3690  cloud->enableScalarField();
3692 
3693  DgmOctree* octree = new DgmOctree(cloud);
3694  if (octree->build(progressCb) < 1) {
3695  delete octree;
3696  return false;
3697  }
3698 
3700  if (fm.init(cloud, octree, octreeLevel, true) < 0) {
3701  delete octree;
3702  return false;
3703  }
3704 
3705  // on cherche la cellule de l'octree qui englobe le "seedPoint"
3706  Tuple3i cellPos;
3707  octree->getTheCellPosWhichIncludesThePoint(cloud->getPoint(seedPointIndex),
3708  cellPos, octreeLevel);
3709  fm.setSeedCell(cellPos);
3710 
3711  bool result = false;
3712  if (fm.propagate() >= 0) result = fm.setPropagationTimingsAsDistances();
3713 
3714  delete octree;
3715  octree = nullptr;
3716 
3717  return result;
3718 }
3719 
3721  GenericIndexedCloudPersist* referenceCloud,
3722  GenericProgressCallback* progressCb) {
3723  if (!comparedCloud || !referenceCloud)
3724  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3725 
3726  unsigned nA = comparedCloud->size();
3727  if (nA == 0) return DISTANCE_COMPUTATION_RESULTS::ERROR_EMPTY_COMPAREDCLOUD;
3728 
3729  // Reference cloud to store closest point set
3730  ReferenceCloud A_in_B(referenceCloud);
3731 
3733  params.octreeLevel = DgmOctree::MAX_OCTREE_LEVEL - 1;
3734  params.CPSet = &A_in_B;
3735 
3736  int result = computeCloud2CloudDistances(comparedCloud, referenceCloud,
3737  params, progressCb);
3738  if (result < 0) return result;
3739 
3740  for (unsigned i = 0; i < nA; ++i) {
3741  ScalarType dA = comparedCloud->getPointScalarValue(i);
3742  ScalarType dB = A_in_B.getPointScalarValue(i);
3743 
3744  // handle invalid values
3745  comparedCloud->setPointScalarValue(
3747  ? dA - dB
3748  : NAN_VALUE);
3749  }
3750 
3751  return DISTANCE_COMPUTATION_RESULTS::SUCCESS;
3752 }
3753 
3755  GenericIndexedCloudPersist* comparedCloud,
3756  GenericIndexedCloudPersist* referenceCloud,
3757  unsigned char octreeLevel,
3758  PointCoordinateType maxSearchDist /*=-PC_ONE*/,
3759  GenericProgressCallback* progressCb /*=nullptr*/,
3760  DgmOctree* compOctree /*=nullptr*/,
3761  DgmOctree* refOctree /*=nullptr*/) {
3762  if (nullptr == comparedCloud) {
3763  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_COMPAREDCLOUD;
3764  }
3765  if (nullptr == referenceCloud) {
3766  return DISTANCE_COMPUTATION_RESULTS::ERROR_NULL_REFERENCECLOUD;
3767  }
3768  if (octreeLevel < 1) {
3769  return DISTANCE_COMPUTATION_RESULTS::ERROR_OCTREE_LEVEL_LT_ONE;
3770  }
3772  return DISTANCE_COMPUTATION_RESULTS::
3773  ERROR_OCTREE_LEVEL_GT_MAX_OCTREE_LEVEL;
3774  }
3775 
3776  // compute octrees with the same bounding-box
3777  DgmOctree* octreeA = compOctree;
3778  DgmOctree* octreeB = refOctree;
3779  if (synchronizeOctrees(comparedCloud, referenceCloud, octreeA, octreeB,
3780  maxSearchDist, progressCb) != SYNCHRONIZED) {
3781  return DISTANCE_COMPUTATION_RESULTS::ERROR_SYNCHRONIZE_OCTREES_FAILURE;
3782  }
3783  if (nullptr == octreeA || nullptr == octreeB) {
3784  assert(false);
3785  delete octreeA;
3786  delete octreeB;
3787  return DISTANCE_COMPUTATION_RESULTS::ERROR_INTERNAL;
3788  }
3789 
3790  const int* minIndexesA = octreeA->getMinFillIndexes(octreeLevel);
3791  const int* maxIndexesA = octreeA->getMaxFillIndexes(octreeLevel);
3792  const int* minIndexesB = octreeB->getMinFillIndexes(octreeLevel);
3793  const int* maxIndexesB = octreeB->getMaxFillIndexes(octreeLevel);
3794 
3795  Tuple3i minIndexes(std::min(minIndexesA[0], minIndexesB[0]),
3796  std::min(minIndexesA[1], minIndexesB[1]),
3797  std::min(minIndexesA[2], minIndexesB[2]));
3798  Tuple3i maxIndexes(std::max(maxIndexesA[0], maxIndexesB[0]),
3799  std::max(maxIndexesA[1], maxIndexesB[1]),
3800  std::max(maxIndexesA[2], maxIndexesB[2]));
3801 
3802  Tuple3ui boxSize(static_cast<unsigned>(maxIndexes.x - minIndexes.x + 1),
3803  static_cast<unsigned>(maxIndexes.y - minIndexes.y + 1),
3804  static_cast<unsigned>(maxIndexes.z - minIndexes.z + 1));
3805 
3806  if (!comparedCloud->enableScalarField()) {
3807  // not enough memory
3808  return DISTANCE_COMPUTATION_RESULTS::ERROR_OUT_OF_MEMORY;
3809  }
3810  if (maxSearchDist > 0) {
3811  // if maxSearchDist is defined, we might skip some points
3812  // so we set a default distance for all of them
3813  const ScalarType resetValue = static_cast<ScalarType>(maxSearchDist);
3814  for (unsigned i = 0; i < comparedCloud->size(); ++i) {
3815  comparedCloud->setPointScalarValue(i, resetValue);
3816  }
3817  }
3818 
3819  int result = 0;
3820 
3821  // instantiate the Distance Transform grid
3823  if (dtGrid.initGrid(boxSize)) {
3824  // project the (filled) cells of octree B in the DT grid
3825  {
3827  octreeB->getCellCodes(octreeLevel, theCodes, true);
3828 
3829  while (!theCodes.empty()) {
3830  DgmOctree::CellCode theCode = theCodes.back();
3831  theCodes.pop_back();
3832  Tuple3i cellPos;
3833  octreeB->getCellPos(theCode, octreeLevel, cellPos, true);
3834  cellPos -= minIndexes;
3835  dtGrid.setValue(cellPos, 1);
3836  }
3837  }
3838 
3839  // propagate the Distance Transform over the grid
3840  dtGrid.propagateDistance(progressCb);
3841 
3842  // eventually get the approx. distance for each cell of octree A
3843  // and assign it to the points inside
3844  ScalarType cellSize =
3845  static_cast<ScalarType>(octreeA->getCellSize(octreeLevel));
3846 
3848  if (!octreeA->getCellIndexes(octreeLevel, theIndexes)) {
3849  // not enough memory
3850  if (!compOctree) delete octreeA;
3851  if (!refOctree) delete octreeB;
3852  return DISTANCE_COMPUTATION_RESULTS::ERROR_GET_CELL_INDEXES_FAILURE;
3853  }
3854 
3855  ScalarType maxD = 0;
3856  ReferenceCloud Yk(octreeA->associatedCloud());
3857 
3858  while (!theIndexes.empty()) {
3859  unsigned theIndex = theIndexes.back();
3860  theIndexes.pop_back();
3861 
3862  Tuple3i cellPos;
3863  octreeA->getCellPos(octreeA->getCellCode(theIndex), octreeLevel,
3864  cellPos, false);
3865  cellPos -= minIndexes;
3866  unsigned di = dtGrid.getValue(cellPos);
3867  ScalarType d = sqrt(static_cast<ScalarType>(di)) * cellSize;
3868  if (d > maxD) maxD = d;
3869 
3870  // the maximum distance is 'maxSearchDist' (if defined)
3871  if (maxSearchDist <= 0 || d < maxSearchDist) {
3872  octreeA->getPointsInCellByCellIndex(&Yk, theIndex, octreeLevel);
3873  for (unsigned j = 0; j < Yk.size(); ++j)
3874  Yk.setPointScalarValue(j, d);
3875  }
3876  }
3877 
3878  result = static_cast<int>(maxD);
3879  } else // DT grid init failed
3880  {
3881  result = DISTANCE_COMPUTATION_RESULTS::
3882  ERROR_INIT_DISTANCE_TRANSFORM_GRID_FAILURE;
3883  }
3884 
3885  if (!compOctree) {
3886  delete octreeA;
3887  octreeA = nullptr;
3888  }
3889  if (!refOctree) {
3890  delete octreeB;
3891  octreeB = nullptr;
3892  }
3893 
3894  return result;
3895 }
3896 
3898  const CCVector2& P,
3899  const CCVector2& A,
3900  const CCVector2& B,
3901  bool onlyOrthogonal /*=false*/) {
3902  CCVector2 AP = P - A;
3903  CCVector2 AB = B - A;
3904  PointCoordinateType dot = AB.dot(AP); // = cos(PAB) * ||AP|| * ||AB||
3905  if (dot < 0) {
3906  return onlyOrthogonal ? -PC_ONE : AP.norm2();
3907  } else {
3908  PointCoordinateType squareLengthAB = AB.norm2();
3909  if (dot > squareLengthAB) {
3910  return onlyOrthogonal ? -PC_ONE : (P - B).norm2();
3911  } else {
3912  CCVector2 HP = AP - AB * (dot / squareLengthAB);
3913  return HP.norm2();
3914  }
3915  }
3916 }
constexpr unsigned char POINT_VISIBLE
Definition: CVConst.h:92
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
Definition: CVConst.h:67
constexpr unsigned CV_LOCAL_MODEL_MIN_SIZE[]
Min number of points to compute local models (see CV_LOCAL_MODEL_TYPES)
Definition: CVConst.h:129
@ NO_MODEL
Definition: CVConst.h:122
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
Definition: CVConst.h:76
Tuple3Tpl< int > Tuple3i
Tuple of 3 int values.
Definition: CVGeom.h:217
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
double normal[3]
int count
cmdLineReadable * params[]
core::Tensor result
Definition: VtkUtils.cpp:76
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 Vector2Tpl &v) const
Dot product.
Definition: CVGeom.h:71
Type norm2() const
Returns vector square norm.
Definition: CVGeom.h:61
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
double normd() const
Returns vector norm (forces double precision output)
Definition: CVGeom.h:426
Type dot(const Vector3Tpl &v) const
Dot product.
Definition: CVGeom.h:408
Type norm2() const
Returns vector square norm.
Definition: CVGeom.h:417
double norm2d() const
Returns vector square norm (forces double precision output)
Definition: CVGeom.h:419
static PointCoordinateType vnorm(const PointCoordinateType p[])
Definition: CVGeom.h:594
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
Definition: CVGeom.h:412
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
static double vdotd(const PointCoordinateType p[], const PointCoordinateType q[])
Definition: CVGeom.h:523
static PointCoordinateType vdot(const PointCoordinateType p[], const PointCoordinateType q[])
Definition: CVGeom.h:520
static PointCoordinateType vnorm2(const PointCoordinateType p[])
Definition: CVGeom.h:573
static bool TriBoxOverlap(const CCVector3 &boxcenter, const CCVector3 &boxhalfsize, const CCVector3 *triverts[3])
Ovelap test between a 3D box and a triangle.
static void MakeMinAndMaxCubical(CCVector3 &dimMin, CCVector3 &dimMax, double enlargeFactor=0.01)
Transforms a 3D box into a 3D cube.
Definition: CVMiscTools.cpp:88
A kind of ReferenceCloud based on the DgmOctree::NeighboursSet structure.
The octree structure used throughout the library.
Definition: DgmOctree.h:39
const int * getMaxFillIndexes(unsigned char level) const
Definition: DgmOctree.h:485
unsigned CellCode
Type of the code of an octree cell.
Definition: DgmOctree.h:78
GenericIndexedCloudPersist * associatedCloud() const
Returns the associated cloud.
Definition: DgmOctree.h:1191
std::vector< unsigned int > cellIndexesContainer
Octree cell indexes container.
Definition: DgmOctree.h:95
void getCellPos(CellCode code, unsigned char level, Tuple3i &cellPos, bool isCodeTruncated) const
Definition: DgmOctree.cpp:498
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
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
virtual void clear()
Clears the octree.
Definition: DgmOctree.cpp:183
void getTheCellPosWhichIncludesThePoint(const CCVector3 *thePoint, Tuple3i &cellPos) const
Definition: DgmOctree.h:779
unsigned getNumberOfProjectedPoints() const
Returns the number of points projected into the octree.
Definition: DgmOctree.h:446
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
void computeCellCenter(CellCode code, unsigned char level, CCVector3 &center, bool isCodeTruncated=false) const
Definition: DgmOctree.h:862
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
const CCVector3 & getOctreeMins() const
Returns the lower boundaries of the octree.
Definition: DgmOctree.h:453
int build(GenericProgressCallback *progressCb=nullptr)
Builds the structure.
Definition: DgmOctree.cpp:196
const CCVector3 & getOctreeMaxs() const
Returns the higher boundaries of the octree.
Definition: DgmOctree.h:458
std::vector< IndexAndCode > cellsContainer
Container of 'IndexAndCode' structures.
Definition: DgmOctree.h:351
const CellCode & getCellCode(unsigned index) const
Returns the ith cell code.
Definition: DgmOctree.h:963
bool getCellCodes(unsigned char level, cellCodesContainer &vec, bool truncatedCodes=false) const
Definition: DgmOctree.cpp:2822
bool getCellCodesAndIndexes(unsigned char level, cellsContainer &vec, bool truncatedCodes=false) const
Definition: DgmOctree.cpp:2794
static ScalarType ComputeCloud2PlaneDistance(cloudViewer::GenericCloud *cloud, const PointCoordinateType *planeEquation, ERROR_MEASURES measureType)
SOReturnCode
Return codes for DistanceComputationTools::synchronizeOctrees.
static ScalarType computePoint2PlaneDistance(const CCVector3 *P, const PointCoordinateType *planeEquation)
Computes the (signed) distance between a point and a plane.
static int computeApproxCloud2CloudDistance(GenericIndexedCloudPersist *comparedCloud, GenericIndexedCloudPersist *referenceCloud, unsigned char octreeLevel, PointCoordinateType maxSearchDist=0, GenericProgressCallback *progressCb=nullptr, DgmOctree *compOctree=nullptr, DgmOctree *refOctree=nullptr)
Computes approximate distances between two point clouds.
static ScalarType computePoint2LineSegmentDistSquared(const CCVector3 *point, const CCVector3 *start, const CCVector3 *end)
Computes the square of the distance between a point and a line segment.
static int computeCloud2MeshDistances(GenericIndexedCloudPersist *pointCloud, GenericIndexedMesh *mesh, Cloud2MeshDistancesComputationParams &params, GenericProgressCallback *progressCb=nullptr, DgmOctree *cloudOctree=nullptr)
Computes the distance between a point cloud and a mesh.
static int computeCloud2PlaneEquation(GenericIndexedCloudPersist *cloud, const PointCoordinateType *planeEquation, bool signedDistances=true, double *rms=nullptr)
Computes the distance between each point in a cloud and a plane.
static int computeCloud2CloudDistances(GenericIndexedCloudPersist *comparedCloud, GenericIndexedCloudPersist *referenceCloud, Cloud2CloudDistancesComputationParams &params, GenericProgressCallback *progressCb=nullptr, DgmOctree *compOctree=nullptr, DgmOctree *refOctree=nullptr)
static int computeCloud2ConeEquation(GenericIndexedCloudPersist *cloud, const CCVector3 &coneP1, const CCVector3 &coneP2, const PointCoordinateType coneR1, const PointCoordinateType coneR2, bool signedDistances=true, bool solutionType=false, double *rms=nullptr)
Computes the distance between each point in a cloud and a cone.
static ScalarType ComputeCloud2PlaneRobustMax(GenericCloud *cloud, const PointCoordinateType *planeEquation, float percent)
Computes the maximum distance between a point cloud and a plane.
static ScalarType computeCloud2PlaneDistanceRMS(GenericCloud *cloud, const PointCoordinateType *planeEquation)
Computes the Root Mean Square (RMS) distance between a cloud and a plane.
static int computeCloud2DiscEquation(GenericIndexedCloudPersist *cloud, const CCVector3 &discCenter, const PointCoordinateType discRadius, const SquareMatrix &rotationTransform, bool signedDistances=true, double *rms=nullptr)
Computes the distance between each point in a cloud and a disc.
static SOReturnCode synchronizeOctrees(GenericIndexedCloudPersist *comparedCloud, GenericIndexedCloudPersist *referenceCloud, DgmOctree *&comparedOctree, DgmOctree *&referenceOctree, PointCoordinateType maxSearchDist=0, GenericProgressCallback *progressCb=nullptr)
Synchronizes (and re-build if necessary) two octrees.
static int computeCloud2SphereEquation(GenericIndexedCloudPersist *cloud, const CCVector3 &sphereCenter, const PointCoordinateType sphereRadius, bool signedDistances=true, double *rms=nullptr)
Computes the distance between each point in a cloud and a sphere.
static bool computeGeodesicDistances(GenericIndexedCloudPersist *cloud, unsigned seedPointIndex, unsigned char octreeLevel, GenericProgressCallback *progressCb=nullptr)
static bool computeCellHausdorffDistanceWithLocalModel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
static bool computeCellHausdorffDistance(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
static int computePoint2MeshDistancesWithOctree(const CCVector3 &P, ScalarType &distance, OctreeAndMeshIntersection *intersection, Cloud2MeshDistancesComputationParams &params)
static ScalarType computePoint2TriangleDistance(const CCVector3 *P, const GenericTriangle *theTriangle, bool signedDist, CCVector3 *nearestP=nullptr)
Computes the distance between a point and a triangle.
static ScalarType ComputeCloud2PlaneMaxDistance(GenericCloud *cloud, const PointCoordinateType *planeEquation)
Computes the maximum distance between a point cloud and a plane.
static int computeCloud2CylinderEquation(GenericIndexedCloudPersist *cloud, const CCVector3 &cylinderP1, const CCVector3 &cylinderP2, const PointCoordinateType cylinderRadius, bool signedDistances=true, bool solutionType=false, double *rms=nullptr)
Computes the distance between each point in a cloud and a cylinder.
static int computeCloud2PolylineEquation(GenericIndexedCloudPersist *cloud, const Polyline *polyline, double *rms=nullptr)
Computes the distance between each point in a cloud and a polyline.
static int computeCloud2BoxEquation(GenericIndexedCloudPersist *cloud, const CCVector3 &boxDimensions, const SquareMatrix &rotationTransform, const CCVector3 &boxCenter, bool signedDist=true, double *rms=nullptr)
static PointCoordinateType ComputeSquareDistToSegment(const CCVector2 &P, const CCVector2 &A, const CCVector2 &B, bool onlyOrthogonal=false)
Returns the (squared) distance from a point to a segment.
static int computeCloud2RectangleEquation(GenericIndexedCloudPersist *cloud, PointCoordinateType widthX, PointCoordinateType widthY, const SquareMatrix &rotationTransform, const CCVector3 &center, bool signedDist=true, double *rms=nullptr)
static int computeCloud2MeshDistanceWithOctree(OctreeAndMeshIntersection *theIntersection, Cloud2MeshDistancesComputationParams &params, GenericProgressCallback *progressCb=nullptr)
static int intersectMeshWithOctree(OctreeAndMeshIntersection *theIntersection, unsigned char octreeLevel, GenericProgressCallback *progressCb=nullptr)
Intersects a mesh with a grid structure.
static int computeCloud2MeshDistancesWithOctree(const DgmOctree *octree, OctreeAndMeshIntersection *intersection, Cloud2MeshDistancesComputationParams &params, GenericProgressCallback *progressCb=nullptr)
static int diff(GenericIndexedCloudPersist *comparedCloud, GenericIndexedCloudPersist *referenceCloud, GenericProgressCallback *progressCb=nullptr)
Fast Marching algorithm for surface front propagation.
int propagate() override
Propagates the front.
int init(GenericCloud *theCloud, DgmOctree *theOctree, unsigned char gridLevel, bool constantAcceleration=false)
Initializes the grid with a point cloud (and ist corresponding octree)
bool setPropagationTimingsAsDistances()
Sets the propagation timings as distances for each point.
virtual bool setSeedCell(const Tuple3i &pos)
Sets a given cell as "seed".
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 void forEach(genericPointAction action)=0
Fast iteration mechanism.
virtual void placeIteratorAtBeginning()=0
Sets the cloud iterator at the beginning.
virtual unsigned char testVisibility(const CCVector3 &P) const
Definition: GenericCloud.h:65
virtual const CCVector3 * getNextPoint()=0
Returns the next point (relatively to the global iterator position)
virtual unsigned size() const =0
Returns the number of points.
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 * getPoint(unsigned index) const =0
Returns the ith point.
A generic mesh with index-based vertex access.
virtual void getTriangleVertices(unsigned triangleIndex, CCVector3 &A, CCVector3 &B, CCVector3 &C) const =0
Returns the vertices of a given triangle.
virtual unsigned size() const =0
Returns the number of triangles.
virtual void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax)=0
Returns the mesh bounding-box.
virtual GenericTriangle * _getNextTriangle()=0
Returns the next triangle (relatively to the global iterator position)
virtual void placeIteratorAtBeginning()=0
Places the mesh iterator at the beginning.
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.
A generic triangle interface.
virtual const CCVector3 * _getA() const =0
Returns the first vertex (A)
virtual const CCVector3 * _getC() const =0
Returns the third vertex (C)
virtual const CCVector3 * _getB() const =0
Returns the second vertex (B)
Simple 3D grid structure.
Definition: Grid3D.h:32
const GridElement & getValue(int i, int j, int k) const
Returns the value of a given cell (const version)
Definition: Grid3D.h:498
void setValue(int i, int j, int k, GridElement value)
Sets the value of a given cell.
Definition: Grid3D.h:480
Local modelization (generic interface)
Definition: LocalModel.h:17
static LocalModel * New(CV_LOCAL_MODEL_TYPES type, Neighbourhood &subset, const CCVector3 &center, PointCoordinateType squaredRadius)
Factory.
Definition: LocalModel.cpp:165
virtual ScalarType computeDistanceFromModelToPoint(const CCVector3 *P, CCVector3 *nearestPoint=nullptr) const =0
Compute the (unsigned) distance between a 3D point and this model.
bool oneStep()
Increments total progress value of a single unit.
void setPointScalarValue(unsigned pointIndex, ScalarType value) override
virtual bool reserve(unsigned newCapacity)
Reserves memory for the point database.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
ScalarType getPointScalarValue(unsigned pointIndex) const override
bool enableScalarField() override
Definition: PointCloudTpl.h:77
A simple polyline class.
Definition: Polyline.h:20
A very simple point cloud (no point duplication)
void setPointScalarValue(unsigned pointIndex, ScalarType value) override
Sets the ith point associated scalar value.
virtual ScalarType getCurrentPointScalarValue() const
Returns the current point associated scalar value.
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
void placeIteratorAtBeginning() override
Sets the cloud iterator at the beginning.
unsigned size() const override
Returns the number of points.
virtual void removeCurrentPointGlobalIndex()
Removes current element.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
const CCVector3 * getPointPersistentPtr(unsigned index) override
Returns the ith point as a persistent pointer.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
ScalarType getPointScalarValue(unsigned pointIndex) const override
Returns the ith point associated scalar value.
virtual void forwardIterator()
Forwards the local element iterator.
bool propagateDistance(GenericProgressCallback *progressCb=nullptr)
Computes the exact Squared Distance Transform on the whole grid.
bool initGrid(const Tuple3ui &gridSize)
Initializes the grid.
static void SetScalarValueToNaN(const CCVector3 &P, ScalarType &scalarValue)
Sets the distance value associated to a point.
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
Definition: ScalarField.h:61
A simple triangle class.
static int ComputeNeighborhood2MeshDistancesWithOctree(OctreeAndMeshIntersection *intersection, DistanceComputationTools::Cloud2MeshDistancesComputationParams &params, ReferenceCloud &Yk, unsigned cellIndex, const Tuple3i &cellPos, TrianglesToTest &ttt, bool boundedSearch, int maxNeighbourhoodLength)
Helper function to compute neighborhood to mesh distances with octree.
void applySqrtToPointDist(const CCVector3 &aPoint, ScalarType &aScalarValue)
bool ComparePointsAndTriangles(ReferenceCloud &Yk, unsigned &remainingPoints, cloudViewer::GenericIndexedMesh *mesh, std::vector< unsigned > &trianglesToTest, std::size_t &trianglesToTestCount, std::vector< ScalarType > &minDists, ScalarType maxRadius, cloudViewer::DistanceComputationTools::Cloud2MeshDistancesComputationParams &params)
Method used by computeCloud2MeshDistanceWithOctree.
int ComputeMaxNeighborhoodLength(ScalarType maxSearchDist, PointCoordinateType cellSize)
__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
static double dist(double x1, double y1, double x2, double y2)
Definition: lsd.c:207
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
Generic file read and write utility for python interface.
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:37
bool LessThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:23
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
cloudViewer::NormalizedProgress * nProgress
cloudViewer::DgmOctree * octree
unsigned char octreeLevel
#define a01
#define a11
#define a00
Tuple3i pos
Cell position.
unsigned char level
Subdivision level.
Helper structure for triangles to test.
std::vector< unsigned > processTriangles
TrianglesToTest(const GenericIndexedMesh &mesh)
std::vector< unsigned > trianglesToTest
Association between an index and the code of an octree cell.
Definition: DgmOctree.h:301
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
void prepare(PointCoordinateType radius, PointCoordinateType cellSize)
Updates maxD2 and minD2 with search radius and cellSize.
Definition: DgmOctree.h:270
Octree cell descriptor.
Definition: DgmOctree.h:354
ReferenceCloud * points
Set of points lying inside this cell.
Definition: DgmOctree.h:365
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
Cloud-to-cloud "Hausdorff" distance computation parameters.
SaitoSquaredDistanceTransform * distanceTransform
Distance transform.
Tuple3i maxFillIndexes
Grid occupancy of mesh (maximum indexes for each dimension)
Tuple3i minFillIndexes
Grid occupancy of mesh (minimum indexes for each dimension)
Grid3D< TriangleList * > perCellTriangleList
Array of FacesInCellPtr structures.
List of triangles (indexes)
bool push(unsigned index)
Adds a triangle index.
std::vector< unsigned > indexes
Triangles indexes.