ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qVoxFallProcess.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "qVoxFallProcess.h"
9 
10 // system
11 #include <atomic>
12 #include <unordered_set>
13 
14 // local
15 #include "qVoxFallDialog.h"
16 #include "qVoxFallTools.h"
17 
18 // CVCoreLib
19 #include <CloudSamplingTools.h>
20 
21 #include "Grid3D.h"
22 
23 // qCC_plugins
24 #include <ecvMainAppInterface.h>
25 #include <ecvQtHelpers.h>
26 
27 // qCC_db
28 #include <ecvGenericPointCloud.h>
29 #include <ecvHObjectCaster.h>
30 #include <ecvMesh.h>
31 #include <ecvOctree.h>
32 #include <ecvOctreeProxy.h>
33 #include <ecvPointCloud.h>
34 #include <ecvProgressDialog.h>
35 #include <ecvScalarField.h>
36 
37 // Qt
38 #include <QApplication>
39 #include <QElapsedTimer>
40 #include <QMessageBox>
41 #include <QtConcurrentMap>
42 #include <QtCore>
43 #include <QtGui>
44 
45 #if defined(_OPENMP)
46 // OpenMP
47 #include <omp.h>
48 #endif
49 using namespace cloudViewer;
50 
52 static const char OCCUPANCY_SF_NAME[] = "Occupancy";
53 static const char CLUSTER_SF_NAME[] = "Cluster ID";
54 static const char CHANGE_TYPE_SF_NAME[] = "Loss/gain";
55 static const char VOLUME_SF_NAME[] = "Volume (m3)";
56 static const char UNCERTAINTY_SF_NAME[] = "Uncertainty (%)";
57 
58 // Structure for parallel call
59 struct VoxFallParams {
60  // main options
61  float voxelSize = 0;
62  int clusterLabel = 0;
65  bool exportBlocksAsMeshes = false;
66  bool exportLossGain = false;
67  CCVector3 minBound, maxBound, extent, steps;
68 
69  // helpers
70  std::vector<std::vector<int>> nbs;
71  std::vector<bool> isEmpty;
72  std::vector<bool> isEmptyBefore;
73  std::vector<bool> nonEmptyVoxelsVisited;
74  std::vector<int> clusters;
75  int emptyVoxelCount = 0;
78  std::vector<float> volumes;
79  std::vector<unsigned int> clusterIndices;
81 
82  // export
83  ccPointCloud* voxfall = nullptr;
84 
85  // scalar fields
86  ccScalarField* clusterSF = nullptr; // cluster ID
87  ccScalarField* changeTypeSF = nullptr; // loss or gain
88  ccScalarField* volumeSF = nullptr; // block volume
89  ccScalarField* uncertaintySF = nullptr; // volume uncertainty
90 
91  // progress notification
93  bool processCanceled = false;
94  bool processFailed = false;
95 };
97 
98 bool InitializeOutputCloud(int voxelCount,
99  GenericProgressCallback* progressCb = nullptr) {
100  // progress notification
101  NormalizedProgress nProgress(progressCb, voxelCount);
102  if (progressCb) {
103  if (progressCb->textCanBeEdited()) {
104  progressCb->setInfo("Initialization");
105  progressCb->setMethodTitle("VoxFall Detection");
106  }
107  progressCb->update(0);
108  progressCb->start();
109  }
110 
111  float voxelSize = s_VoxFallParams.voxelSize;
113 
114  for (int index = 0; index < voxelCount; ++index) {
116  CCVector3 P(
117  static_cast<PointCoordinateType>(V.x * voxelSize + minBound.x),
118  static_cast<PointCoordinateType>(V.y * voxelSize + minBound.y),
119  static_cast<PointCoordinateType>(V.z * voxelSize + minBound.z));
121 
122  // progress bar
123  if (progressCb && !nProgress.oneStep()) {
124  return false;
125  }
126  }
127 
128  return true;
129 }
130 
131 void GetVoxelOccupancy(const Tuple3i& cellPos, unsigned n) {
132  int index = qVoxFallTools::Grid2Index(cellPos, s_VoxFallParams.steps);
133  s_VoxFallParams.isEmpty[index] = false;
134 }
135 
136 void GetVoxelOccupancyBefore(const Tuple3i& cellPos, unsigned n) {
137  int index = qVoxFallTools::Grid2Index(cellPos, s_VoxFallParams.steps);
138  s_VoxFallParams.isEmptyBefore[index] = false;
139 }
140 
141 bool ClusterEmptySpace(int maxThreads,
142  int voxelCount,
143  GenericProgressCallback* progressCb = nullptr) {
144  // progress notification
145  NormalizedProgress nProgress(progressCb, voxelCount);
146  if (progressCb) {
147  if (progressCb->textCanBeEdited()) {
148  char buffer[64];
149  snprintf(buffer, 64, "Clustering empty space \n Voxels: %u",
150  voxelCount);
151  progressCb->setInfo(buffer);
152  progressCb->setMethodTitle("VoxFall Detection");
153  }
154  progressCb->update(0);
155  progressCb->start();
156  }
157 
158  auto steps = s_VoxFallParams.steps;
159  s_VoxFallParams.nbs.resize(voxelCount);
160 #if defined(_OPENMP)
161 #pragma omp parallel for schedule(static) num_threads(maxThreads)
162 #endif
163  for (int index = 0; index < voxelCount; ++index) {
164  auto V = qVoxFallTools::Index2Grid(index, steps);
165  auto NN = qVoxFallTools::FindAdjacents(V, steps, false);
166  for (auto const& n : NN) {
167  int nIdx = qVoxFallTools::Grid2Index(n, steps);
168  s_VoxFallParams.nbs[index].push_back(nIdx);
169  }
170 #if defined(_OPENMP)
171 #pragma omp critical(ClusterEmptySpace)
172  { nProgress.oneStep(); }
173 #endif
174  }
175  for (int index = 0; index < voxelCount; ++index) {
176  // Check if voxel is empty.
177  if (!s_VoxFallParams.isEmpty[index]) continue;
178 
179  // Label is not undefined.
180  if (s_VoxFallParams.clusterSF->getValue(index) != -1) {
181  continue;
182  }
183 
184  // Check density.
185  int nCount = 0;
186  for (auto const& n : s_VoxFallParams.nbs[index]) {
187  if (s_VoxFallParams.isEmpty[n]) {
188  nCount++;
189  }
190  }
191 
192  std::unordered_set<unsigned int> nbs_next(
193  s_VoxFallParams.nbs[index].begin(),
194  s_VoxFallParams.nbs[index].end());
195  std::unordered_set<unsigned int> visited;
196  visited.insert(index);
197 
199  index, static_cast<ScalarType>(s_VoxFallParams.clusterLabel));
201  0) // keep track of the total voxels included in volumes
202  {
204  }
205  if (progressCb && !nProgress.oneStep()) // progress bar
206  {
207  return false;
208  }
209  while (!nbs_next.empty()) {
210  unsigned nb = *nbs_next.begin();
211  nbs_next.erase(nbs_next.begin());
212  // Check empty neighbor.
213  if (!s_VoxFallParams.isEmpty[nb]) {
214  continue;
215  }
216  visited.insert(nb);
217 
218  // Not undefined label.
219  if (s_VoxFallParams.clusterSF->getValue(nb) != -1) {
220  continue;
221  }
223  nb, static_cast<ScalarType>(s_VoxFallParams.clusterLabel));
225  0) // keep track of the total voxels included in volumes
226  {
228  }
229  if (progressCb && !nProgress.oneStep()) // progress bar
230  {
231  return false;
232  }
233 
234  // Get neighbor's density.
235  int nCount = 0;
236  for (auto const& n : s_VoxFallParams.nbs[nb]) {
237  if (s_VoxFallParams.isEmpty[n]) {
238  nCount++;
239  }
240  }
241  if (nCount >= 1) {
242  for (int qnb : s_VoxFallParams.nbs[nb]) {
243  if (s_VoxFallParams.isEmpty[qnb]) {
244  if (visited.count(qnb) == 0) {
245  nbs_next.insert(qnb);
246  }
247  }
248  }
249  }
250  }
252  }
253  return true;
254 }
255 
256 bool ComputeClusterVolume(int maxThreads,
257  int clusterCount,
258  ccHObject* clusterGroup = nullptr) {
259  std::atomic<bool> error(false);
262  int count = 0;
263 
265 
266 #if defined(_OPENMP)
267 #pragma omp parallel for schedule(static) num_threads(maxThreads)
268 #endif
269  for (int i = 0; i < clusterCount; i++) {
270  int index = s_VoxFallParams.clusterIndices[i];
271 
272  if (error) {
273  continue;
274  }
275 
276  std::unordered_set<unsigned int> nbs_next(
277  s_VoxFallParams.nbs[index].begin(),
278  s_VoxFallParams.nbs[index].end());
279  while (!nbs_next.empty()) {
280  unsigned nb = *nbs_next.begin();
281  nbs_next.erase(nbs_next.begin());
282 
283  // Check non empty neighbor.
284  if (s_VoxFallParams.isEmpty[nb]) {
285  continue;
286  }
287  if (s_VoxFallParams.nonEmptyVoxelsVisited[nb] == false) {
290 
293  nb, s_VoxFallParams.steps);
294  CCVector3 voxel(static_cast<PointCoordinateType>(
297  static_cast<PointCoordinateType>(
300  static_cast<PointCoordinateType>(
303 
304  if (voxel.x > maxBound.x)
305  maxBound.x = static_cast<PointCoordinateType>(voxel.x);
306  if (voxel.y > maxBound.y)
307  maxBound.y = static_cast<PointCoordinateType>(voxel.y);
308  if (voxel.z > maxBound.z)
309  maxBound.z = static_cast<PointCoordinateType>(voxel.z);
310 
311  if (voxel.x < minBound.x)
312  minBound.x = static_cast<PointCoordinateType>(voxel.x);
313  if (voxel.y < minBound.y)
314  minBound.y = static_cast<PointCoordinateType>(voxel.y);
315  if (voxel.z < minBound.z)
316  minBound.z = static_cast<PointCoordinateType>(voxel.z);
317  }
318  }
321  }
322  }
323 
324  // progress bar
326  error = true;
327  }
328  }
329 
331  float ymin = minBound.y;
332  float ymax = maxBound.y;
333  CCVector3 extent = maxBound - minBound;
334  CCVector3 center = minBound + extent / 2;
335  minBound += extent / static_cast<PointCoordinateType>(2 * 0.9);
336  maxBound -= extent / static_cast<PointCoordinateType>(2 * 0.9);
337  maxBound.y = ymax + (ymax - ymin) / 2.0;
338 
339  s_VoxFallParams.centroid = minBound + (maxBound - minBound) / 1.5;
340  s_VoxFallParams.bbDims = (maxBound - minBound) / 2;
341  }
342 
343  if (error) return !error;
344  return !error;
345 }
346 
348  QString& errorMessage,
349  bool allowDialogs,
350  QWidget* parentWidget /*=nullptr*/,
351  ecvMainAppInterface* app /*=nullptr*/) {
352  errorMessage.clear();
353 
354  // get the input meshes in the right order
355  ccMesh* mesh1 = dlg.getMesh1();
356  ccMesh* mesh2 = dlg.getMesh2();
357 
358  if (!mesh1 || !mesh2) {
359  assert(false);
360  return false;
361  }
362 
363  // get parameters from dialog
364  double azimuth = dlg.getAzimuth();
365 
366  // max thread count
367  int maxThreadCount = dlg.getMaxThreadCount();
368 
369  if (app)
370  app->dispToConsole(
371  QString("[VoxFall] Will use %1 threads")
372  .arg(maxThreadCount == 0
373  ? "the max number of"
374  : QString::number(maxThreadCount)),
376 
377  // progress dialog
378  ecvProgressDialog pDlg(parentWidget);
379 
380  // Duration: initialization
381  QElapsedTimer initTimer;
382  initTimer.start();
383 
384  auto mesh = mesh1->cloneMesh();
385  mesh->merge(mesh2, false);
386 
387  auto transform = qVoxFallTransform(azimuth);
388  mesh->applyGLTransformation_recursive(&transform.matrix);
389  mesh1->applyGLTransformation_recursive(&transform.matrix);
390 
391  mesh1->setEnabled(false);
392 
393  // parameters are stored in 's_VoxFallParams' for parallel call
396  s_VoxFallParams.minBound = mesh->getOwnBB().minCorner();
397  s_VoxFallParams.maxBound = mesh->getOwnBB().maxCorner();
402  Vector3Tpl<float>(1, 1, 1);
406  new ccPointCloud(mesh1->getName() + "_to_" + mesh2->getName() +
407  QString(" [VoxFall grid] (voxel %1 m)")
408  .arg(s_VoxFallParams.voxelSize));
409 
410  // Initialize voxel grid
411  auto voxelGrid = cloudViewer::Grid3D<int>();
412  if (!voxelGrid.init(int(s_VoxFallParams.steps.x),
413  int(s_VoxFallParams.steps.y),
414  int(s_VoxFallParams.steps.z),
415  0)) // margin
416  {
417  errorMessage = "Failed to initialize voxel grid!";
418  return false;
419  }
420 
421  // Initialize heplpers
422  s_VoxFallParams.voxfall->reserve(voxelGrid.innerCellCount());
423  s_VoxFallParams.nbs.resize(voxelGrid.innerCellCount());
424  s_VoxFallParams.isEmpty.resize(voxelGrid.innerCellCount(), true);
425  s_VoxFallParams.isEmptyBefore.resize(voxelGrid.innerCellCount(), true);
427  s_VoxFallParams.clusters.resize(voxelGrid.innerCellCount(), 0);
428  }
429 
430  // allocate cluster ID SF
433  if (!s_VoxFallParams.clusterSF->resizeSafe(voxelGrid.innerCellCount(), true,
434  static_cast<ScalarType>(-1.0))) {
435  errorMessage = "Failed to allocate memory for cluster ID values!";
436  return false;
437  }
438 
440  // allocate change type SF
444  voxelGrid.innerCellCount(), true, NAN_VALUE)) {
445  errorMessage = "Failed to allocate memory for change type values!";
446  return false;
447  }
448  }
449  // allocate volume SF
452  if (!s_VoxFallParams.volumeSF->resizeSafe(voxelGrid.innerCellCount(), true,
453  NAN_VALUE)) {
454  errorMessage = "Failed to allocate memory for volume values!";
455  return false;
456  }
457  // allocate volume uncertainty SF
460  if (!s_VoxFallParams.uncertaintySF->resizeSafe(voxelGrid.innerCellCount(),
461  true, NAN_VALUE)) {
462  errorMessage =
463  "Failed to allocate memory for volume uncertainty values!";
464  return false;
465  }
466 
467  // Initialize output cloud
468  if (!InitializeOutputCloud(voxelGrid.innerCellCount(), &pDlg)) {
469  errorMessage = "Failed to initialize output data!";
470  return false;
471  }
472 
473  qint64 initTime_ms = initTimer.elapsed();
474  // we display init. timing only if no error occurred!
475  if (app)
476  app->dispToConsole(QString("[VoxFall] Initialization: %1 s")
477  .arg(initTime_ms / 1000.0, 0, 'f', 3),
479 
480  // BLOCK DETECTION
481  //=======================================================================================================================
482 
483  // Duration: Detection
484  QElapsedTimer detectTimer;
485  detectTimer.start();
486 
487  if (!voxelGrid.intersectWith(mesh, s_VoxFallParams.voxelSize,
489  &pDlg)) {
490  errorMessage = "Failed to compute grid occupancy!";
491  return false;
492  }
493 
495  if (!voxelGrid.intersectWith(mesh1, s_VoxFallParams.voxelSize,
497  GetVoxelOccupancyBefore, &pDlg)) {
498  errorMessage = "Failed to compute grid occupancy!";
499  return false;
500  }
501  }
502 
503  // cluster DBSCAN
504  if (!ClusterEmptySpace(maxThreadCount, voxelGrid.innerCellCount(), &pDlg)) {
505  errorMessage = "Failed to compute grid occupancy!";
506  return false;
507  }
508 
509  qint64 detectTime_ms = detectTimer.elapsed();
510  // we display block extraction timing only if no error occurred!
511  if (app)
512  app->dispToConsole(QString("[VoxFall] Block detection: %1 s")
513  .arg(detectTime_ms / 1000.0, 0, 'f', 3),
515  app->dispToConsole(QString("[VoxFall] Blocks found: %1")
516  .arg(s_VoxFallParams.clusterLabel - 1),
518 
519  // COMPUTE VOLUMES
520  //=======================================================================================================================
521 
522  // Duration: volume computation
523  QElapsedTimer volumeTimer;
524  volumeTimer.start();
525 
526  // progress notification
527  pDlg.reset();
529  char buffer[64];
530  snprintf(buffer, 64, "VoxFall clusters: %u \n Empty voxels: %u",
532  pDlg.setInfo(buffer);
533  pDlg.setMethodTitle(QObject::tr("Compute Volumes"));
534  pDlg.update(0);
535  pDlg.start();
537 
539  s_VoxFallParams.nonEmptyVoxelsVisited.resize(voxelGrid.innerCellCount(),
540  false);
541  for (int label = 1; label < s_VoxFallParams.clusterLabel; ++label) {
542  for (unsigned i = 0;
543  i < static_cast<unsigned>(s_VoxFallParams.clusterSF->size());
544  ++i) {
546  static_cast<ScalarType>(label)) {
547  s_VoxFallParams.clusterIndices.push_back(i);
548  }
549  }
550 
553 
555  maxThreadCount,
556  static_cast<int>(s_VoxFallParams.clusterIndices.size()))) {
557  errorMessage = "Failed to compute cluster volume!";
558  return false;
559  }
560 
562  int count = 0;
563  mesh1->placeIteratorAtBeginning();
564  for (unsigned n = 0; n < mesh1->size(); n++) {
565  // get the positions (in the grid) of each vertex
566  const GenericTriangle* T = mesh1->_getNextTriangle();
567 
568  // current triangle vertices
569  const CCVector3* triPoints[3]{T->_getA(), T->_getB(),
570  T->_getC()};
571 
574  triPoints)) {
575  count++;
576  }
577  }
578  if (count > 0) {
580  } else {
582  }
583  }
584  ScalarType changeType =
585  static_cast<ScalarType>(s_VoxFallParams.changeType);
586  ScalarType uncertainty = static_cast<ScalarType>(
587  pow(s_VoxFallParams.voxelSize, 3) *
589  ScalarType volume = static_cast<ScalarType>(
590  pow(s_VoxFallParams.voxelSize, 3) *
592  uncertainty);
593  s_VoxFallParams.volumes[label - 1] = volume;
594 
595  for (unsigned i = 0; i < s_VoxFallParams.clusterIndices.size(); i++) {
598  s_VoxFallParams.clusterIndices[i], changeType);
599  }
601  s_VoxFallParams.clusterIndices[i], volume);
604  volume / uncertainty / 100);
605  }
607  }
608 
609  qint64 volumeTime_ms = volumeTimer.elapsed();
610  // we display block volume computation timing only if no error occurred!
611  if (app)
612  app->dispToConsole(QString("[VoxFall] Volume computation: %1 s")
613  .arg(volumeTime_ms / 1000.0, 0, 'f', 3),
615 
616  // EXPORT BLOCKS AS VOXEL MESH MODELS (IF SELECTED)
617  //=======================================================================================================================
618 
620  // Duration: block meshing
621  QElapsedTimer meshTimer;
622  meshTimer.start();
623 
624  // progress notification
625  pDlg.reset();
627  char buffer[64];
628  snprintf(buffer, 64, "Blocks: %u", s_VoxFallParams.clusterLabel - 1);
629  pDlg.setInfo(buffer);
630  pDlg.setMethodTitle(QObject::tr("Exporting blocks as meshes"));
631  pDlg.update(0);
632  pDlg.start();
633 
634  // we create a new group to store all output meshes as 'VoxFall
635  // clusters'
636  ccHObject* ccGroup =
637  new ccHObject(mesh1->getName() + "_to_" + mesh2->getName() +
638  QString(" [VoxFall clusters] (voxel %1 m)")
639  .arg(s_VoxFallParams.voxelSize));
640 
641  for (int label = 1; label < s_VoxFallParams.clusterLabel; ++label) {
642  std::vector<unsigned int> indices;
643  auto it = std::find(s_VoxFallParams.clusters.begin(),
644  s_VoxFallParams.clusters.end(), label);
645  while (it != s_VoxFallParams.clusters.end()) {
646  indices.push_back(it - s_VoxFallParams.clusters.begin());
647  it = std::find(it + 1, s_VoxFallParams.clusters.end(), label);
648  }
649 
650  ccHObject* clusterGroup = new ccHObject(
651  QString("Cluster#%1 - (v: %2 m3)")
652  .arg(label)
653  .arg(s_VoxFallParams.volumes[label - 1]));
654  clusterGroup->setVisible(true);
655  ccGroup->addChild(clusterGroup);
656  for (int i = 0; i < indices.size(); i++) {
657  CCVector3 V;
658  s_VoxFallParams.voxfall->getPoint(indices[i], V);
660  V, s_VoxFallParams.voxelSize, indices[i]);
661  clusterGroup->addChild(voxel);
662 
663  // progress bar
664  if (!nProgress.oneStep()) {
665  return false;
666  }
667  }
668  indices.clear();
669  }
670  ccGroup->applyGLTransformation_recursive(&transform.inverse);
671  ccGroup->setVisible(true);
672  app->addToDB(ccGroup);
673 
674  qint64 meshTime_ms = meshTimer.elapsed();
675  // we display block as mesh export timing only if no error occurred!
676  if (app)
677  app->dispToConsole(QString("[VoxFall] Block as mesh export: %1 s")
678  .arg(meshTime_ms / 1000.0, 0, 'f', 3),
680  }
681 
682  // OUTPUT FORMATION
683  //=======================================================================================================================
684 
685  // associate cluster ID scalar fields to the voxel grid
686  int sfIdx = -1;
688  // add cluster ID SF to voxel grid
692  }
694  // associate change type scalar fields to the voxel grid
696  // add cluster ID SF to voxel grid
700  }
701  }
702  // associate volume scalar field to the voxel grid
704  // add volume SF to voxel grid
708  }
709  // associate volume uncertainty scalar field to the voxel grid
711  // add volume uncertainty SF to voxel grid
715  }
716 
717  // prepare export cloud
718  mesh1->applyGLTransformation_recursive(&transform.inverse);
720  &transform.inverse);
723  ;
727  }
729 
730  if (app) app->refreshAll();
731 
732  return true;
733 }
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
Definition: CVConst.h:76
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int count
virtual void link()
Increase counter.
Definition: CVShareable.cpp:33
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Triangular mesh.
Definition: ecvMesh.h:35
bool merge(const ccMesh *mesh, bool createSubMesh)
Merges another mesh into this one.
ccMesh * cloneMesh(ccGenericPointCloud *vertices=nullptr, ccMaterialSet *clonedMaterials=nullptr, NormsIndexesTableType *clonedNormsTable=nullptr, TextureCoordsContainer *cloneTexCoords=nullptr)
Clones this entity.
void placeIteratorAtBeginning() override
Places the mesh iterator at the beginning.
virtual unsigned size() const override
Returns the number of triangles.
cloudViewer::GenericTriangle * _getNextTriangle() override
Returns the next triangle (relatively to the global iterator position)
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
A scalar field associated to display-related parameters.
void computeMinAndMax() override
Determines the min and max values.
static bool TriBoxOverlap(const CCVector3 &boxcenter, const CCVector3 &boxhalfsize, const CCVector3 *triverts[3])
Ovelap test between a 3D box and a triangle.
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
bool oneStep()
Increments total progress value of a single unit.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPoint(unsigned index) const override
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
void setValue(std::size_t index, ScalarType value)
Definition: ScalarField.h:96
bool resizeSafe(std::size_t count, bool initNewElements=false, ScalarType valueForNewElements=0)
Resizes memory (no exception thrown)
Definition: ScalarField.cpp:81
Main application interface (for plugins)
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
Graphical progress indicator (thread-safe)
virtual void update(float percent) override
Notifies the algorithm progress.
virtual void start() override
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
VOXFALL plugin's main dialog.
ccMesh * getMesh1() const
Returns mesh #1.
bool getExportMeshesActivation() const
Returns whether the blocks will be exported as meshes.
bool getLossGainActivation() const
Labels the blocks as loss or gain clusters.
double getVoxelSize() const
Returns voxel size.
ccMesh * getMesh2() const
Returns mesh #2.
int getMaxThreadCount() const
Returns the max number of threads to use.
double getAzimuth() const
Returns slope azimuth.
static bool Compute(const qVoxFallDialog &dlg, QString &errorMessage, bool allowDialogs, QWidget *parentWidget=nullptr, ecvMainAppInterface *app=nullptr)
static std::vector< Tuple3i > FindAdjacents(Tuple3i V, CCVector3 steps, bool facetsOnly)
static int Grid2Index(Tuple3i n, CCVector3 steps)
static Tuple3i Index2Grid(unsigned index, CCVector3 steps)
static ccBox * CreateVoxelMesh(CCVector3 V, float voxelSize, int voxelIdx)
static void error(char *msg)
Definition: lsd.c:159
Generic file read and write utility for python interface.
cloudViewer::NormalizedProgress * nProgress
bool processCanceled
void GetVoxelOccupancy(const Tuple3i &cellPos, unsigned n)
static const char CLUSTER_SF_NAME[]
void GetVoxelOccupancyBefore(const Tuple3i &cellPos, unsigned n)
bool ComputeClusterVolume(int maxThreads, int clusterCount, ccHObject *clusterGroup=nullptr)
bool InitializeOutputCloud(int voxelCount, GenericProgressCallback *progressCb=nullptr)
bool ClusterEmptySpace(int maxThreads, int voxelCount, GenericProgressCallback *progressCb=nullptr)
static const char CHANGE_TYPE_SF_NAME[]
static const char VOLUME_SF_NAME[]
static const char OCCUPANCY_SF_NAME[]
Default name for VoxFall scalar fields.
static const char UNCERTAINTY_SF_NAME[]
static VoxFallParams s_VoxFallParams
#define NN
Definition: sqlite3.c:71144
ccScalarField * uncertaintySF
CCVector3 centroid
std::vector< float > volumes
std::vector< bool > isEmptyBefore
ccScalarField * volumeSF
std::vector< bool > nonEmptyVoxelsVisited
ccScalarField * clusterSF
std::vector< std::vector< int > > nbs
std::vector< bool > isEmpty
CCVector3 minBound
ccScalarField * changeTypeSF
cloudViewer::NormalizedProgress * nProgress
std::vector< int > clusters
ccPointCloud * voxfall
CCVector3 maxBound
std::vector< unsigned int > clusterIndices