ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvPointCloud.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 "ecvPointCloud.h"
9 
10 // cloudViewer
12 #include <Helper.h>
13 #include <Logging.h>
15 #include <ReferenceCloud.h>
16 
17 // local
18 #include <Eigen/Dense>
19 
20 #include "ecv2DLabel.h"
21 #include "ecvChunk.h"
22 #include "ecvColorScalesManager.h"
23 #include "ecvDisplayTools.h"
25 #include "ecvFrustum.h"
26 #include "ecvGBLSensor.h"
27 #include "ecvGenericMesh.h"
28 #include "ecvHObjectCaster.h"
29 #include "ecvImage.h"
30 #include "ecvKdTree.h"
31 #include "ecvMaterial.h"
32 #include "ecvMesh.h"
34 #include "ecvNormalVectors.h"
35 #include "ecvOctree.h"
36 #include "ecvPointCloudLOD.h"
37 #include "ecvPolyline.h"
38 #include "ecvProgressDialog.h"
39 #include "ecvScalarField.h"
40 #include "ecvSensor.h"
41 
42 // Qt
43 #include <QCoreApplication>
44 #include <QElapsedTimer>
45 #include <QSharedPointer>
46 
47 // system
48 #include <cassert>
49 #include <limits>
50 #include <queue>
51 #include <unordered_map>
52 
53 static const char s_deviationSFName[] = "Deviation";
54 
57  m_rgbColors(nullptr),
58  m_normals(nullptr),
59  m_sfColorScaleDisplayed(false),
60  m_currentDisplayedScalarField(nullptr),
61  m_currentDisplayedScalarFieldIndex(-1),
62  m_visibilityCheckEnabled(false),
63  m_lod(nullptr),
64  m_fwfData(nullptr) {
65  setName(name); // sadly we cannot use the ccGenericPointCloud constructor
66  // argument
67  showSF(false);
68 }
69 
71  : ccPointCloud(cloud.getName()) {
72  *this = cloud;
73 }
74 
75 ccPointCloud::ccPointCloud(const std::vector<Eigen::Vector3d>& points,
76  const std::string& name)
77  : ccPointCloud(name.c_str()) {
78  if (reserveThePointsTable(static_cast<unsigned>(points.size()))) {
80  }
81 }
82 
85  const ccGenericPointCloud* sourceCloud /*=0*/) {
86  ccPointCloud* pc = new ccPointCloud("Cloud");
87 
88  unsigned n = cloud->size();
89  if (n == 0) {
90  CVLog::Warning("[ccPointCloud::From] Input cloud is empty!");
91  } else {
92  if (!pc->reserveThePointsTable(n)) {
94  "[ccPointCloud::From] Not enough memory to duplicate "
95  "cloud!");
96  delete pc;
97  pc = nullptr;
98  } else {
99  // import points
100  cloud->placeIteratorAtBeginning();
101  for (unsigned i = 0; i < n; i++) {
102  pc->addPoint(*cloud->getNextPoint());
103  }
104  }
105  }
106 
107  if (pc && sourceCloud) {
109  }
110 
111  return pc;
112 }
113 
116  const ccGenericPointCloud* sourceCloud /*=0*/) {
117  ccPointCloud* pc = new ccPointCloud("Cloud");
118 
119  unsigned n = cloud->size();
120  if (n == 0) {
121  CVLog::Warning("[ccPointCloud::From] Input cloud is empty!");
122  } else {
123  if (!pc->reserveThePointsTable(n)) {
124  CVLog::Error(
125  "[ccPointCloud] Not enough memory to duplicate cloud!");
126  delete pc;
127  pc = nullptr;
128  } else {
129  // import points
130  for (unsigned i = 0; i < n; i++) {
131  CCVector3 P;
132  cloud->getPoint(i, P);
133  pc->addPoint(P);
134  }
135  }
136  }
137 
139 
140  return pc;
141 }
142 
144  const std::vector<size_t>& indices,
145  bool invert /* = false*/) {
146  unsigned sourceSize = sourceCloud->size();
147  assert(indices.size() <= sourceSize);
148  if (indices.size() == 0) {
149  CVLog::Warning("[ccPointCloud::From] Input index is empty!");
150  return nullptr;
151  }
152 
153  unsigned int out_n =
154  invert ? static_cast<unsigned int>(sourceSize - indices.size())
155  : static_cast<unsigned int>(indices.size());
156 
157  std::vector<bool> mask = std::vector<bool>(sourceSize, invert);
158  for (size_t i : indices) {
159  mask[i] = !invert;
160  }
161 
162  // scalar fields (resized)
163  unsigned sfCount = sourceCloud->getNumberOfScalarFields();
164  ccPointCloud* pc = new ccPointCloud("Cloud");
165  {
166  if (!pc->reserveThePointsTable(out_n)) {
167  CVLog::Error(
168  "[ccPointCloud::From] Not enough memory to duplicate "
169  "cloud!");
170  delete pc;
171  pc = nullptr;
172  } else {
173  // import ccPoint parameters
174  for (unsigned i = 0; i < sourceSize; i++) {
175  if (mask[i]) {
176  // import points
177  pc->addPoint(*sourceCloud->getPoint(i));
178 
179  // Colors (already reserved)
180  if (sourceCloud->hasColors()) {
181  if (!pc->hasColors()) {
182  if (!pc->reserveTheRGBTable()) {
183  CVLog::Error(
184  "[ccPointCloud::From] Not enough "
185  "memory to duplicate cloud color!");
186  continue;
187  }
188  }
190  }
191 
192  // normals (reserved)
193  if (sourceCloud->hasNormals()) {
194  // we import normals (if necessary)
195  if (!pc->hasNormals()) {
196  if (!pc->reserveTheNormsTable()) {
197  CVLog::Error(
198  "[ccPointCloud::From] Not enough "
199  "memory to duplicate cloud normal!");
200  continue;
201  }
202  }
203 
205  }
206 
207  // loop existing SF from sourceCloud
208  for (unsigned k = 0; k < sfCount; ++k) {
209  const ccScalarField* sf = static_cast<ccScalarField*>(
210  sourceCloud->getScalarField(
211  static_cast<int>(k)));
212 
213  int sfIndex =
215  if (sfIndex < 0) {
216  sfIndex = pc->addScalarField(sf->getName());
217  assert(sfIndex >= 0);
218  static_cast<ccScalarField*>(
219  pc->getScalarField(sfIndex))
221  }
222 
223  ccScalarField* sameSF = static_cast<ccScalarField*>(
224  pc->getScalarField(sfIndex));
225  if (sf && sameSF) {
226  // FIXME: we could have accuracy issues here
227  sameSF->addElement(
228  static_cast<ScalarType>(sf->getValue(i)));
229  }
230  }
231  }
232  }
233 
234  unsigned sfCount = pc->getNumberOfScalarFields();
235  if (sfCount != 0) {
236  // first we loop existing SF
237  for (unsigned k = 0; k < sfCount; ++k) {
238  ccScalarField* sf = static_cast<ccScalarField*>(
239  pc->getScalarField(static_cast<int>(k)));
240  sf->computeMinAndMax();
241  }
242  }
243  }
244  }
245 
246  if (pc && sourceCloud) {
248  pc->showSF(sourceCloud->sfShown());
252  sourceCloud->getCurrentDisplayedScalarFieldIndex());
254  }
255 
256  return pc;
257 }
258 
259 void UpdateGridIndexes(const std::vector<int>& newIndexMap,
260  std::vector<ccPointCloud::Grid::Shared>& grids) {
261  for (ccPointCloud::Grid::Shared& scanGrid : grids) {
262  unsigned cellCount = scanGrid->w * scanGrid->h;
263  scanGrid->validCount = 0;
264  scanGrid->minValidIndex = -1;
265  scanGrid->maxValidIndex = -1;
266  int* _gridIndex = scanGrid->indexes.data();
267  for (size_t j = 0; j < cellCount; ++j, ++_gridIndex) {
268  if (*_gridIndex >= 0) {
269  assert(static_cast<size_t>(*_gridIndex) < newIndexMap.size());
270  *_gridIndex = newIndexMap[*_gridIndex];
271  if (*_gridIndex >= 0) {
272  if (scanGrid->validCount) {
273  scanGrid->minValidIndex =
274  std::min(scanGrid->minValidIndex,
275  static_cast<unsigned>(*_gridIndex));
276  scanGrid->maxValidIndex =
277  std::max(scanGrid->maxValidIndex,
278  static_cast<unsigned>(*_gridIndex));
279  } else {
280  scanGrid->minValidIndex = scanGrid->maxValidIndex =
281  static_cast<unsigned>(*_gridIndex);
282  }
283  ++scanGrid->validCount;
284  }
285  }
286  }
287  }
288 }
289 
291  const cloudViewer::ReferenceCloud* selection,
292  int* warnings /*=nullptr*/,
293  bool withChildEntities /*=true*/) const {
294  if (warnings) {
295  *warnings = 0;
296  }
297 
298  if (!selection || selection->getAssociatedCloud() !=
299  static_cast<const GenericIndexedCloud*>(this)) {
300  CVLog::Error("[ccPointCloud::partialClone] Invalid parameters");
301  return nullptr;
302  }
303 
304  static constexpr const char* DefaultSuffix = ".extract";
305  QString cloneName = getName();
306  if (!cloneName.endsWith(
307  DefaultSuffix)) // avoid adding a multitude of suffixes
308  {
309  cloneName += DefaultSuffix;
310  }
311 
312  ccPointCloud* result = new ccPointCloud(cloneName);
313 
314  // visibility
315  result->setVisible(isVisible());
316  result->setEnabled(isEnabled());
317 
318  // other parameters
319  result->importParametersFrom(this);
320 
321  // from now on we will need some points to proceed ;)
322  unsigned selectionSize = selection->size();
323  if (selectionSize != 0) {
324  if (!result->reserveThePointsTable(selectionSize)) {
325  CVLog::Error(
326  "[ccPointCloud::partialClone] Not enough memory to "
327  "duplicate cloud!");
328  delete result;
329  return nullptr;
330  }
331 
332  // import points
333  {
334  for (unsigned i = 0; i < selectionSize; i++) {
335  result->addPoint(*getPointPersistentPtr(
336  selection->getPointGlobalIndex(i)));
337  }
338  }
339 
340  // RGB colors
341  if (hasColors()) {
342  if (result->reserveTheRGBTable()) {
343  for (unsigned i = 0; i < selectionSize; i++) {
344  result->addRGBColor(
345  getPointColor(selection->getPointGlobalIndex(i)));
346  }
347  result->showColors(colorsShown());
348  } else {
350  "[ccPointCloud::partialClone] Not enough memory to "
351  "copy RGB colors!");
352  if (warnings) *warnings |= WRN_OUT_OF_MEM_FOR_COLORS;
353  }
354  }
355 
356  // normals
357  if (hasNormals()) {
358  if (result->reserveTheNormsTable()) {
359  for (unsigned i = 0; i < selectionSize; i++) {
360  result->addNormIndex(getPointNormalIndex(
361  selection->getPointGlobalIndex(i)));
362  }
363  result->showNormals(normalsShown());
364  } else {
366  "[ccPointCloud::partialClone] Not enough memory to "
367  "copy normals!");
368  if (warnings) *warnings |= WRN_OUT_OF_MEM_FOR_NORMALS;
369  }
370  }
371 
372  // waveform
373  if (hasFWF()) {
374  if (result->reserveTheFWFTable()) {
375  try {
376  for (unsigned i = 0; i < selectionSize; i++) {
377  const ccWaveform& w =
379  i)];
380  if (!result->fwfDescriptors().contains(
381  w.descriptorID())) {
382  // copy only the necessary descriptors
383  result->fwfDescriptors().insert(
384  w.descriptorID(),
386  }
387  result->waveforms().push_back(w);
388  }
389  // we will use the same FWF data container
390  result->fwfData() = fwfData();
391  } catch (const std::bad_alloc&) {
393  "[ccPointCloud::partialClone] Not enough memory to "
394  "copy waveform signals!");
395  result->clearFWFData();
396  if (warnings) *warnings |= WRN_OUT_OF_MEM_FOR_FWF;
397  }
398  } else {
400  "[ccPointCloud::partialClone] Not enough memory to "
401  "copy waveform signals!");
402  if (warnings) *warnings |= WRN_OUT_OF_MEM_FOR_FWF;
403  }
404  }
405 
406  // scalar fields
407  unsigned sfCount = getNumberOfScalarFields();
408  if (sfCount != 0) {
409  for (unsigned k = 0; k < sfCount; ++k) {
410  const ccScalarField* sf =
411  static_cast<ccScalarField*>(getScalarField(k));
412  assert(sf);
413  if (sf) {
414  // we create a new scalar field with same name
415  int sfIdx = result->addScalarField(sf->getName());
416  if (sfIdx >= 0) // success
417  {
418  ccScalarField* currentScalarField =
419  static_cast<ccScalarField*>(
420  result->getScalarField(sfIdx));
421  assert(currentScalarField);
422  if (currentScalarField->resizeSafe(selectionSize)) {
423  currentScalarField->setGlobalShift(
424  sf->getGlobalShift());
425 
426  // we copy data to new SF
427  for (unsigned i = 0; i < selectionSize; i++)
428  currentScalarField->setValue(
429  i,
430  sf->getValue(
431  selection->getPointGlobalIndex(
432  i)));
433 
434  currentScalarField->computeMinAndMax();
435  // copy display parameters
436  currentScalarField->importParametersFrom(sf);
437  } else {
438  // if we don't have enough memory, we cancel SF
439  // creation
440  result->deleteScalarField(sfIdx);
442  QString("[ccPointCloud::partialClone] Not "
443  "enough memory to copy scalar "
444  "field '%1'!")
445  .arg(sf->getName()));
446  if (warnings) *warnings |= WRN_OUT_OF_MEM_FOR_SFS;
447  }
448  }
449  }
450  }
451 
452  unsigned copiedSFCount = getNumberOfScalarFields();
453  if (copiedSFCount) {
454  // we display the same scalar field as the source (if we managed
455  // to copy it!)
457  int sfIdx = result->getScalarFieldIndexByName(
459  if (sfIdx >= 0)
460  result->setCurrentDisplayedScalarField(sfIdx);
461  else
462  result->setCurrentDisplayedScalarField(
463  static_cast<int>(copiedSFCount) - 1);
464  }
465  // copy visibility
466  result->showSF(sfShown());
467  }
468  }
469 
470  std::vector<int> newIndexMap;
471  if (gridCount() != 0 || withChildEntities) {
472  // we need a map between old and new indexes
473  try {
474  newIndexMap.resize(size(), -1);
475  for (unsigned i = 0; i < selectionSize; i++) {
476  newIndexMap[selection->getPointGlobalIndex(i)] = i;
477  }
478  } catch (const std::bad_alloc&) {
479  CVLog::Warning("Not enough memory");
480  }
481  }
482 
483  // scan grids
484  if (gridCount() != 0) {
485  assert(newIndexMap.size() == size());
486  try {
487  // duplicate the grid structure(s)
488  std::vector<Grid::Shared> newGrids;
489  {
490  for (size_t i = 0; i < gridCount(); ++i) {
491  const Grid::Shared& scanGrid = grid(i);
492  if (scanGrid->validCount !=
493  0) // no need to copy empty grids!
494  {
495  // duplicate the grid
496  newGrids.push_back(
497  Grid::Shared(new Grid(*scanGrid)));
498  }
499  }
500  }
501 
502  // then update the indexes
503  UpdateGridIndexes(newIndexMap, newGrids);
504 
505  // and keep the valid (non empty) ones
506  for (Grid::Shared& scanGrid : newGrids) {
507  if (scanGrid->validCount) {
508  result->addGrid(scanGrid);
509  }
510  }
511  } catch (const std::bad_alloc&) {
512  // not enough memory
514  QString("[ccPointCloud::partialClone] Not enough "
515  "memory to copy the grid structure(s)"));
516  }
517  }
518 
519  if (withChildEntities) {
520  assert(newIndexMap.size() == size());
521  ccHObjectCaster::CloneChildren(this, result, &newIndexMap);
522  }
523  }
524 
525  return result;
526 }
527 
529 
533  unallocateNorms();
534  covariances_.clear();
535 }
536 
538  // clearLOD(); // we have to clear the LOD structure before clearing
539  // the colors / SFs, so we can't leave it to notifyGeometryUpdate()
540  showSFColorsScale(false); // SFs will be destroyed
543 
544  notifyGeometryUpdate(); // calls releaseVBOs()
545 }
546 
549  releaseVBOs();
550 }
551 
553  bool ignoreChildren /*=false*/) {
554  if (destCloud && !destCloud->isA(CV_TYPES::POINT_CLOUD)) {
555  CVLog::Error(
556  "[ccPointCloud::clone] Invalid destination cloud provided! Not "
557  "a ccPointCloud...");
558  return nullptr;
559  }
560 
561  return cloneThis(static_cast<ccPointCloud*>(destCloud), ignoreChildren);
562 }
563 
565  bool ignoreChildren /*=false*/) {
566  ccPointCloud* result = destCloud ? destCloud : new ccPointCloud();
567 
568  result->setVisible(isVisible());
569 
570  result->append(this, 0,
571  ignoreChildren); // there was (virtually) no point before
572 
573  result->showColors(colorsShown());
574  result->showSF(sfShown());
575  result->showNormals(normalsShown());
576  result->setEnabled(isEnabled());
577  result->setCurrentDisplayedScalarField(
579 
580  // import other parameters
581  result->importParametersFrom(this);
582 
583  result->setName(getName() + QString(".clone"));
584 
585  return result;
586 }
587 
589  if (this == &cloud) {
590  return (*this);
591  }
592 
593  this->clear();
594  this->setVisible(cloud.isVisible());
595  this->append(const_cast<ccPointCloud*>(&cloud), size(),
596  false); // there was (virtually) no point before
597 
598  this->showColors(cloud.colorsShown());
599  this->showSF(cloud.sfShown());
600  this->showNormals(cloud.normalsShown());
601  this->setEnabled(cloud.isEnabled());
604 
605  // import other parameters
606  this->importParametersFrom(const_cast<ccPointCloud*>(&cloud));
607 
608  this->setName(cloud.getName() + QString(".clone"));
609 
610  return (*this);
611 }
612 
614  if (isLocked()) {
615  CVLog::Error("[ccPointCloud::fusion] Cloud is locked");
616  return *this;
617  }
618 
619  return append(const_cast<ccPointCloud*>(&cloud), size());
620 }
621 
623  if (isLocked()) {
624  CVLog::Error("[ccPointCloud::fusion] Cloud is locked");
625  return *this;
626  }
627 
628  return append(addedCloud, size());
629 }
630 
632  unsigned pointCountBefore,
633  bool ignoreChildren /*=false*/) {
634  assert(addedCloud);
635 
636  unsigned addedPoints = addedCloud->size();
637 
638  if (!reserve(pointCountBefore + addedPoints)) {
639  CVLog::Error("[ccPointCloud::append] Not enough memory!");
640  return *this;
641  }
642 
643  // merge display parameters
644  setVisible(isVisible() || addedCloud->isVisible());
645 
646  // 3D points (already reserved)
647  // in some cases points have already been
648  // copied! (ok it's tricky)
649  if (size() == pointCountBefore) {
650  // we remove structures that are not compatible with fusion process
651  deleteOctree();
653 
654  for (unsigned i = 0; i < addedPoints; i++) {
655  addPoint(*addedCloud->getPoint(i));
656  }
657  }
658 
659  // deprecate internal structures
660  notifyGeometryUpdate(); // calls releaseVBOs()
661 
662  // Colors (already reserved)
663  if (hasColors() || addedCloud->hasColors()) {
664  // merge display parameters
665  showColors(colorsShown() || addedCloud->colorsShown());
666 
667  // if the added cloud has no color
668  if (!addedCloud->hasColors()) {
669  // we set a white color to new points
670  for (unsigned i = 0; i < addedPoints; i++) {
672  }
673  } else // otherwise
674  {
675  // if this cloud hadn't any color before
676  if (!hasColors()) {
677  // we try to reserve a new array
678  if (reserveTheRGBTable()) {
679  for (unsigned i = 0; i < pointCountBefore; i++) {
681  }
682  } else {
684  "[ccPointCloud::fusion] Not enough memory: failed "
685  "to allocate colors!");
686  showColors(false);
687  }
688  }
689 
690  // we import colors (if necessary)
691  if (hasColors() && m_rgbColors->currentSize() == pointCountBefore) {
692  for (unsigned i = 0; i < addedPoints; i++) {
693  addRGBColor(addedCloud->m_rgbColors->getValue(i));
694  }
695  }
696  }
697  }
698 
699  // normals (reserved)
700  if (hasNormals() || addedCloud->hasNormals()) {
701  // merge display parameters
702  showNormals(normalsShown() || addedCloud->normalsShown());
703 
704  // if the added cloud hasn't any normal
705  if (!addedCloud->hasNormals()) {
706  // we associate imported points with '0' normals
707  for (unsigned i = 0; i < addedPoints; i++) {
708  addNormIndex(0);
709  }
710  } else // otherwise
711  {
712  // if this cloud hasn't any normal
713  if (!hasNormals()) {
714  // we try to reserve a new array
715  if (reserveTheNormsTable()) {
716  for (unsigned i = 0; i < pointCountBefore; i++) {
717  addNormIndex(0);
718  }
719  } else {
721  "[ccPointCloud::fusion] Not enough memory: failed "
722  "to allocate normals!");
723  showNormals(false);
724  }
725  }
726 
727  // we import normals (if necessary)
728  if (hasNormals() && m_normals->currentSize() == pointCountBefore) {
729  for (unsigned i = 0; i < addedPoints; i++) {
730  addNormIndex(addedCloud->m_normals->getValue(i));
731  }
732  }
733  }
734  }
735 
736  // waveform
737  if (hasFWF() || addedCloud->hasFWF()) {
738  // if the added cloud hasn't any waveform
739  if (!addedCloud->hasFWF()) {
740  // we associate imported points with empty waveform
741  for (unsigned i = 0; i < addedPoints; i++) {
742  m_fwfWaveforms.emplace_back(0);
743  }
744  } else // otherwise
745  {
746  // if this cloud hasn't any FWF
747  bool success = true;
748  uint64_t fwfDataOffset = 0;
749  if (!hasFWF()) {
750  // we try to reserve a new array
751  if (reserveTheFWFTable()) {
752  for (unsigned i = 0; i < pointCountBefore; i++) {
753  m_fwfWaveforms.emplace_back(0);
754  }
755  // we will simply use the other cloud FWF data container
756  fwfData() = addedCloud->fwfData();
757  } else {
758  success = false;
760  "[ccPointCloud::fusion] Not enough memory: failed "
761  "to allocate waveforms!");
762  }
763  } else if (fwfData() != addedCloud->fwfData()) {
764  // we need to merge the two FWF data containers!
765  assert(!fwfData()->empty() && !addedCloud->fwfData()->empty());
766  FWFDataContainer* mergedContainer = new FWFDataContainer;
767  try {
768  mergedContainer->reserve(fwfData()->size() +
769  addedCloud->fwfData()->size());
770  mergedContainer->insert(mergedContainer->end(),
771  fwfData()->begin(),
772  fwfData()->end());
773  mergedContainer->insert(mergedContainer->end(),
774  addedCloud->fwfData()->begin(),
775  addedCloud->fwfData()->end());
776  fwfData() = SharedFWFDataContainer(mergedContainer);
777  fwfDataOffset = addedCloud->fwfData()->size();
778  } catch (const std::bad_alloc&) {
779  success = false;
780  delete mergedContainer;
781  mergedContainer = nullptr;
783  "[ccPointCloud::fusion] Not enough memory: failed "
784  "to merge waveform containers!");
785  }
786  }
787 
788  if (success) {
789  // assert(hasFWF()); //DGM: new waveforms are not pushed yet, so
790  // there might not be any in the cloud at the moment!
791  size_t lostWaveformCount = 0;
792 
793  // map from old descritpor IDs to new ones
794  QMap<uint8_t, uint8_t> descriptorIDMap;
795 
796  // first: copy the wave descriptors
797  try {
798  size_t newKeyCount = addedCloud->m_fwfDescriptors.size();
799  assert(newKeyCount < 256); // IDs should range from 1 to
800  // 255
801 
802  if (!m_fwfDescriptors.empty()) {
803  assert(m_fwfDescriptors.size() <
804  256); // IDs should range from 1 to 255
805 
806  // we'll have to find free descriptor IDs (not used in
807  // the destination cloud) before merging
808  std::queue<uint8_t> freeDescriptorIDs;
809  for (uint8_t k = 0; k < 255; ++k) {
810  if (!m_fwfDescriptors.contains(k)) {
811  freeDescriptorIDs.push(k);
812  // if we have found enough free descriptor IDs
813  if (freeDescriptorIDs.size() == newKeyCount) {
814  // we can stop here
815  break;
816  }
817  }
818  }
819 
820  for (auto it = addedCloud->m_fwfDescriptors.begin();
821  it != addedCloud->m_fwfDescriptors.end(); ++it) {
822  if (freeDescriptorIDs.empty()) {
824  "[ccPointCloud::fusion] Not enough "
825  "free FWF descriptor IDs on "
826  "destination cloud: some waveforms "
827  "won't be imported!");
828  break;
829  }
830  uint8_t newKey = freeDescriptorIDs.front();
831  freeDescriptorIDs.pop();
832  descriptorIDMap.insert(
833  it.key(),
834  newKey); // remember the ID transposition
835  m_fwfDescriptors.insert(
836  newKey,
837  it.value()); // insert the descriptor at
838  // its new position (ID)
839  }
840  } else {
841  for (auto it = addedCloud->m_fwfDescriptors.begin();
842  it != addedCloud->m_fwfDescriptors.end(); ++it) {
843  descriptorIDMap.insert(
844  it.key(), it.key()); // same descriptor ID,
845  // no conversion
846  m_fwfDescriptors.insert(
847  it.key(),
848  it.value()); // insert the descriptor at
849  // the same position (ID)
850  }
851  }
852  } catch (const std::bad_alloc&) {
853  success = false;
854  clearFWFData();
856  "[ccPointCloud::fusion] Not enough memory: failed "
857  "to copy waveform descriptors!");
858  }
859 
860  // and now import waveforms
861  if (success && m_fwfWaveforms.size() == pointCountBefore) {
862  for (unsigned i = 0; i < addedPoints; i++) {
863  ccWaveform w = addedCloud->waveforms()[i];
864  if (descriptorIDMap.contains(
865  w.descriptorID())) // the waveform can be
866  // imported :)
867  {
868  // update the byte offset
869  w.setDataDescription(w.dataOffset() + fwfDataOffset,
870  w.byteCount());
871  // and the (potentially new) descriptor ID
872  w.setDescriptorID(
873  descriptorIDMap[w.descriptorID()]);
874 
875  m_fwfWaveforms.push_back(w);
876  } else // the waveform is associated to a descriptor
877  // that couldn't be imported :(
878  {
879  m_fwfWaveforms.emplace_back(0);
880  ++lostWaveformCount;
881  }
882  }
883  }
884 
885  if (lostWaveformCount) {
887  QString("[ccPointCloud::fusion] %1 waveform(s) "
888  "were lost in the fusion process")
889  .arg(lostWaveformCount));
890  }
891  }
892  }
893  }
894 
895  // scalar fields (resized)
896  unsigned sfCount = getNumberOfScalarFields();
897  unsigned newSFCount = addedCloud->getNumberOfScalarFields();
898  if (sfCount != 0 || newSFCount != 0) {
899  std::vector<bool> sfUpdated(sfCount, false);
900 
901  // first we merge the new SF with the existing one
902  for (unsigned k = 0; k < newSFCount; ++k) {
903  const ccScalarField* sf = static_cast<ccScalarField*>(
904  addedCloud->getScalarField(static_cast<int>(k)));
905  if (sf) {
906  // does this field already exist (same name)?
907  int sfIdx = getScalarFieldIndexByName(sf->getName());
908  if (sfIdx >= 0) // yes
909  {
910  ccScalarField* sameSF =
911  static_cast<ccScalarField*>(getScalarField(sfIdx));
912  assert(sameSF && sameSF->capacity() >=
913  pointCountBefore + addedPoints);
914  // we fill it with new values (it should have been already
915  // 'reserved' (if necessary)
916  if (sameSF->currentSize() == pointCountBefore) {
917  double shift =
918  sf->getGlobalShift() - sameSF->getGlobalShift();
919  for (unsigned i = 0; i < addedPoints; i++) {
920  sameSF->addElement(static_cast<ScalarType>(
921  shift +
922  sf->getValue(i))); // FIXME: we could have
923  // accuracy issues here
924  }
925  }
926  sameSF->computeMinAndMax();
927 
928  // flag this SF as 'updated'
929  assert(sfIdx < static_cast<int>(sfCount));
930  sfUpdated[sfIdx] = true;
931  } else // otherwise we create a new SF
932  {
933  ccScalarField* newSF = new ccScalarField(sf->getName());
934  newSF->setGlobalShift(sf->getGlobalShift());
935  // we fill the beginning with NaN (as there is no equivalent
936  // in the current cloud)
937  if (newSF->resizeSafe(pointCountBefore + addedPoints, true,
938  NAN_VALUE)) {
939  // we copy the new values
940  for (unsigned i = 0; i < addedPoints; i++) {
941  newSF->setValue(pointCountBefore + i,
942  sf->getValue(i));
943  }
944  newSF->computeMinAndMax();
945  // copy display parameters
946  newSF->importParametersFrom(sf);
947 
948  // add scalar field to this cloud
949  sfIdx = addScalarField(newSF);
950  assert(sfIdx >= 0);
951  } else {
952  newSF->release();
953  newSF = nullptr;
955  "[ccPointCloud::fusion] Not enough memory: "
956  "failed to allocate a copy of scalar field "
957  "'%s'",
958  sf->getName());
959  }
960  }
961  }
962  }
963 
964  // let's check if there are non-updated fields
965  for (unsigned j = 0; j < sfCount; ++j) {
966  if (!sfUpdated[j]) {
968  assert(sf);
969 
970  if (sf->currentSize() == pointCountBefore) {
971  // we fill the end with NaN (as there is no equivalent in
972  // the added cloud)
973  ScalarType NaN = sf->NaN();
974  for (unsigned i = 0; i < addedPoints; i++) {
975  sf->addElement(NaN);
976  }
977  }
978  }
979  }
980 
981  // in case something bad happened
982  if (getNumberOfScalarFields() == 0) {
984  showSF(false);
985  } else {
986  // if there was no scalar field before
987  if (sfCount == 0) {
988  // and if the added cloud has one displayed
989  const ccScalarField* dispSF =
990  addedCloud->getCurrentDisplayedScalarField();
991  if (dispSF) {
992  // we set it as displayed on the current cloud also
993  int sfIdx = getScalarFieldIndexByName(
994  dispSF->getName()); // same name!
996  }
997  }
998 
999  // merge display parameters
1000  showSF(sfShown() || addedCloud->sfShown());
1001  }
1002  }
1003 
1004  // if the merged cloud has grid structures AND this one is blank or also has
1005  // grid structures
1006  if (addedCloud->gridCount() != 0 &&
1007  (gridCount() != 0 || pointCountBefore == 0)) {
1008  // copy the grid structures
1009  for (size_t i = 0; i < addedCloud->gridCount(); ++i) {
1010  const Grid::Shared& otherGrid = addedCloud->grid(i);
1011  if (otherGrid &&
1012  otherGrid->validCount != 0) // no need to copy empty grids!
1013  {
1014  try {
1015  // copy the grid data
1016  Grid::Shared grid(new Grid(*otherGrid));
1017  {
1018  // then update the indexes
1019  unsigned cellCount = grid->w * grid->h;
1020  int* _gridIndex = grid->indexes.data();
1021  for (size_t j = 0; j < cellCount; ++j, ++_gridIndex) {
1022  if (*_gridIndex >= 0) {
1023  // shift the index
1024  *_gridIndex +=
1025  static_cast<int>(pointCountBefore);
1026  }
1027  }
1028 
1029  // don't forget to shift the boundaries as well
1030  grid->minValidIndex += pointCountBefore;
1031  grid->maxValidIndex += pointCountBefore;
1032  }
1033 
1034  addGrid(grid);
1035  } catch (const std::bad_alloc&) {
1036  // not enough memory
1037  m_grids.resize(0);
1038  CVLog::Warning(QString("[ccPointCloud::fusion] Not enough "
1039  "memory: failed to copy the grid "
1040  "structure(s) from '%1'")
1041  .arg(addedCloud->getName()));
1042  break;
1043  }
1044  } else {
1045  assert(otherGrid);
1046  }
1047  }
1048  } else if (gridCount() !=
1049  0) // otherwise we'll have to drop the former grid structures!
1050  {
1052  QString("[ccPointCloud::fusion] Grid structure(s) will be "
1053  "dropped as the merged cloud is unstructured"));
1054  m_grids.resize(0);
1055  }
1056 
1057  // Covariances
1058  if ((!hasPoints() || HasCovariances()) && addedCloud->HasCovariances()) {
1059  covariances_.resize(this->size());
1060  for (size_t i = 0; i < addedPoints; i++)
1061  covariances_[pointCountBefore + i] = addedCloud->covariances_[i];
1062  } else {
1063  covariances_.clear();
1064  }
1065 
1066  // has the cloud been recentered/rescaled?
1067  {
1068  if (addedCloud->isShifted()) {
1069  if (!isShifted()) {
1070  // we can keep the global shift information of the merged cloud
1071  setGlobalShift(addedCloud->getGlobalShift());
1072  setGlobalScale(addedCloud->getGlobalScale());
1073  } else if (getGlobalScale() != addedCloud->getGlobalScale() ||
1074  (getGlobalShift() - addedCloud->getGlobalShift())
1075  .norm2d() > 1.0e-6) {
1076  // the clouds have different shift & scale information!
1078  QString("[ccPointCloud::fusion] Global shift/scale "
1079  "information conflict: shift/scale of cloud "
1080  "'%1' will be ignored!")
1081  .arg(addedCloud->getName()));
1082  }
1083  }
1084  }
1085 
1086  // children (not yet reserved)
1087  if (!ignoreChildren) {
1088  unsigned childrenCount = addedCloud->getChildrenNumber();
1089  for (unsigned c = 0; c < childrenCount; ++c) {
1090  ccHObject* child = addedCloud->getChild(c);
1091  if (!child) {
1092  assert(false);
1093  continue;
1094  }
1095  if (child->isA(CV_TYPES::MESH)) // mesh --> FIXME: what for the
1096  // other types of MESH?
1097  {
1098  ccMesh* mesh = static_cast<ccMesh*>(child);
1099 
1100  // detach from father?
1101  // addedCloud->detachChild(mesh);
1102  // ccGenericMesh* addedTri = mesh;
1103 
1104  // or clone?
1105  ccMesh* cloneMesh = mesh->cloneMesh(
1106  mesh->getAssociatedCloud() == addedCloud ? this
1107  : nullptr);
1108  if (cloneMesh) {
1109  // change mesh vertices
1110  if (cloneMesh->getAssociatedCloud() == this) {
1111  cloneMesh->shiftTriangleIndexes(pointCountBefore);
1112  }
1113  addChild(cloneMesh);
1114  } else {
1116  QString("[ccPointCloud::fusion] Not enough memory: "
1117  "failed to clone sub mesh %1!")
1118  .arg(mesh->getName()));
1119  }
1120  } else if (child->isKindOf(CV_TYPES::IMAGE)) {
1121  // ccImage* image = static_cast<ccImage*>(child);
1122 
1123  // DGM FIXME: take image ownership! (dirty)
1124  addedCloud->transferChild(child, *this);
1125  } else if (child->isA(CV_TYPES::LABEL_2D)) {
1126  // clone label and update points if necessary
1127  cc2DLabel* label = static_cast<cc2DLabel*>(child);
1128  cc2DLabel* newLabel = new cc2DLabel(label->getName());
1129  for (unsigned j = 0; j < label->size(); ++j) {
1130  const cc2DLabel::PickedPoint& P = label->getPickedPoint(j);
1131  if (P.cloud == addedCloud)
1132  newLabel->addPickedPoint(this,
1133  pointCountBefore + P.index);
1134  else
1135  newLabel->addPickedPoint(P.cloud, P.index);
1136  }
1137  newLabel->displayPointLegend(label->isPointLegendDisplayed());
1138  newLabel->setDisplayedIn2D(label->isDisplayedIn2D());
1139  newLabel->setCollapsed(label->isCollapsed());
1140  newLabel->setPosition(label->getPosition()[0],
1141  label->getPosition()[1]);
1142  newLabel->setVisible(label->isVisible());
1143  // newLabel->setDisplay(getDisplay());
1144  addChild(newLabel);
1145  } else if (child->isA(CV_TYPES::GBL_SENSOR)) {
1146  // copy sensor object
1147  ccGBLSensor* sensor =
1148  new ccGBLSensor(*static_cast<ccGBLSensor*>(child));
1149  addChild(sensor);
1150  // sensor->setDisplay(getDisplay());
1151  sensor->setVisible(child->isVisible());
1152  }
1153  }
1154  }
1155 
1156  return *this;
1157 }
1158 
1160  if (m_normals) {
1161  m_normals->release();
1162  m_normals = nullptr;
1163 
1164  // We should update the VBOs to gain some free space in VRAM
1165  releaseVBOs();
1166  }
1167 
1168  showNormals(false);
1169 }
1170 
1172  if (m_rgbColors) {
1173  m_rgbColors->release();
1174  m_rgbColors = nullptr;
1175 
1176  // We should update the VBOs to gain some free space in VRAM
1177  releaseVBOs();
1178  }
1179 
1180  // remove the grid colors as well!
1181  for (size_t i = 0; i < m_grids.size(); ++i) {
1182  if (m_grids[i]) {
1183  m_grids[i]->colors.resize(0);
1184  }
1185  }
1186 
1187  showColors(false);
1188  enableTempColor(false);
1189 }
1190 
1191 bool ccPointCloud::reserveThePointsTable(unsigned newNumberOfPoints) {
1192  try {
1193  m_points.reserve(newNumberOfPoints);
1194  } catch (const std::bad_alloc&) {
1195  return false;
1196  }
1197  return true;
1198 }
1199 
1201  if (m_points.capacity() == 0) {
1203  "[ccPointCloud::reserveTheRGBTable] Internal error: properties "
1204  "(re)allocation before points allocation is forbidden!");
1205  return false;
1206  }
1207 
1208  if (!m_rgbColors) {
1209  m_rgbColors = new ColorsTableType();
1210  m_rgbColors->link();
1211  }
1212 
1213  if (!m_rgbColors->reserveSafe(m_points.capacity())) {
1214  m_rgbColors->release();
1215  m_rgbColors = nullptr;
1216  CVLog::Error("[ccPointCloud::reserveTheRGBTable] Not enough memory!");
1217  }
1218 
1219  // We must update the VBOs
1221 
1222  // double check
1223  return m_rgbColors && m_rgbColors->capacity() >= m_points.capacity();
1224 }
1225 
1226 bool ccPointCloud::resizeTheRGBTable(bool fillWithWhite /*=false*/) {
1227  if (m_points.empty()) {
1229  "[ccPointCloud::resizeTheRGBTable] Internal error: properties "
1230  "(re)allocation before points allocation is forbidden!");
1231  return false;
1232  }
1233 
1234  if (!m_rgbColors) {
1235  m_rgbColors = new ColorsTableType();
1236  m_rgbColors->link();
1237  }
1238 
1239  static const ecvColor::Rgb s_white(255, 255, 255);
1240  if (!m_rgbColors->resizeSafe(m_points.size(), fillWithWhite, &s_white)) {
1241  m_rgbColors->release();
1242  m_rgbColors = nullptr;
1243  CVLog::Error("[ccPointCloud::resizeTheRGBTable] Not enough memory!");
1244  }
1245 
1246  // We must update the VBOs
1248 
1249  // double check
1250  return m_rgbColors && m_rgbColors->size() == m_points.size();
1251 }
1252 
1254  if (m_points.capacity() == 0) {
1256  "[ccPointCloud::reserveTheNormsTable] Internal error: "
1257  "properties (re)allocation before points allocation is "
1258  "forbidden!");
1259  return false;
1260  }
1261 
1262  if (!m_normals) {
1264  m_normals->link();
1265  }
1266 
1267  if (!m_normals->reserveSafe(m_points.capacity())) {
1268  m_normals->release();
1269  m_normals = nullptr;
1270 
1271  CVLog::Error("[ccPointCloud::reserveTheNormsTable] Not enough memory!");
1272  }
1273 
1274  // We must update the VBOs
1276 
1277  // double check
1278  return m_normals && m_normals->capacity() >= m_points.capacity();
1279 }
1280 
1282  if (m_points.empty()) {
1284  "[ccPointCloud::resizeTheNormsTable] Internal error: "
1285  "properties (re)allocation before points allocation is "
1286  "forbidden!");
1287  return false;
1288  }
1289 
1290  if (!m_normals) {
1292  m_normals->link();
1293  }
1294 
1295  static const CompressedNormType s_normZero = 0;
1296  if (!m_normals->resizeSafe(m_points.size(), true, &s_normZero)) {
1297  m_normals->release();
1298  m_normals = nullptr;
1299 
1300  CVLog::Error("[ccPointCloud::resizeTheNormsTable] Not enough memory!");
1301  }
1302 
1303  // We must update the VBOs
1305 
1306  // double check
1307  return m_normals && m_normals->size() == m_points.size();
1308 }
1309 
1311  if (!m_fwfData || m_fwfData->empty()) {
1312  return false;
1313  }
1314 
1315  try {
1316  size_t initialCount = m_fwfData->size();
1317  std::vector<size_t> usedIndexes;
1318  usedIndexes.resize(initialCount, 0);
1319 
1320  for (const ccWaveform& w : m_fwfWaveforms) {
1321  if (w.byteCount() == 0) {
1322  assert(false);
1323  continue;
1324  }
1325 
1326  size_t start = w.dataOffset();
1327  size_t end = w.dataOffset() + w.byteCount();
1328  for (size_t i = start; i < end; ++i) {
1329  usedIndexes[i] = 1;
1330  }
1331  }
1332 
1333  size_t newIndex = 0;
1334  for (size_t& index : usedIndexes) {
1335  if (index != 0) {
1336  index = ++newIndex; // we need to start at 1 (as 0 means 'not
1337  // used')
1338  }
1339  }
1340 
1341  if (newIndex >= initialCount) {
1342  // nothing to do
1343  CVLog::Print(QString("[ccPointCloud::compressFWFData] Cloud '%1': "
1344  "no need to compress FWF data")
1345  .arg(getName()));
1346  return true;
1347  }
1348 
1349  // now create the new container
1350  FWFDataContainer* newContainer = new FWFDataContainer;
1351  newContainer->reserve(newIndex);
1352 
1353  for (size_t i = 0; i < initialCount; ++i) {
1354  if (usedIndexes[i]) {
1355  newContainer->push_back(m_fwfData->at(i));
1356  }
1357  }
1358 
1359  // and don't forget to update the waveform descriptors!
1360  for (ccWaveform& w : m_fwfWaveforms) {
1361  uint64_t offset = w.dataOffset();
1362  assert(usedIndexes[offset] != 0);
1363  w.setDataOffset(usedIndexes[offset] - 1);
1364  }
1365  m_fwfData = SharedFWFDataContainer(newContainer);
1366 
1367  CVLog::Print(QString("[ccPointCloud::compressFWFData] Cloud '%1': FWF "
1368  "data compressed --> %2 / %3 (%4%)")
1369  .arg(getName())
1370  .arg(newIndex)
1371  .arg(initialCount)
1372  .arg(100.0 - (newIndex * 100.0) / initialCount, 0,
1373  'f', 1));
1374  } catch (const std::bad_alloc&) {
1375  CVLog::Warning("[ccPointCloud::compressFWFData] Not enough memory!");
1376  return false;
1377  }
1378 
1379  return true;
1380 }
1381 
1383  if (m_points.capacity() == 0) {
1385  "[ccPointCloud::reserveTheFWFTable] Internal error: properties "
1386  "(re)allocation before points allocation is forbidden!");
1387  return false;
1388  }
1389 
1390  try {
1391  m_fwfWaveforms.reserve(m_points.capacity());
1392  } catch (const std::bad_alloc&) {
1393  CVLog::Error("[ccPointCloud::reserveTheFWFTable] Not enough memory!");
1394  m_fwfWaveforms.resize(0);
1395  }
1396 
1397  // double check
1398  return m_fwfWaveforms.capacity() >= m_points.capacity();
1399 }
1400 
1401 bool ccPointCloud::hasFWF() const {
1402  return m_fwfData && !m_fwfData->empty() && !m_fwfWaveforms.empty();
1403 }
1404 
1406  static const ccWaveform invalidW;
1407  static const WaveformDescriptor invalidD;
1408 
1409  if (index < m_fwfWaveforms.size()) {
1410  const ccWaveform& w = m_fwfWaveforms[index];
1411  // check data consistency
1412  if (m_fwfData && w.dataOffset() + w.byteCount() <= m_fwfData->size()) {
1413  if (m_fwfDescriptors.contains(w.descriptorID())) {
1414  WaveformDescriptor& d =
1415  const_cast<ccPointCloud*>(this)->m_fwfDescriptors
1416  [w.descriptorID()]; // DGM: we really want the
1417  // reference to the
1418  // element, not a copy as
1419  // QMap returns in the
1420  // const case :(
1421  return ccWaveformProxy(w, d, m_fwfData->data());
1422  } else {
1423  return ccWaveformProxy(w, invalidD, nullptr);
1424  }
1425  }
1426  }
1427 
1428  // if we are here, then something is wrong
1429  assert(false);
1430  return ccWaveformProxy(invalidW, invalidD, nullptr);
1431 }
1432 
1434  if (m_points.capacity() == 0) {
1436  "[ccPointCloud::resizeTheFWFTable] Internal error: properties "
1437  "(re)allocation before points allocation is forbidden!");
1438  return false;
1439  }
1440 
1441  try {
1442  m_fwfWaveforms.resize(m_points.capacity());
1443  } catch (const std::bad_alloc&) {
1444  CVLog::Error("[ccPointCloud::resizeTheFWFTable] Not enough memory!");
1445  m_fwfWaveforms.resize(0);
1446  }
1447 
1448  // double check
1449  return m_fwfWaveforms.capacity() >= m_points.capacity();
1450 }
1451 
1452 bool ccPointCloud::reserve(unsigned newNumberOfPoints) {
1453  // reserve works only to enlarge the cloud
1454  if (newNumberOfPoints < size()) return false;
1455 
1456  // call parent method first (for points + scalar fields)
1457  if (!BaseClass::reserve(newNumberOfPoints) ||
1458  (hasColors() && !reserveTheRGBTable()) ||
1459  (hasNormals() && !reserveTheNormsTable()) ||
1460  (hasFWF() && !reserveTheFWFTable())) {
1461  CVLog::Error("[ccPointCloud::reserve] Not enough memory!");
1462  return false;
1463  }
1464 
1465  // CVLog::Warning(QString("[ccPointCloud::reserve] Cloud is %1 and its
1466  // capacity is '%2'").arg(m_points.capacity() != 0 ? "allocated" : "not
1467  // allocated").arg(m_points.capacity()));
1468 
1469  // double check
1470  return m_points.capacity() >= newNumberOfPoints &&
1471  (!hasColors() || m_rgbColors->capacity() >= newNumberOfPoints) &&
1472  (!hasNormals() || m_normals->capacity() >= newNumberOfPoints) &&
1473  (!hasFWF() || m_fwfWaveforms.capacity() >= newNumberOfPoints);
1474 }
1475 
1476 bool ccPointCloud::resize(unsigned newNumberOfPoints) {
1477  // can't reduce the size if the cloud if it is locked!
1478  if (newNumberOfPoints < size() && isLocked()) return false;
1479 
1480  // call parent method first (for points + scalar fields)
1481  if (!BaseClass::resize(newNumberOfPoints)) {
1482  CVLog::Error("[ccPointCloud::resize] Not enough memory!");
1483  return false;
1484  }
1485 
1486  notifyGeometryUpdate(); // calls releaseVBOs()
1487 
1488  if ((hasColors() && !resizeTheRGBTable(false)) ||
1489  (hasNormals() && !resizeTheNormsTable()) ||
1490  (hasFWF() && !resizeTheFWFTable())) {
1491  CVLog::Error("[ccPointCloud::resize] Not enough memory!");
1492  return false;
1493  }
1494 
1495  if (HasCovariances()) {
1496  covariances_.resize(m_points.size());
1497  }
1498 
1499  // double check
1500  return m_points.size() == newNumberOfPoints &&
1501  (!hasColors() || m_rgbColors->currentSize() == newNumberOfPoints) &&
1502  (!hasNormals() || m_normals->currentSize() == newNumberOfPoints) &&
1503  (!HasCovariances() || covariances_.size() == newNumberOfPoints) &&
1504  (!hasFWF() || m_fwfWaveforms.size() == newNumberOfPoints);
1505 }
1506 
1508  m_sfColorScaleDisplayed = state;
1509 }
1510 
1512 
1514  unsigned pointIndex) const {
1517 
1518  return m_currentDisplayedScalarField->getValueColor(pointIndex);
1519 }
1520 
1524 
1526 }
1527 
1528 ScalarType ccPointCloud::getPointDisplayedDistance(unsigned pointIndex) const {
1530  assert(pointIndex < m_currentDisplayedScalarField->currentSize());
1531 
1532  return m_currentDisplayedScalarField->getValue(pointIndex);
1533 }
1534 
1535 const ecvColor::Rgb& ccPointCloud::getPointColor(unsigned pointIndex) const {
1536  assert(hasColors());
1537  assert(m_rgbColors && pointIndex < m_rgbColors->currentSize());
1538 
1539  return m_rgbColors->at(pointIndex);
1540 }
1541 
1543  assert(hasColors());
1544  assert(m_rgbColors && pointIndex < m_rgbColors->currentSize());
1545 
1546  return m_rgbColors->at(pointIndex);
1547 }
1548 
1550  unsigned pointIndex) const {
1551  assert(m_normals && pointIndex < m_normals->currentSize());
1552  return m_normals->getValue(pointIndex);
1553 }
1554 
1555 const CCVector3& ccPointCloud::getPointNormal(unsigned pointIndex) const {
1556  assert(m_normals && pointIndex < m_normals->currentSize());
1557 
1558  return ccNormalVectors::GetNormal(m_normals->getValue(pointIndex));
1559 }
1560 
1561 const CCVector3* ccPointCloud::getNormal(unsigned pointIndex) const {
1562  assert(m_normals && pointIndex < m_normals->currentSize());
1563 
1564  return &ccNormalVectors::GetNormal(m_normals->getValue(pointIndex));
1565 }
1566 
1567 CCVector3& ccPointCloud::getPointNormalPtr(size_t pointIndex) const {
1568  assert(m_normals && pointIndex < m_normals->currentSize());
1569 
1571  m_normals->getValue(static_cast<unsigned int>(pointIndex)));
1572 }
1573 
1574 std::vector<CCVector3> ccPointCloud::getPointNormals() const {
1575  std::vector<CCVector3> normals(size());
1576  for (unsigned i = 0; i < size(); ++i) {
1577  normals[i] = getPointNormal(i);
1578  }
1579  return normals;
1580 }
1581 
1582 std::vector<CCVector3*> ccPointCloud::getPointNormalsPtr() const {
1583  std::vector<CCVector3*> normals;
1584  for (unsigned i = 0; i < this->size(); ++i) {
1585  normals.push_back(&getPointNormalPtr(i));
1586  }
1587  return normals;
1588 }
1589 
1590 void ccPointCloud::setPointNormals(const std::vector<CCVector3>& normals) {
1591  if (normals.size() > size()) {
1592  return;
1593  }
1594 
1595  for (size_t i = 0; i < normals.size(); ++i) {
1596  setPointNormal(i, normals[i]);
1597  }
1598 }
1599 
1600 Eigen::Vector3d ccPointCloud::getEigenNormal(size_t index) const {
1601  return CCVector3d::fromArray(getPointNormal(static_cast<unsigned>(index)));
1602 }
1603 
1604 std::vector<Eigen::Vector3d> ccPointCloud::getEigenNormals() const {
1605  if (!m_normals || m_normals->size() != size()) {
1606  return {};
1607  }
1608 
1609  std::vector<Eigen::Vector3d> normals(size());
1610  for (size_t i = 0; i < size(); i++) {
1611  normals[i] = getEigenNormal(i);
1612  }
1613  return normals;
1614 }
1615 
1617  const std::vector<Eigen::Vector3d>& normals) {
1618  if (m_normals && m_normals->size() == normals.size()) {
1619  for (size_t i = 0; i < normals.size(); i++) {
1620  setPointNormal(i, normals[i]);
1621  }
1622  }
1623 }
1624 
1625 Eigen::Vector3d ccPointCloud::getEigenColor(size_t index) const {
1626  return ecvColor::Rgb::ToEigen(getPointColor(static_cast<unsigned>(index)));
1627 }
1628 
1629 std::vector<Eigen::Vector3d> ccPointCloud::getEigenColors() const {
1630  if (!m_rgbColors || m_rgbColors->size() != size()) {
1631  return {};
1632  }
1633 
1634  std::vector<Eigen::Vector3d> colors(size());
1635  for (size_t i = 0; i < size(); i++) {
1636  colors[i] = getEigenColor(i);
1637  }
1638  return colors;
1639 }
1640 
1641 void ccPointCloud::setEigenColors(const std::vector<Eigen::Vector3d>& colors) {
1642  if (m_rgbColors && m_rgbColors->size() == colors.size()) {
1643  for (size_t i = 0; i < colors.size(); i++) {
1644  setPointColor(i, colors[i]);
1645  }
1646  }
1647 }
1648 
1649 void ccPointCloud::setPointColor(size_t pointIndex, const ecvColor::Rgb& col) {
1650  assert(m_rgbColors && pointIndex < m_rgbColors->currentSize());
1651 
1652  m_rgbColors->setValue(pointIndex, col);
1653 
1654  // We must update the VBOs
1656 }
1657 
1658 void ccPointCloud::setPointColor(size_t pointIndex, const ecvColor::Rgba& col) {
1659  setPointColor(pointIndex, ecvColor::FromRgbaToRgb(col));
1660 }
1661 
1662 void ccPointCloud::setPointColor(size_t pointIndex,
1663  const Eigen::Vector3d& col) {
1664  setPointColor(pointIndex, ecvColor::Rgb::FromEigen(col));
1665 }
1666 
1667 void ccPointCloud::setEigenColor(size_t index, const Eigen::Vector3d& color) {
1669 }
1670 
1671 void ccPointCloud::setPointNormalIndex(size_t pointIndex,
1672  CompressedNormType norm) {
1673  assert(m_normals && pointIndex < m_normals->currentSize());
1674 
1675  m_normals->setValue(static_cast<unsigned>(pointIndex), norm);
1676 
1677  // We must update the VBOs
1679 }
1680 
1681 void ccPointCloud::setPointNormal(size_t pointIndex, const CCVector3& N) {
1683 }
1684 
1685 void ccPointCloud::setEigenNormal(size_t index, const Eigen::Vector3d& normal) {
1687 }
1688 
1690  return m_rgbColors && m_rgbColors->isAllocated();
1691 }
1692 
1694  return m_normals && m_normals->isAllocated();
1695 }
1696 
1698  return (getNumberOfScalarFields() > 0);
1699 }
1700 
1704 }
1705 
1708 
1709  notifyGeometryUpdate(); // calls releaseVBOs()
1710 }
1711 
1713  // allocate colors if necessary
1714  if (!hasColors())
1715  if (!reserveTheRGBTable()) return;
1716  m_rgbColors->emplace_back(C);
1717  // We must update the VBOs
1719 }
1720 
1721 void ccPointCloud::addEigenColor(const Eigen::Vector3d& color) {
1723 }
1724 
1725 void ccPointCloud::addEigenColors(const std::vector<Eigen::Vector3d>& colors) {
1726  if (!hasColors()) {
1728  }
1729 
1730  if (colors.size() == m_rgbColors->size()) {
1732  } else {
1733  m_rgbColors->clear();
1734  if (!m_rgbColors->reserveSafe(colors.size())) {
1735  m_rgbColors->release();
1736  m_rgbColors = nullptr;
1738  "[ccPointCloud::addEigenColors] Not enough memory!");
1739  return;
1740  }
1741 
1742  for (auto& C : colors) {
1743  addEigenColor(C);
1744  }
1745  }
1746 }
1747 
1748 void ccPointCloud::addRGBColors(const std::vector<ecvColor::Rgb>& colors) {
1749  for (auto& C : colors) {
1750  addRGBColor(C);
1751  }
1752 }
1753 
1756 }
1757 
1758 void ccPointCloud::addEigenNorm(const Eigen::Vector3d& N) { addNorm(N); }
1759 
1760 void ccPointCloud::addEigenNorms(const std::vector<Eigen::Vector3d>& normals) {
1761  if (!hasNormals()) {
1763  }
1764 
1765  if (normals.size() == m_normals->size()) {
1767  } else {
1768  m_normals->clear();
1769  if (!m_normals->reserveSafe(normals.size())) {
1770  m_normals->release();
1771  m_normals = nullptr;
1773  "[ccPointCloud::addEigenNorms] Not enough memory!");
1774  return;
1775  }
1776 
1777  for (auto& N : normals) {
1778  addEigenNorm(N);
1779  }
1780  }
1781 }
1782 
1783 void ccPointCloud::addNorms(const std::vector<CCVector3>& Ns) {
1784  for (auto& N : Ns) {
1785  addNorm(N);
1786  }
1787 }
1788 
1789 void ccPointCloud::addNorms(const std::vector<CompressedNormType>& idxes) {
1790  for (auto& idx : idxes) {
1791  addNormIndex(idx);
1792  }
1793 }
1794 
1795 std::vector<CompressedNormType> ccPointCloud::getNorms() const {
1796  std::vector<CompressedNormType> temp;
1797  getNorms(temp);
1798  return temp;
1799 }
1800 
1801 void ccPointCloud::getNorms(std::vector<CompressedNormType>& idxes) const {
1802  idxes.resize(m_normals->size());
1803  for (unsigned int i = 0; i < static_cast<unsigned int>(m_normals->size());
1804  ++i) {
1805  idxes[i] = getPointNormalIndex(i);
1806  }
1807 }
1808 
1810  if (!hasNormals()) {
1812  }
1813  m_normals->addElement(index);
1814 }
1815 
1817  unsigned index) {
1818  if (!hasNormals()) {
1820  }
1821 
1822  // we get the real normal vector corresponding to current index
1824  // we add the provided vector (N)
1825  CCVector3::vadd(P.u, N, P.u);
1826  P.normalize();
1827  // we recode the resulting vector
1829  m_normals->setValue(index, nIndex);
1830 
1831  // We must update the VBOs
1833 }
1834 
1836  if (!hasNormals()) return false;
1837 
1838  if (!ccNormalVectors::GetUniqueInstance()->enableNormalHSVColorsArray()) {
1839  CVLog::Warning("[ccPointCloud::convertNormalToRGB] Not enough memory!");
1840  return false;
1841  }
1842  const std::vector<ecvColor::Rgb>& normalHSV =
1844 
1845  if (!resizeTheRGBTable(false)) {
1846  CVLog::Warning("[ccPointCloud::convertNormalToRGB] Not enough memory!");
1847  return false;
1848  }
1849  assert(m_normals && m_rgbColors);
1850 
1851  unsigned count = size();
1852  for (unsigned i = 0; i < count; ++i) {
1853  const ecvColor::Rgb& rgb = normalHSV[m_normals->getValue(i)];
1854  m_rgbColors->setValue(i, rgb);
1855  }
1856 
1857  // We must update the VBOs
1859 
1860  return true;
1861 }
1862 
1864  if (!hasColors()) {
1865  return false;
1866  }
1867  assert(m_rgbColors);
1868 
1869  unsigned count = size();
1870  for (unsigned i = 0; i < count; ++i) {
1871  ecvColor::Rgb& rgb = m_rgbColors->at(i);
1872  // conversion from RGB to grey scale (see
1873  // https://en.wikipedia.org/wiki/Luma_%28video%29)
1874  double luminance = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
1875  rgb.r = rgb.g = rgb.b = static_cast<unsigned char>(
1876  std::max(std::min(luminance, 255.0), 0.0));
1877  }
1878 
1879  // We must update the VBOs
1881 
1882  return true;
1883 }
1884 
1886  ccScalarField* dipDirSF) {
1887  if (!dipSF && !dipDirSF) {
1888  assert(false);
1889  return false;
1890  }
1891 
1892  if ((dipSF && !dipSF->resizeSafe(size())) ||
1893  (dipDirSF && !dipDirSF->resizeSafe(size()))) {
1895  "[ccPointCloud::convertNormalToDipDirSFs] Not enough memory!");
1896  return false;
1897  }
1898 
1899  unsigned count = size();
1900  for (unsigned i = 0; i < count; ++i) {
1901  CCVector3 N(this->getPointNormal(i));
1902  PointCoordinateType dip, dipDir;
1904  if (dipSF) dipSF->setValue(i, static_cast<ScalarType>(dip));
1905  if (dipDirSF) dipDirSF->setValue(i, static_cast<ScalarType>(dipDir));
1906  }
1907 
1908  if (dipSF) dipSF->computeMinAndMax();
1909  if (dipDirSF) dipDirSF->computeMinAndMax();
1910 
1911  return true;
1912 }
1913 
1915  if (m_normals == norms) return;
1916 
1917  if (m_normals) m_normals->release();
1918 
1919  m_normals = norms;
1920  if (m_normals) m_normals->link();
1921 
1922  // We must update the VBOs
1924 }
1925 
1926 bool ccPointCloud::colorize(float r, float g, float b) {
1927  assert(r >= 0.0f && r <= 1.0f);
1928  assert(g >= 0.0f && g <= 1.0f);
1929  assert(b >= 0.0f && b <= 1.0f);
1930 
1931  if (hasColors()) {
1932  assert(m_rgbColors);
1933  for (unsigned i = 0; i < m_rgbColors->currentSize(); i++) {
1934  ecvColor::Rgb& p = m_rgbColors->at(i);
1935  {
1936  p.r = static_cast<ColorCompType>(p.r * r);
1937  p.g = static_cast<ColorCompType>(p.g * g);
1938  p.b = static_cast<ColorCompType>(p.b * b);
1939  }
1940  }
1941  } else {
1942  if (!resizeTheRGBTable(false)) return false;
1943 
1944  ecvColor::Rgb C(static_cast<ColorCompType>(ecvColor::MAX * r),
1945  static_cast<ColorCompType>(ecvColor::MAX * g),
1946  static_cast<ColorCompType>(ecvColor::MAX * b));
1947  m_rgbColors->fill(C);
1948  }
1949 
1950  // We must update the VBOs
1952 
1953  return true;
1954 }
1955 
1956 // Contribution from Michael J Smith
1957 bool ccPointCloud::setRGBColorByBanding(unsigned char dim, double freq) {
1958  if (freq == 0 || dim > 2) // X=0, Y=1, Z=2
1959  {
1961  "[ccPointCloud::setRGBColorByBanding] Invalid parameter!");
1962  return false;
1963  }
1964 
1965  // allocate colors if necessary
1966  if (!hasColors())
1967  if (!resizeTheRGBTable(false)) return false;
1968 
1969  enableTempColor(false);
1970  assert(m_rgbColors);
1971 
1972  float bands = (2.0 * M_PI) / freq;
1973 
1974  unsigned count = size();
1975  for (unsigned i = 0; i < count; i++) {
1976  const CCVector3* P = getPoint(i);
1977 
1978  float z = bands * P->u[dim];
1979  ecvColor::Rgb C(
1980  static_cast<ColorCompType>(((sin(z + 0.0f) + 1.0f) / 2.0f) *
1981  ecvColor::MAX),
1982  static_cast<ColorCompType>(((sin(z + 2.0944f) + 1.0f) / 2.0f) *
1983  ecvColor::MAX),
1984  static_cast<ColorCompType>(((sin(z + 4.1888f) + 1.0f) / 2.0f) *
1985  ecvColor::MAX));
1986 
1987  m_rgbColors->setValue(i, C);
1988  }
1989 
1990  // We must update the VBOs
1992 
1993  return true;
1994 }
1995 
1996 bool ccPointCloud::setRGBColorByHeight(unsigned char heightDim,
1997  ccColorScale::Shared colorScale) {
1998  if (!colorScale || heightDim > 2) // X=0, Y=1, Z=2
1999  {
2000  CVLog::Error("[ccPointCloud::setRGBColorByHeight] Invalid parameter!");
2001  return false;
2002  }
2003 
2004  // allocate colors if necessary
2005  if (!hasColors())
2006  if (!resizeTheRGBTable(false)) return false;
2007 
2008  enableTempColor(false);
2009  assert(m_rgbColors);
2010 
2011  double minHeight = getOwnBB().minCorner().u[heightDim];
2012  double height = getOwnBB().getDiagVec().u[heightDim];
2013  if (cloudViewer::LessThanEpsilon(fabs(height))) // flat cloud!
2014  {
2015  const ecvColor::Rgb& col = colorScale->getColorByIndex(0);
2016  return setRGBColor(col);
2017  }
2018 
2019  unsigned count = size();
2020  for (unsigned i = 0; i < count; i++) {
2021  const CCVector3* Q = getPoint(i);
2022  double realtivePos = (Q->u[heightDim] - minHeight) / height;
2023  const ecvColor::Rgb* col =
2024  colorScale->getColorByRelativePos(realtivePos);
2025  if (!col) // DGM: yes it happens if we encounter a point with NaN
2026  // coordinates!!!
2027  {
2028  col = &ecvColor::black;
2029  }
2030  m_rgbColors->setValue(i, *col);
2031  }
2032 
2033  // We must update the VBOs
2035 
2036  return true;
2037 }
2038 
2040  bool mixWithExistingColor /*=false*/) {
2041  if (!hasDisplayedScalarField()) {
2043  "[ccPointCloud::setColorWithCurrentScalarField] No active "
2044  "scalar field or color scale!");
2045  return false;
2046  }
2047 
2048  unsigned count = size();
2049 
2050  if (!mixWithExistingColor || !hasColors()) {
2051  if (!hasColors() && !resizeTheRGBTable(false)) {
2052  return false;
2053  }
2054 
2055  for (unsigned i = 0; i < count; i++) {
2056  const ecvColor::Rgb* col = getPointScalarValueColor(i);
2057  setPointColor(i, col ? *col : ecvColor::black);
2058  }
2059  } else // mix with existing colors
2060  {
2061  for (unsigned i = 0; i < count; i++) {
2062  const ecvColor::Rgb* col = getPointScalarValueColor(i);
2063  if (col) {
2064  ecvColor::Rgb& _color = m_rgbColors->at(i);
2065  _color.r = static_cast<ColorCompType>(
2066  _color.r *
2067  (static_cast<float>(col->r) / ecvColor::MAX));
2068  _color.g = static_cast<ColorCompType>(
2069  _color.g *
2070  (static_cast<float>(col->g) / ecvColor::MAX));
2071  _color.b = static_cast<ColorCompType>(
2072  _color.b *
2073  (static_cast<float>(col->b) / ecvColor::MAX));
2074  }
2075  }
2076  }
2077 
2078  // We must update the VBOs
2080 
2081  return true;
2082 }
2083 
2085  enableTempColor(false);
2086 
2087  // allocate colors if necessary
2088  if (!hasColors())
2089  if (!reserveTheRGBTable()) return false;
2090 
2091  assert(m_rgbColors);
2092  m_rgbColors->fill(col);
2093 
2094  // update the grid colors as well!
2095  for (size_t i = 0; i < m_grids.size(); ++i) {
2096  if (m_grids[i] && !m_grids[i]->colors.empty()) {
2097  std::fill(m_grids[i]->colors.begin(), m_grids[i]->colors.end(),
2098  col);
2099  }
2100  }
2101 
2102  // We must update the VBOs
2104 
2105  return true;
2106 }
2107 
2110 
2127  void** additionalParameters,
2129  // additional parameters
2130  PointCoordinateType sigma =
2131  *(static_cast<PointCoordinateType*>(additionalParameters[0]));
2132  PointCoordinateType sigmaSF =
2133  *(static_cast<PointCoordinateType*>(additionalParameters[1]));
2134  ccPointCloud::RgbFilterOptions filterParams =
2135  *(static_cast<ccPointCloud::RgbFilterOptions*>(
2136  additionalParameters[2]));
2137  bool applyToSF = filterParams.applyToSFduringRGB;
2138  unsigned char burntOutColorThresholdMin =
2139  filterParams.burntOutColorThreshold;
2140  unsigned char burntOutColorThresholdMax = 255 - burntOutColorThresholdMin;
2141  bool mean = filterParams.filterType == ccPointCloud::RGB_FILTER_TYPES::MEAN;
2142  bool median =
2143  filterParams.filterType == ccPointCloud::RGB_FILTER_TYPES::MEDIAN;
2144 
2145  // we only use the squared value of sigma
2146  double sigma2 = (2.0 * sigma) * sigma;
2147  double radius = 3.0 * sigma; // 3 * sigma > 99.7%
2148 
2149  // we only use the squared value of sigmaSF
2150  PointCoordinateType sigmaSF2 = 2 * sigmaSF * sigmaSF;
2151 
2152  // number of points inside the current cell
2153  unsigned n = cell.points->size();
2154 
2156  nNSS.level = cell.level;
2157  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
2158  true);
2159  cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level,
2160  nNSS.cellCenter);
2161 
2162  // we already know the points lying in the first cell (this is the one we
2163  // are treating :)
2164  try {
2165  nNSS.pointsInNeighbourhood.resize(n);
2166  } catch (... /*const std::bad_alloc&*/) // out of memory
2167  {
2168  return false;
2169  }
2170 
2171  cloudViewer::DgmOctree::NeighboursSet::iterator it =
2172  nNSS.pointsInNeighbourhood.begin();
2173  {
2174  for (unsigned i = 0; i < n; ++i, ++it) {
2175  it->point = cell.points->getPointPersistentPtr(i);
2176  it->pointIndex = cell.points->getPointGlobalIndex(i);
2177  }
2178  }
2180 
2181  ccPointCloud* cloud =
2182  static_cast<ccPointCloud*>(cell.points->getAssociatedCloud());
2183  assert(cloud);
2184 
2185  bool bilateralFilter = (sigmaSF > 0.0) && !mean && !median;
2186 
2187  // For the median filter
2188  std::vector<unsigned char> rValues;
2189  std::vector<unsigned char> gValues;
2190  std::vector<unsigned char> bValues;
2191  std::vector<ScalarType> sfValues;
2192 
2193  for (unsigned i = 0; i < n; ++i) // for each point in cell
2194  {
2195  ScalarType queryValue = 0; // scalar of the query point
2196  unsigned queryPointIndex = cell.points->getPointGlobalIndex(i);
2197 
2198  if (bilateralFilter) {
2199  queryValue = cell.points->getPointScalarValue(i);
2200 
2201  // check that the query SF value is valid, otherwise no need to
2202  // compute anything
2203  if (!cloudViewer::ScalarField::ValidValue(queryValue)) {
2204  // leave original color
2205  continue;
2206  }
2207  }
2208 
2209  // we retrieve the points inside a spherical neighbourhood (radius:
2210  // '3*sigma')
2211  cell.points->getPoint(i, nNSS.queryPoint);
2212  // warning: there may be more points at the end of
2213  // nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)!
2215  nNSS, radius, false);
2216 
2217  // each point adds a contribution weighted by its distance to the sphere
2218  // center
2219  it = nNSS.pointsInNeighbourhood.begin();
2220  if (median) {
2221  rValues.clear();
2222  gValues.clear();
2223  bValues.clear();
2224  sfValues.clear();
2225  for (unsigned j = 0; j < k; ++j, ++it) {
2226  const ecvColor::Rgb& col = cloud->getPointColor(it->pointIndex);
2227 
2228  if ((col.r >= burntOutColorThresholdMax &&
2229  col.g >= burntOutColorThresholdMax &&
2230  col.b >= burntOutColorThresholdMax) ||
2231  (col.r <= burntOutColorThresholdMin &&
2232  col.g <= burntOutColorThresholdMin &&
2233  col.b <= burntOutColorThresholdMin)) {
2234  continue;
2235  }
2236 
2237  rValues.push_back(col.r);
2238  gValues.push_back(col.g);
2239  bValues.push_back(col.b);
2240 
2241  if (applyToSF) {
2242  ScalarType val = cloud->getPointScalarValue(it->pointIndex);
2244  sfValues.push_back(val);
2245  }
2246  }
2247  }
2248 
2249  if (rValues.size() != 0) {
2250  std::vector<unsigned char>::iterator medR =
2251  rValues.begin() + rValues.size() / 2;
2252  std::nth_element(rValues.begin(), medR, rValues.end());
2253  std::vector<unsigned char>::iterator medG =
2254  gValues.begin() + gValues.size() / 2;
2255  std::nth_element(gValues.begin(), medG, gValues.end());
2256  std::vector<unsigned char>::iterator medB =
2257  bValues.begin() + bValues.size() / 2;
2258  std::nth_element(bValues.begin(), medB, bValues.end());
2259 
2260  ecvColor::Rgb medCol(static_cast<ColorCompType>(*medR),
2261  static_cast<ColorCompType>(*medG),
2262  static_cast<ColorCompType>(*medB));
2263 
2264  cloud->setPointColor(queryPointIndex, medCol);
2265  }
2266 
2267  if (sfValues.size() != 0) {
2268  std::vector<ScalarType>::iterator medSF =
2269  sfValues.begin() + sfValues.size() / 2;
2270  std::nth_element(sfValues.begin(), medSF, sfValues.end());
2271  cloud->setPointScalarValue(queryPointIndex,
2272  static_cast<ScalarType>(*medSF));
2273  }
2274  } else {
2275  ecvColor::RgbTpl<double> rgbSum(0.0, 0.0, 0.0);
2276  double wSum = 0.0;
2277  double sfSum = 0.0;
2278  double sfWSum = 0.0;
2279  ecvColor::RgbTpl<double> rgbGrayscaleSum(0.0, 0.0, 0.0);
2280  double wGrayscaleSum = 0.0;
2281  size_t nrOfGrayscale = 0;
2282  size_t nrOfUsedNeighbours = 0;
2283 
2284  for (unsigned j = 0; j < k; ++j, ++it) {
2285  double weight =
2286  mean ? 1.0
2287  : exp(-(it->squareDistd) /
2288  sigma2); // PDF: -exp(-(x-mu)^2/(2*sigma^2))
2289 
2290  const ecvColor::Rgb& col = cloud->getPointColor(it->pointIndex);
2291 
2292  if (bilateralFilter || applyToSF) {
2293  ScalarType val = cloud->getPointScalarValue(it->pointIndex);
2295  if (bilateralFilter) {
2296  double dSF = queryValue - val;
2297  weight *= exp(-(dSF * dSF) / sigmaSF2);
2298  }
2299  sfSum += weight * val;
2300  sfWSum += weight;
2301  } else {
2302  continue;
2303  }
2304  }
2305 
2306  if ((col.r >= burntOutColorThresholdMax &&
2307  col.g >= burntOutColorThresholdMax &&
2308  col.b >= burntOutColorThresholdMax) ||
2309  (col.r <= burntOutColorThresholdMin &&
2310  col.g <= burntOutColorThresholdMin &&
2311  col.b <= burntOutColorThresholdMin)) {
2312  continue;
2313  }
2314 
2315  rgbSum.r += weight * col.r;
2316  rgbSum.g += weight * col.g;
2317  rgbSum.b += weight * col.b;
2318  wSum += weight;
2319  ++nrOfUsedNeighbours;
2320 
2321  if (filterParams.blendGrayscale) {
2322  double grayscaleMin = (col.r / 3.0) + (col.g / 3.0) +
2323  (col.b / 3.0) -
2324  filterParams.blendGrayscaleThreshold;
2325  double grayscaleMax =
2326  grayscaleMin +
2327  2.0 * filterParams.blendGrayscaleThreshold;
2328  if (static_cast<double>(col.r) >= grayscaleMin &&
2329  static_cast<double>(col.g) >= grayscaleMin &&
2330  static_cast<double>(col.b) >= grayscaleMin &&
2331  static_cast<double>(col.r) <= grayscaleMax &&
2332  static_cast<double>(col.g) <= grayscaleMax &&
2333  static_cast<double>(col.b) <= grayscaleMax) {
2334  // grayscale color based on threshold value
2335  rgbGrayscaleSum.r += weight * col.r;
2336  rgbGrayscaleSum.g += weight * col.g;
2337  rgbGrayscaleSum.b += weight * col.b;
2338  wGrayscaleSum += weight;
2339  ++nrOfGrayscale;
2340  }
2341  }
2342  }
2343 
2344  if (wSum != 0.0) {
2345  ecvColor::Rgb avgCol(
2346  static_cast<ColorCompType>(std::max(
2347  std::min(255.0, rgbSum.r / wSum), 0.0)),
2348  static_cast<ColorCompType>(std::max(
2349  std::min(255.0, rgbSum.g / wSum), 0.0)),
2350  static_cast<ColorCompType>(std::max(
2351  std::min(255.0, rgbSum.b / wSum), 0.0)));
2352 
2353  // blend grayscale modifications
2354  if (filterParams.blendGrayscale) {
2355  // if the neighbor set contains more grayscale point than
2356  // given percent, so use only use grayscale points
2357  if ((static_cast<double>(nrOfGrayscale) >
2358  filterParams.blendGrayscalePercent *
2359  nrOfUsedNeighbours) &&
2360  wGrayscaleSum != 0) {
2361  avgCol.r = static_cast<ColorCompType>(std::max(
2362  std::min(255.0,
2363  rgbGrayscaleSum.r / wGrayscaleSum),
2364  0.0));
2365  avgCol.g = static_cast<ColorCompType>(std::max(
2366  std::min(255.0,
2367  rgbGrayscaleSum.g / wGrayscaleSum),
2368  0.0));
2369  avgCol.b = static_cast<ColorCompType>(std::max(
2370  std::min(255.0,
2371  rgbGrayscaleSum.b / wGrayscaleSum),
2372  0.0));
2373  } else // else, we have more RGB colors than grayscale
2374  // ones. We use only the RGB values.
2375  {
2376  double wRGBSum = wSum - wGrayscaleSum;
2377  if (wRGBSum != 0.0) {
2378  avgCol.r = static_cast<ColorCompType>(std::max(
2379  std::min(255.0,
2380  (rgbSum.r - rgbGrayscaleSum.r) /
2381  wRGBSum),
2382  0.0));
2383  avgCol.g = static_cast<ColorCompType>(std::max(
2384  std::min(255.0,
2385  (rgbSum.g - rgbGrayscaleSum.g) /
2386  wRGBSum),
2387  0.0));
2388  avgCol.b = static_cast<ColorCompType>(std::max(
2389  std::min(255.0,
2390  (rgbSum.b - rgbGrayscaleSum.b) /
2391  wRGBSum),
2392  0.0));
2393  }
2394  }
2395  }
2396 
2397  cloud->setPointColor(queryPointIndex, avgCol);
2398  }
2399 
2400  if (applyToSF) {
2401  if (sfWSum != 0.0) {
2402  cloud->setPointScalarValue(
2403  queryPointIndex,
2404  static_cast<ScalarType>(sfSum / sfWSum));
2405  }
2406  }
2407  }
2408 
2409  if (nProgress && !nProgress->oneStep()) {
2410  return false;
2411  }
2412  }
2413 
2414  return true;
2415 }
2416 
2418  PointCoordinateType sigma,
2419  PointCoordinateType sigmaSF,
2420  RgbFilterOptions filterParams,
2421  cloudViewer::GenericProgressCallback* progressCb /*=nullptr*/) {
2422  unsigned n = size();
2423  if (n == 0) {
2424  CVLog::Warning("[ccPointCloud::applyFilterToRGB] Cloud is empty");
2425  return false;
2426  }
2427 
2428  if (!hasColors()) {
2430  "[ccPointCloud::applyFilterToRGB] Cloud has no RGB color");
2431  return false;
2432  }
2433 
2434  if ((sigmaSF > 0) && (nullptr == getCurrentOutScalarField())) {
2436  "[ccPointCloud::applyFilterToRGB] A non-zero scalar field "
2437  "variance was set without an active 'input' scalar-field");
2438  return false;
2439  }
2440 
2441  ccOctree* theOctree = getOctree().data();
2442  if (!theOctree) {
2443  if (!computeOctree(progressCb)) {
2445  "[ccPointCloud::applyFilterToRGB] Failed to compute the "
2446  "octree");
2447  delete theOctree;
2448  return false;
2449  } else {
2450  theOctree = getOctree().data();
2451  }
2452  }
2453 
2454  // best octree level
2455  unsigned char level =
2457  sigma);
2458 
2459  if (progressCb) {
2460  if (progressCb->textCanBeEdited()) {
2461  progressCb->setMethodTitle("RGB filter");
2462  char infos[32];
2463  snprintf(infos, 32, "Level: %i", level);
2464  progressCb->setInfo(infos);
2465  }
2466  progressCb->update(0);
2467  }
2468 
2469  void* additionalParameters[]{reinterpret_cast<void*>(&sigma),
2470  reinterpret_cast<void*>(&sigmaSF),
2471  reinterpret_cast<void*>(&filterParams)};
2472 
2473  bool success = true;
2474 
2475  if (theOctree->executeFunctionForAllCellsAtLevel(
2476  level, ComputeCellGaussianFilter, additionalParameters, true,
2477  progressCb, "Filter computation") == 0) {
2478  // something went wrong
2479  success = false;
2480  }
2481 
2482  return success;
2483 }
2484 
2487 }
2488 
2490  applyRigidTransformation(trans);
2491 }
2492 
2494  // transparent call
2496 
2497  unsigned count = size();
2498  for (unsigned i = 0; i < count; i++) {
2499  trans.apply(*point(i));
2500  }
2501 
2502  // we must also take care of the normals!
2503  if (hasNormals()) {
2504  bool recoded = false;
2505 
2506  // if there is more points than the size of the compressed normals
2507  // array, we recompress the array instead of recompressing each normal
2509  NormsIndexesTableType newNorms;
2511  for (unsigned i = 0; i < ccNormalVectors::GetNumberOfVectors();
2512  i++) {
2514  trans.applyRotation(new_n);
2515  CompressedNormType newNormIndex =
2517  newNorms.emplace_back(newNormIndex);
2518  }
2519 
2520  for (unsigned j = 0; j < count; j++) {
2521  m_normals->setValue(j, newNorms[m_normals->at(j)]);
2522  }
2523  recoded = true;
2524  }
2525  }
2526 
2527  // if there is less points than the compressed normals array size
2528  //(or if there is not enough memory to instantiate the temporary
2529  // array), we recompress each normal ...
2530  if (!recoded) {
2531  // on recode direct chaque normale
2532  for (CompressedNormType& _theNormIndex : *m_normals) {
2533  CCVector3 new_n(ccNormalVectors::GetNormal(_theNormIndex));
2534  trans.applyRotation(new_n);
2535  _theNormIndex = ccNormalVectors::GetNormIndex(new_n.u);
2536  }
2537  }
2538  }
2539 
2540  // and the scan grids!
2541  if (!m_grids.empty()) {
2542  ccGLMatrixd transd(trans.data());
2543 
2544  for (Grid::Shared& grid : m_grids) {
2545  if (!grid) {
2546  continue;
2547  }
2548  grid->sensorPosition = transd * grid->sensorPosition;
2549  }
2550  }
2551 
2552  // and the waveform!
2553  for (ccWaveform& w : m_fwfWaveforms) {
2554  if (w.descriptorID() != 0) {
2555  w.applyRigidTransformation(trans);
2556  }
2557  }
2558 
2559  // the octree is invalidated by rotation...
2560  deleteOctree();
2561 
2562  // ... as the bounding box
2563  refreshBB(); // calls notifyGeometryUpdate + releaseVBOs
2564 }
2565 
2566 ccPointCloud& ccPointCloud::Transform(const Eigen::Matrix4d& trans) {
2569  return *this;
2570 }
2571 
2572 ccPointCloud& ccPointCloud::Translate(const Eigen::Vector3d& translation,
2573  bool relative) {
2574  CCVector3 T = translation;
2575  if (cloudViewer::LessThanEpsilon(fabs(T.x) + fabs(T.y) + fabs(T.z)))
2576  return *this;
2577 
2578  unsigned count = size();
2579  {
2580  if (!relative) {
2581  T -= computeGravityCenter();
2582  }
2583 
2584  for (unsigned i = 0; i < count; i++) *point(i) += T;
2585  }
2586 
2587  notifyGeometryUpdate(); // calls releaseVBOs()
2589 
2590  // same thing for the octree
2592  if (octree) {
2593  octree->translateBoundingBox(T);
2594  }
2595 
2596  // and same thing for the Kd-tree(s)!
2597  ccHObject::Container kdtrees;
2598  filterChildren(kdtrees, false, CV_TYPES::POINT_KDTREE);
2599  {
2600  for (size_t i = 0; i < kdtrees.size(); ++i) {
2601  static_cast<ccKdTree*>(kdtrees[i])->translateBoundingBox(T);
2602  }
2603  }
2604 
2605  // update the transformation history
2606  {
2607  ccGLMatrix trans;
2608  trans.setTranslation(T);
2610  }
2611 
2612  return *this;
2613 }
2614 
2616  const Eigen::Vector3d& center) {
2617  scale(static_cast<PointCoordinateType>(s),
2618  static_cast<PointCoordinateType>(s),
2619  static_cast<PointCoordinateType>(s), center);
2620 
2621  return *this;
2622 }
2623 
2624 ccPointCloud& ccPointCloud::Rotate(const Eigen::Matrix3d& R,
2625  const Eigen::Vector3d& center) {
2626  ccGLMatrix trans;
2627  trans.setRotation(R.data());
2628  trans.shiftRotationCenter(center);
2629  applyRigidTransformation(trans);
2631  return *this;
2632 }
2633 
2637  CCVector3 center) {
2638  // transform the points
2639  {
2640  unsigned count = size();
2641  for (unsigned i = 0; i < count; i++) {
2642  CCVector3* P = point(i);
2643  P->x = (P->x - center.x) * fx + center.x;
2644  P->y = (P->y - center.y) * fy + center.y;
2645  P->z = (P->z - center.z) * fz + center.z;
2646  }
2647  }
2648 
2650 
2651  // same thing for the octree
2653  if (octree) {
2654  if (fx == fy && fx == fz && fx > 0) {
2655  CCVector3 centerInv = -center;
2656  octree->translateBoundingBox(centerInv);
2657  octree->multiplyBoundingBox(fx);
2658  octree->translateBoundingBox(center);
2659  } else {
2660  // we can't keep the octree
2661  deleteOctree();
2662  }
2663  }
2664 
2665  // and same thing for the Kd-tree(s)!
2666  {
2667  ccHObject::Container kdtrees;
2668  filterChildren(kdtrees, false, CV_TYPES::POINT_KDTREE);
2669  if (fx == fy && fx == fz && fx > 0) {
2670  for (size_t i = 0; i < kdtrees.size(); ++i) {
2671  ccKdTree* kdTree = static_cast<ccKdTree*>(kdtrees[i]);
2672  CCVector3 centerInv = -center;
2673  kdTree->translateBoundingBox(centerInv);
2674  kdTree->multiplyBoundingBox(fx);
2675  kdTree->translateBoundingBox(center);
2676  }
2677  } else {
2678  // we can't keep the kd-trees
2679  for (size_t i = 0; i < kdtrees.size(); ++i) {
2680  removeChild(kdtrees[kdtrees.size() - 1 -
2681  i]); // faster to remove the last objects
2682  }
2683  }
2684  kdtrees.resize(0);
2685  }
2686 
2687  // new we have to compute a proper transformation matrix
2688  ccGLMatrix scaleTrans;
2689  {
2690  ccGLMatrix transToCenter;
2691  transToCenter.setTranslation(-center);
2692 
2693  ccGLMatrix scaleAndReposition;
2694  scaleAndReposition.data()[0] = fx;
2695  scaleAndReposition.data()[5] = fy;
2696  scaleAndReposition.data()[10] = fz;
2697  // go back to the original position
2698  scaleAndReposition.setTranslation(center);
2699 
2700  scaleTrans = scaleAndReposition * transToCenter;
2701  }
2702 
2703  // update the grids as well
2704  {
2705  for (Grid::Shared& grid : m_grids) {
2706  if (grid) {
2707  // update the scan position
2708  grid->sensorPosition =
2709  ccGLMatrixd(scaleTrans.data()) * grid->sensorPosition;
2710  }
2711  }
2712  }
2713 
2714  // updates the sensors
2715  {
2716  for (ccHObject* child : m_children) {
2717  if (child && child->isKindOf(CV_TYPES::SENSOR)) {
2718  ccSensor* sensor = static_cast<ccSensor*>(child);
2719 
2720  sensor->applyGLTransformation(scaleTrans);
2721 
2722  // update the graphic scale, etc.
2723  PointCoordinateType meanScale = (fx + fy + fz) / 3;
2724  // sensor->setGraphicScale(sensor->getGraphicScale() *
2725  // meanScale);
2726  if (sensor->isA(CV_TYPES::GBL_SENSOR)) {
2727  ccGBLSensor* gblSensor = static_cast<ccGBLSensor*>(sensor);
2728  gblSensor->setSensorRange(gblSensor->getSensorRange() *
2729  meanScale);
2730  }
2731  }
2732  }
2733  }
2734 
2735  // update the transformation history
2736  { m_glTransHistory = scaleTrans * m_glTransHistory; }
2737 
2738  notifyGeometryUpdate(); // calls releaseVBOs()
2739 }
2740 
2742  if (!hasNormals()) return;
2743 
2744  for (CompressedNormType& n : *m_normals) {
2746  }
2747 
2748  // We must update the VBOs
2750 }
2751 
2752 void ccPointCloud::swapPoints(unsigned firstIndex, unsigned secondIndex) {
2753  assert(!isLocked());
2754  assert(firstIndex < size() && secondIndex < size());
2755 
2756  if (firstIndex == secondIndex) return;
2757 
2758  // points + associated SF values
2759  BaseClass::swapPoints(firstIndex, secondIndex);
2760 
2761  // colors
2762  if (hasColors()) {
2763  assert(m_rgbColors);
2764  m_rgbColors->swap(firstIndex, secondIndex);
2765  }
2766 
2767  // normals
2768  if (hasNormals()) {
2769  assert(m_normals);
2770  m_normals->swap(firstIndex, secondIndex);
2771  }
2772 
2773  // We must update the VBOs
2774  releaseVBOs();
2775 }
2776 
2777 void ccPointCloud::removePoints(size_t index) {
2778  if (index >= size()) {
2779  return;
2780  }
2781 
2782  m_points.erase(m_points.begin() + index);
2783  // colors
2784  if (hasColors()) {
2785  assert(m_rgbColors);
2786  m_rgbColors->erase(m_rgbColors->begin() + index);
2787  }
2788 
2789  // normals
2790  if (hasNormals()) {
2791  assert(m_normals);
2792  m_normals->erase(m_normals->begin() + index);
2793  }
2794 }
2795 
2797  // color override
2798  if (isColorOverridden()) {
2799  params.showColors = true;
2800  params.showNorms = false;
2801  params.showSF = false;
2802  } else {
2803  // a scalar field must have been selected for display!
2804  params.showSF = hasDisplayedScalarField() && sfShown() &&
2806  params.showNorms = hasNormals() && normalsShown() &&
2807  m_normals->currentSize() >= size();
2808  // colors are not displayed if scalar field is displayed
2809  params.showColors = !params.showSF && hasColors() && colorsShown() &&
2810  m_rgbColors->currentSize() >= size();
2811  }
2812 }
2813 
2814 // helpers (for ColorRamp shader)
2815 
2816 inline float GetNormalizedValue(const ScalarType& sfVal,
2817  const ccScalarField::Range& displayRange) {
2818  return static_cast<float>((sfVal - displayRange.start()) /
2819  displayRange.range());
2820 }
2821 
2823  const ScalarType& sfVal, const ccScalarField::Range& saturationRange) {
2824  // normalized sf value
2825  ScalarType relativeValue = 0;
2826  if (fabs(sfVal) >
2827  saturationRange.start()) // we avoid the 'flat' SF case by the way
2828  {
2829  if (sfVal < 0)
2830  relativeValue =
2831  (sfVal + saturationRange.start()) / saturationRange.max();
2832  else
2833  relativeValue =
2834  (sfVal - saturationRange.start()) / saturationRange.max();
2835  }
2836  return (1.0f + relativeValue) / 2; // normalized sf value
2837 }
2838 
2840 // warning MUST BE GREATER THAN 'MAX_NUMBER_OF_ELEMENTS_PER_CHUNK'
2841 #ifdef _DEBUG
2842 static const unsigned MAX_POINT_COUNT_PER_LOD_RENDER_PASS = (1 << 16); //~ 64K
2843 #else
2844 static const unsigned MAX_POINT_COUNT_PER_LOD_RENDER_PASS = (1 << 19); //~ 512K
2845 #endif
2846 
2847 // description of the (sub)set of points to display
2851  : LODLevelDesc(), endIndex(0), decimStep(1), indexMap(nullptr) {}
2852 
2854  DisplayDesc(unsigned startIndex, unsigned count)
2857  decimStep(1),
2858  indexMap(nullptr) {}
2859 
2862  startIndex = desc.startIndex;
2863  count = desc.count;
2865  return *this;
2866  }
2867 
2869  unsigned endIndex;
2870 
2872  unsigned decimStep;
2873 
2876 };
2877 
2879  if (m_points.empty()) return;
2880 
2881  if (MACRO_Draw3D(context)) {
2882  // we get display parameters
2883  glDrawParams glParams;
2884  getDrawingParameters(glParams);
2885  // no normals shading without light!
2886  if (!MACRO_LightIsEnabled(context)) {
2887  glParams.showNorms = false;
2888  }
2889 
2890  // can't display a SF without... a SF... and an active color scale!
2891  assert(!glParams.showSF || hasDisplayedScalarField());
2892 
2893  // standard case: list names pushing
2894  bool entityPickingMode = MACRO_EntityPicking(context);
2895  if (entityPickingMode) {
2896  // not fast at all!
2898  return;
2899  }
2900 
2901  // minimal display for picking mode!
2902 
2903  glParams.showNorms = false;
2904  glParams.showColors = false;
2905  if (glParams.showSF &&
2907  glParams.showSF = false; //--> we keep it only if SF 'NaN'
2908  // values are potentially hidden
2909  }
2910  }
2911 
2912  bool colorMaterialEnabled = false;
2913 
2914  if (glParams.showColors && isColorOverridden()) {
2915  // ccGL::Color3v(glFunc, m_tempColor.rgb);
2916  context.pointsCurrentCol = m_tempColor;
2917  glParams.showColors = false;
2918  } else {
2919  context.pointsCurrentCol = context.pointsDefaultCol;
2920  }
2921 
2922  if (glParams.showSF || glParams.showColors) {
2923  colorMaterialEnabled = true;
2924  }
2925 
2926  // start draw point cloud
2927  context.drawParam = glParams;
2928  // custom point size?
2929  if (getPointSize() > 0 && getPointSize() <= MAX_POINT_SIZE_F) {
2930  context.defaultPointSize = getPointSize();
2931  }
2932 
2933  // main display procedure
2935  } else if (MACRO_Draw2D(context)) {
2936  if (MACRO_Foreground(context) && !context.sfColorScaleToDisplay) {
2937  if (sfColorScaleShown() && sfShown()) {
2938  // drawScale(context);
2940  }
2941  }
2942  }
2943 }
2944 
2947  if (sfIdx < 0) return;
2948 
2949  context.sfColorScaleToDisplay =
2950  static_cast<ccScalarField*>(getScalarField(sfIdx));
2951 }
2952 
2954  ScalarType maxVal,
2955  bool outside /*=false*/) {
2956  if (!getCurrentOutScalarField()) {
2957  return nullptr;
2958  }
2959 
2960  QSharedPointer<cloudViewer::ReferenceCloud> c(
2961  cloudViewer::ManualSegmentationTools::segment(this, minVal, maxVal,
2962  outside));
2963 
2964  return (c ? partialClone(c.data()) : nullptr);
2965 }
2966 
2968  std::vector<ScalarType> values, bool outside) {
2969  if (!getCurrentOutScalarField()) {
2970  return nullptr;
2971  }
2972 
2973  QSharedPointer<cloudViewer::ReferenceCloud> c(
2975  outside));
2976 
2977  return (c ? partialClone(c.data()) : nullptr);
2978 }
2979 
2981  ScalarType maxVal) {
2982  if (!resetVisibilityArray()) {
2983  CVLog::Error(QString("[Cloud %1] Visibility table could not be "
2984  "instantiated!")
2985  .arg(getName()));
2986  return;
2987  }
2988 
2990  if (!sf) {
2991  CVLog::Error(QString("[Cloud %1] Internal error: no activated output "
2992  "scalar field!")
2993  .arg(getName()));
2994  return;
2995  }
2996 
2997  // we use the visibility table to tag the points to filter out
2998  unsigned count = size();
2999  for (unsigned i = 0; i < count; ++i) {
3000  const ScalarType& val = sf->getValue(i);
3001  if (val < minVal || val > maxVal || val != val) // handle NaN values!
3002  {
3004  }
3005  }
3006 }
3007 
3008 void ccPointCloud::hidePointsByScalarValue(std::vector<ScalarType> values) {
3009  if (!resetVisibilityArray()) {
3010  CVLog::Error(QString("[Cloud %1] Visibility table could not be "
3011  "instantiated!")
3012  .arg(getName()));
3013  return;
3014  }
3015 
3017  if (!sf) {
3018  CVLog::Error(QString("[Cloud %1] Internal error: no activated output "
3019  "scalar field!")
3020  .arg(getName()));
3021  return;
3022  }
3023 
3024  // we use the visibility table to tag the points to filter out
3025  unsigned count = size();
3026  for (unsigned i = 0; i < count; ++i) {
3027  const ScalarType& val = sf->getValue(i);
3028  bool find_flag = false;
3029  for (auto label : values) {
3030  if (std::abs(label - val) < EPSILON_VALUE) {
3031  find_flag = true;
3032  break;
3033  }
3034  }
3035 
3036  if (!find_flag || val != val) // handle NaN values!
3037  {
3039  }
3040  }
3041 }
3042 
3044  bool removeSelectedPoints /*=false*/,
3045  VisibilityTableType* visTable /*=nullptr*/,
3046  std::vector<int>* newIndexesOfRemainingPoints /*=nullptr*/,
3047  bool silent /*=false*/,
3048  cloudViewer::ReferenceCloud* selection /*=nullptr*/) {
3049  if (!visTable) {
3051  CVLog::Error(
3052  QString("[Cloud %1] Visibility table not instantiated!")
3053  .arg(getName()));
3054  return nullptr;
3055  }
3056  visTable = &m_pointsVisibility;
3057  } else {
3058  if (visTable->size() != size()) {
3059  CVLog::Error(QString("[Cloud %1] Invalid input visibility table")
3060  .arg(getName()));
3061  return nullptr;
3062  }
3063  }
3064 
3065  // count the number of visible points
3066  {
3067  unsigned visiblePoints = 0;
3068  for (size_t i = 0; i < visTable->size(); ++i) {
3069  if (visTable->at(i) == POINT_VISIBLE) {
3070  ++visiblePoints;
3071  }
3072  }
3073  if (visiblePoints == size()) {
3074  // all points are visible: nothing to do
3075  return this;
3076  }
3077  }
3078 
3079  // we create a new cloud with the "visible" points
3080  ccPointCloud* result = nullptr;
3081  {
3082  // we create a temporary entity with the visible points only
3084  getTheVisiblePoints(visTable, silent, selection);
3085  if (!rc) {
3086  // a warning message has already been issued by getTheVisiblePoints!
3087  // CVLog::Warning("[ccPointCloud] An error occurred during points
3088  // selection!");
3089  return nullptr;
3090  }
3091 
3092  // convert selection to cloud
3093  result = partialClone(rc);
3094 
3095  // don't need this one anymore
3096  if (rc != selection) {
3097  delete rc;
3098  rc = nullptr;
3099  }
3100  }
3101 
3102  if (!result) {
3104  "[ccPointCloud::createNewCloudFromVisibilitySelection] Failed "
3105  "to generate a subset cloud");
3106  return nullptr;
3107  }
3108 
3109  static constexpr const char* DefaultSuffix = ".segmented";
3110  QString newName = getName();
3111  if (!newName.endsWith(
3112  DefaultSuffix)) // avoid adding a multitude of suffixes
3113  newName += DefaultSuffix;
3114 
3115  result->setName(newName);
3116 
3117  // shall the visible points be erased from this cloud?
3118  if (removeSelectedPoints) {
3119  if (isLocked()) {
3121  "[ccPointCloud::createNewCloudFromVisibilitySelection] "
3122  "Can't remove selected points as cloud is locked");
3123  if (newIndexesOfRemainingPoints) {
3124  newIndexesOfRemainingPoints->clear();
3125  }
3126  } else {
3127  removeVisiblePoints(visTable, newIndexesOfRemainingPoints);
3128  }
3129  }
3130 
3131  return result;
3132 }
3133 
3135  VisibilityTableType* visTable /*=nullptr*/,
3136  std::vector<int>* newIndexes /*=nullptr*/) {
3137  if (!visTable) {
3139  CVLog::Error(
3140  "[removeVisiblePoints] Visibility table not instantiated!");
3141  return false;
3142  }
3143  visTable = &m_pointsVisibility;
3144  } else {
3145  if (visTable->size() != size()) {
3146  CVLog::Error(
3147  "[removeVisiblePoints] Invalid input visibility table");
3148  return false;
3149  }
3150  }
3151 
3152  std::vector<int> localNewIndexes;
3153  std::vector<int>* _newIndexes = nullptr;
3154  try {
3155  if (newIndexes) {
3156  if (newIndexes->empty()) {
3157  newIndexes->resize(size());
3158  } else if (newIndexes->size() != size()) {
3159  CVLog::Error(
3160  "[removeVisiblePoints] Input 'new indexes' has a wrong "
3161  "size");
3162  return false;
3163  }
3164  _newIndexes = newIndexes;
3165  } else if (!m_grids.empty()) {
3166  // we still need the mapping between old and new indexes
3167  localNewIndexes.resize(size());
3168  _newIndexes = &localNewIndexes;
3169  }
3170  } catch (const std::bad_alloc&) {
3171  CVLog::Error("[removeVisiblePoints] Not enough memory");
3172  return false;
3173  }
3174 
3175  // we drop the octree before modifying this cloud's contents
3176  deleteOctree();
3177  clearLOD();
3178 
3179  // we remove all visible points
3180  unsigned lastPointIndex = 0;
3181  unsigned previousCount = size();
3182  for (unsigned i = 0; i < previousCount; ++i) {
3183  if (visTable->at(i) != POINT_VISIBLE) {
3184  if (_newIndexes) {
3185  _newIndexes->at(i) = lastPointIndex;
3186  }
3187  if (i != lastPointIndex) {
3188  swapPoints(lastPointIndex, i);
3189  }
3190  ++lastPointIndex;
3191  } else if (_newIndexes) {
3192  _newIndexes->at(i) = -1;
3193  }
3194  }
3195 
3196  // we have to take care of scan grids
3197  if (!m_grids.empty()) {
3198  assert(_newIndexes);
3199  // then update the indexes
3200  UpdateGridIndexes(*_newIndexes, m_grids);
3201 
3202  // and reset the invalid (empty) ones
3203  //(DGM: we don't erase them as they may still be useful?)
3204  for (Grid::Shared& grid : m_grids) {
3205  if (grid->validCount == 0) {
3206  grid->indexes.resize(0);
3207  }
3208  }
3209  }
3210 
3211  resize(lastPointIndex);
3212 
3213  refreshBB(); // calls notifyGeometryUpdate + releaseVBOs
3214 
3215  return true;
3216 }
3217 
3219  return static_cast<ccScalarField*>(
3221 }
3222 
3225 }
3226 
3230  static_cast<ccScalarField*>(getScalarField(index));
3231 
3235 }
3236 
3238  // we 'store' the currently displayed SF, as the SF order may be mixed up
3240 
3241  // the father does all the work
3243 
3244  // current SF should still be up-to-date!
3247  static_cast<int>(getNumberOfScalarFields() - 1));
3248 
3251 }
3252 
3254  // the father does all the work
3256 
3257  // update the currently displayed SF
3259  showSF(false);
3260 }
3261 
3263  bool mixWithExistingColor /*=false*/) {
3264  if (!hasDisplayedScalarField()) {
3266  "[ccPointCloud::setColorWithCurrentScalarField] No active "
3267  "scalar field or color scale!");
3268  return false;
3269  }
3270 
3271  unsigned count = size();
3272 
3273  if (!mixWithExistingColor || !hasColors()) {
3274  if (!hasColors())
3275  if (!resizeTheRGBTable(false)) return false;
3276 
3277  for (unsigned i = 0; i < count; i++) {
3278  const ecvColor::Rgb* col = getPointScalarValueColor(i);
3279  m_rgbColors->setValue(i, col ? *col : ecvColor::black);
3280  }
3281  } else {
3282  for (unsigned i = 0; i < count; i++) {
3283  const ecvColor::Rgb* col = getPointScalarValueColor(i);
3284  if (col) {
3285  ecvColor::Rgb& _color = m_rgbColors->at(i);
3286  _color.r = static_cast<ColorCompType>(
3287  _color.r *
3288  (static_cast<float>(col->r) / ecvColor::MAX));
3289  _color.g = static_cast<ColorCompType>(
3290  _color.g *
3291  (static_cast<float>(col->g) / ecvColor::MAX));
3292  _color.b = static_cast<ColorCompType>(
3293  _color.b *
3294  (static_cast<float>(col->b) / ecvColor::MAX));
3295  }
3296  }
3297  }
3298 
3299  // We must update the VBOs
3301 
3302  return true;
3303 }
3304 
3305 QSharedPointer<cloudViewer::ReferenceCloud> ccPointCloud::computeCPSet(
3306  ccGenericPointCloud& otherCloud,
3307  cloudViewer::GenericProgressCallback* progressCb /*=nullptr*/,
3308  unsigned char octreeLevel /*=0*/) {
3309  int result = 0;
3310  QSharedPointer<cloudViewer::ReferenceCloud> CPSet;
3311  CPSet.reset(new cloudViewer::ReferenceCloud(&otherCloud));
3312 
3314  params;
3315  {
3316  params.CPSet = CPSet.data();
3317  params.octreeLevel = octreeLevel;
3318  }
3319 
3320  // create temporary SF for the nearest neighors determination
3321  // (computeCloud2CloudDistances) so that we can properly remove it
3322  // afterwards!
3323  static const char s_defaultTempSFName[] = "CPSetComputationTempSF";
3324  int sfIdx = getScalarFieldIndexByName(s_defaultTempSFName);
3325  if (sfIdx < 0) sfIdx = addScalarField(s_defaultTempSFName);
3326  if (sfIdx < 0) {
3327  CVLog::Warning("[ccPointCloud::ComputeCPSet] Not enough memory!");
3328  return QSharedPointer<cloudViewer::ReferenceCloud>(nullptr);
3329  }
3330 
3331  int currentInSFIndex = m_currentInScalarFieldIndex;
3332  int currentOutSFIndex = m_currentOutScalarFieldIndex;
3333  setCurrentScalarField(sfIdx);
3334 
3336  this, &otherCloud, params, progressCb);
3337 
3338  // restore previous parameters
3339  setCurrentInScalarField(currentInSFIndex);
3340  setCurrentOutScalarField(currentOutSFIndex);
3341  deleteScalarField(sfIdx);
3342 
3343  if (result < 0) {
3345  "[ccPointCloud::ComputeCPSet] Closest-point set computation "
3346  "failed!");
3347  CPSet.clear();
3348  }
3349 
3350  return CPSet;
3351 }
3352 
3354  ccGenericPointCloud* otherCloud,
3355  cloudViewer::GenericProgressCallback* progressCb /*=nullptr*/,
3356  unsigned char octreeLevel /*=0*/) {
3357  if (!otherCloud || otherCloud->size() == 0) {
3359  "[ccPointCloud::interpolateColorsFrom] Invalid/empty input "
3360  "cloud!");
3361  return false;
3362  }
3363 
3364  // check that both bounding boxes intersect!
3365  ccBBox box = getOwnBB();
3366  ccBBox otherBox = otherCloud->getOwnBB();
3367 
3368  CCVector3 dimSum = box.getDiagVec() + otherBox.getDiagVec();
3369  CCVector3 dist = box.getCenter() - otherBox.getCenter();
3370  if (fabs(dist.x) > dimSum.x / 2 || fabs(dist.y) > dimSum.y / 2 ||
3371  fabs(dist.z) > dimSum.z / 2) {
3373  "[ccPointCloud::interpolateColorsFrom] Clouds are too far from "
3374  "each other! Can't proceed.");
3375  return false;
3376  }
3377 
3378  // compute the closest-point set of 'this cloud' relatively to 'input cloud'
3379  //(to get a mapping between the resulting vertices and the input points)
3380  QSharedPointer<cloudViewer::ReferenceCloud> CPSet =
3381  computeCPSet(*otherCloud, progressCb, octreeLevel);
3382  if (!CPSet) {
3383  return false;
3384  }
3385 
3386  if (!resizeTheRGBTable(false)) {
3388  "[ccPointCloud::interpolateColorsFrom] Not enough memory!");
3389  return false;
3390  }
3391 
3392  // import colors
3393  unsigned CPSetSize = CPSet->size();
3394  assert(CPSetSize == size());
3395  for (unsigned i = 0; i < CPSetSize; ++i) {
3396  unsigned index = CPSet->getPointGlobalIndex(i);
3397  setPointColor(i, otherCloud->getPointColor(index));
3398  }
3399 
3400  // We must update the VBOs
3402 
3403  return true;
3404 }
3405 
3406 #if 0
3407 ccPointCloud* ccPointCloud::unrollOnCylinder(PointCoordinateType radius,
3408  unsigned char coneAxisDim,
3409  CCVector3* center,
3410  bool exportDeviationSF/*=false*/,
3411  double startAngle_deg/*=0.0*/,
3412  double stopAngle_deg/*=360.0*/,
3413  cloudViewer::GenericProgressCallback* progressCb/*=nullptr*/) const
3414 {
3415 
3416  if (startAngle_deg >= stopAngle_deg)
3417  {
3418  assert(false);
3419  return nullptr;
3420  }
3421  if (coneAxisDim > 2)
3422  {
3423  assert(false);
3424  return nullptr;
3425  }
3426 
3427  Tuple3ub dim;
3428  dim.z = coneAxisDim;
3429  dim.x = (dim.z < 2 ? dim.z + 1 : 0);
3430  dim.y = (dim.x < 2 ? dim.x + 1 : 0);
3431 
3432  unsigned numberOfPoints = size();
3433 
3434  cloudViewer::NormalizedProgress nprogress(progressCb, numberOfPoints);
3435  if (progressCb)
3436  {
3437  if (progressCb->textCanBeEdited())
3438  {
3439  progressCb->setMethodTitle("Unroll (cylinder)");
3440  progressCb->setInfo(qPrintable(QString("Number of points = %1").arg(numberOfPoints)));
3441  }
3442  progressCb->update(0);
3443  progressCb->start();
3444  }
3445 
3446  //ccPointCloud* clone = const_cast<ccPointCloud*>(this)->cloneThis(0, true);
3447  //if (!clone)
3448  //{
3449  // return 0;
3450  //}
3451  cloudViewer::ReferenceCloud duplicatedPoints(const_cast<ccPointCloud*>(this));
3452  std::vector<CCVector3> unrolledPoints;
3453  {
3454  //compute an estimate of the final point count
3455  unsigned newSize = static_cast<unsigned>(std::ceil((stopAngle_deg - startAngle_deg) / 360.0 * size()));
3456  if (!duplicatedPoints.reserve(newSize))
3457  {
3458  CVLog::Error("Not enough memory");
3459  return nullptr;
3460  }
3461 
3462  try
3463  {
3464  unrolledPoints.reserve(newSize);
3465  }
3466  catch (const std::bad_alloc&)
3467  {
3468  CVLog::Error("Not enough memory");
3469  return nullptr;
3470  }
3471  }
3472 
3473 
3474  std::vector<CCVector3> unrolledNormals;
3475  std::vector<ScalarType> deviationValues;
3476 
3477  bool withNormals = hasNormals();
3478  if (withNormals)
3479  {
3480  //for normals, we can simply store at most one unrolled normal per original one
3481  //same thing for deviation values
3482  try
3483  {
3484  unrolledNormals.resize(size());
3485  deviationValues.resize(size());
3486  }
3487  catch (const std::bad_alloc&)
3488  {
3489  //not enough memory
3490  return nullptr;
3491  }
3492  }
3493 
3494  //compute cylinder center (if none was provided)
3495  CCVector3 C;
3496  if (!center)
3497  {
3498  C = const_cast<ccPointCloud*>(this)->getOwnBB().getCenter();
3499  center = &C;
3500  }
3501 
3502  double startAngle_rad = cloudViewer::DegreesToRadians(startAngle_deg);
3503  double stopAngle_rad = cloudViewer::DegreesToRadians(stopAngle_deg);
3504 
3505  for (unsigned i = 0; i < numberOfPoints; i++)
3506  {
3507  const CCVector3* Pin = getPoint(i);
3508 
3509  CCVector3 CP = *Pin - *center;
3510 
3511  PointCoordinateType u = sqrt(CP.u[dim.x] * CP.u[dim.x] + CP.u[dim.y] * CP.u[dim.y]);
3512  double longitude_rad = atan2(static_cast<double>(CP.u[dim.x]), static_cast<double>(CP.u[dim.y]));
3513 
3514  //we project the point
3515  CCVector3 Pout;
3516  //Pout.u[dim.x] = longitude_rad * radius;
3517  Pout.u[dim.y] = u - radius;
3518  Pout.u[dim.z] = Pin->u[dim.z];
3519 
3520  // first unroll its normal if necessary
3521  if (withNormals)
3522  {
3523  const CCVector3& N = getPointNormal(i);
3524 
3525  PointCoordinateType px = CP.u[dim.x] + N.u[dim.x];
3526  PointCoordinateType py = CP.u[dim.y] + N.u[dim.y];
3527  PointCoordinateType nu = sqrt(px*px + py*py);
3528  double nLongitude_rad = atan2(static_cast<double>(px), static_cast<double>(py));
3529 
3530  CCVector3 N2;
3531  N2.u[dim.x] = static_cast<PointCoordinateType>((nLongitude_rad - longitude_rad) * radius);
3532  N2.u[dim.y] = nu - u;
3533  N2.u[dim.z] = N.u[dim.z];
3534  N2.normalize();
3535  unrolledNormals[i] = N2;
3536  //clone->setPointNormal(i, N2);
3537  }
3538 
3539  //then compute the deviation (if necessary)
3540  if (exportDeviationSF)
3541  {
3542  deviationValues[i] = static_cast<ScalarType>(Pout.u[dim.y]);
3543  }
3544 
3545  //then repeat the unrolling process for the coordinates
3546  //1) poition the 'point' at the beginning of the angular range
3547  while (longitude_rad >= startAngle_rad)
3548  {
3549  longitude_rad -= 2 * M_PI;
3550  }
3551  longitude_rad += 2 * M_PI;
3552 
3553  //2) repeat the unrolling process
3554  for (; longitude_rad < stopAngle_rad; longitude_rad += 2 * M_PI)
3555  {
3556  //do we need to reserve more memory?
3557  if (duplicatedPoints.size() == duplicatedPoints.capacity())
3558  {
3559  unsigned newSize = duplicatedPoints.size() + (1 << 20);
3560  if (!duplicatedPoints.reserve(newSize))
3561  {
3562  CVLog::Error("Not enough memory");
3563  return nullptr;
3564  }
3565 
3566  try
3567  {
3568  unrolledPoints.reserve(newSize);
3569  }
3570  catch (const std::bad_alloc&)
3571  {
3572  CVLog::Error("Not enough memory");
3573  return nullptr;
3574  }
3575  }
3576 
3577  //add the point
3578  Pout.u[dim.x] = longitude_rad * radius;
3579  unrolledPoints.push_back(Pout);
3580  duplicatedPoints.addPointIndex(i);
3581  }
3582 
3583  //process canceled by user?
3584  if (progressCb && !nprogress.oneStep())
3585  {
3586  CVLog::Warning("Process cancelled by user");
3587  return nullptr;
3588  }
3589  }
3590 
3591  if (progressCb)
3592  {
3593  progressCb->stop();
3594  }
3595 
3596  //now create the real cloud
3597  ccPointCloud* clone = partialClone(&duplicatedPoints);
3598  if (clone)
3599  {
3600  cloudViewer::ScalarField* deviationSF = nullptr;
3601  if (exportDeviationSF)
3602  {
3603  int sfIdx = clone->getScalarFieldIndexByName(s_deviationSFName);
3604  if (sfIdx < 0)
3605  {
3606  sfIdx = clone->addScalarField(s_deviationSFName);
3607  if (sfIdx < 0)
3608  {
3609  CVLog::Warning("[unrollOnCylinder] Not enough memory to init the deviation scalar field");
3610  }
3611  }
3612  if (sfIdx >= 0)
3613  {
3614  deviationSF = clone->getScalarField(sfIdx);
3615  clone->setCurrentDisplayedScalarField(sfIdx);
3616  clone->showSF(true);
3617  }
3618  }
3619 
3620  //update the coordinates, the normals and the deviation SF
3621  for (unsigned i = 0; i < duplicatedPoints.size(); ++i)
3622  {
3623  CCVector3* P = clone->point(i);
3624  *P = unrolledPoints[i];
3625 
3626  unsigned globalIndex = duplicatedPoints.getPointGlobalIndex(i);
3627  if (withNormals)
3628  {
3629  clone->setPointNormal(i, unrolledNormals[globalIndex]);
3630  }
3631  if (deviationSF)
3632  {
3633  deviationSF->setValue(i, deviationValues[globalIndex]);
3634  }
3635  }
3636 
3637  if (deviationSF)
3638  {
3639  deviationSF->computeMinAndMax();
3640  }
3641 
3642  clone->setName(getName() + ".unrolled");
3643  clone->refreshBB(); //calls notifyGeometryUpdate + releaseVBOs
3644  }
3645 
3646  return clone;
3647 }
3648 #endif
3649 
3650 static void ProjectOnCylinder(const CCVector3& AP,
3651  const Tuple3ub& dim,
3652  PointCoordinateType radius,
3653  PointCoordinateType& delta,
3654  PointCoordinateType& phi_rad) {
3655  // 2D distance to the center (XY plane)
3656  PointCoordinateType APnorm_XY =
3657  sqrt(AP.u[dim.x] * AP.u[dim.x] + AP.u[dim.y] * AP.u[dim.y]);
3658  // longitude (0 = +X = east)
3659  phi_rad = atan2(AP.u[dim.y], AP.u[dim.x]);
3660  // deviation
3661  delta = APnorm_XY - radius;
3662 }
3663 
3664 static void ProjectOnCone(const CCVector3& AP,
3665  PointCoordinateType alpha_rad,
3666  const Tuple3ub& dim,
3668  PointCoordinateType& delta,
3669  PointCoordinateType& phi_rad) {
3670  // 3D distance to the apex
3671  PointCoordinateType normAP = AP.norm();
3672  // 2D distance to the apex (XY plane)
3673  PointCoordinateType normAP_XY =
3674  sqrt(AP.u[dim.x] * AP.u[dim.x] + AP.u[dim.y] * AP.u[dim.y]);
3675 
3676  // angle between +Z and AP
3677  PointCoordinateType beta_rad = atan2(normAP_XY, -AP.u[dim.z]);
3678  // angular deviation
3679  PointCoordinateType gamma_rad =
3680  beta_rad -
3681  alpha_rad; // if gamma_rad > 0, the point is outside the cone
3682 
3683  // projection on the cone
3684  {
3685  // longitude (0 = +X = east)
3686  phi_rad = atan2(AP.u[dim.y], AP.u[dim.x]);
3687  // curvilinear distance from the Apex
3688  s = normAP * cos(gamma_rad);
3689  //(normal) deviation
3690  delta = normAP * sin(gamma_rad);
3691  }
3692 }
3693 
3695  UnrollMode mode,
3696  UnrollBaseParams* params,
3697  bool exportDeviationSF /*=false*/,
3698  double startAngle_deg /*=0.0*/,
3699  double stopAngle_deg /*=360.0*/,
3700  cloudViewer::GenericProgressCallback* progressCb /*=nullptr*/) const {
3701  if (!params || params->axisDim > 2 || startAngle_deg >= stopAngle_deg) {
3702  // invalid input parameters
3703  assert(false);
3704  return nullptr;
3705  }
3706 
3707  QString modeStr;
3708  UnrollCylinderParams* cylParams = nullptr;
3709  UnrollConeParams* coneParams = nullptr;
3710 
3711  switch (mode) {
3712  case CYLINDER:
3713  modeStr = "Cylinder";
3714  cylParams = static_cast<UnrollCylinderParams*>(params);
3715  break;
3716  case CONE:
3717  modeStr = "Cone";
3718  coneParams = static_cast<UnrollConeParams*>(params);
3719  break;
3720  case STRAIGHTENED_CONE:
3721  case STRAIGHTENED_CONE2:
3722  modeStr = "Straightened cone";
3723  coneParams = static_cast<UnrollConeParams*>(params);
3724  break;
3725  default:
3726  assert(false);
3727  return nullptr;
3728  }
3729 
3730  Tuple3ub dim;
3731  dim.z = params->axisDim;
3732  dim.x = (dim.z < 2 ? dim.z + 1 : 0);
3733  dim.y = (dim.x < 2 ? dim.x + 1 : 0);
3734 
3735  unsigned numberOfPoints = size();
3736  cloudViewer::NormalizedProgress nprogress(progressCb, numberOfPoints);
3737  if (progressCb) {
3738  if (progressCb->textCanBeEdited()) {
3739  progressCb->setMethodTitle(
3740  qPrintable(QString("Unroll (%1)").arg(modeStr)));
3741  progressCb->setInfo(qPrintable(
3742  QString("Number of points = %1").arg(numberOfPoints)));
3743  }
3744  progressCb->update(0);
3745  progressCb->start();
3746  }
3747 
3748  cloudViewer::ReferenceCloud duplicatedPoints(
3749  const_cast<ccPointCloud*>(this));
3750  std::vector<CCVector3> unrolledPoints;
3751  {
3752  // compute an estimate of the final point count
3753  unsigned newSize = static_cast<unsigned>(
3754  std::ceil((stopAngle_deg - startAngle_deg) / 360.0 * size()));
3755  if (!duplicatedPoints.reserve(newSize)) {
3756  CVLog::Error("Not enough memory");
3757  return nullptr;
3758  }
3759 
3760  try {
3761  unrolledPoints.reserve(newSize);
3762  } catch (const std::bad_alloc&) {
3763  CVLog::Error("Not enough memory");
3764  return nullptr;
3765  }
3766  }
3767 
3768  std::vector<ScalarType> deviationValues;
3769  if (exportDeviationSF) try {
3770  deviationValues.resize(size());
3771  } catch (const std::bad_alloc&) {
3772  // not enough memory
3773  return nullptr;
3774  }
3775 
3776  std::vector<CCVector3> unrolledNormals;
3777  bool withNormals = hasNormals();
3778  if (withNormals) {
3779  // for normals, we can simply store at most one unrolled normal per
3780  // original one same thing for deviation values
3781  try {
3782  unrolledNormals.resize(size());
3783  } catch (const std::bad_alloc&) {
3784  // not enough memory
3785  return nullptr;
3786  }
3787  }
3788 
3789  double startAngle_rad = cloudViewer::DegreesToRadians(startAngle_deg);
3790  double stopAngle_rad = cloudViewer::DegreesToRadians(stopAngle_deg);
3791 
3792  PointCoordinateType alpha_rad = 0;
3793  PointCoordinateType sin_alpha = 0;
3794  if (mode != CYLINDER) {
3795  alpha_rad = cloudViewer::DegreesToRadians(coneParams->coneAngle_deg);
3796  sin_alpha = static_cast<PointCoordinateType>(sin(alpha_rad));
3797  }
3798 
3799  for (unsigned i = 0; i < numberOfPoints; i++) {
3800  const CCVector3* Pin = getPoint(i);
3801 
3802  // we project the point
3803  CCVector3 AP;
3804  CCVector3 Pout;
3805  PointCoordinateType longitude_rad = 0; // longitude (rad)
3806  PointCoordinateType delta = 0; // distance to the cone/cylinder surface
3807  PointCoordinateType coneAbscissa = 0;
3808 
3809  switch (mode) {
3810  case CYLINDER: {
3811  AP = *Pin - cylParams->center;
3812  ProjectOnCylinder(AP, dim, params->radius, delta,
3813  longitude_rad);
3814 
3815  // we project the point
3816  // Pout.u[dim.x] = longitude_rad * radius;
3817  Pout.u[dim.y] = -delta;
3818  Pout.u[dim.z] = Pin->u[dim.z];
3819  } break;
3820 
3821  case STRAIGHTENED_CONE: {
3822  AP = *Pin - coneParams->apex;
3823  ProjectOnCone(AP, alpha_rad, dim, coneAbscissa, delta,
3824  longitude_rad);
3825  // we simply develop the cone as a cylinder
3826  // Pout.u[dim.x] = phi_rad * params->radius;
3827  Pout.u[dim.y] = -delta;
3828  // Pout.u[dim.z] = Pin->u[dim.z];
3829  Pout.u[dim.z] = coneParams->apex.u[dim.z] - coneAbscissa;
3830  } break;
3831 
3832  case STRAIGHTENED_CONE2: {
3833  AP = *Pin - coneParams->apex;
3834  ProjectOnCone(AP, alpha_rad, dim, coneAbscissa, delta,
3835  longitude_rad);
3836  // we simply develop the cone as a cylinder
3837  // Pout.u[dim.x] = phi_rad * coneAbscissa * sin_alpha;
3838  Pout.u[dim.y] = -delta;
3839  // Pout.u[dim.z] = Pin->u[dim.z];
3840  Pout.u[dim.z] = coneParams->apex.u[dim.z] - coneAbscissa;
3841  } break;
3842 
3843  case CONE: {
3844  AP = *Pin - coneParams->apex;
3845  ProjectOnCone(AP, alpha_rad, dim, coneAbscissa, delta,
3846  longitude_rad);
3847  // unrolling
3848  PointCoordinateType theta_rad =
3849  longitude_rad *
3850  sin_alpha; // sin_alpha is a bit arbitrary here. The
3851  // aim is mostly to reduce the angular range
3852  // project the point
3853  Pout.u[dim.y] = -coneAbscissa * cos(theta_rad);
3854  Pout.u[dim.x] = coneAbscissa * sin(theta_rad);
3855  Pout.u[dim.z] = delta;
3856  } break;
3857 
3858  default:
3859  assert(false);
3860  }
3861 
3862  // first unroll its normal if necessary
3863  if (withNormals) {
3864  const CCVector3& N = getPointNormal(i);
3865  CCVector3 AP2 = AP + N;
3866  CCVector3 N2;
3867 
3868  switch (mode) {
3869  case CYLINDER: {
3870  PointCoordinateType delta2;
3871  PointCoordinateType longitude2_rad;
3872  ProjectOnCylinder(AP2, dim, params->radius, delta2,
3873  longitude2_rad);
3874 
3875  N2.u[dim.x] = static_cast<PointCoordinateType>(
3876  (longitude2_rad - longitude_rad) * params->radius);
3877  N2.u[dim.y] = -(delta2 - delta);
3878  N2.u[dim.z] = N.u[dim.z];
3879  } break;
3880 
3881  case STRAIGHTENED_CONE: {
3882  PointCoordinateType coneAbscissa2;
3883  PointCoordinateType delta2;
3884  PointCoordinateType longitude2_rad;
3885  ProjectOnCone(AP2, alpha_rad, dim, coneAbscissa2, delta2,
3886  longitude2_rad);
3887  // we simply develop the cone as a cylinder
3888  N2.u[dim.x] = static_cast<PointCoordinateType>(
3889  (longitude2_rad - longitude_rad) * params->radius);
3890  N2.u[dim.y] = -(delta2 - delta);
3891  N2.u[dim.z] = coneAbscissa - coneAbscissa2;
3892  } break;
3893 
3894  case STRAIGHTENED_CONE2: {
3895  PointCoordinateType coneAbscissa2;
3896  PointCoordinateType delta2;
3897  PointCoordinateType longitude2_rad;
3898  ProjectOnCone(AP2, alpha_rad, dim, coneAbscissa2, delta2,
3899  longitude2_rad);
3900  // we simply develop the cone as a cylinder
3901  N2.u[dim.x] = static_cast<PointCoordinateType>(
3902  (longitude2_rad * coneAbscissa -
3903  longitude_rad * coneAbscissa2) *
3904  sin_alpha);
3905  N2.u[dim.y] = -(delta2 - delta);
3906  N2.u[dim.z] = coneAbscissa - coneAbscissa2;
3907  } break;
3908 
3909  case CONE: {
3910  PointCoordinateType coneAbscissa2;
3911  PointCoordinateType delta2;
3912  PointCoordinateType longitude2_rad;
3913  ProjectOnCone(AP2, alpha_rad, dim, coneAbscissa2, delta2,
3914  longitude2_rad);
3915  // unrolling
3916  PointCoordinateType theta2_rad =
3917  longitude2_rad *
3918  sin_alpha; // sin_alpha is a bit arbitrary here.
3919  // The aim is mostly to reduce the
3920  // angular range
3921  // project the point
3922  CCVector3 P2out;
3923  P2out.u[dim.x] = coneAbscissa2 * sin(theta2_rad);
3924  P2out.u[dim.y] = -coneAbscissa2 * cos(theta2_rad);
3925  P2out.u[dim.z] = delta2;
3926  N2 = P2out - Pout;
3927  } break;
3928 
3929  default:
3930  assert(false);
3931  break;
3932  }
3933 
3934  N2.normalize();
3935  unrolledNormals[i] = N2;
3936  }
3937 
3938  // then compute the deviation (if necessary)
3939  if (exportDeviationSF) {
3940  deviationValues[i] = static_cast<ScalarType>(delta);
3941  }
3942 
3943  // then repeat the unrolling process for the coordinates
3944  // 1) position the 'point' at the beginning of the angular range
3945  double dLongitude_rad = longitude_rad;
3946  while (dLongitude_rad >= startAngle_rad) {
3947  dLongitude_rad -= 2 * M_PI;
3948  }
3949  dLongitude_rad += 2 * M_PI;
3950 
3951  // 2) repeat the unrolling process
3952  for (; dLongitude_rad < stopAngle_rad; dLongitude_rad += 2 * M_PI) {
3953  // do we need to reserve more memory?
3954  if (duplicatedPoints.size() == duplicatedPoints.capacity()) {
3955  unsigned newSize = duplicatedPoints.size() + (1 << 20);
3956  if (!duplicatedPoints.reserve(newSize)) {
3957  CVLog::Error("Not enough memory");
3958  return nullptr;
3959  }
3960 
3961  try {
3962  unrolledPoints.reserve(newSize);
3963  } catch (const std::bad_alloc&) {
3964  CVLog::Error("Not enough memory");
3965  return nullptr;
3966  }
3967  }
3968 
3969  // add the point
3970  switch (mode) {
3971  case CYLINDER:
3972  case STRAIGHTENED_CONE:
3973  Pout.u[dim.x] = dLongitude_rad * params->radius;
3974  break;
3975  case STRAIGHTENED_CONE2:
3976  Pout.u[dim.x] = dLongitude_rad * coneAbscissa * sin_alpha;
3977  break;
3978 
3979  case CONE:
3980  Pout.u[dim.x] = coneAbscissa * sin(dLongitude_rad);
3981  Pout.u[dim.y] = -coneAbscissa * cos(dLongitude_rad);
3982  // Pout = coneParams->apex + Pout; //nope, this projection
3983  // is arbitrary and should be centered on (0, 0, 0)
3984  break;
3985 
3986  default:
3987  assert(false);
3988  }
3989 
3990  unrolledPoints.push_back(Pout);
3991  duplicatedPoints.addPointIndex(i);
3992  }
3993 
3994  // process canceled by user?
3995  if (progressCb && !nprogress.oneStep()) {
3996  CVLog::Warning("Process cancelled by user");
3997  return nullptr;
3998  }
3999  }
4000 
4001  if (progressCb) {
4002  progressCb->stop();
4003  }
4004 
4005  // now create the real cloud
4006  ccPointCloud* clone = partialClone(&duplicatedPoints);
4007  if (clone) {
4008  cloudViewer::ScalarField* deviationSF = nullptr;
4009  if (exportDeviationSF) {
4010  int sfIdx = clone->getScalarFieldIndexByName(s_deviationSFName);
4011  if (sfIdx < 0) {
4012  sfIdx = clone->addScalarField(s_deviationSFName);
4013  if (sfIdx < 0) {
4015  "[unrollOnCylinder] Not enough memory to init the "
4016  "deviation scalar field");
4017  }
4018  }
4019  if (sfIdx >= 0) {
4020  deviationSF = clone->getScalarField(sfIdx);
4021  clone->setCurrentDisplayedScalarField(sfIdx);
4022  clone->showSF(true);
4023  }
4024  }
4025 
4026  // update the coordinates, the normals and the deviation SF
4027  for (unsigned i = 0; i < duplicatedPoints.size(); ++i) {
4028  CCVector3* P = clone->point(i);
4029  *P = unrolledPoints[i];
4030 
4031  unsigned globalIndex = duplicatedPoints.getPointGlobalIndex(i);
4032  if (withNormals) {
4033  clone->setPointNormal(i, unrolledNormals[globalIndex]);
4034  }
4035  if (deviationSF) {
4036  deviationSF->setValue(i, deviationValues[globalIndex]);
4037  }
4038  }
4039 
4040  if (deviationSF) {
4041  deviationSF->computeMinAndMax();
4042  }
4043 
4044  clone->setName(getName() + ".unrolled");
4045  clone->refreshBB(); // calls notifyGeometryUpdate + releaseVBOs
4046  }
4047 
4048  return clone;
4049 }
4050 
4051 #if 0
4052 ccPointCloud* ccPointCloud::unrollOnCone( double coneAngle_deg,
4053  const CCVector3& coneApex,
4054  unsigned char coneAxisDim,
4055  bool developStraightenedCone,
4056  PointCoordinateType baseRadius,
4057  bool exportDeviationSF/*=false*/,
4058  cloudViewer::GenericProgressCallback* progressCb/*=nullptr*/) const
4059 {
4060  if (coneAxisDim > 2)
4061  {
4062  assert(false);
4063  return nullptr;
4064  }
4065 
4066  Tuple3ub dim;
4067  dim.z = coneAxisDim;
4068  dim.x = (dim.z < 2 ? dim.z + 1 : 0);
4069  dim.y = (dim.x < 2 ? dim.x + 1 : 0);
4070 
4071  unsigned numberOfPoints = size();
4072 
4073  cloudViewer::NormalizedProgress nprogress(progressCb, numberOfPoints);
4074  if (progressCb)
4075  {
4076  if (progressCb->textCanBeEdited())
4077  {
4078  progressCb->setMethodTitle("Unroll (cone)");
4079  progressCb->setInfo(qPrintable(QString("Number of points = %1").arg(numberOfPoints)));
4080  }
4081  progressCb->update(0);
4082  progressCb->start();
4083  }
4084 
4085  ccPointCloud* clone = const_cast<ccPointCloud*>(this)->cloneThis(nullptr, true);
4086  if (!clone)
4087  {
4088  return nullptr;
4089  }
4090 
4091  cloudViewer::ScalarField* deviationSF = nullptr;
4092  if (exportDeviationSF)
4093  {
4094  int sfIdx = clone->getScalarFieldIndexByName(s_deviationSFName);
4095  if (sfIdx < 0)
4096  {
4097  sfIdx = clone->addScalarField(s_deviationSFName);
4098  if (sfIdx < 0)
4099  {
4100  CVLog::Warning("[unrollOnCone] Not enough memory to init the deviation scalar field");
4101  }
4102  }
4103  if (sfIdx >= 0)
4104  {
4105  deviationSF = clone->getScalarField(sfIdx);
4106  }
4107  clone->setCurrentDisplayedScalarField(sfIdx);
4108  clone->showSF(true);
4109  }
4110 
4111  PointCoordinateType alpha_rad = cloudViewer::DegreesToRadians(coneAngle_deg);
4112  PointCoordinateType sin_alpha = static_cast<PointCoordinateType>( sin(alpha_rad) );
4113 
4114  for (unsigned i = 0; i < numberOfPoints; i++)
4115  {
4116  const CCVector3* Pin = getPoint(i);
4117 
4118  PointCoordinateType s, delta, phi_rad;
4119  ProjectOnCone(*Pin, coneApex, alpha_rad, dim, s, delta, phi_rad);
4120 
4121  if (deviationSF)
4122  {
4123  deviationSF->setValue(i, delta);
4124  }
4125 
4126  CCVector3 Pout;
4127  if (developStraightenedCone)
4128  {
4129  //we simply develop the cone as a cylinder
4130  Pout.u[dim.x] = (baseRadius + delta) * cos(phi_rad);
4131  Pout.u[dim.y] = (baseRadius + delta) * sin(phi_rad);
4132  Pout.u[dim.z] = coneApex.u[dim.z] - s;
4133  }
4134  else
4135  {
4136  //unrolling
4137  PointCoordinateType theta_rad = phi_rad * sin_alpha;
4138 
4139  //project the point
4140  Pout.u[dim.y] = -s * cos(theta_rad);
4141  Pout.u[dim.x] = s * sin(theta_rad);
4142  Pout.u[dim.z] = delta;
4143  }
4144 
4145  //replace the point in the destination cloud
4146  *clone->point(i) = Pout;
4147 
4148  //and its normal if necessary
4149  if (clone->hasNormals())
4150  {
4151  const CCVector3& N = clone->getPointNormal(i);
4152 
4153  PointCoordinateType s2, delta2, phi2_rad;
4154  ProjectOnCone(*Pin + N, coneApex, alpha_rad, dim, s2, delta2, phi2_rad);
4155 
4156  CCVector3 P2out;
4157  if (developStraightenedCone)
4158  {
4159  //we simply develop the cone as a cylinder
4160  P2out.u[dim.x] = (baseRadius + delta2) * cos(phi2_rad);
4161  P2out.u[dim.y] = (baseRadius + delta2) * sin(phi2_rad);
4162  P2out.u[dim.z] = coneApex.u[dim.z] - s2;
4163  }
4164  else
4165  {
4166  //unrolling
4167  PointCoordinateType theta2_rad = phi2_rad * sin_alpha;
4168 
4169  //project the point
4170  P2out.u[dim.y] = -s2 * cos(theta2_rad);
4171  P2out.u[dim.x] = s2 * sin(theta2_rad);
4172  P2out.u[dim.z] = delta2;
4173  }
4174 
4175  CCVector3 N2 = P2out - Pout;
4176  N2.normalize();
4177 
4178  clone->setPointNormal(i, N2);
4179  }
4180 
4181  //process canceled by user?
4182  if (progressCb && !nprogress.oneStep())
4183  {
4184  delete clone;
4185  clone = nullptr;
4186  break;
4187  }
4188  }
4189 
4190  if (progressCb)
4191  {
4192  progressCb->stop();
4193  }
4194 
4195  if (clone)
4196  {
4197  if (deviationSF)
4198  {
4199  deviationSF->computeMinAndMax();
4200  }
4201 
4202  clone->setName(getName() + ".unrolled");
4203  clone->refreshBB(); //calls notifyGeometryUpdate + releaseVBOs
4204  }
4205 
4206  return clone;
4207 }
4208 #endif
4209 
4210 int ccPointCloud::addScalarField(const char* uniqueName) {
4211  // create new scalar field
4212  ccScalarField* sf = new ccScalarField(uniqueName);
4213 
4214  int sfIdx = addScalarField(sf);
4215 
4216  // failure?
4217  if (sfIdx < 0) {
4218  sf->release();
4219  return -1;
4220  }
4221 
4222  return sfIdx;
4223 }
4224 
4226  assert(sf);
4227 
4228  // we don't accept two SFs with the same name!
4229  if (getScalarFieldIndexByName(sf->getName()) >= 0) {
4230  CVLog::Warning(QString("[ccPointCloud::addScalarField] Name '%1' "
4231  "already exists!")
4232  .arg(sf->getName()));
4233  return -1;
4234  }
4235 
4236  // auto-resize
4237  if (sf->size() < m_points.size()) {
4238  if (!sf->resizeSafe(m_points.size())) {
4239  CVLog::Warning("[ccPointCloud::addScalarField] Not enough memory!");
4240  return -1;
4241  }
4242  }
4243  if (sf->capacity() < m_points.capacity()) // yes, it happens ;)
4244  {
4245  if (!sf->reserveSafe(m_points.capacity())) {
4246  CVLog::Warning("[ccPointCloud::addScalarField] Not enough memory!");
4247  return -1;
4248  }
4249  }
4250 
4251  try {
4252  m_scalarFields.push_back(sf);
4253  } catch (const std::bad_alloc&) {
4254  CVLog::Warning("[ccPointCloud::addScalarField] Not enough memory!");
4255  return -1;
4256  }
4257 
4258  sf->link();
4259 
4260  return static_cast<int>(m_scalarFields.size()) - 1;
4261 }
4262 
4263 bool ccPointCloud::toFile_MeOnly(QFile& out, short dataVersion) const {
4264  assert(out.isOpen() && (out.openMode() & QIODevice::WriteOnly));
4265  if (dataVersion < 27) {
4266  assert(false);
4267  return false;
4268  }
4269 
4270  if (!ccGenericPointCloud::toFile_MeOnly(out, dataVersion)) return false;
4271 
4272  // points array (dataVersion>=20)
4275  m_points, out))
4276  return false;
4277 
4278  // colors array (dataVersion>=20)
4279  {
4280  bool hasColorsArray = hasColors();
4281  if (out.write((const char*)&hasColorsArray, sizeof(bool)) < 0)
4282  return WriteError();
4283  if (hasColorsArray) {
4284  assert(m_rgbColors);
4285  if (!m_rgbColors->toFile(out, dataVersion)) return false;
4286  }
4287  }
4288 
4289  // normals array (dataVersion>=20)
4290  {
4291  bool hasNormalsArray = hasNormals();
4292  if (out.write((const char*)&hasNormalsArray, sizeof(bool)) < 0)
4293  return WriteError();
4294  if (hasNormalsArray) {
4295  assert(m_normals);
4296  if (!m_normals->toFile(out, dataVersion)) return false;
4297  }
4298  }
4299 
4300  // scalar field(s)
4301  {
4302  // number of scalar fields (dataVersion>=20)
4303  uint32_t sfCount = static_cast<uint32_t>(getNumberOfScalarFields());
4304  if (out.write((const char*)&sfCount, 4) < 0) return WriteError();
4305 
4306  // scalar fields (dataVersion>=20)
4307  for (uint32_t i = 0; i < sfCount; ++i) {
4308  ccScalarField* sf = static_cast<ccScalarField*>(getScalarField(i));
4309  assert(sf);
4310  if (!sf || !sf->toFile(out, dataVersion)) return false;
4311  }
4312 
4313  //'show NaN values in grey' state (27>dataVersion>=20)
4314  // if (out.write((const char*)&m_greyForNanScalarValues,sizeof(bool)) <
4315  // 0) return WriteError();
4316 
4317  //'show current sf color scale' state (dataVersion>=20)
4318  if (out.write((const char*)&m_sfColorScaleDisplayed, sizeof(bool)) < 0)
4319  return WriteError();
4320 
4321  // Displayed scalar field index (dataVersion>=20)
4322  int32_t displayedScalarFieldIndex =
4324  if (out.write((const char*)&displayedScalarFieldIndex, 4) < 0)
4325  return WriteError();
4326  }
4327 
4328  // grid structures (dataVersion>=41)
4329  {
4330  // number of grids
4331  uint32_t count = static_cast<uint32_t>(this->gridCount());
4332  if (out.write((const char*)&count, 4) < 0) return WriteError();
4333 
4334  // save each grid
4335  for (uint32_t i = 0; i < count; ++i) {
4336  const Grid::Shared& g = grid(static_cast<unsigned>(i));
4337  if (g && !g->indexes.empty()) {
4338  if (!g->toFile(out, dataVersion)) {
4339  return false;
4340  }
4341  }
4342  }
4343  }
4344 
4345  // Waveforms (dataVersion >= 44)
4346  bool withFWF = hasFWF();
4347  if (out.write((const char*)&withFWF, sizeof(bool)) < 0) {
4348  return WriteError();
4349  }
4350  if (withFWF) {
4351  // first save the descriptors
4352  uint32_t descriptorCount =
4353  static_cast<uint32_t>(m_fwfDescriptors.size());
4354  if (out.write((const char*)&descriptorCount, 4) < 0) {
4355  return WriteError();
4356  }
4357  for (auto it = m_fwfDescriptors.begin(); it != m_fwfDescriptors.end();
4358  ++it) {
4359  // write the key (descriptor ID)
4360  if (out.write((const char*)&it.key(), 1) < 0) {
4361  return WriteError();
4362  }
4363  // write the descriptor
4364  if (!it.value().toFile(out, dataVersion)) {
4365  return WriteError();
4366  }
4367  }
4368 
4369  // then the waveforms
4370  uint32_t waveformCount = static_cast<uint32_t>(m_fwfWaveforms.size());
4371  if (out.write((const char*)&waveformCount, 4) < 0) {
4372  return WriteError();
4373  }
4374  for (const ccWaveform& w : m_fwfWaveforms) {
4375  if (!w.toFile(out, dataVersion)) {
4376  return WriteError();
4377  }
4378  }
4379 
4380  // eventually save the data
4381  uint64_t dataSize =
4382  static_cast<uint64_t>(m_fwfData ? m_fwfData->size() : 0);
4383  if (out.write((const char*)&dataSize, 8) < 0) {
4384  return WriteError();
4385  }
4386  if (m_fwfData &&
4387  out.write((const char*)m_fwfData->data(), dataSize) < 0) {
4388  return WriteError();
4389  }
4390  }
4391 
4392  return true;
4393 }
4394 
4395 // Grid methods implementation
4397  : w(0), h(0), validCount(0), minValidIndex(0), maxValidIndex(0) {
4399 }
4400 
4401 bool ccPointCloud::Grid::init(unsigned rowCount,
4402  unsigned colCount,
4403  bool withRGB /*=false*/) {
4404  size_t scanSize = static_cast<size_t>(rowCount) * colCount;
4405  try {
4406  indexes.resize(scanSize, -1);
4407  if (withRGB) {
4408  colors.resize(scanSize, ecvColor::Rgb(0, 0, 0));
4409  }
4410  } catch (const std::bad_alloc&) {
4411  // not enough memory
4412  return false;
4413  }
4414 
4415  w = colCount;
4416  h = rowCount;
4417 
4418  return true;
4419 }
4420 
4421 void ccPointCloud::Grid::setIndex(unsigned row, unsigned col, int index) {
4422  assert(row < h);
4423  assert(col < w);
4424  assert(!indexes.empty());
4425  indexes[row * w + col] = index;
4426 }
4427 
4429  unsigned col,
4430  const ecvColor::Rgb& rgb) {
4431  assert(row < h);
4432  assert(col < w);
4433  assert(!colors.empty());
4434  colors[row * w + col] = rgb;
4435 }
4436 
4438  validCount = minValidIndex = maxValidIndex = 0;
4439 
4440  if (!indexes.empty()) {
4441  minValidIndex = std::numeric_limits<int>::max();
4442  for (int index : indexes) {
4443  if (index < 0) {
4444  continue;
4445  }
4446 
4447  ++validCount;
4448 
4449  unsigned uIndex = static_cast<unsigned>(index);
4450  if (uIndex < minValidIndex) {
4451  minValidIndex = uIndex;
4452  } else if (uIndex > maxValidIndex) {
4453  maxValidIndex = uIndex;
4454  }
4455  }
4456 
4457  if (minValidIndex == std::numeric_limits<int>::max()) {
4458  // empty grid!
4459  minValidIndex = 0;
4460  }
4461  }
4462 }
4463 
4465  if (colors.size() == static_cast<size_t>(w) * h) {
4466  QImage image(w, h, QImage::Format_ARGB32);
4467  for (unsigned j = 0; j < h; ++j) {
4468  for (unsigned i = 0; i < w; ++i) {
4469  const ecvColor::Rgb& col = colors[j * w + i];
4470  image.setPixel(i, j, qRgb(col.r, col.g, col.b));
4471  }
4472  }
4473  return image;
4474  } else {
4475  return QImage();
4476  }
4477 }
4478 
4479 bool ccPointCloud::Grid::toFile(QFile& out, short dataVersion) const {
4480  // grid (dataVersion>=41)
4481  if (dataVersion < 41) {
4482  assert(false);
4483  return false;
4484  }
4485 
4486  // width (dataVersion>=41)
4487  uint32_t _w = static_cast<uint32_t>(w);
4488  if (out.write((const char*)&_w, 4) < 0) return WriteError();
4489  // height (dataVersion>=41)
4490  uint32_t _h = static_cast<uint32_t>(h);
4491  if (out.write((const char*)&_h, 4) < 0) return WriteError();
4492 
4493  // sensor matrix (dataVersion>=41)
4494  if (!sensorPosition.toFile(out, dataVersion)) return WriteError();
4495 
4496  // indexes (dataVersion>=41)
4497  const int* _index = indexes.data();
4498  for (uint32_t j = 0; j < w * h; ++j, ++_index) {
4499  int32_t index = static_cast<int32_t>(*_index);
4500  if (out.write((const char*)&index, 4) < 0) return WriteError();
4501  }
4502 
4503  // grid colors (dataVersion>=41)
4504  {
4505  bool hasColors = (colors.size() == indexes.size());
4506  if (out.write((const char*)&hasColors, 1) < 0) {
4507  return WriteError();
4508  }
4509 
4510  if (hasColors) {
4511  for (uint32_t j = 0; j < colors.size(); ++j) {
4512  if (out.write((const char*)colors[j].rgb, 3) < 0)
4513  return WriteError();
4514  }
4515  }
4516  }
4517 
4518  return true;
4519 }
4520 
4522  short dataVersion,
4523  int flags,
4524  LoadedIDMap& oldToNewIDMap) {
4525  // width (dataVersion>=41)
4526  uint32_t _w = 0;
4527  if (in.read((char*)&_w, 4) < 0) return ReadError();
4528  // height (dataVersion>=41)
4529  uint32_t _h = 0;
4530  if (in.read((char*)&_h, 4) < 0) return ReadError();
4531 
4532  w = static_cast<unsigned>(_w);
4533  h = static_cast<unsigned>(_h);
4534 
4535  // sensor matrix (dataVersion>=41)
4536  if (!sensorPosition.fromFile(in, dataVersion, flags, oldToNewIDMap))
4537  return WriteError();
4538 
4539  try {
4540  indexes.resize(static_cast<size_t>(w) * h);
4541  } catch (const std::bad_alloc&) {
4542  return MemoryError();
4543  }
4544 
4545  // indexes (dataVersion>=41)
4546  int* _index = indexes.data();
4547  for (size_t j = 0; j < static_cast<size_t>(w) * h; ++j, ++_index) {
4548  int32_t index = 0;
4549  if (in.read((char*)&index, 4) < 0) return ReadError();
4550 
4551  *_index = index;
4552  if (index >= 0) {
4553  // update min, max and count for valid indexes
4554  if (validCount) {
4555  minValidIndex =
4556  std::min(minValidIndex, static_cast<unsigned>(index));
4557  maxValidIndex =
4558  std::max(maxValidIndex, static_cast<unsigned>(index));
4559  } else {
4560  minValidIndex = maxValidIndex = static_cast<unsigned>(index);
4561  }
4562  ++validCount;
4563  }
4564  }
4565 
4566  // grid colors (dataVersion>=41)
4567  bool hasColors = false;
4568  if (in.read((char*)&hasColors, 1) < 0) return ReadError();
4569 
4570  if (hasColors) {
4571  try {
4572  colors.resize(indexes.size());
4573  } catch (const std::bad_alloc&) {
4574  return MemoryError();
4575  }
4576 
4577  for (uint32_t j = 0; j < colors.size(); ++j) {
4578  if (in.read((char*)colors[j].rgb, 3) < 0) return ReadError();
4579  }
4580  }
4581 
4582  return true;
4583 }
4584 
4586  return std::max(static_cast<short>(41),
4587  sensorPosition.minimumFileVersion());
4588 }
4589 
4591  short minVersion =
4592  std::max(static_cast<short>(27),
4594  minVersion = std::max(
4596 
4597  if (m_rgbColors)
4598  minVersion = std::max(minVersion, m_rgbColors->minimumFileVersion());
4599  if (m_normals)
4600  minVersion = std::max(minVersion, m_normals->minimumFileVersion());
4601 
4602  if (hasScalarFields()) {
4603  for (auto& sf : m_scalarFields) {
4604  minVersion = std::max(
4605  minVersion,
4606  static_cast<ccScalarField*>(sf)
4607  ->minimumFileVersion()); // we have to test each
4608  // scalar field
4609  }
4610  }
4611 
4612  if (gridCount() != 0) {
4613  minVersion = std::max(minVersion, static_cast<short>(41));
4614  minVersion = std::max(
4615  minVersion, grid(0)->minimumFileVersion()); // we assume they
4616  // are all the same
4617  }
4618 
4619  if (hasFWF()) {
4620  minVersion = std::max(minVersion, static_cast<short>(44));
4621  if (!m_fwfDescriptors.empty()) {
4622  minVersion = std::max(
4623  minVersion,
4624  m_fwfDescriptors.begin()
4625  ->minimumFileVersion()); // we assume they are all
4626  // the same
4627  }
4628  if (!m_fwfWaveforms.empty()) {
4629  minVersion = std::max(
4630  minVersion,
4631  m_fwfWaveforms.front()
4632  .minimumFileVersion()); // we assume they are all
4633  // the same
4634  }
4635  }
4636 
4637  return minVersion;
4638 }
4639 
4641  short dataVersion,
4642  int flags,
4643  LoadedIDMap& oldToNewIDMap) {
4644  if (!ccGenericPointCloud::fromFile_MeOnly(in, dataVersion, flags,
4645  oldToNewIDMap))
4646  return false;
4647 
4648  // points array (dataVersion>=20)
4649  {
4650  bool result = false;
4651  bool fileCoordIsDouble =
4653  if (!fileCoordIsDouble &&
4654  sizeof(PointCoordinateType) ==
4655  8) // file is 'float' and current type is 'double'
4656  {
4658  CCVector3, 3, PointCoordinateType, float>(
4659  m_points, in, dataVersion, "3D points");
4660  } else if (fileCoordIsDouble &&
4661  sizeof(PointCoordinateType) ==
4662  4) // file is 'double' and current type is 'float'
4663  {
4665  CCVector3, 3, PointCoordinateType, double>(
4666  m_points, in, dataVersion, "3D points");
4667  } else {
4670  m_points, in, dataVersion, "3D points");
4671  }
4672  if (!result) {
4673  return false;
4674  }
4675 
4676 #ifdef QT_DEBUG
4677  // test: look for NaN values
4678  {
4679  unsigned nanPointsCount = 0;
4680  for (unsigned i = 0; i < size(); ++i) {
4681  if (point(i)->x != point(i)->x || point(i)->y != point(i)->y ||
4682  point(i)->z != point(i)->z) {
4683  *point(i) = CCVector3(0, 0, 0);
4684  ++nanPointsCount;
4685  }
4686  }
4687 
4688  if (nanPointsCount) {
4690  QString("[BIN] Cloud '%1' contains %2 NaN point(s)!")
4691  .arg(getName())
4692  .arg(nanPointsCount));
4693  }
4694  }
4695 #endif
4696  }
4697 
4698  // colors array (dataVersion>=20)
4699  {
4700  bool hasColorsArray = false;
4701  if (in.read((char*)&hasColorsArray, sizeof(bool)) < 0)
4702  return ReadError();
4703  if (hasColorsArray) {
4704  if (!m_rgbColors) {
4706  m_rgbColors->link();
4707  }
4708  CV_CLASS_ENUM classID = ReadClassIDFromFile(in, dataVersion);
4709  if (classID == CV_TYPES::RGB_COLOR_ARRAY) {
4710  if (!m_rgbColors->fromFile(in, dataVersion, flags,
4711  oldToNewIDMap)) {
4712  unallocateColors();
4713  return false;
4714  }
4715  } else if (classID == CV_TYPES::RGBA_COLOR_ARRAY) {
4716  QSharedPointer<RGBAColorsTableType> oldRGBAColors(
4717  new RGBAColorsTableType);
4718  if (!oldRGBAColors->fromFile(in, dataVersion, flags,
4719  oldToNewIDMap)) {
4720  return false;
4721  }
4722 
4723  size_t count = oldRGBAColors->size();
4724  if (!m_rgbColors->reserveSafe(count)) {
4725  unallocateColors();
4726  return MemoryError();
4727  }
4728 
4729  for (size_t i = 0; i < count; ++i) {
4731  oldRGBAColors->getValue(i)));
4732  }
4733  } else {
4734  return CorruptError();
4735  }
4736  }
4737  }
4738 
4739  // normals array (dataVersion>=20)
4740  {
4741  bool hasNormalsArray = false;
4742  if (in.read((char*)&hasNormalsArray, sizeof(bool)) < 0)
4743  return ReadError();
4744  if (hasNormalsArray) {
4745  if (!m_normals) {
4747  m_normals->link();
4748  }
4749  CV_CLASS_ENUM classID = ReadClassIDFromFile(in, dataVersion);
4750  if (classID != CV_TYPES::NORMAL_INDEXES_ARRAY)
4751  return CorruptError();
4752  if (!m_normals->fromFile(in, dataVersion, flags, oldToNewIDMap)) {
4753  unallocateNorms();
4754  return false;
4755  }
4756  }
4757  }
4758 
4759  // scalar field(s)
4760  {
4761  // number of scalar fields (dataVersion>=20)
4762  uint32_t sfCount = 0;
4763  if (in.read((char*)&sfCount, 4) < 0) return ReadError();
4764 
4765  // scalar fields (dataVersion>=20)
4766  for (uint32_t i = 0; i < sfCount; ++i) {
4767  ccScalarField* sf = new ccScalarField();
4768  if (!sf->fromFile(in, dataVersion, flags, oldToNewIDMap)) {
4769  sf->release();
4770  return false;
4771  }
4772  addScalarField(sf);
4773  }
4774 
4775  if (dataVersion < 27) {
4776  //'show NaN values in grey' state (27>dataVersion>=20)
4777  bool greyForNanScalarValues = true;
4778  if (in.read((char*)&greyForNanScalarValues, sizeof(bool)) < 0)
4779  return ReadError();
4780 
4781  // update all scalar fields accordingly (old way)
4782  for (unsigned i = 0; i < getNumberOfScalarFields(); ++i) {
4783  static_cast<ccScalarField*>(getScalarField(i))
4784  ->showNaNValuesInGrey(greyForNanScalarValues);
4785  }
4786  }
4787 
4788  //'show current sf color scale' state (dataVersion>=20)
4789  if (in.read((char*)&m_sfColorScaleDisplayed, sizeof(bool)) < 0)
4790  return ReadError();
4791 
4792  // Displayed scalar field index (dataVersion>=20)
4793  int32_t displayedScalarFieldIndex = 0;
4794  if (in.read((char*)&displayedScalarFieldIndex, 4) < 0)
4795  return ReadError();
4796  if (displayedScalarFieldIndex < (int32_t)sfCount)
4797  setCurrentDisplayedScalarField(displayedScalarFieldIndex);
4798  }
4799 
4800  // grid structures (dataVersion>=41)
4801  if (dataVersion >= 41) {
4802  // number of grids
4803  uint32_t count = 0;
4804  if (in.read((char*)&count, 4) < 0) return ReadError();
4805 
4806  // load each grid
4807  for (uint32_t i = 0; i < count; ++i) {
4808  Grid::Shared g(new Grid);
4809 
4810  if (!g->fromFile(in, dataVersion, flags, oldToNewIDMap)) {
4811  return false;
4812  }
4813 
4814  addGrid(g);
4815  }
4816  }
4817 
4818  // Waveforms (dataVersion >= 44)
4819  if (dataVersion >= 44) {
4820  bool withFWF = false;
4821  if (in.read((char*)&withFWF, sizeof(bool)) < 0) {
4822  return ReadError();
4823  }
4824  if (withFWF) {
4825  // first read the descriptors
4826  uint32_t descriptorCount = 0;
4827  if (in.read((char*)&descriptorCount, 4) < 0) {
4828  return ReadError();
4829  }
4830  for (uint32_t i = 0; i < descriptorCount; ++i) {
4831  // read the descriptor ID
4832  uint8_t id = 0;
4833  if (in.read((char*)&id, 1) < 0) {
4834  return ReadError();
4835  }
4836  // read the descriptor
4838  if (!d.fromFile(in, dataVersion, flags, oldToNewIDMap)) {
4839  return ReadError();
4840  }
4841  // add the descriptor to the set
4842  m_fwfDescriptors.insert(id, d);
4843  }
4844 
4845  // then the waveforms
4846  uint32_t waveformCount = 0;
4847  if (in.read((char*)&waveformCount, 4) < 0) {
4848  return ReadError();
4849  }
4850  assert(waveformCount >= size());
4851  try {
4852  m_fwfWaveforms.resize(waveformCount);
4853  } catch (const std::bad_alloc&) {
4854  return MemoryError();
4855  }
4856  for (uint32_t i = 0; i < waveformCount; ++i) {
4857  if (!m_fwfWaveforms[i].fromFile(in, dataVersion, flags,
4858  oldToNewIDMap)) {
4859  return ReadError();
4860  }
4861  }
4862 
4863  // eventually save the data
4864  uint64_t dataSize = 0;
4865  if (in.read((char*)&dataSize, 8) < 0) {
4866  return ReadError();
4867  }
4868  if (dataSize != 0) {
4869  FWFDataContainer* container = new FWFDataContainer;
4870  try {
4871  container->resize(dataSize);
4872  } catch (const std::bad_alloc&) {
4873  return MemoryError();
4874  }
4875  m_fwfData = SharedFWFDataContainer(container);
4876 
4877  if (in.read((char*)m_fwfData->data(), dataSize) < 0) {
4878  return ReadError();
4879  }
4880  }
4881  }
4882  }
4883 
4884  // notifyGeometryUpdate(); //FIXME: we can't call it now as the dependent
4885  // 'pointers' are not valid yet!
4886 
4887  // We should update the VBOs (just in case)
4888  releaseVBOs();
4889 
4890  return true;
4891 }
4892 
4895  return m_parent->getUniqueID();
4896  else
4897  return getUniqueID();
4898 }
4899 
4901  bool inside /*=true*/) {
4902  if (!box.isValid()) {
4903  CVLog::Warning("[ccPointCloud::crop] Invalid bounding-box");
4904  return nullptr;
4905  }
4906 
4907  unsigned count = size();
4908  if (count == 0) {
4909  CVLog::Warning("[ccPointCloud::crop] Cloud is empty!");
4910  return nullptr;
4911  }
4912 
4914  if (!ref->reserve(count)) {
4915  CVLog::Warning("[ccPointCloud::crop] Not enough memory!");
4916  delete ref;
4917  return nullptr;
4918  }
4919 
4920  for (unsigned i = 0; i < count; ++i) {
4921  const CCVector3* P = point(i);
4922  bool pointIsInside = box.contains(*P);
4923  if (inside == pointIsInside) {
4924  ref->addPointIndex(i);
4925  }
4926  }
4927 
4928  if (ref->size() == 0) {
4929  // no points inside selection!
4930  ref->clear(true);
4931  } else {
4932  ref->resize(ref->size());
4933  }
4934 
4935  return ref;
4936 }
4937 
4939  if (bbox.IsEmpty()) {
4940  CVLog::Warning("[ccPointCloud::crop] Invalid bounding-box");
4941  return nullptr;
4942  }
4943 
4944  unsigned count = size();
4945  if (count == 0) {
4946  CVLog::Warning("[ccPointCloud::crop] Cloud is empty!");
4947  return nullptr;
4948  }
4949 
4951  if (!ref->reserve(count)) {
4952  CVLog::Warning("[ccPointCloud::crop] Not enough memory!");
4953  delete ref;
4954  return nullptr;
4955  }
4956 
4957  std::vector<size_t> indices =
4959 
4960  for (size_t index : indices) {
4961  ref->addPointIndex(static_cast<unsigned int>(index));
4962  }
4963 
4964  if (ref->size() == 0) {
4965  // no points inside selection!
4966  ref->clear(true);
4967  } else {
4968  ref->resize(ref->size());
4969  }
4970 
4971  return ref;
4972 }
4973 
4976 }
4977 
4980 }
4981 
4983  unsigned char orthoDim,
4984  bool inside /*=true*/) {
4985  if (!poly) {
4986  CVLog::Warning("[ccPointCloud::crop2D] Invalid input polyline");
4987  return nullptr;
4988  }
4989  if (orthoDim > 2) {
4990  CVLog::Warning("[ccPointCloud::crop2D] Invalid input orthoDim");
4991  return nullptr;
4992  }
4993 
4994  unsigned count = size();
4995  if (count == 0) {
4996  CVLog::Warning("[ccPointCloud::crop] Cloud is empty!");
4997  return nullptr;
4998  }
4999 
5001  if (!ref->reserve(count)) {
5002  CVLog::Warning("[ccPointCloud::crop] Not enough memory!");
5003  delete ref;
5004  return nullptr;
5005  }
5006 
5007  unsigned char X = ((orthoDim + 1) % 3);
5008  unsigned char Y = ((X + 1) % 3);
5009 
5010  for (unsigned i = 0; i < count; ++i) {
5011  const CCVector3* P = point(i);
5012 
5013  CCVector2 P2D(P->u[X], P->u[Y]);
5014  bool pointIsInside =
5016  poly);
5017  if (inside == pointIsInside) {
5018  ref->addPointIndex(i);
5019  }
5020  }
5021 
5022  if (ref->size() == 0) {
5023  // no points inside selection!
5024  ref->clear(true);
5025  } else {
5026  ref->resize(ref->size());
5027  }
5028 
5029  return ref;
5030 }
5031 
5032 static bool CatchGLErrors(GLenum err, const char* context) {
5033  // catch GL errors
5034  {
5035  // see http://www.opengl.org/sdk/docs/man/xhtml/glGetError.xml
5036  switch (err) {
5037  case GL_NO_ERROR:
5038  return false;
5039  case GL_INVALID_ENUM:
5040  CVLog::Warning("[%s] OpenGL error: invalid enumerator",
5041  context);
5042  break;
5043  case GL_INVALID_VALUE:
5044  CVLog::Warning("[%s] OpenGL error: invalid value", context);
5045  break;
5046  case GL_INVALID_OPERATION:
5047  CVLog::Warning("[%s] OpenGL error: invalid operation", context);
5048  break;
5049  case GL_STACK_OVERFLOW:
5050  CVLog::Warning("[%s] OpenGL error: stack overflow", context);
5051  break;
5052  case GL_STACK_UNDERFLOW:
5053  CVLog::Warning("[%s] OpenGL error: stack underflow", context);
5054  break;
5055  case GL_OUT_OF_MEMORY:
5056  CVLog::Warning("[%s] OpenGL error: out of memory", context);
5057  break;
5058  case GL_INVALID_FRAMEBUFFER_OPERATION:
5060  "[%s] OpenGL error: invalid framebuffer operation",
5061  context);
5062  break;
5063  }
5064  }
5065 
5066  return true;
5067 }
5068 
5069 // DGM: normals are so slow that it's a waste of memory and time to load them in
5070 // VBOs!
5071 #define DONT_LOAD_NORMALS_IN_VBOS
5072 
5074  bool withColors,
5075  bool withNormals,
5076  bool* reallocated /*=0*/) {
5077  // required memory
5078  int totalSizeBytes = sizeof(PointCoordinateType) * count * 3;
5079  if (withColors) {
5080  rgbShift = totalSizeBytes;
5081  totalSizeBytes += sizeof(ColorCompType) * count * 3;
5082  }
5083  if (withNormals) {
5084  normalShift = totalSizeBytes;
5085  totalSizeBytes += sizeof(PointCoordinateType) * count * 3;
5086  }
5087 #if 0
5088 
5089  if (!isCreated())
5090  {
5091  if (!create())
5092  {
5093  //no message as it will probably happen on a lot on (old) graphic cards
5094  return -1;
5095  }
5096 
5097  setUsagePattern(QGLBuffer::DynamicDraw); //"StaticDraw: The data will be set once and used many times for drawing operations."
5098  //"DynamicDraw: The data will be modified repeatedly and used many times for drawing operations.
5099  }
5100 
5101  if (!bind())
5102  {
5103  CVLog::Warning("[ccPointCloud::VBO::init] Failed to bind VBO to active context!");
5104  destroy();
5105  return -1;
5106  }
5107 
5108  if (totalSizeBytes != size())
5109  {
5110  allocate(totalSizeBytes);
5111  if (reallocated)
5112  *reallocated = true;
5113 
5114  if (size() != totalSizeBytes)
5115  {
5116  CVLog::Warning("[ccPointCloud::VBO::init] Not enough (GPU) memory!");
5117  release();
5118  destroy();
5119  return -1;
5120  }
5121  }
5122  else
5123  {
5124  //nothing to do
5125  }
5126  release();
5127 #endif
5128 
5129  return totalSizeBytes;
5130 }
5131 
5133 #if 0
5135  return;
5136 
5137  // 'destroy' all vbos
5138  for (size_t i = 0; i < m_vboManager.vbos.size(); ++i)
5139  {
5140  if (m_vboManager.vbos[i])
5141  {
5142  m_vboManager.vbos[i]->destroy();
5143  delete m_vboManager.vbos[i];
5144  m_vboManager.vbos[i] = nullptr;
5145  }
5146  }
5147 
5148  m_vboManager.vbos.resize(0);
5149  m_vboManager.hasColors = false;
5150  m_vboManager.hasNormals = false;
5151  m_vboManager.colorIsSF = false;
5152  m_vboManager.sourceSF = nullptr;
5155 #endif
5156 }
5157 
5158 bool ccPointCloud::computeNormalsWithGrids(double minTriangleAngle_deg /*=1.0*/,
5159  ecvProgressDialog* pDlg /*=0*/) {
5160  unsigned pointCount = size();
5161  if (pointCount < 3) {
5162  CVLog::Warning(QString("[computeNormalsWithGrids] Cloud '%1' has not "
5163  "enough points")
5164  .arg(getName()));
5165  return false;
5166  }
5167 
5168  // we instantiate a temporary structure to store each vertex normal
5169  // (uncompressed)
5170  std::vector<CCVector3> theNorms;
5171  try {
5172  CCVector3 blankNorm(0, 0, 0);
5173  theNorms.resize(pointCount, blankNorm);
5174  } catch (const std::bad_alloc&) {
5175  CVLog::Warning("[ccMesh::computePerVertexNormals] Not enough memory!");
5176  return false;
5177  }
5178 
5179  // and we also reserve the memory for the (compressed) normals
5180  if (!hasNormals()) {
5181  if (!resizeTheNormsTable()) {
5182  CVLog::Warning("[computeNormalsWithGrids] Not enough memory");
5183  return false;
5184  }
5185  }
5186 
5187  // we hide normals during process
5188  showNormals(false);
5189 
5190  // progress dialog
5191  if (pDlg) {
5192  pDlg->setWindowTitle(QObject::tr("Normals computation"));
5193  pDlg->setAutoClose(false);
5194  pDlg->show();
5195  QCoreApplication::processEvents();
5196  }
5197 
5198  PointCoordinateType minAngleCos = static_cast<PointCoordinateType>(
5199  cos(cloudViewer::DegreesToRadians(minTriangleAngle_deg)));
5200  // double minTriangleAngle_rad =
5201  // cloudViewer::DegreesToRadians(minTriangleAngle_deg);
5202 
5203  // for each grid cell
5204  for (size_t gi = 0; gi < gridCount(); ++gi) {
5205  const Grid::Shared& scanGrid = grid(gi);
5206  if (scanGrid && scanGrid->indexes.empty()) {
5207  // empty grid, we skip it
5208  continue;
5209  }
5210  if (!scanGrid || scanGrid->h == 0 || scanGrid->w == 0 ||
5211  scanGrid->indexes.size() != scanGrid->h * scanGrid->w) {
5212  // invalid grid
5213  CVLog::Warning(QString("[computeNormalsWithGrids] Grid structure "
5214  "#%i is invalid")
5215  .arg(gi + 1));
5216  continue;
5217  }
5218 
5219  // progress dialog
5220  if (pDlg) {
5221  pDlg->setLabelText(QObject::tr("Grid: %1 x %2")
5222  .arg(scanGrid->w)
5223  .arg(scanGrid->h));
5224  pDlg->setValue(0);
5225  pDlg->setRange(0, static_cast<int>(scanGrid->indexes.size()));
5226  QCoreApplication::processEvents();
5227  }
5228 
5229  // the code below has been kindly provided by Romain Janvier
5230  CCVector3 sensorOrigin = CCVector3::fromArray(
5231  (scanGrid->sensorPosition
5232  .getTranslationAsVec3D() /* + m_globalShift*/)
5233  .u);
5234 
5235  for (int j = 0; j < static_cast<int>(scanGrid->h) - 1; ++j) {
5236  for (int i = 0; i < static_cast<int>(scanGrid->w) - 1; ++i) {
5237  // form the triangles with the nearest neighbors
5238  // and accumulate the corresponding normals
5239  const int& v0 = scanGrid->indexes[j * scanGrid->w + i];
5240  const int& v1 = scanGrid->indexes[j * scanGrid->w + (i + 1)];
5241  const int& v2 = scanGrid->indexes[(j + 1) * scanGrid->w + i];
5242  const int& v3 =
5243  scanGrid->indexes[(j + 1) * scanGrid->w + (i + 1)];
5244 
5245  bool topo[4] = {v0 >= 0, v1 >= 0, v2 >= 0, v3 >= 0};
5246 
5247  int mask = 0;
5248  int pixels = 0;
5249 
5250  for (int j = 0; j < 4; ++j) {
5251  if (topo[j]) {
5252  mask |= 1 << j;
5253  pixels += 1;
5254  }
5255  }
5256 
5257  if (pixels < 3) {
5258  continue;
5259  }
5260 
5261  Tuple3i tris[4] = {
5262  {v0, v2, v1}, {v0, v3, v1}, {v0, v2, v3}, {v1, v2, v3}};
5263 
5264  int tri[2] = {-1, -1};
5265 
5266  switch (mask) {
5267  case 7:
5268  tri[0] = 0;
5269  break;
5270  case 11:
5271  tri[0] = 1;
5272  break;
5273  case 13:
5274  tri[0] = 2;
5275  break;
5276  case 14:
5277  tri[0] = 3;
5278  break;
5279  case 15: {
5280  /* Choose the triangulation with smaller diagonal. */
5281  double d0 = (*getPoint(v0) - sensorOrigin).normd();
5282  double d1 = (*getPoint(v1) - sensorOrigin).normd();
5283  double d2 = (*getPoint(v2) - sensorOrigin).normd();
5284  double d3 = (*getPoint(v3) - sensorOrigin).normd();
5285  float ddiff1 = std::abs(d0 - d3);
5286  float ddiff2 = std::abs(d1 - d2);
5287  if (ddiff1 < ddiff2) {
5288  tri[0] = 1;
5289  tri[1] = 2;
5290  } else {
5291  tri[0] = 0;
5292  tri[1] = 3;
5293  }
5294  break;
5295  }
5296 
5297  default:
5298  continue;
5299  }
5300 
5301  for (int trCount = 0; trCount < 2; ++trCount) {
5302  int idx = tri[trCount];
5303  if (idx < 0) {
5304  continue;
5305  }
5306  const Tuple3i& t = tris[idx];
5307 
5308  const CCVector3* A = getPoint(t.u[0]);
5309  const CCVector3* B = getPoint(t.u[1]);
5310  const CCVector3* C = getPoint(t.u[2]);
5311 
5312  // now check the triangle angles
5313  if (minTriangleAngle_deg > 0) {
5314  CCVector3 uAB = (*B - *A);
5315  uAB.normalize();
5316  CCVector3 uCA = (*A - *C);
5317  uCA.normalize();
5318 
5319  PointCoordinateType cosA = -uCA.dot(uAB);
5320  if (cosA > minAngleCos) {
5321  continue;
5322  }
5323 
5324  CCVector3 uBC = (*C - *B);
5325  uBC.normalize();
5326  PointCoordinateType cosB = -uAB.dot(uBC);
5327  if (cosB > minAngleCos) {
5328  continue;
5329  }
5330 
5331  PointCoordinateType cosC = -uBC.dot(uCA);
5332  if (cosC > minAngleCos) {
5333  continue;
5334  }
5335  }
5336 
5337  // compute face normal (right hand rule)
5338  CCVector3 N = (*B - *A).cross(*C - *A);
5339 
5340  // we add this normal to all triangle vertices
5341  theNorms[t.u[0]] += N;
5342  theNorms[t.u[1]] += N;
5343  theNorms[t.u[2]] += N;
5344  }
5345  }
5346 
5347  if (pDlg) {
5348  // update progress dialog
5349  if (pDlg->wasCanceled()) {
5350  unallocateNorms();
5352  "[computeNormalsWithGrids] Process cancelled by "
5353  "user");
5354  return false;
5355  } else {
5356  pDlg->setValue(static_cast<unsigned>(j + 1) * scanGrid->w);
5357  }
5358  }
5359  }
5360  }
5361 
5362  // for each vertex
5363  {
5364  for (unsigned i = 0; i < pointCount; i++) {
5365  CCVector3& N = theNorms[i];
5366  // normalize the 'mean' normal
5367  N.normalize();
5368  setPointNormal(i, N);
5369  }
5370  }
5371 
5372  // We must update the VBOs
5374 
5375  // we restore the normals
5376  showNormals(true);
5377 
5378  return true;
5379 }
5380 
5382  unsigned pointCount = size();
5383  if (pointCount == 0) {
5384  CVLog::Warning(QString("[orientNormalsWithGrids] Cloud '%1' is empty")
5385  .arg(getName()));
5386  return false;
5387  }
5388 
5389  if (!hasNormals()) {
5391  QString("[orientNormalsWithGrids] Cloud '%1' has no normals")
5392  .arg(getName()));
5393  return false;
5394  }
5395  if (gridCount() == 0) {
5396  CVLog::Warning(QString("[orientNormalsWithGrids] Cloud '%1' has no "
5397  "associated grid scan")
5398  .arg(getName()));
5399  return false;
5400  }
5401 
5402  // progress dialog
5403  if (pDlg) {
5404  pDlg->setWindowTitle(QObject::tr("Orienting normals"));
5405  pDlg->setLabelText(QObject::tr("Points: %L1").arg(pointCount));
5406  pDlg->setRange(0, static_cast<int>(pointCount));
5407  pDlg->show();
5408  QCoreApplication::processEvents();
5409  }
5410 
5411  // for each grid cell
5412  int progressIndex = 0;
5413  for (size_t gi = 0; gi < gridCount(); ++gi) {
5414  const ccPointCloud::Grid::Shared& scanGrid = grid(gi);
5415  if (scanGrid && scanGrid->indexes.empty()) {
5416  // empty grid, we skip it
5417  continue;
5418  }
5419  if (!scanGrid || scanGrid->h == 0 || scanGrid->w == 0 ||
5420  scanGrid->indexes.size() != scanGrid->h * scanGrid->w) {
5421  // invalid grid
5422  CVLog::Warning(QString("[orientNormalsWithGrids] Grid structure "
5423  "#%i is invalid")
5424  .arg(gi + 1));
5425  continue;
5426  }
5427 
5428  // ccGLMatrixd toSensorCS = scanGrid->sensorPosition.inverse();
5429  CCVector3 sensorOrigin = CCVector3::fromArray(
5430  (scanGrid->sensorPosition
5431  .getTranslationAsVec3D() /* + m_globalShift*/)
5432  .u);
5433 
5434  const int* _indexGrid = scanGrid->indexes.data();
5435  for (int j = 0; j < static_cast<int>(scanGrid->h); ++j) {
5436  for (int i = 0; i < static_cast<int>(scanGrid->w);
5437  ++i, ++_indexGrid) {
5438  if (*_indexGrid >= 0) {
5439  unsigned pointIndex = static_cast<unsigned>(*_indexGrid);
5440  assert(pointIndex <= pointCount);
5441  const CCVector3* P = getPoint(pointIndex);
5442  // CCVector3 PinSensorCS = toSensorCS * (*P);
5443 
5444  CCVector3 N = getPointNormal(pointIndex);
5445 
5446  // check normal vector sign
5447  // CCVector3 NinSensorCS(N);
5448  // toSensorCS.applyRotation(NinSensorCS);
5449  CCVector3 OP = *P - sensorOrigin;
5450  OP.normalize();
5451  PointCoordinateType dotProd = OP.dot(N);
5452  if (dotProd > 0) {
5453  N = -N;
5454  setPointNormalIndex(pointIndex,
5456  }
5457 
5458  if (pDlg) {
5459  // update progress dialog
5460  if (pDlg->wasCanceled()) {
5461  unallocateNorms();
5463  "[orientNormalsWithGrids] Process "
5464  "cancelled by user");
5465  return false;
5466  } else {
5467  pDlg->setValue(++progressIndex);
5468  }
5469  }
5470  }
5471  }
5472  }
5473  }
5474 
5475  return true;
5476 }
5477 
5479  ecvProgressDialog* pDlg) {
5480  int progressIndex = 0;
5481  for (unsigned pointIndex = 0; pointIndex < m_points.capacity();
5482  ++pointIndex) {
5483  const CCVector3* P = getPoint(pointIndex);
5484  CCVector3 N = getPointNormal(pointIndex);
5485  CCVector3 OP = *P - VP;
5486  OP.normalize();
5487  PointCoordinateType dotProd = OP.dot(N);
5488  if (dotProd > 0) {
5489  N = -N;
5491  }
5492 
5493  if (pDlg) {
5494  // update progress dialog
5495  if (pDlg->wasCanceled()) {
5496  unallocateNorms();
5498  "[orientNormalsWithSensors] Process cancelled by user");
5499  return false;
5500  } else {
5501  pDlg->setValue(++progressIndex);
5502  }
5503  }
5504  }
5505  return true;
5506 }
5507 
5509  CV_LOCAL_MODEL_TYPES model,
5510  ccNormalVectors::Orientation preferredOrientation,
5511  PointCoordinateType defaultRadius,
5512  ecvProgressDialog* pDlg /*=nullptr*/) {
5513  // compute the normals the 'old' way ;)
5514  if (!getOctree() && !computeOctree(pDlg)) {
5515  CVLog::Warning(QString("[computeNormals] Could not compute octree for "
5516  "cloud '%1'")
5517  .arg(getName()));
5518  return false;
5519  }
5520 
5521  // computes cloud normals
5522  QElapsedTimer eTimer;
5523  eTimer.start();
5524  NormsIndexesTableType* normsIndexes = new NormsIndexesTableType;
5526  this, *normsIndexes, model, defaultRadius, preferredOrientation,
5527  static_cast<cloudViewer::GenericProgressCallback*>(pDlg),
5528  getOctree().data())) {
5529  CVLog::Warning(QString("[computeNormals] Failed to compute normals on "
5530  "cloud '%1'")
5531  .arg(getName()));
5532  return false;
5533  }
5534 
5535  CVLog::Print("[ComputeCloudNormals] Timing: %3.2f s.",
5536  eTimer.elapsed() / 1000.0);
5537 
5538  if (!hasNormals()) {
5539  if (!resizeTheNormsTable()) {
5540  CVLog::Error(QString("Not enough memory to compute normals on "
5541  "cloud '%1'")
5542  .arg(getName()));
5543  normsIndexes->release();
5544  return false;
5545  }
5546  }
5547 
5548  // we hide normals during process
5549  showNormals(false);
5550 
5551  // compress the normals
5552  {
5553  for (unsigned j = 0; j < normsIndexes->currentSize(); j++) {
5554  setPointNormalIndex(j, normsIndexes->getValue(j));
5555  }
5556  }
5557 
5558  // we don't need this anymore...
5559  normsIndexes->release();
5560  normsIndexes = nullptr;
5561 
5562  // we restore the normals
5563  showNormals(true);
5564 
5565  return true;
5566 }
5567 
5568 bool ccPointCloud::orientNormalsWithMST(unsigned kNN /*=6*/,
5569  ecvProgressDialog* pDlg /*=0*/) {
5571  pDlg);
5572 }
5573 
5574 bool ccPointCloud::orientNormalsWithFM(unsigned char level,
5575  ecvProgressDialog* pDlg /*=0*/) {
5576  return ccFastMarchingForNormsDirection::OrientNormals(this, level, pDlg);
5577 }
5578 
5580  for (size_t i = 0; i < m_children.size(); ++i) {
5581  ccHObject* child = m_children[i];
5582  if (child && child->isKindOf(CV_TYPES::SENSOR)) {
5583  return true;
5584  }
5585  }
5586 
5587  return false;
5588 }
5589 
5590 unsigned char ccPointCloud::testVisibility(const CCVector3& P) const {
5592  // if we have associated sensors, we can use them to check the
5593  // visibility of other points
5594  unsigned char bestVisibility = 255;
5595  for (size_t i = 0; i < m_children.size(); ++i) {
5596  ccHObject* child = m_children[i];
5597  if (child && child->isA(CV_TYPES::GBL_SENSOR)) {
5598  ccGBLSensor* sensor = static_cast<ccGBLSensor*>(child);
5599  unsigned char visibility = sensor->checkVisibility(P);
5600 
5601  if (visibility == POINT_VISIBLE)
5602  return POINT_VISIBLE;
5603  else if (visibility < bestVisibility)
5604  bestVisibility = visibility;
5605  }
5606  }
5607  if (bestVisibility != 255) return bestVisibility;
5608  }
5609 
5610  return POINT_VISIBLE;
5611 }
5612 
5614  if (!m_lod) {
5615  m_lod = new ccPointCloudLOD;
5616  }
5617  return m_lod->init(this);
5618 }
5619 
5621  if (m_lod) {
5622  m_lod->clear();
5623  }
5624 }
5625 
5627  m_fwfWaveforms.resize(0);
5628  m_fwfDescriptors.clear();
5629 }
5630 
5632  double& maxVal,
5633  ecvProgressDialog* pDlg /*=0*/) const {
5634  minVal = maxVal = 0;
5635 
5636  if (size() != m_fwfWaveforms.size()) {
5637  return false;
5638  }
5639 
5640  // progress dialog
5642  pDlg, static_cast<unsigned>(m_fwfWaveforms.size()));
5643  if (pDlg) {
5644  pDlg->setWindowTitle(QObject::tr("FWF amplitude"));
5645  pDlg->setLabelText(
5646  QObject::tr("Determining min and max FWF values\nPoints: ") +
5647  QString::number(m_fwfWaveforms.size()));
5648  pDlg->show();
5649  QCoreApplication::processEvents();
5650  }
5651 
5652  // for all waveforms
5653  bool firstTest = true;
5654  for (unsigned i = 0; i < size(); ++i) {
5655  if (pDlg && !nProgress.oneStep()) {
5656  return false;
5657  }
5658 
5659  ccWaveformProxy proxy = waveformProxy(i);
5660  if (!proxy.isValid()) {
5661  continue;
5662  }
5663 
5664  double wMinVal, wMaxVal;
5665  proxy.getRange(wMinVal, wMaxVal);
5666 
5667  if (firstTest) {
5668  minVal = wMinVal;
5669  maxVal = wMaxVal;
5670  firstTest = false;
5671  } else {
5672  if (wMaxVal > maxVal) {
5673  maxVal = wMaxVal;
5674  }
5675  if (wMinVal < minVal) {
5676  minVal = wMinVal;
5677  }
5678  }
5679  }
5680 
5681  return !firstTest;
5682 }
5683 
5685  int sfIdx,
5686  bool useCustomIntensityRange /*=false*/,
5687  double minI /*=0.0*/,
5688  double maxI /*=1.0*/) {
5690  if (!sf || !hasColors()) {
5691  // invalid input
5692  assert(false);
5693  return false;
5694  }
5695 
5696  // apply Broovey transform to each point (color)
5697  if (!useCustomIntensityRange) {
5698  minI = sf->getMin();
5699  maxI = sf->getMax();
5700  }
5701 
5702  double intRange = maxI - minI;
5703  if (intRange < 1.0e-6) {
5705  "[ccPointCloud::enhanceRGBWithIntensitySF] Intensity range is "
5706  "too small");
5707  return false;
5708  }
5709 
5710  for (unsigned i = 0; i < size(); ++i) {
5711  ecvColor::Rgb& col = m_rgbColors->at(i);
5712 
5713  // current intensity (x3)
5714  int I = static_cast<int>(col.r) + static_cast<int>(col.g) +
5715  static_cast<int>(col.b);
5716  if (I == 0) {
5717  continue; // black remains black!
5718  }
5719  // new intensity
5720  double newI = 255 * ((sf->getValue(i) - minI) / intRange); // in [0 ;
5721  // 1]
5722  // scale factor
5723  double scale = (3 * newI) / I;
5724 
5725  col.r = static_cast<ColorCompType>(std::max<ScalarType>(
5726  std::min<ScalarType>(scale * col.r, 255), 0));
5727  col.g = static_cast<ColorCompType>(std::max<ScalarType>(
5728  std::min<ScalarType>(scale * col.g, 255), 0));
5729  col.b = static_cast<ColorCompType>(std::max<ScalarType>(
5730  std::min<ScalarType>(scale * col.b, 255), 0));
5731  }
5732 
5733  // We must update the VBOs
5735 
5736  return true;
5737 }
5738 
5740  const Grid& grid, double minTriangleAngle_deg /*=0.0*/) const {
5741  // the code below has been kindly provided by Romain Janvier
5742  CCVector3 sensorOrigin = CCVector3::fromArray(
5743  (grid.sensorPosition.getTranslationAsVec3D() /* + m_globalShift*/)
5744  .u);
5745 
5746  ccMesh* mesh = new ccMesh(const_cast<ccPointCloud*>(this));
5747  mesh->setName("Grid mesh");
5748  if (!mesh->reserve(grid.h * grid.w * 2)) {
5749  CVLog::Warning("[ccPointCloud::triangulateGrid] Not enough memory");
5750  return nullptr;
5751  }
5752 
5753  PointCoordinateType minAngleCos = static_cast<PointCoordinateType>(
5754  cos(cloudViewer::DegreesToRadians(minTriangleAngle_deg)));
5755  // double minTriangleAngle_rad =
5756  // cloudViewer::DegreesToRadians(minTriangleAngle_deg);
5757 
5758  for (int j = 0; j < static_cast<int>(grid.h) - 1; ++j) {
5759  for (int i = 0; i < static_cast<int>(grid.w) - 1; ++i) {
5760  const int& v0 = grid.indexes[j * grid.w + i];
5761  const int& v1 = grid.indexes[j * grid.w + (i + 1)];
5762  const int& v2 = grid.indexes[(j + 1) * grid.w + i];
5763  const int& v3 = grid.indexes[(j + 1) * grid.w + (i + 1)];
5764 
5765  bool topo[4] = {v0 >= 0, v1 >= 0, v2 >= 0, v3 >= 0};
5766 
5767  int mask = 0;
5768  int pixels = 0;
5769 
5770  for (int j = 0; j < 4; ++j) {
5771  if (topo[j]) {
5772  mask |= 1 << j;
5773  pixels += 1;
5774  }
5775  }
5776 
5777  if (pixels < 3) {
5778  continue;
5779  }
5780 
5781  Tuple3i tris[4] = {
5782  {v0, v2, v1}, {v0, v3, v1}, {v0, v2, v3}, {v1, v2, v3}};
5783 
5784  int tri[2] = {-1, -1};
5785 
5786  switch (mask) {
5787  case 7:
5788  tri[0] = 0;
5789  break;
5790  case 11:
5791  tri[0] = 1;
5792  break;
5793  case 13:
5794  tri[0] = 2;
5795  break;
5796  case 14:
5797  tri[0] = 3;
5798  break;
5799  case 15: {
5800  /* Choose the triangulation with smaller diagonal. */
5801  double d0 = (*getPoint(v0) - sensorOrigin).normd();
5802  double d1 = (*getPoint(v1) - sensorOrigin).normd();
5803  double d2 = (*getPoint(v2) - sensorOrigin).normd();
5804  double d3 = (*getPoint(v3) - sensorOrigin).normd();
5805  float ddiff1 = std::abs(d0 - d3);
5806  float ddiff2 = std::abs(d1 - d2);
5807  if (ddiff1 < ddiff2) {
5808  tri[0] = 1;
5809  tri[1] = 2;
5810  } else {
5811  tri[0] = 0;
5812  tri[1] = 3;
5813  }
5814  break;
5815  }
5816 
5817  default:
5818  continue;
5819  }
5820 
5821  for (int trCount = 0; trCount < 2; ++trCount) {
5822  int idx = tri[trCount];
5823  if (idx < 0) {
5824  continue;
5825  }
5826  const Tuple3i& t = tris[idx];
5827 
5828  // now check the triangle angles
5829  if (minTriangleAngle_deg > 0) {
5830  const CCVector3* A = getPoint(t.u[0]);
5831  const CCVector3* B = getPoint(t.u[1]);
5832  const CCVector3* C = getPoint(t.u[2]);
5833 
5834  CCVector3 uAB = (*B - *A);
5835  uAB.normalize();
5836  CCVector3 uCA = (*A - *C);
5837  uCA.normalize();
5838 
5839  PointCoordinateType cosA = -uCA.dot(uAB);
5840  if (cosA > minAngleCos) {
5841  continue;
5842  }
5843 
5844  CCVector3 uBC = (*C - *B);
5845  uBC.normalize();
5846  PointCoordinateType cosB = -uAB.dot(uBC);
5847  if (cosB > minAngleCos) {
5848  continue;
5849  }
5850 
5851  PointCoordinateType cosC = -uBC.dot(uCA);
5852  if (cosC > minAngleCos) {
5853  continue;
5854  }
5855  }
5856 
5857  mesh->addTriangle(t.u[0], t.u[1], t.u[2]);
5858  }
5859  }
5860  }
5861 
5862  if (mesh->size() == 0) {
5863  delete mesh;
5864  mesh = nullptr;
5865  } else {
5866  mesh->shrinkToFit();
5867  mesh->showColors(colorsShown());
5868  mesh->showSF(sfShown());
5869  mesh->showNormals(normalsShown());
5870  // mesh->setEnabled(isEnabled());
5871  }
5872 
5873  return mesh;
5874 };
5875 
5876 bool ccPointCloud::exportCoordToSF(bool exportDims[3]) {
5877  if (!exportDims[0] && !exportDims[1] && !exportDims[2]) {
5878  // nothing to do?!
5879  assert(false);
5880  return true;
5881  }
5882 
5883  const QString defaultSFName[3] = {"Coord. X", "Coord. Y", "Coord. Z"};
5884 
5885  unsigned ptsCount = size();
5886 
5887  // test each dimension
5888  for (unsigned d = 0; d < 3; ++d) {
5889  if (!exportDims[d]) {
5890  continue;
5891  }
5892 
5893  int sfIndex = getScalarFieldIndexByName(qPrintable(defaultSFName[d]));
5894  if (sfIndex < 0) {
5895  sfIndex = addScalarField(qPrintable(defaultSFName[d]));
5896  }
5897  if (sfIndex < 0) {
5899  "[ccPointCloud::exportCoordToSF] Not enough memory!");
5900  return false;
5901  }
5902 
5904  if (!sf) {
5905  assert(false);
5906  return false;
5907  }
5908 
5909  for (unsigned k = 0; k < ptsCount; ++k) {
5910  ScalarType s = static_cast<ScalarType>(getPoint(k)->u[d]);
5911  sf->setValue(k, s);
5912  }
5913  sf->computeMinAndMax();
5914 
5916  showSF(true);
5917  }
5918 
5919  return true;
5920 }
5921 
5922 bool ccPointCloud::setCoordFromSF(bool importDims[3],
5924  PointCoordinateType defaultValueForNaN) {
5925  unsigned pointCount = size();
5926 
5927  if (!sf || sf->size() < pointCount) {
5928  CVLog::Error("Invalid scalar field");
5929  return false;
5930  }
5931 
5932  for (unsigned i = 0; i < pointCount; ++i) {
5933  CCVector3& P = m_points[i];
5934  ScalarType s = sf->getValue(i);
5935 
5936  // handle NaN values
5937  PointCoordinateType coord =
5939  ? static_cast<PointCoordinateType>(s)
5940  : defaultValueForNaN;
5941 
5942  // test each dimension
5943  if (importDims[0]) P.x = coord;
5944  if (importDims[1]) P.y = coord;
5945  if (importDims[2]) P.z = coord;
5946  }
5947 
5949 
5950  return true;
5951 }
5952 
5953 bool ccPointCloud::exportNormalToSF(bool exportDims[3]) {
5954  if (!exportDims[0] && !exportDims[1] && !exportDims[2]) {
5955  // nothing to do?!
5956  assert(false);
5957  return true;
5958  }
5959 
5960  if (!hasNormals()) {
5961  CVLog::Warning("Cloud has no normals");
5962  return false;
5963  }
5964 
5965  const QString defaultSFName[3] = {"Nx", "Ny", "Nz"};
5966 
5967  unsigned ptsCount = static_cast<unsigned>(m_normals->size());
5968 
5969  // test each dimension
5970  for (unsigned d = 0; d < 3; ++d) {
5971  if (!exportDims[d]) {
5972  continue;
5973  }
5974 
5975  int sfIndex = getScalarFieldIndexByName(qPrintable(defaultSFName[d]));
5976  if (sfIndex < 0) {
5977  sfIndex = addScalarField(qPrintable(defaultSFName[d]));
5978  }
5979  if (sfIndex < 0) {
5981  "[ccPointCloud::exportNormalToSF] Not enough memory!");
5982  return false;
5983  }
5984 
5986  if (!sf) {
5987  assert(false);
5988  return false;
5989  }
5990 
5991  for (unsigned k = 0; k < ptsCount; ++k) {
5992  ScalarType s = static_cast<ScalarType>(getPointNormal(k).u[d]);
5993  sf->setValue(k, s);
5994  }
5995  sf->computeMinAndMax();
5996 
5998  showSF(true);
5999  }
6000 
6001  return true;
6002 }
6003 
6004 std::shared_ptr<ccPointCloud> ccPointCloud::SelectByIndex(
6005  const std::vector<size_t>& indices, bool invert /* = false */) const {
6006  auto output = std::make_shared<ccPointCloud>("pointCloud");
6007  bool has_normals = hasNormals();
6008  bool has_colors = hasColors();
6009  bool has_covariance = HasCovariances();
6010  bool has_fwf = hasFWF();
6011 
6012  unsigned int n = size();
6013 
6014  unsigned int out_n = invert ? static_cast<unsigned int>(n - indices.size())
6015  : static_cast<unsigned int>(indices.size());
6016 
6017  std::vector<bool> mask = std::vector<bool>(n, invert);
6018  for (size_t i : indices) {
6019  mask[i] = !invert;
6020  }
6021 
6022  // visibility
6023  output->setVisible(isVisible());
6024  output->setEnabled(isEnabled());
6025 
6026  // other parameters
6027  output->importParametersFrom(this);
6028 
6029  // from now on we will need some points to proceed ;)
6030  if (n) {
6031  if (!output->reserveThePointsTable(out_n)) {
6032  CVLog::Error(
6033  "[ccPointCloud::SelectByIndex] Not enough memory to "
6034  "duplicate cloud!");
6035  return nullptr;
6036  }
6037 
6038  // RGB colors
6039  if (has_colors) {
6040  if (output->reserveTheRGBTable()) {
6041  output->showColors(colorsShown());
6042  } else {
6044  "[ccPointCloud::SelectByIndex] Not enough memory to "
6045  "copy RGB colors!");
6046  has_colors = false;
6047  }
6048  }
6049 
6050  // Normals
6051  if (has_normals) {
6052  if (output->reserveTheNormsTable()) {
6053  output->showNormals(normalsShown());
6054  } else {
6056  "[ccPointCloud::SelectByIndex] Not enough memory to "
6057  "copy normals!");
6058  has_normals = false;
6059  }
6060  }
6061 
6062  // waveform
6063  if (has_fwf) {
6064  if (output->reserveTheFWFTable()) {
6065  try {
6066  // we will use the same FWF data container
6067  output->fwfData() = fwfData();
6068  } catch (const std::bad_alloc&) {
6070  "[ccPointCloud::SelectByIndex] Not enough memory "
6071  "to copy waveform signals!");
6072  output->clearFWFData();
6073  has_fwf = false;
6074  }
6075  } else {
6077  "[ccPointCloud::SelectByIndex] Not enough memory to "
6078  "copy waveform signals!");
6079  has_fwf = false;
6080  }
6081  }
6082 
6083  // Import
6084  for (unsigned i = 0; i < n; i++) {
6085  if (mask[i]) {
6086  // import points
6087  output->addPoint(*getPointPersistentPtr(i));
6088 
6089  // import colors
6090  if (has_colors) {
6091  output->addRGBColor(getPointColor(i));
6092  }
6093 
6094  // import normals
6095  if (has_normals) {
6096  output->addNorm(getPointNormal(i));
6097  }
6098 
6099  if (has_covariance) {
6100  output->covariances_.push_back(covariances_[i]);
6101  }
6102 
6103  // import waveform
6104  if (has_fwf) {
6105  const ccWaveform& w = m_fwfWaveforms[i];
6106  if (!output->fwfDescriptors().contains(w.descriptorID())) {
6107  // copy only the necessary descriptors
6108  output->fwfDescriptors().insert(
6109  w.descriptorID(),
6111  }
6112  output->waveforms().push_back(w);
6113  }
6114  }
6115  }
6116 
6117  // scalar fields
6118  unsigned sfCount = getNumberOfScalarFields();
6119  if (sfCount != 0) {
6120  for (unsigned k = 0; k < sfCount; ++k) {
6121  const ccScalarField* sf =
6122  static_cast<ccScalarField*>(getScalarField(k));
6123  assert(sf);
6124  if (sf) {
6125  // we create a new scalar field with same name
6126  int sfIdx = output->addScalarField(sf->getName());
6127  if (sfIdx >= 0) // success
6128  {
6129  ccScalarField* currentScalarField =
6130  static_cast<ccScalarField*>(
6131  output->getScalarField(sfIdx));
6132  assert(currentScalarField);
6133  if (currentScalarField->reserveSafe(out_n)) {
6134  currentScalarField->setGlobalShift(
6135  sf->getGlobalShift());
6136 
6137  // we copy data to new SF
6138  for (unsigned i = 0; i < n; i++) {
6139  if (mask[i]) {
6140  currentScalarField->addElement(
6141  sf->getValue(i));
6142  }
6143  }
6144 
6145  currentScalarField->computeMinAndMax();
6146  // copy display parameters
6147  currentScalarField->importParametersFrom(sf);
6148  } else {
6149  // if we don't have enough memory, we cancel SF
6150  // creation
6151  output->deleteScalarField(sfIdx);
6153  QString("[ccPointCloud::SelectByIndex] Not "
6154  "enough memory to copy scalar "
6155  "field '%1'!")
6156  .arg(sf->getName()));
6157  }
6158  }
6159  }
6160  }
6161 
6162  unsigned copiedSFCount = getNumberOfScalarFields();
6163  if (copiedSFCount) {
6164  // we display the same scalar field as the source (if we managed
6165  // to copy it!)
6167  int sfIdx = output->getScalarFieldIndexByName(
6169  if (sfIdx >= 0)
6170  output->setCurrentDisplayedScalarField(sfIdx);
6171  else
6172  output->setCurrentDisplayedScalarField(
6173  static_cast<int>(copiedSFCount) - 1);
6174  }
6175  // copy visibility
6176  output->showSF(sfShown());
6177  }
6178  }
6179 
6180  // scan grids
6181  if (gridCount() != 0) {
6182  try {
6183  // we need a map between old and new indexes
6184  std::vector<int> newIndexMap(size(), -1);
6185  {
6186  for (unsigned i = 0; i < out_n; i++) {
6187  newIndexMap[i] = i;
6188  }
6189  }
6190 
6191  // duplicate the grid structure(s)
6192  std::vector<Grid::Shared> newGrids;
6193  {
6194  for (size_t i = 0; i < gridCount(); ++i) {
6195  const Grid::Shared& scanGrid = grid(i);
6196  if (scanGrid->validCount !=
6197  0) // no need to copy empty grids!
6198  {
6199  // duplicate the grid
6200  newGrids.push_back(
6201  Grid::Shared(new Grid(*scanGrid)));
6202  }
6203  }
6204  }
6205 
6206  // then update the indexes
6207  UpdateGridIndexes(newIndexMap, newGrids);
6208 
6209  // and keep the valid (non empty) ones
6210  for (Grid::Shared& scanGrid : newGrids) {
6211  if (scanGrid->validCount) {
6212  output->addGrid(scanGrid);
6213  }
6214  }
6215  } catch (const std::bad_alloc&) {
6216  // not enough memory
6218  QString("[ccPointCloud::SelectByIndex] Not enough "
6219  "memory to copy the grid structure(s)"));
6220  }
6221  }
6222  }
6223 
6225  "ccPointCloud down sampled from {:d} points to {:d} points.",
6226  (int)size(), (int)output->size());
6227 
6228  return output;
6229 }
constexpr unsigned char POINT_VISIBLE
Definition: CVConst.h:92
CV_LOCAL_MODEL_TYPES
Definition: CVConst.h:121
constexpr unsigned char POINT_HIDDEN
Definition: CVConst.h:94
constexpr double M_PI
Pi.
Definition: CVConst.h:19
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
Definition: CVConst.h:76
constexpr double EPSILON_VALUE
Definition: CVConst.h:87
constexpr float MAX_POINT_SIZE_F
Definition: CVConst.h:79
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
Definition: CVTypes.h:97
std::shared_ptr< core::Tensor > image
double normal[3]
bool has_colors
std::string name
bool has_normals
int height
int count
int offset
int points
math::float4 color
void * X
Definition: SmallVector.cpp:45
core::Tensor result
Definition: VtkUtils.cpp:76
virtual void link()
Increase counter.
Definition: CVShareable.cpp:33
virtual void release()
Decrease counter and deletes object when 0.
Definition: CVShareable.cpp:35
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Array of RGB colors for each point.
Array of compressed 3D normals (single index)
Array of RGBA colors for each point.
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
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
Type dot(const Vector3Tpl &v) const
Dot product.
Definition: CVGeom.h:408
Type norm() const
Returns vector norm.
Definition: CVGeom.h:424
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
static void vadd(const PointCoordinateType p[], const PointCoordinateType q[], PointCoordinateType r[])
Definition: CVGeom.h:544
Waveform descriptor.
Definition: ecvWaveform.h:23
bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads data from binary stream.
Definition: ecvWaveform.cpp:52
2D label (typically attached to points)
Definition: ecv2DLabel.h:22
bool addPickedPoint(ccGenericPointCloud *cloud, unsigned pointIndex, bool entityCenter=false)
Adds a point to this label.
Definition: ecv2DLabel.cpp:400
const PickedPoint & getPickedPoint(unsigned index) const
Returns a given point.
Definition: ecv2DLabel.h:194
bool isCollapsed() const
Returns whether the label is collapsed or not.
Definition: ecv2DLabel.h:105
const float * getPosition() const
Returns relative position.
Definition: ecv2DLabel.h:63
void setCollapsed(bool state)
Whether to collapse label or not.
Definition: ecv2DLabel.h:102
bool isPointLegendDisplayed() const
Returns whether the point(s) legend is displayed.
Definition: ecv2DLabel.h:111
void setDisplayedIn2D(bool state)
Whether to display the label in 2D.
Definition: ecv2DLabel.h:114
unsigned size() const
Returns current size.
Definition: ecv2DLabel.h:74
void setPosition(float x, float y)
Sets relative position.
Definition: ecv2DLabel.cpp:225
bool isDisplayedIn2D() const
Returns whether the label is displayed in 2D.
Definition: ecv2DLabel.h:117
virtual QString getName() const override
Returns object name.
Definition: ecv2DLabel.cpp:196
void displayPointLegend(bool state)
Whether to display the point(s) legend (title only)
Definition: ecv2DLabel.h:108
Type & getValue(size_t index)
Definition: ecvArray.h:100
void fill(const Type &value)
Definition: ecvArray.h:106
bool isAllocated() const
Returns whether some memory has been allocated or not.
Definition: ecvArray.h:67
bool resizeSafe(size_t count, bool initNewElements=false, const Type *valueForNewElements=nullptr)
Resizes memory (no exception thrown)
Definition: ecvArray.h:70
bool reserveSafe(size_t count)
Reserves memory (no exception thrown)
Definition: ecvArray.h:56
void setValue(size_t index, const Type &value)
Definition: ecvArray.h:102
unsigned currentSize() const
Definition: ecvArray.h:112
void addElement(const Type &value)
Definition: ecvArray.h:105
void swap(size_t i1, size_t i2)
Definition: ecvArray.h:121
void clear(bool releaseMemory=false)
Definition: ecvArray.h:115
Bounding box structure.
Definition: ecvBBox.h:25
static ccBBox CreateFromPoints(const std::vector< CCVector3 > &points)
Definition: ecvBBox.cpp:82
QSharedPointer< ccColorScale > Shared
Shared pointer type.
Definition: ecvColorScale.h:74
virtual bool colorsShown() const
Returns whether colors are shown or not.
virtual bool isVisible() const
Returns whether entity is visible or not.
virtual bool hasColors() const
Returns whether colors are enabled or not.
virtual void setVisible(bool state)
Sets entity visibility.
virtual bool sfShown() const
Returns whether active scalar field is visible.
virtual bool normalsShown() const
Returns whether normals are shown or not.
virtual bool hasNormals() const
Returns whether normals are enabled or not.
virtual bool isColorOverridden() const
virtual void enableTempColor(bool state)
Set temporary color activation state.
virtual void showNormals(bool state)
Sets normals visibility.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
ecvColor::Rgb m_tempColor
Temporary (unique) color.
static int OrientNormals(ccPointCloud *theCloud, unsigned char octreeLevel, ecvProgressDialog *progressCb=nullptr)
Static entry point (helper)
Ground-based Laser sensor.
Definition: ecvGBLSensor.h:26
PointCoordinateType getSensorRange() const
Returns the sensor max. range.
Definition: ecvGBLSensor.h:137
unsigned char checkVisibility(const CCVector3 &P) const override
void setSensorRange(PointCoordinateType range)
Sets the sensor max. range.
Definition: ecvGBLSensor.h:142
static ccGLMatrixTpl< float > FromEigenMatrix(const Eigen::Matrix< double, 4, 4 > &mat)
void applyRotation(Vector3Tpl< float > &vec) const
Applies rotation only to a 3D vector (in place) - float version.
T * data()
Returns a pointer to internal data.
void shiftRotationCenter(const Vector3Tpl< T > &vec)
Shifts rotation center.
void setRotation(const float Rt[9])
Sets Rotation from a float array.
void apply(float vec[3]) const
Applies transformation to a 3D vector (in place) - float version.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
virtual void toIdentity()
Sets matrix to identity.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
void showNormals(bool state) override
Sets normals visibility.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual const CCVector3 & getPointNormal(unsigned pointIndex) const =0
Returns normal corresponding to a given point.
virtual void deleteOctree()
Erases the octree.
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
VisibilityTableType m_pointsVisibility
Per-point visibility table.
unsigned char getPointSize() const
Returns current point size.
virtual void refreshBB()=0
Forces bounding-box update.
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
virtual cloudViewer::ReferenceCloud * getTheVisiblePoints(const VisibilityTableType *visTable=nullptr, bool silent=false, cloudViewer::ReferenceCloud *selection=nullptr) const
Returns a ReferenceCloud equivalent to the visibility array.
virtual bool isVisibilityTableInstantiated() const
Returns whether the visibility array is allocated or not.
short minimumFileVersion_MeOnly() const override
virtual void clear()
Clears the entity from all its points and features.
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
virtual const ecvColor::Rgb & getPointColor(unsigned pointIndex) const =0
Returns color corresponding to a given point.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
virtual bool resetVisibilityArray()
Resets the associated visibility array.
std::vector< unsigned char > VisibilityTableType
Array of "visibility" information for each point.
virtual const CompressedNormType & getPointNormalIndex(unsigned pointIndex) const =0
Returns compressed normal corresponding to a given point.
void importParametersFrom(const ccGenericPointCloud *cloud)
Imports the parameters from another cloud.
virtual void unallocateVisibilityArray()
Erases the points visibility information.
static bool CloneChildren(const ccHObject *sourceEntity, ccHObject *destEntity, std::vector< int > *newPointOrTriangleIndex=nullptr, const ccHObject *sourceEntityProxy=nullptr, ccHObject *destEntityProxy=nullptr)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual void notifyGeometryUpdate()
Definition: ecvHObject.cpp:104
bool toFile(QFile &out, short dataVersion) const override
Saves data to binary stream.
ccGLMatrix m_glTransHistory
Cumulative GL transformation.
Definition: ecvHObject.h:727
short minimumFileVersion() const override
Returns the minimum file version required to save this instance.
static void RotateCovariances(const Eigen::Matrix3d &R, std::vector< Eigen::Matrix3d > &covariances)
Rotate all covariance matrices with the rotation matrix R.
Definition: ecvHObject.cpp:371
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual void applyGLTransformation(const ccGLMatrix &trans)
Applies a GL transformation to the entity.
Definition: ecvHObject.cpp:944
void transferChild(ccHObject *child, ccHObject &newParent)
Transfer a given child to another parent.
Definition: ecvHObject.cpp:646
ccHObject * m_parent
Parent.
Definition: ecvHObject.h:709
void removeChild(ccHObject *child)
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
Definition: ecvHObject.cpp:611
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Definition: ecvHObject.cpp:534
bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads data from binary stream.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
static void TransformCovariances(const Eigen::Matrix4d &transformation, std::vector< Eigen::Matrix3d > &covariances)
Transforms all covariance matrices with the transformation.
Definition: ecvHObject.cpp:327
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
Container m_children
Children.
Definition: ecvHObject.h:712
KD-tree structure.
Definition: ecvKdTree.h:25
void translateBoundingBox(const CCVector3 &T)
Translates the bounding-box of the tree.
Definition: ecvKdTree.cpp:72
void multiplyBoundingBox(const PointCoordinateType multFactor)
Multiplies the bounding-box of the tree.
Definition: ecvKdTree.cpp:50
Triangular mesh.
Definition: ecvMesh.h:35
ccMesh * cloneMesh(ccGenericPointCloud *vertices=nullptr, ccMaterialSet *clonedMaterials=nullptr, NormsIndexesTableType *clonedNormsTable=nullptr, TextureCoordsContainer *cloneTexCoords=nullptr)
Clones this entity.
Definition: ecvMesh.cpp:1109
virtual unsigned size() const override
Returns the number of triangles.
Definition: ecvMesh.cpp:2143
bool reserve(std::size_t n)
Reserves the memory to store the vertex indexes (3 per triangle)
Definition: ecvMesh.cpp:2475
ccGenericPointCloud * getAssociatedCloud() const override
Returns the vertices cloud.
Definition: ecvMesh.h:143
void shiftTriangleIndexes(unsigned shift)
Shifts all triangles indexes.
Definition: ecvMesh.cpp:3279
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
Definition: ecvMesh.cpp:2428
void shrinkToFit()
Removes unused capacity.
Definition: ecvMesh.h:302
static bool OrientNormals(ccPointCloud *cloud, unsigned kNN=6, ecvProgressDialog *progressDlg=0)
Main entry point.
static void InvertNormal(CompressedNormType &code)
Inverts a (compressed) normal.
const std::vector< ecvColor::Rgb > & getNormalHSVColorArray() const
Returns the HSV color array.
static void ConvertNormalToDipAndDipDir(const CCVector3 &N, PointCoordinateType &dip_deg, PointCoordinateType &dipDir_deg)
Converts a normal vector to geological 'dip direction & dip' parameters.
static bool ComputeCloudNormals(ccGenericPointCloud *cloud, NormsIndexesTableType &theNormsCodes, CV_LOCAL_MODEL_TYPES localModel, PointCoordinateType localRadius, Orientation preferredOrientation=UNDEFINED, cloudViewer::GenericProgressCallback *progressCb=nullptr, cloudViewer::DgmOctree *inputOctree=nullptr)
Computes normal at each point of a given cloud.
static const CCVector3 & GetNormal(unsigned normIndex)
Static access to ccNormalVectors::getNormal.
static unsigned GetNumberOfVectors()
Returns the number of compressed normal vectors.
Orientation
'Default' orientations
static ccNormalVectors * GetUniqueInstance()
Returns unique instance.
static CompressedNormType GetNormIndex(const PointCoordinateType N[])
Returns the compressed index corresponding to a normal vector.
static CCVector3 & GetNormalPtr(unsigned normIndex)
virtual bool isLocked() const
Returns whether the object is locked or not.
Definition: ecvObject.h:112
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual unsigned getUniqueID() const
Returns object unique ID.
Definition: ecvObject.h:86
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
static CV_CLASS_ENUM ReadClassIDFromFile(QFile &in, short dataVersion)
Helper: reads out class ID from a binary stream.
Definition: ecvObject.cpp:189
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
virtual bool isEnabled() const
Returns whether the object is enabled or not.
Definition: ecvObject.h:97
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
Octree structure.
Definition: ecvOctree.h:27
QSharedPointer< ccOctree > Shared
Shared pointer.
Definition: ecvOctree.h:32
L.O.D. (Level of Detail) structure.
void clear()
Clears the structure.
bool init(ccPointCloud *cloud)
Initializes the construction process (asynchronous)
int init(int count, bool withColors, bool withNormals, bool *reallocated=nullptr)
Inits the VBO.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool applyFilterToRGB(PointCoordinateType sigma, PointCoordinateType sigmaSF, RgbFilterOptions filterParams, cloudViewer::GenericProgressCallback *progressCb=nullptr)
Applies a spatial Gaussian filter on RGB colors.
void drawMeOnly(CC_DRAW_CONTEXT &context) override
Draws the entity only (not its children)
void addEigenNorm(const Eigen::Vector3d &N)
std::vector< CCVector3 * > getPointNormalsPtr() const
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
void addRGBColors(const std::vector< ecvColor::Rgb > &colors)
void setEigenColor(size_t index, const Eigen::Vector3d &color)
ccScalarField * m_currentDisplayedScalarField
Currently displayed scalar field.
ecvColor::Rgb & getPointColorPtr(size_t pointIndex)
void addColorRampInfo(CC_DRAW_CONTEXT &context)
Adds associated SF color ramp info to current GL context.
std::vector< ccWaveform > m_fwfWaveforms
Per-point waveform accessors.
std::vector< Eigen::Vector3d > getEigenNormals() const
bool sfColorScaleShown() const
Returns whether color scale should be displayed or not.
vboSet m_vboManager
Set of VBOs attached to this cloud.
std::vector< CompressedNormType > getNorms() const
bool setRGBColorByBanding(unsigned char dim, double freq)
Assigns color to points by 'banding'.
bool hasSensor() const
Returns whether the mesh as an associated sensor or not.
bool convertNormalToRGB()
Converts normals to RGB colors.
bool m_sfColorScaleDisplayed
void clearFWFData()
Clears all associated FWF data.
~ccPointCloud() override
Default destructor.
ScalarType getPointDisplayedDistance(unsigned pointIndex) const override
Returns scalar value associated to a given point.
virtual void removePoints(size_t index) override
bool resizeTheNormsTable()
Resizes the compressed normals array.
void addNorms(const std::vector< CCVector3 > &Ns)
void unallocateColors()
Erases the cloud colors.
NormsIndexesTableType * m_normals
Normals (compressed)
virtual void scale(PointCoordinateType fx, PointCoordinateType fy, PointCoordinateType fz, CCVector3 center=CCVector3(0, 0, 0)) override
Multiplies all coordinates by constant factors (one per dimension)
void deleteAllScalarFields() override
Deletes all scalar fields associated to this cloud.
void refreshBB() override
Forces bounding-box update.
bool initLOD()
Intializes the LOD structure.
void addEigenColor(const Eigen::Vector3d &color)
virtual ccBBox GetAxisAlignedBoundingBox() const override
Returns an axis-aligned bounding box of the geometry.
cloudViewer::ReferenceCloud * crop2D(const ccPolyline *poly, unsigned char orthoDim, bool inside=true)
Crops the cloud inside (or outside) a 2D polyline.
void getDrawingParameters(glDrawParams &params) const override
Returns main OpenGL parameters for this entity.
void setEigenNormal(size_t index, const Eigen::Vector3d &normal)
void setPointNormalIndex(size_t pointIndex, CompressedNormType norm)
Sets a particular point compressed normal.
void addEigenNorms(const std::vector< Eigen::Vector3d > &normals)
bool orientNormalsTowardViewPoint(CCVector3 &VP, ecvProgressDialog *pDlg=nullptr)
Normals are forced to point to O.
bool HasCovariances() const
Returns 'true' if the point cloud contains per-point covariance matrix.
const ecvColor::Rgb * getScalarValueColor(ScalarType d) const override
Returns color corresponding to a given scalar value.
void setEigenColors(const std::vector< Eigen::Vector3d > &colors)
void setNormsTable(NormsIndexesTableType *norms)
Sets the (compressed) normals table.
ccPointCloudLOD * m_lod
L.O.D. structure.
Eigen::Vector3d getEigenNormal(size_t index) const
bool convertRGBToGreyScale()
Converts RGB to grey scale colors.
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
void addNorm(const CCVector3 &N)
Pushes a normal vector on stack (shortcut)
NormsIndexesTableType * normals() const
Returns pointer on compressed normals indexes table.
ccPointCloud * unroll(UnrollMode mode, UnrollBaseParams *params, bool exportDeviationSF=false, double startAngle_deg=0.0, double stopAngle_deg=360.0, cloudViewer::GenericProgressCallback *progressCb=nullptr) const
Unrolls the cloud and its normals on a cylinder or a cone.
std::vector< CCVector3 > getPointNormals() const
short minimumFileVersion_MeOnly() const override
std::vector< ccWaveform > & waveforms()
Gives access to the associated FWF data.
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
bool computeNormalsWithGrids(double minTriangleAngle_deg=1.0, ecvProgressDialog *pDlg=nullptr)
Compute the normals with the associated grid structure(s)
bool reserveTheFWFTable()
Reserves the FWF table.
QSharedPointer< const FWFDataContainer > SharedFWFDataContainer
const ccPointCloud & operator+=(const ccPointCloud &cloud)
ccWaveformProxy waveformProxy(unsigned index) const
Returns a proxy on a given waveform.
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool interpolateColorsFrom(ccGenericPointCloud *cloud, cloudViewer::GenericProgressCallback *progressCb=nullptr, unsigned char octreeLevel=0)
Interpolate colors from another cloud (nearest neighbor only)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool enhanceRGBWithIntensitySF(int sfIdx, bool useCustomIntensityRange=false, double minI=0.0, double maxI=1.0)
void applyGLTransformation(const ccGLMatrix &trans) override
Applies a GL transformation to the entity.
bool resizeTheRGBTable(bool fillWithWhite=false)
Resizes the RGB colors array.
void normalsHaveChanged()
Notify a modification of normals display parameters or contents.
const ecvColor::Rgb * getPointScalarValueColor(unsigned pointIndex) const override
Returns color corresponding to a given point associated scalar value.
const ccPointCloud & append(ccPointCloud *cloud, unsigned pointCountBefore, bool ignoreChildren=false)
Appends a cloud to this one.
void notifyGeometryUpdate() override
virtual ccPointCloud & Rotate(const Eigen::Matrix3d &R, const Eigen::Vector3d &center) override
Apply rotation to the geometry coordinates and normals. Given a rotation matrix , and center ,...
void showSFColorsScale(bool state)
Sets whether color scale should be displayed or not.
unsigned char testVisibility(const CCVector3 &P) const override
void unalloactePoints()
Erases the cloud points.
int m_currentDisplayedScalarFieldIndex
Currently displayed scalar field index.
void invalidateBoundingBox() override
Invalidates bounding box.
bool computeNormalsWithOctree(CV_LOCAL_MODEL_TYPES model, ccNormalVectors::Orientation preferredOrientation, PointCoordinateType defaultRadius, ecvProgressDialog *pDlg=nullptr)
Compute the normals by approximating the local surface around each point.
bool orientNormalsWithFM(unsigned char level, ecvProgressDialog *pDlg=nullptr)
Orient normals with Fast Marching.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool resizeTheFWFTable()
Resizes the FWF table.
bool removeVisiblePoints(VisibilityTableType *visTable=nullptr, std::vector< int > *newIndexes=nullptr) override
Removes all the 'visible' points (as defined by the visibility array)
bool hasFWF() const
Returns whether the cloud has associated Full WaveForm data.
const CompressedNormType & getPointNormalIndex(unsigned pointIndex) const override
Returns compressed normal corresponding to a given point.
bool exportCoordToSF(bool exportDims[3])
Exports the specified coordinate dimension(s) to scalar field(s)
virtual ccPointCloud & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
ccPointCloud(QString name=QString())
Default constructor.
bool computeFWFAmplitude(double &minVal, double &maxVal, ecvProgressDialog *pDlg=nullptr) const
Computes the maximum amplitude of all associated waveforms.
void releaseVBOs()
Release VBOs.
bool orientNormalsWithMST(unsigned kNN=6, ecvProgressDialog *pDlg=nullptr)
Orient the normals with a Minimum Spanning Tree.
ccGenericPointCloud * createNewCloudFromVisibilitySelection(bool removeSelectedPoints=false, VisibilityTableType *visTable=nullptr, std::vector< int > *newIndexesOfRemainingPoints=nullptr, bool silent=false, cloudViewer::ReferenceCloud *selection=nullptr) override
bool colorize(float r, float g, float b)
Multiplies all color components of all points by coefficients.
static ccPointCloud * From(const cloudViewer::GenericIndexedCloud *cloud, const ccGenericPointCloud *sourceCloud=nullptr)
Creates a new point cloud object from a GenericIndexedCloud.
void setEigenNormals(const std::vector< Eigen::Vector3d > &normals)
void hidePointsByScalarValue(ScalarType minVal, ScalarType maxVal)
Hides points whose scalar values falls into an interval.
bool hasDisplayedScalarField() const override
Returns whether an active scalar field is available or not.
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
ccPointCloud & operator=(const ccPointCloud &cloud)
Fuses another 3D entity with this one.
virtual ccPointCloud & Scale(const double s, const Eigen::Vector3d &center) override
Apply scaling to the geometry coordinates. Given a scaling factor , and center , a given point is tr...
bool convertNormalToDipDirSFs(ccScalarField *dipSF, ccScalarField *dipDirSF)
Converts normals to two scalar fields: 'dip' and 'dip direction'.
bool exportNormalToSF(bool exportDims[3])
Exports the specified normal dimension(s) to scalar field(s)
bool setCoordFromSF(bool importDims[3], cloudViewer::ScalarField *sf, PointCoordinateType defaultValueForNaN)
Sets coordinate(s) from a scalar field.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
bool compressFWFData()
Compresses the associated FWF data container.
std::shared_ptr< ccPointCloud > SelectByIndex(const std::vector< size_t > &indices, bool invert=false) const
Function to select points from input ccPointCloud into output ccPointCloud.
bool m_visibilityCheckEnabled
Whether visibility check is available or not (during comparison)
int getCurrentDisplayedScalarFieldIndex() const
Returns the currently displayed scalar field index (or -1 if none)
void clear() override
Clears the entity from all its points and features.
void addNormAtIndex(const PointCoordinateType *N, unsigned index)
Adds a normal vector to the one at a specific index.
void unallocateNorms()
Erases the cloud normals.
void swapPoints(unsigned firstIndex, unsigned secondIndex) override
virtual void applyRigidTransformation(const ccGLMatrix &trans) override
Applies a rigid transformation (rotation + translation)
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
bool hasColors() const override
Returns whether colors are enabled or not.
std::vector< uint8_t > FWFDataContainer
Waveform data container.
unsigned getUniqueIDForDisplay() const override
Returns object unqiue ID used for display.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
ccPointCloud * cloneThis(ccPointCloud *destCloud=nullptr, bool ignoreChildren=false)
Clones this entity.
std::vector< Grid::Shared > m_grids
Associated grid structure.
bool setRGBColorByHeight(unsigned char heightDim, ccColorScale::Shared colorScale)
Assigns color to points proportionnaly to their 'height'.
void setPointNormals(const std::vector< CCVector3 > &normals)
void deleteScalarField(int index) override
Deletes a specific scalar field.
const CCVector3 * getNormal(unsigned pointIndex) const override
If per-point normals are available, returns the one at a specific index.
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
bool addGrid(Grid::Shared grid)
Adds an associated grid.
void setPointNormal(size_t pointIndex, const CCVector3 &N)
Sets a particular point normal (shortcut)
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
SharedFWFDataContainer & fwfData()
Gives access to the associated FWF data container.
ColorsTableType * m_rgbColors
Colors.
SharedFWFDataContainer m_fwfData
Waveforms raw data storage.
Grid::Shared & grid(size_t gridIndex)
Returns an associated grid.
std::vector< Eigen::Matrix3d > covariances_
Covariance Matrix for each point.
Eigen::Vector3d getEigenColor(size_t index) const
void addEigenColors(const std::vector< Eigen::Vector3d > &colors)
ccMesh * triangulateGrid(const Grid &grid, double minTriangleAngle_deg=0.0) const
Meshes a scan grid.
FWFDescriptorSet m_fwfDescriptors
General waveform descriptors.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
bool convertCurrentScalarFieldToColors(bool mixWithExistingColor=false)
void colorsHaveChanged()
CCVector3 computeGravityCenter()
Returns the cloud gravity center.
bool setRGBColor(ColorCompType r, ColorCompType g, ColorCompType b)
Set a unique color for the whole cloud (shortcut)
bool hasScalarFields() const override
Returns whether one or more scalar fields are instantiated.
void addNormIndex(CompressedNormType index)
Pushes a compressed normal vector.
ccPointCloud * filterPointsByScalarValue(ScalarType minVal, ScalarType maxVal, bool outside=false)
Filters out points whose scalar values falls into an interval.
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
virtual ccPointCloud & Transform(const Eigen::Matrix4d &trans) override
Apply transformation (4x4 matrix) to the geometry coordinates.
virtual ecvOrientedBBox GetOrientedBoundingBox() const override
void clearLOD()
Clears the LOD structure.
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
bool orientNormalsWithGrids(ecvProgressDialog *pDlg=nullptr)
Orient the normals with the associated grid structure(s)
bool setRGBColorWithCurrentScalarField(bool mixWithExistingColor=false)
Sets RGB colors with current scalar field (values & parameters)
CCVector3 & getPointNormalPtr(size_t pointIndex) const
std::vector< Eigen::Vector3d > getEigenColors() const
cloudViewer::ReferenceCloud * crop(const ccBBox &box, bool inside=true) override
Crops the cloud inside (or outside) a bounding box.
QSharedPointer< cloudViewer::ReferenceCloud > computeCPSet(ccGenericPointCloud &otherCloud, cloudViewer::GenericProgressCallback *progressCb=nullptr, unsigned char octreeLevel=0)
Computes the closest point of this cloud relatively to another cloud.
@ WRN_OUT_OF_MEM_FOR_COLORS
@ WRN_OUT_OF_MEM_FOR_NORMALS
void invertNormals()
Inverts normals (if any)
ccGenericPointCloud * clone(ccGenericPointCloud *destCloud=nullptr, bool ignoreChildren=false) override
Clones this entity.
size_t gridCount() const
Returns the number of associated grids.
Colored polyline.
Definition: ecvPolyline.h:24
Scalar field range structure.
ScalarType max() const
ScalarType range() const
ScalarType start() const
A scalar field associated to display-related parameters.
const ccColorScale::Shared & getColorScale() const
Returns associated color scale.
void setGlobalShift(double shift)
Sets the global shift.
bool toFile(QFile &out, short dataVersion) const override
Saves data to binary stream.
double getGlobalShift() const
Returns the global shift (if any)
bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads data from binary stream.
const ecvColor::Rgb * getColor(ScalarType value) const
void computeMinAndMax() override
Determines the min and max values.
void importParametersFrom(const ccScalarField *sf)
Imports the parameters from another scalar field.
const ecvColor::Rgb * getValueColor(unsigned index) const
Shortcut to getColor.
bool areNaNValuesShownInGrey() const
Returns whether NaN values are displayed in gray or hidden.
Generic sensor interface.
Definition: ecvSensor.h:27
virtual void applyGLTransformation(const ccGLMatrix &trans) override
Applies a GL transformation to the entity.
Definition: ecvSensor.cpp:61
static bool CorruptError()
Sends a custom error message (corrupted file) and returns 'false'.
QMultiMap< unsigned, unsigned > LoadedIDMap
Map of loaded unique IDs (old ID --> new ID)
static bool ReadError()
Sends a custom error message (read error) and returns 'false'.
static bool WriteError()
Sends a custom error message (write error) and returns 'false'.
static bool MemoryError()
Sends a custom error message (not enough memory) and returns 'false'.
static bool GenericArrayFromFile(std::vector< Type > &data, QFile &in, short dataVersion, const QString &verboseDescription)
Helper: loads a vector structure from file.
static bool GenericArrayToFile(const std::vector< Type > &data, QFile &out)
Helper: saves a vector to file.
static bool GenericArrayFromTypedFile(std::vector< Type > &data, QFile &in, short dataVersion, const QString &verboseDescription, FileComponentType *_autoOffset=nullptr)
static short GenericArrayToFileMinVersion()
Returns the minimum file version to save/load a 'generic array'.
bool isShifted() const
Returns whether the cloud is shifted or not.
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual void setGlobalScale(double scale)
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
Waveform proxy.
Definition: ecvWaveform.h:189
bool isValid() const
Returns whether the waveform (proxy) is valid or not.
Definition: ecvWaveform.h:198
double getRange(double &minVal, double &maxVal) const
Returns the range of (real) samples.
Definition: ecvWaveform.h:219
Waveform.
Definition: ecvWaveform.h:55
uint32_t byteCount() const
Returns the number of allocated bytes.
Definition: ecvWaveform.h:112
void setDescriptorID(uint8_t id)
Sets the associated descriptor (ID)
Definition: ecvWaveform.h:70
uint8_t descriptorID() const
Returns the associated descriptor (ID)
Definition: ecvWaveform.h:67
void setDataDescription(uint64_t dataOffset, uint32_t byteCount)
Describes the waveform data.
Definition: ecvWaveform.cpp:78
uint64_t dataOffset() const
Returns the byte offset to waveform data.
Definition: ecvWaveform.h:115
Vector3Tpl< T > getDiagVec() const
Returns diagonal vector.
Definition: BoundingBox.h:169
Vector3Tpl< T > getCenter() const
Returns center.
Definition: BoundingBox.h:164
bool contains(const Vector3Tpl< T > &P) const
Returns whether a points is inside the box or not.
Definition: BoundingBox.h:231
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
Definition: BoundingBox.h:154
bool isValid() const
Returns whether bounding box is valid or not.
Definition: BoundingBox.h:203
void getCellPos(CellCode code, unsigned char level, Tuple3i &cellPos, bool isCodeTruncated) const
Definition: DgmOctree.cpp:498
unsigned char findBestLevelForAGivenNeighbourhoodSizeExtraction(PointCoordinateType radius) const
Definition: DgmOctree.cpp:2664
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
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
static int computeCloud2CloudDistances(GenericIndexedCloudPersist *comparedCloud, GenericIndexedCloudPersist *referenceCloud, Cloud2CloudDistancesComputationParams &params, GenericProgressCallback *progressCb=nullptr, DgmOctree *compOctree=nullptr, DgmOctree *refOctree=nullptr)
virtual void placeIteratorAtBeginning()=0
Sets the cloud iterator at the beginning.
virtual const CCVector3 * getNextPoint()=0
Returns the next point (relatively to the global iterator position)
virtual unsigned size() const =0
Returns the number of points.
virtual bool hasPoints() const
Definition: GenericCloud.h:37
A generic 3D point cloud with index-based point access.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
virtual void stop()=0
Notifies the fact that the process has ended.
virtual void setInfo(const char *infoStr)=0
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle)=0
Notifies the algorithm title.
virtual bool textCanBeEdited() const
Returns whether the dialog title and info can be updated or not.
virtual void update(float percent)=0
Notifies the algorithm progress.
static CCVector3 ComputeGravityCenter(const GenericCloud *theCloud)
Computes the gravity center of a point cloud.
static ReferenceCloud * segment(GenericIndexedCloudPersist *aCloud, const Polyline *poly, bool keepInside, const float *viewMat=nullptr)
static bool isPointInsidePoly(const CCVector2 &P, const GenericIndexedCloud *polyVertices)
Tests if a point is inside a polygon (2D)
bool oneStep()
Increments total progress value of a single unit.
std::vector< size_t > GetPointIndicesWithinBoundingBox(const std::vector< Eigen::Vector3d > &points) const
Return indices to points that are within the bounding box.
std::vector< ScalarField * > m_scalarFields
Associated scalar fields.
std::vector< CCVector3 > m_points
3D Points database
ScalarField * getCurrentOutScalarField() const
Returns the scalar field currently associated to the cloud output.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
void reset()
Clears the cloud database.
virtual void swapPoints(unsigned firstIndex, unsigned secondIndex)
Swaps two points (and their associated scalar values!)
virtual void deleteScalarField(int index)
Deletes a specific scalar field.
virtual void invalidateBoundingBox()
Invalidates bounding box.
void setCurrentOutScalarField(int index)
Sets the OUTPUT scalar field.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
void setPointScalarValue(unsigned pointIndex, ScalarType value) override
CCVector3 * point(unsigned index)
Returns non const access to a given point.
virtual bool reserve(unsigned newCapacity)
Reserves memory for the point database.
void setCurrentScalarField(int index)
Sets both the INPUT & OUTPUT scalar field.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPointPersistentPtr(unsigned index) override
Returns the ith point as a persistent pointer.
unsigned size() const override
Returns the number of points.
Definition: PointCloudTpl.h:38
virtual void deleteAllScalarFields()
Deletes all scalar fields associated to this cloud.
ScalarType getPointScalarValue(unsigned pointIndex) const override
int m_currentInScalarFieldIndex
Index of current scalar field used for input.
virtual bool resize(unsigned newCount)
Resizes the point database.
void setCurrentInScalarField(int index)
Sets the INPUT scalar field.
int m_currentOutScalarFieldIndex
Index of current scalar field used for output.
const CCVector3 * getPoint(unsigned index) const override
void addPoints(const std::vector< CCVector3 > &points)
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
virtual GenericIndexedCloudPersist * getAssociatedCloud()
Returns the associated (source) cloud.
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
virtual bool resize(unsigned n)
Presets the size of the vector used to store point references.
virtual unsigned capacity() const
Returns max capacity.
virtual void clear(bool releaseMemory=false)
Clears the cloud.
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.
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
virtual void computeMinAndMax()
Determines the min and max values.
Definition: ScalarField.h:123
ScalarType getMin() const
Returns the minimum value.
Definition: ScalarField.h:72
void addElement(ScalarType value)
Definition: ScalarField.h:99
static ScalarType NaN()
Returns the specific NaN value.
Definition: ScalarField.h:46
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
void setValue(std::size_t index, ScalarType value)
Definition: ScalarField.h:96
const char * getName() const
Returns scalar field name.
Definition: ScalarField.h:43
bool reserveSafe(std::size_t count)
Reserves memory (no exception thrown)
Definition: ScalarField.cpp:71
bool resizeSafe(std::size_t count, bool initNewElements=false, ScalarType valueForNewElements=0)
Resizes memory (no exception thrown)
Definition: ScalarField.cpp:81
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
Definition: ScalarField.h:61
unsigned currentSize() const
Definition: ScalarField.h:100
ScalarType getMax() const
Returns the maximum value.
Definition: ScalarField.h:74
RGB color structure.
Definition: ecvColorTypes.h:49
constexpr static RgbTpl FromEigen(const Eigen::Vector3d &t)
Definition: ecvColorTypes.h:86
static Eigen::Vector3d ToEigen(const Type col[3])
Definition: ecvColorTypes.h:72
RGBA color structure.
static void Draw(const CC_DRAW_CONTEXT &context, const ccHObject *obj)
virtual bool IsEmpty() const override
static ecvOrientedBBox CreateFromPoints(const std::vector< Eigen::Vector3d > &points)
Graphical progress indicator (thread-safe)
double colors[3]
double normals[3]
#define LogWarning(...)
Definition: Logging.h:72
#define LogDebug(...)
Definition: Logging.h:90
unsigned int CompressedNormType
Compressed normals type.
Definition: ecvBasicTypes.h:16
unsigned char ColorCompType
Default color components type (R,G and B)
Definition: ecvColorTypes.h:29
#define MACRO_Draw2D(context)
#define MACRO_Draw3D(context)
#define MACRO_LightIsEnabled(context)
#define MACRO_Foreground(context)
#define MACRO_FastEntityPicking(context)
#define MACRO_EntityPicking(context)
std::vector< unsigned > LODIndexSet
L.O.D. indexes set.
static void ProjectOnCylinder(const CCVector3 &AP, const Tuple3ub &dim, PointCoordinateType radius, PointCoordinateType &delta, PointCoordinateType &phi_rad)
float GetSymmetricalNormalizedValue(const ScalarType &sfVal, const ccScalarField::Range &saturationRange)
void UpdateGridIndexes(const std::vector< int > &newIndexMap, std::vector< ccPointCloud::Grid::Shared > &grids)
float GetNormalizedValue(const ScalarType &sfVal, const ccScalarField::Range &displayRange)
static bool ComputeCellGaussianFilter(const cloudViewer::DgmOctree::octreeCell &cell, void **additionalParameters, cloudViewer::NormalizedProgress *nProgress=nullptr)
static void ProjectOnCone(const CCVector3 &AP, PointCoordinateType alpha_rad, const Tuple3ub &dim, PointCoordinateType &s, PointCoordinateType &delta, PointCoordinateType &phi_rad)
static const unsigned MAX_POINT_COUNT_PER_LOD_RENDER_PASS
Maximum number of points (per cloud) displayed in a single LOD iteration.
static bool CatchGLErrors(GLenum err, const char *context)
static const char s_deviationSFName[]
GraphType data
Definition: graph_cut.cc:138
ImGuiContext * context
Definition: Window.cpp:76
normal_z rgb
normal_z z
Definition: API.h:123
@ SENSOR
Definition: CVTypes.h:116
@ MESH
Definition: CVTypes.h:105
@ GBL_SENSOR
Definition: CVTypes.h:117
@ NORMAL_INDEXES_ARRAY
Definition: CVTypes.h:135
@ RGB_COLOR_ARRAY
Definition: CVTypes.h:137
@ RGBA_COLOR_ARRAY
Definition: CVTypes.h:138
@ IMAGE
Definition: CVTypes.h:114
@ POINT_CLOUD
Definition: CVTypes.h:104
@ LABEL_2D
Definition: CVTypes.h:140
@ POINT_KDTREE
Definition: CVTypes.h:111
@ FACET
Definition: CVTypes.h:109
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
float DegreesToRadians(int degrees)
Convert degrees to radians.
Definition: CVMath.h:98
bool LessThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:23
constexpr Rgb black(0, 0, 0)
constexpr Rgb white(MAX, MAX, MAX)
constexpr ColorCompType MAX
Max value of a single color component (default type)
Definition: ecvColorTypes.h:34
Rgb FromRgbaToRgb(const Rgba &color)
Conversion from Rgba to Rgb.
cloudViewer::NormalizedProgress * nProgress
cloudViewer::DgmOctree * octree
unsigned char octreeLevel
ccGenericPointCloud * sourceCloud
DisplayDesc & operator=(const LODLevelDesc &desc)
Set operator.
DisplayDesc()
Default constructor.
LODIndexSet * indexMap
Map of indexes (to invert the natural order)
unsigned endIndex
Last index (excluded)
unsigned decimStep
Decimation step (for non-octree based LoD)
DisplayDesc(unsigned startIndex, unsigned count)
Constructor from a start index and a count value.
Level descriptor.
unsigned count
Index count for this level.
unsigned startIndex
Start index (refers to the 'indexes' table)
Picked point descriptor.
Definition: ecv2DLabel.h:122
unsigned index
Point/triangle index.
Definition: ecv2DLabel.h:128
ccGenericPointCloud * cloud
Cloud.
Definition: ecv2DLabel.h:124
Display context.
Grid structure.
bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads data from binary stream.
short minimumFileVersion() const override
Returns the minimum file version required to save this instance.
ccGLMatrixd sensorPosition
Sensor position (expressed relatively to the cloud points)
Grid()
Default constructor.
QImage toImage() const
Converts the grid to an RGB image (needs colors)
bool toFile(QFile &out, short dataVersion) const override
Saves data to binary stream.
void setIndex(unsigned row, unsigned col, int index)
Sets an index at a given position inside the grid.
void updateMinAndMaxValidIndexes()
Updates the min and max valid indexes.
void setColor(unsigned row, unsigned col, const ecvColor::Rgb &rgb)
Sets a color at a given position inside the grid.
bool init(unsigned rowCount, unsigned colCount, bool withRGB=false)
Inits the grid.
QSharedPointer< Grid > Shared
Shared type.
PointCoordinateType radius
unrolling cylinder radius (or cone base radius)
unsigned char axisDim
unrolling cylinder/cone axis (X=0, Y=1 or Z=2)
STATES state
Current state.
std::vector< VBO * > vbos
ccScalarField * sourceSF
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
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
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.
ReferenceCloud * CPSet
Container of (references to) points to store the "Closest Point Set".
Display parameters of a 3D entity.
bool showColors
Display colors.
bool showNorms
Display normals.
bool showSF
Display scalar field (prioritary on colors)