ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvMesh.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 "ecvMesh.h"
9 
10 // Local
11 #include "ecvBBox.h"
12 #include "ecvChunk.h"
13 #include "ecvColorScalesManager.h"
14 #include "ecvDisplayTools.h"
15 #include "ecvGenericPointCloud.h"
16 #include "ecvHObjectCaster.h"
17 #include "ecvMaterialSet.h"
18 #include "ecvNormalVectors.h"
19 #include "ecvOrientedBBox.h"
20 #include "ecvPointCloud.h"
21 #include "ecvPolyline.h"
22 #include "ecvProgressDialog.h"
23 #include "ecvScalarField.h"
24 #include "ecvSubMesh.h"
25 
26 // cloudViewer
27 #include <Delaunay2dMesh.h>
28 #include <Logging.h>
30 #include <Neighbourhood.h>
31 #include <PointProjectionTools.h>
32 #include <ReferenceCloud.h>
33 
34 // System
35 #include <assert.h>
36 #include <string.h>
37 
38 #include <cmath> //for std::modf
39 
40 static CCVector3 s_blankNorm(0, 0, 0);
41 
43  : ccGenericMesh("Mesh"),
44  m_associatedCloud(nullptr),
45  m_triNormals(nullptr),
46  m_texCoords(nullptr),
47  m_materials(nullptr),
48  m_triVertIndexes(nullptr),
49  m_globalIterator(0),
50  m_triMtlIndexes(nullptr),
51  m_texCoordIndexes(nullptr),
52  m_triNormalIndexes(nullptr) {
53  setAssociatedCloud(vertices);
54 
57 }
58 
59 ccMesh::ccMesh(const ccMesh& mesh) : ccMesh(new ccPointCloud("vertices")) {
60  if (m_associatedCloud && getChildrenNumber() == 0) {
62  // DGM: no need to lock it as it is only used by one mesh!
65  }
66 
67  *this = mesh;
68 }
69 
70 ccMesh::ccMesh(const std::vector<Eigen::Vector3d>& vertices,
71  const std::vector<Eigen::Vector3i>& triangles)
72  : ccMesh(new ccPointCloud("vertices")) {
73  assert(m_associatedCloud);
76  // DGM: no need to lock it as it is only used by one mesh!
79  if (cloud->resize(static_cast<unsigned>(vertices.size()))) {
80  this->setEigenVertices(vertices);
81  }
82 
83  this->setTriangles(triangles);
84 }
85 
87  ccGenericPointCloud* giVertices)
88  : ccGenericMesh("Mesh"),
89  m_associatedCloud(nullptr),
90  m_triNormals(nullptr),
91  m_texCoords(nullptr),
92  m_materials(nullptr),
93  m_triVertIndexes(nullptr),
94  m_globalIterator(0),
95  m_triMtlIndexes(nullptr),
96  m_texCoordIndexes(nullptr),
97  m_triNormalIndexes(nullptr) {
98  setAssociatedCloud(giVertices);
99 
102 
103  unsigned triNum = giMesh->size();
104  if (!reserve(triNum)) return;
105 
106  giMesh->placeIteratorAtBeginning();
107  for (unsigned i = 0; i < triNum; ++i) {
108  const cloudViewer::VerticesIndexes* tsi =
109  giMesh->getNextTriangleVertIndexes();
110  addTriangle(tsi->i1, tsi->i2, tsi->i3);
111  }
112 
113  // if (!giVertices->hasNormals())
114  // computeNormals();
115  showNormals(giVertices->hasNormals());
116 
117  if (giVertices->hasColors()) showColors(giVertices->colorsShown());
118 
119  if (giVertices->hasDisplayedScalarField()) showSF(giVertices->sfShown());
120 }
121 
123  clearTriNormals();
124  setMaterialSet(nullptr);
125  setTexCoordinatesTable(nullptr);
126 
131 }
132 
134  if (!getAssociatedCloud()) {
135  ccPointCloud* baseVertices = new ccPointCloud("vertices");
136  baseVertices->setEnabled(false);
137  // DGM: no need to lock it as it is only used by one mesh!
138  baseVertices->setLocked(false);
139  setAssociatedCloud(baseVertices);
140  this->setName("Merged mesh");
141  this->addChild(baseVertices);
142 
143  } else {
145  }
146 
147  this->resize(0);
148 
149  this->adjacency_list_ = mesh.adjacency_list_;
150 
151  this->merge(&mesh, false);
152  return (*this);
153 }
154 
156  if (!this->merge(&mesh, false)) {
157  CVLog::Error("Fusion failed! (not enough memory?)");
158  }
159 
160  this->adjacency_list_ = mesh.adjacency_list_;
161 
162  return (*this);
163 }
164 
165 ccMesh ccMesh::operator+(const ccMesh& mesh) const {
166  return (ccMesh(*this) += mesh);
167 }
168 
170  m_associatedCloud = cloud;
171 
172  if (m_associatedCloud)
175 
176  m_bBox.setValidity(false);
177 }
178 
179 bool ccMesh::CreateInternalCloud() {
180  if (getAssociatedCloud()) {
181  if (getChildrenNumber() == 0) {
183  }
185  "Already has associated vertices cloud!");
186  return false;
187  }
188 
189  ccPointCloud* cloud = new ccPointCloud("vertices");
190  if (!cloud) {
192  "Creating associated vertices cloud failed!");
193  return false;
194  }
195  cloud->setEnabled(false);
196  cloud->setLocked(false);
197  // DGM: no need to lock it as it is only used by one mesh!
198  setAssociatedCloud(cloud);
199 
200  if (getChildrenNumber() == 0) {
201  this->addChild(cloud);
202  }
203  return true;
204 }
205 
207  if (obj == m_associatedCloud) {
208  m_bBox.setValidity(false);
209  notifyGeometryUpdate(); // for sub-meshes
210  }
211 
213 }
214 
215 void ccMesh::onDeletionOf(const ccHObject* obj) {
216  if (obj == m_associatedCloud) setAssociatedCloud(nullptr);
217 
219 }
220 
221 bool ccMesh::hasColors() const {
222  return (m_associatedCloud ? m_associatedCloud->hasColors() : false);
223 }
224 
225 bool ccMesh::hasNormals() const {
226  return (HasVertexNormals() || hasTriNormals());
227 }
228 
230  return m_associatedCloud ? m_associatedCloud->hasNormals() : false;
231 }
232 
235  : false);
236 }
237 
240 }
241 
242 bool ccMesh::computeNormals(bool perVertex) {
243  return perVertex ? computePerVertexNormals() : computePerTriangleNormals();
244 }
245 
247  if (!m_associatedCloud ||
249  {
251  "[ccMesh::computePerVertexNormals] Vertex set is not a "
252  "standard cloud?!");
253  return false;
254  }
255 
256  unsigned triCount = size();
257  if (triCount == 0) {
258  CVLog::Warning("[ccMesh::computePerVertexNormals] Empty mesh!");
259  return false;
260  }
261  unsigned vertCount = m_associatedCloud->size();
262  if (vertCount < 3) {
264  "[ccMesh::computePerVertexNormals] Not enough vertices! (<3)");
265  return false;
266  }
267 
268  ccPointCloud* cloud = static_cast<ccPointCloud*>(m_associatedCloud);
269 
270  // we instantiate a temporary structure to store each vertex normal
271  // (uncompressed)
272  std::vector<CCVector3> theNorms;
273  try {
274  theNorms.resize(vertCount, s_blankNorm);
275  } catch (const std::bad_alloc&) {
276  CVLog::Warning("[ccMesh::computePerVertexNormals] Not enough memory!");
277  return false;
278  }
279 
280  // allocate compressed normals array on vertices cloud
281  bool normalsWereAllocated = cloud->hasNormals();
282  if ( !cloud
283  ->resizeTheNormsTable()) // we call it whatever the case (just
284  // to be sure)
285  {
286  // warning message should have been already issued!
287  return false;
288  }
289 
290  // for each triangle
292  {
293  for (unsigned i = 0; i < triCount; ++i) {
295 
296  assert(tsi->i1 < vertCount && tsi->i2 < vertCount &&
297  tsi->i3 < vertCount);
298  const CCVector3* A = cloud->getPoint(tsi->i1);
299  const CCVector3* B = cloud->getPoint(tsi->i2);
300  const CCVector3* C = cloud->getPoint(tsi->i3);
301 
302  // compute face normal (right hand rule)
303  CCVector3 N = (*B - *A).cross(*C - *A);
304  // N.normalize(); //DGM: no normalization = weighting by surface!
305 
306  // we add this normal to all triangle vertices
307  theNorms[tsi->i1] += N;
308  theNorms[tsi->i2] += N;
309  theNorms[tsi->i3] += N;
310  }
311  }
312 
313  // for each vertex
314  {
315  for (unsigned i = 0; i < vertCount; i++) {
316  CCVector3& N = theNorms[i];
317  // normalize the 'mean' normal
318  N.normalize();
319  cloud->setPointNormal(i, N);
320  }
321  }
322 
323  // apply it also to sub-meshes!
324  showNormals_extended(true);
325 
326  if (!normalsWereAllocated) cloud->showNormals(true);
327 
328  return true;
329 }
330 
332  unsigned triCount = size();
333  if (triCount == 0) {
334  CVLog::Warning("[ccMesh::computePerTriangleNormals] Empty mesh!");
335  return false;
336  }
337 
338  // if some normal indexes already exists, we remove them (easier)
340  setTriNormsTable(nullptr);
341 
342  NormsIndexesTableType* normIndexes = new NormsIndexesTableType();
343  if (!normIndexes->reserveSafe(triCount)) {
344  normIndexes->release();
346  "[ccMesh::computePerTriangleNormals] Not enough memory!");
347  return false;
348  }
349 
350  // for each triangle
351  {
352  for (unsigned i = 0; i < triCount; ++i) {
353  const cloudViewer::VerticesIndexes& tri =
355  const CCVector3* A = m_associatedCloud->getPoint(tri.i1);
356  const CCVector3* B = m_associatedCloud->getPoint(tri.i2);
357  const CCVector3* C = m_associatedCloud->getPoint(tri.i3);
358 
359  // compute face normal (right hand rule)
360  CCVector3 N = (*B - *A).cross(*C - *A);
361 
363  normIndexes->emplace_back(nIndex);
364  }
365  }
366 
367  // set the per-triangle normal indexes
368  {
370  normIndexes->release();
372  "[ccMesh::computePerTriangleNormals] Not enough memory!");
373  return false;
374  }
375 
376  setTriNormsTable(normIndexes);
377 
378  for (int i = 0; i < static_cast<int>(triCount); ++i)
379  addTriangleNormalIndexes(i, i, i);
380  }
381 
382  // apply it also to sub-meshes!
383  showNormals_extended(true);
384 
385  return true;
386 }
387 
388 bool ccMesh::normalsShown() const {
389  return (ccHObject::normalsShown() || triNormsShown());
390 }
391 
394  return false;
395 
396  unsigned nPts = m_associatedCloud->size();
397 
398  // instantiate memory for per-vertex mean SF
399  ScalarType* meanSF = new ScalarType[nPts];
400  if (!meanSF) {
401  // Not enough memory!
402  return false;
403  }
404 
405  // per-vertex counters
406  unsigned* count = new unsigned[nPts];
407  if (!count) {
408  // Not enough memory!
409  delete[] meanSF;
410  return false;
411  }
412 
413  // init arrays
414  {
415  for (unsigned i = 0; i < nPts; ++i) {
416  meanSF[i] = m_associatedCloud->getPointScalarValue(i);
417  count[i] = 1;
418  }
419  }
420 
421  // for each triangle
422  unsigned nTri = size();
423  {
425  for (unsigned i = 0; i < nTri; ++i) {
426  const cloudViewer::VerticesIndexes* tsi =
427  getNextTriangleVertIndexes(); // DGM:
428  // getNextTriangleVertIndexes
429  // is faster for mesh groups!
430 
431  // compute the sum of all connected vertices SF values
432  meanSF[tsi->i1] += m_associatedCloud->getPointScalarValue(tsi->i2);
433  meanSF[tsi->i2] += m_associatedCloud->getPointScalarValue(tsi->i3);
434  meanSF[tsi->i3] += m_associatedCloud->getPointScalarValue(tsi->i1);
435 
436  // TODO DGM: we could weight this by the vertices distance?
437  ++count[tsi->i1];
438  ++count[tsi->i2];
439  ++count[tsi->i3];
440  }
441  }
442 
443  // normalize
444  {
445  for (unsigned i = 0; i < nPts; ++i) meanSF[i] /= count[i];
446  }
447 
448  switch (process) {
449  case SMOOTH_MESH_SF: {
450  // Smooth = mean value
451  for (unsigned i = 0; i < nPts; ++i)
452  m_associatedCloud->setPointScalarValue(i, meanSF[i]);
453  } break;
454  case ENHANCE_MESH_SF: {
455  // Enhance = old value + (old value - mean value)
456  for (unsigned i = 0; i < nPts; ++i) {
457  ScalarType v = 2 * m_associatedCloud->getPointScalarValue(i) -
458  meanSF[i];
459  m_associatedCloud->setPointScalarValue(i, v > 0 ? v : 0);
460  }
461  } break;
462  }
463 
464  delete[] meanSF;
465  delete[] count;
466 
467  return true;
468 }
469 
471  bool autoReleaseOldTable /*=true*/) {
472  if (m_triNormals == triNormsTable) return;
473 
474  if (m_triNormals && autoReleaseOldTable) {
475  int childIndex = getChildIndex(m_triNormals);
477  m_triNormals = nullptr;
478  if (childIndex >= 0) removeChild(childIndex);
479  }
480 
481  m_triNormals = triNormsTable;
482  if (m_triNormals) {
483  m_triNormals->link();
484  int childIndex = getChildIndex(m_triNormals);
485  if (childIndex < 0) addChild(m_triNormals);
486  } else {
487  removePerTriangleNormalIndexes(); // auto-remove per-triangle indexes
488  // (we don't need them anymore)
489  }
490 }
491 
493  bool autoReleaseOldMaterialSet /*=true*/) {
494  if (m_materials == materialSet) return;
495 
496  if (m_materials && autoReleaseOldMaterialSet) {
497  int childIndex = getChildIndex(m_materials);
498  m_materials->release();
499  m_materials = nullptr;
500  if (childIndex >= 0) removeChild(childIndex);
501  }
502 
503  m_materials = materialSet;
504  if (m_materials) {
505  m_materials->link();
506  int childIndex = getChildIndex(m_materials);
507  if (childIndex < 0) addChild(m_materials);
508  } else {
509  removePerTriangleMtlIndexes(); // auto-remove per-triangle indexes (we
510  // don't need them anymore)
511  }
512 }
513 
515  // transparent call
517 
518  // we take care of per-triangle normals
519  //(vertices and per-vertex normals should be taken care of by the recursive
520  // call)
521  transformTriNormals(trans);
522 }
523 
525  // we must take care of the triangle normals!
526  if (m_triNormals &&
528  size_t numTriNormals = m_triNormals->size();
529 
530 #if 0 // no use to use memory for this!
531  bool recoded = false;
532 
533  //if there are more triangle normals than the size of the compressed
534  //normals array, we recompress the array instead of recompressing each normal
535  if (numTriNormals > ccNormalVectors::GetNumberOfVectors())
536  {
538  if (newNorms->reserve(ccNormalVectors::GetNumberOfVectors()))
539  {
540  //decode
541  {
542  for (unsigned i=0; i<ccNormalVectors::GetNumberOfVectors(); i++)
543  {
545  trans.applyRotation(new_n);
546  CompressedNormType newNormIndex = ccNormalVectors::GetNormIndex(new_n.u);
547  newNorms->emplace_back(newNormIndex);
548  }
549  }
550 
551  //recode
552  m_triNormals->placeIteratorAtBeginning();
553  {
554  for (unsigned i=0; i<numTriNormals; i++)
555  {
556  m_triNormals->setValue(i,newNorms->getValue(m_triNormals->getCurrentValue()));
557  m_triNormals->forwardIterator();
558  }
559  }
560  recoded = true;
561  }
562  newNorms->clear();
563  newNorms->release();
564  newNorms = 0;
565  }
566 
567  //if there are less triangle normals than the compressed normals array size
568  //(or if there is not enough memory to instantiate the temporary array),
569  //we recompress each normal ...
570  if (!recoded)
571 #endif
572  {
573  for (CompressedNormType& _theNormIndex : *m_triNormals) {
574  CCVector3 new_n(ccNormalVectors::GetNormal(_theNormIndex));
575  trans.applyRotation(new_n);
576  _theNormIndex = ccNormalVectors::GetNormIndex(new_n.u);
577  }
578  }
579  }
580 }
581 
584  void** additionalParameters,
586  std::vector<int>* equivalentIndexes =
587  static_cast<std::vector<int>*>(additionalParameters[0]);
588 
589  // we look for points very close to the others (only if not yet tagged!)
590 
591  // structure for nearest neighbors search
593  nNSS.level = cell.level;
594  static const PointCoordinateType c_defaultSearchRadius =
595  static_cast<PointCoordinateType>(sqrtf(ZERO_TOLERANCE_F));
596  nNSS.prepare(c_defaultSearchRadius,
597  cell.parentOctree->getCellSize(nNSS.level));
598  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
599  true);
600  cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level,
601  nNSS.cellCenter);
602 
603  unsigned n = cell.points->size(); // number of points in the current cell
604 
605  // we already know some of the neighbours: the points in the current cell!
606  try {
607  nNSS.pointsInNeighbourhood.resize(n);
608  } catch (... /*const std::bad_alloc&*/) // out of memory
609  {
610  return false;
611  }
612 
613  // init structure with cell points
614  {
615  cloudViewer::DgmOctree::NeighboursSet::iterator it =
616  nNSS.pointsInNeighbourhood.begin();
617  for (unsigned i = 0; i < n; ++i, ++it) {
618  it->point = cell.points->getPointPersistentPtr(i);
619  it->pointIndex = cell.points->getPointGlobalIndex(i);
620  }
621  nNSS.alreadyVisitedNeighbourhoodSize = 1;
622  }
623 
624  // for each point in the cell
625  for (unsigned i = 0; i < n; ++i) {
626  int thisIndex = static_cast<int>(cell.points->getPointGlobalIndex(i));
627  if (equivalentIndexes->at(thisIndex) < 0) // has no equivalent yet
628  {
629  cell.points->getPoint(i, nNSS.queryPoint);
630 
631  // look for neighbors in a (very small) sphere
632  // warning: there may be more points at the end of
633  // nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)!
634  unsigned k =
636  nNSS, c_defaultSearchRadius, false);
637 
638  // if there are some very close points
639  if (k > 1) {
640  for (unsigned j = 0; j < k; ++j) {
641  // all the other points are equivalent to the query point
642  const unsigned& otherIndex =
643  nNSS.pointsInNeighbourhood[j].pointIndex;
644  if (static_cast<int>(otherIndex) != thisIndex)
645  equivalentIndexes->at(otherIndex) = thisIndex;
646  }
647  }
648 
649  // and the query point is always root
650  equivalentIndexes->at(thisIndex) = thisIndex;
651  }
652 
653  if (nProgress && !nProgress->oneStep()) {
654  return false;
655  }
656  }
657 
658  return true;
659 }
660 
661 bool ccMesh::mergeDuplicatedVertices(unsigned char octreeLevel /*=10*/,
662  QWidget* parentWidget /*=nullptr*/) {
663  if (!m_associatedCloud) {
664  assert(false);
665  return false;
666  }
667 
668  unsigned vertCount = m_associatedCloud->size();
669  unsigned faceCount = size();
670  if (vertCount == 0 || faceCount == 0) {
672  "[ccMesh::mergeDuplicatedVertices] No triangle or no vertex");
673  return false;
674  }
675 
676  try {
677  std::vector<int> equivalentIndexes;
678  const int razValue = -1;
679  equivalentIndexes.resize(vertCount, razValue);
680 
681  // tag the duplicated vertices
682  {
683  QScopedPointer<ecvProgressDialog> pDlg(nullptr);
684  if (parentWidget) {
685  pDlg.reset(new ecvProgressDialog(true, parentWidget));
686  }
687 
688  // try to build the octree
691  if (!octree->build(pDlg.data())) {
692  CVLog::Warning("[MergeDuplicatedVertices] Not enough memory");
693  return false;
694  }
695 
696  void* additionalParameters[] = {
697  static_cast<void*>(&equivalentIndexes)};
699  10, TagDuplicatedVertices, additionalParameters, false,
700  pDlg.data(), "Tag duplicated vertices");
701 
702  if (result == 0) {
704  "[MergeDuplicatedVertices] Duplicated vertices removal "
705  "algorithm failed?!");
706  return false;
707  }
708  }
709 
710  unsigned remainingCount = 0;
711  for (unsigned i = 0; i < vertCount; ++i) {
712  int eqIndex = equivalentIndexes[i];
713  assert(eqIndex >= 0);
714  if (eqIndex == static_cast<int>(i)) // root point
715  {
716  // we replace the root index by its 'new' index (+ vertCount, to
717  // differentiate it later)
718  int newIndex = static_cast<int>(vertCount + remainingCount);
719  equivalentIndexes[i] = newIndex;
720  ++remainingCount;
721  }
722  }
723 
725  if (!newVerticesRef.reserve(remainingCount)) {
726  CVLog::Warning("[MergeDuplicatedVertices] Not enough memory");
727  return false;
728  }
729 
730  // copy root points in a new cloud
731  {
732  for (unsigned i = 0; i < vertCount; ++i) {
733  int eqIndex = equivalentIndexes[i];
734  if (eqIndex >= static_cast<int>(vertCount)) // root point
735  newVerticesRef.addPointIndex(i);
736  else
737  equivalentIndexes[i] =
738  equivalentIndexes[eqIndex]; // and update the other
739  // indexes
740  }
741  }
742 
743  ccPointCloud* newVertices = nullptr;
745  newVertices = static_cast<ccPointCloud*>(m_associatedCloud)
746  ->partialClone(&newVerticesRef);
747  } else {
748  newVertices =
749  ccPointCloud::From(&newVerticesRef, m_associatedCloud);
750  }
751  if (!newVertices) {
752  CVLog::Warning("[MergeDuplicatedVertices] Not enough memory");
753  return false;
754  }
755 
756  // update face indexes
757  {
758  unsigned newFaceCount = 0;
759  for (unsigned i = 0; i < faceCount; ++i) {
761  tri->i1 = static_cast<unsigned>(equivalentIndexes[tri->i1]) -
762  vertCount;
763  tri->i2 = static_cast<unsigned>(equivalentIndexes[tri->i2]) -
764  vertCount;
765  tri->i3 = static_cast<unsigned>(equivalentIndexes[tri->i3]) -
766  vertCount;
767 
768  // very small triangles (or flat ones) may be implicitly removed
769  // by vertex fusion!
770  if (tri->i1 != tri->i2 && tri->i1 != tri->i3 &&
771  tri->i2 != tri->i3) {
772  if (newFaceCount != i) swapTriangles(i, newFaceCount);
773  ++newFaceCount;
774  }
775  }
776 
777  if (newFaceCount == 0) {
779  "[MergeDuplicatedVertices] After vertex fusion, all "
780  "triangles would collapse! We'll keep the non-fused "
781  "version...");
782  delete newVertices;
783  newVertices = nullptr;
784  } else {
785  resize(newFaceCount);
786  }
787  }
788 
789  // update the mesh vertices
790  int childPos = getChildIndex(m_associatedCloud);
791  if (childPos >= 0) {
792  removeChild(childPos);
793  } else {
794  delete m_associatedCloud;
795  m_associatedCloud = nullptr;
796  }
797  setAssociatedCloud(newVertices);
798  if (childPos >= 0) {
800  }
801  vertCount = m_associatedCloud->size();
802  CVLog::Print(
803  "[MergeDuplicatedVertices] Remaining vertices after "
804  "auto-removal of duplicate ones: %i",
805  vertCount);
806  CVLog::Print(
807  "[MergeDuplicatedVertices] Remaining faces after auto-removal "
808  "of duplicate ones: %i",
809  size());
810  return false;
811  } catch (const std::bad_alloc&) {
813  "[MergeDuplicatedVertices] Not enough memory: could not remove "
814  "duplicated vertices!");
815  }
816 
817  return false;
818 }
819 
820 Eigen::Vector3d ccMesh::GetMinBound() const {
822  if (!cloud) {
823  return Eigen::Vector3d(0.0, 0.0, 0.0);
824  }
825  return ComputeMinBound(cloud->getEigenPoints());
826 }
827 
828 Eigen::Vector3d ccMesh::GetMaxBound() const {
830  if (!cloud) {
831  return Eigen::Vector3d(0.0, 0.0, 0.0);
832  }
833  return ComputeMaxBound(cloud->getEigenPoints());
834 }
835 
836 Eigen::Vector3d ccMesh::GetCenter() const {
838  if (!cloud) {
839  return Eigen::Vector3d(0.0, 0.0, 0.0);
840  }
841  return ComputeCenter(cloud->getEigenPoints());
842 }
843 
846 }
847 
850 }
851 
852 ccMesh& ccMesh::Transform(const Eigen::Matrix4d& transformation) {
854  if (!cloud) {
855  return *this;
856  }
857  ccGLMatrix mat = ccGLMatrix::FromEigenMatrix(transformation);
858  cloud->applyRigidTransformation(mat);
859  transformTriNormals(mat);
860  return *this;
861 }
862 
863 ccMesh& ccMesh::Translate(const Eigen::Vector3d& translation, bool relative) {
865  if (!cloud) {
866  return *this;
867  }
868  cloud->Translate(translation, relative);
869  return *this;
870 }
871 
872 ccMesh& ccMesh::Scale(const double s, const Eigen::Vector3d& center) {
874  if (!cloud) {
875  return *this;
876  }
877 
878  cloud->Scale(s, center);
879  return *this;
880 }
881 
882 ccMesh& ccMesh::Rotate(const Eigen::Matrix3d& R,
883  const Eigen::Vector3d& center) {
885  if (!cloud) {
886  return *this;
887  }
888 
889  ccGLMatrix trans;
890  trans.setRotation(R.data());
891  transformTriNormals(trans);
892 
893  trans.shiftRotationCenter(center);
894  cloud->applyRigidTransformation(trans);
895  return *this;
896 }
897 
898 std::shared_ptr<ccMesh> ccMesh::SelectByIndex(
899  const std::vector<size_t>& indices, bool cleanup) const {
900  if (hasTriangleUvs()) {
902  "[SelectByIndices] This mesh contains triangle uvs that are "
903  "not handled in this function");
904  }
905 
906  ccPointCloud* baseVertices = new ccPointCloud("vertices");
907  assert(baseVertices);
908 
909  auto output = std::make_shared<ccMesh>(baseVertices);
910  output->reserve(indices.size());
911  bool has_triangle_normals = hasTriNormals();
912  bool has_vertex_normals = hasNormals();
913  bool has_vertex_colors = hasColors();
914 
915  std::vector<int> new_vert_ind(getVerticeSize(), -1);
916  for (const auto& sel_vidx : indices) {
917  if (sel_vidx < 0 || sel_vidx >= getVerticeSize()) {
919  "[SelectByIndex] indices contains index {} out of range. "
920  "It is ignored.",
921  sel_vidx);
922  continue;
923  }
924  if (new_vert_ind[sel_vidx] >= 0) {
925  continue;
926  }
927  new_vert_ind[sel_vidx] = int(output->getVerticeSize());
928 
929  baseVertices->addPoint(*(getAssociatedCloud()->getPoint(
930  static_cast<unsigned>(sel_vidx))));
931  if (has_vertex_normals) {
932  if (!baseVertices->hasNormals()) {
933  baseVertices->reserveTheNormsTable();
934  }
935  baseVertices->addNorm(getAssociatedCloud()->getPointNormal(
936  static_cast<unsigned>(sel_vidx)));
937  }
938  if (has_vertex_colors) {
939  if (!baseVertices->hasColors()) {
940  baseVertices->reserveTheRGBTable();
941  }
942  baseVertices->addRGBColor(getAssociatedCloud()->getPointColor(
943  static_cast<unsigned>(sel_vidx)));
944  }
945  }
946 
947  for (size_t tidx = 0; tidx < size(); ++tidx) {
948  Eigen::Vector3i triangle;
949  getTriangleVertIndexes(tidx, triangle);
950  int nvidx0 = new_vert_ind[triangle(0)];
951  int nvidx1 = new_vert_ind[triangle(1)];
952  int nvidx2 = new_vert_ind[triangle(2)];
953  if (nvidx0 >= 0 && nvidx1 >= 0 && nvidx2 >= 0) {
954  output->addTriangle(Eigen::Vector3i(nvidx0, nvidx1, nvidx2));
955  if (has_triangle_normals) {
956  output->addTriangleNorm(getTriangleNorm(tidx));
957  }
958  }
959  }
960 
961  // do some cleaning
962  {
963  if (cleanup) {
964  output->RemoveDuplicatedVertices();
965  output->RemoveDuplicatedTriangles();
966  output->RemoveUnreferencedVertices();
967  output->RemoveDegenerateTriangles();
968  }
969 
970  baseVertices->shrinkToFit();
971  output->shrinkToFit();
972  NormsIndexesTableType* normals = output->getTriNormsTable();
973  if (normals) {
974  normals->shrink_to_fit();
975  }
976  }
977 
978  baseVertices->setEnabled(false);
979  // DGM: no need to lock it as it is only used by one mesh!
980  baseVertices->setLocked(false);
981  output->addChild(baseVertices);
982 
984  "Triangle mesh sampled from {:d} vertices and {:d} triangles to "
985  "{:d} vertices and {:d} triangles.",
986  (int)getVerticeSize(), (int)size(), (int)output->getVerticeSize(),
987  (int)output->size());
988 
989  return output;
990 }
991 
992 std::shared_ptr<ccMesh> ccMesh::Crop(const ccBBox& bbox) const {
993  if (!bbox.isValid()) {
995  "[ccMesh::Crop] ccBBox either has zeros "
996  "size, or has wrong bounds.");
997  return std::make_shared<ccMesh>(nullptr);
998  }
1000 }
1001 
1002 std::shared_ptr<ccMesh> ccMesh::Crop(const ecvOrientedBBox& bbox) const {
1003  if (bbox.IsEmpty()) {
1005  "[ccMesh::Crop] ecvOrientedBBox either has zeros "
1006  "size, or has wrong bounds.");
1007  return std::make_shared<ccMesh>(nullptr);
1008  }
1011 }
1012 
1013 bool ccMesh::laplacianSmooth(unsigned nbIteration,
1014  PointCoordinateType factor,
1015  ecvProgressDialog* progressCb /*=0*/) {
1016  if (!m_associatedCloud) return false;
1017 
1018  // vertices
1019  unsigned vertCount = m_associatedCloud->size();
1020  // triangles
1021  unsigned faceCount = size();
1022  if (!vertCount || !faceCount) return false;
1023 
1024  std::vector<CCVector3> verticesDisplacement;
1025  try {
1026  verticesDisplacement.resize(vertCount);
1027  } catch (const std::bad_alloc&) {
1028  // not enough memory
1029  return false;
1030  }
1031 
1032  // compute the number of edges to which belong each vertex
1033  std::vector<unsigned> edgesCount;
1034  try {
1035  edgesCount.resize(vertCount, 0);
1036  } catch (const std::bad_alloc&) {
1037  // not enough memory
1038  return false;
1039  }
1040 
1042  for (unsigned j = 0; j < faceCount; j++) {
1044  edgesCount[tri->i1] += 2;
1045  edgesCount[tri->i2] += 2;
1046  edgesCount[tri->i3] += 2;
1047  }
1048 
1049  // progress dialog
1050  cloudViewer::NormalizedProgress nProgress(progressCb, nbIteration);
1051  if (progressCb) {
1052  progressCb->setMethodTitle(QObject::tr("Laplacian smooth"));
1053  progressCb->setInfo(
1054  QObject::tr("Iterations: %1\nVertices: %2\nFaces: %3")
1055  .arg(nbIteration)
1056  .arg(vertCount)
1057  .arg(faceCount));
1058  progressCb->start();
1059  }
1060 
1061  // repeat Laplacian smoothing iterations
1062  for (unsigned iter = 0; iter < nbIteration; iter++) {
1063  std::fill(verticesDisplacement.begin(), verticesDisplacement.end(),
1064  CCVector3(0, 0, 0));
1065 
1066  // for each triangle
1068  for (unsigned j = 0; j < faceCount; j++) {
1069  const cloudViewer::VerticesIndexes* tri =
1071 
1072  const CCVector3* A = m_associatedCloud->getPoint(tri->i1);
1073  const CCVector3* B = m_associatedCloud->getPoint(tri->i2);
1074  const CCVector3* C = m_associatedCloud->getPoint(tri->i3);
1075 
1076  CCVector3 dAB = (*B - *A);
1077  CCVector3 dAC = (*C - *A);
1078  CCVector3 dBC = (*C - *B);
1079 
1080  verticesDisplacement[tri->i1] += dAB + dAC;
1081  verticesDisplacement[tri->i2] += dBC - dAB;
1082  verticesDisplacement[tri->i3] -= dAC + dBC;
1083  }
1084 
1085  if (!nProgress.oneStep()) {
1086  // cancelled by user
1087  break;
1088  }
1089 
1090  // apply displacement
1091  for (unsigned i = 0; i < vertCount; i++) {
1092  if (edgesCount[i]) {
1093  // this is a "persistent" pointer and we know what type of cloud
1094  // is behind ;)
1095  CCVector3* P = const_cast<CCVector3*>(
1097  (*P) += verticesDisplacement[i] * (factor / edgesCount[i]);
1098  }
1099  }
1100  }
1101 
1103 
1105 
1106  return true;
1107 }
1108 
1110  ccMaterialSet* clonedMaterials /*=0*/,
1111  NormsIndexesTableType* clonedNormsTable /*=0*/,
1112  TextureCoordsContainer* cloneTexCoords /*=0*/) {
1113  assert(m_associatedCloud);
1114 
1115  // vertices
1116  unsigned vertNum = m_associatedCloud->size();
1117  // triangles
1118  unsigned triNum = size();
1119 
1120  // temporary structure to check that vertices are really used (in case of
1121  // vertices set sharing)
1122  std::vector<unsigned> usedVerts;
1123 
1124  ccGenericPointCloud* newVertices = vertices;
1125 
1126  // no input vertices set
1127  if (!newVertices) {
1128  // let's check the real vertex count
1129  try {
1130  usedVerts.resize(vertNum, 0);
1131  } catch (const std::bad_alloc&) {
1132  CVLog::Error("[ccMesh::clone] Not enough memory!");
1133  return nullptr;
1134  }
1135 
1136  // flag used vertices
1137  {
1139  for (unsigned i = 0; i < triNum; ++i) {
1140  const cloudViewer::VerticesIndexes* tsi =
1142  usedVerts[tsi->i1] = 1;
1143  usedVerts[tsi->i2] = 1;
1144  usedVerts[tsi->i3] = 1;
1145  }
1146  }
1147 
1148  // we check that all points in 'associatedCloud' are used by this mesh
1149  unsigned realVertCount = 0;
1150  {
1151  for (unsigned i = 0; i < vertNum; ++i)
1152  usedVerts[i] = (usedVerts[i] == 1 ? realVertCount++ : vertNum);
1153  }
1154 
1155  // the associated cloud is already the exact vertices set --> nothing to
1156  // change
1157  if (realVertCount == vertNum) {
1158  newVertices = m_associatedCloud->clone(nullptr, true);
1159  } else {
1160  // we create a temporary entity with used vertices only
1162  if (rc.reserve(realVertCount)) {
1163  for (unsigned i = 0; i < vertNum; ++i) {
1164  if (usedVerts[i] != vertNum)
1165  rc.addPointIndex(i); // can't fail, see above
1166  }
1167 
1168  // and the associated vertices set
1170  newVertices = static_cast<ccPointCloud*>(m_associatedCloud)
1171  ->partialClone(&rc);
1172  if (newVertices && newVertices->size() < rc.size()) {
1173  // not enough memory!
1174  delete newVertices;
1175  newVertices = nullptr;
1176  }
1177  }
1178  }
1179  }
1180 
1181  // failed to create a new vertices set!
1182  if (!newVertices) {
1183  CVLog::Error("[ccMesh::clone] Not enough memory!");
1184  return nullptr;
1185  }
1186 
1187  // mesh clone
1188  ccMesh* cloneMesh = new ccMesh(newVertices);
1189  if (!cloneMesh->reserve(triNum)) {
1190  if (!vertices) delete newVertices;
1191  delete cloneMesh;
1192  CVLog::Error("[ccMesh::clone] Not enough memory!");
1193  return nullptr;
1194  }
1195 
1196  // let's create the new triangles
1197  if (!usedVerts.empty()) // in case we have an equivalence table
1198  {
1200  for (unsigned i = 0; i < triNum; ++i) {
1201  const cloudViewer::VerticesIndexes* tsi =
1203  cloneMesh->addTriangle(usedVerts[tsi->i1], usedVerts[tsi->i2],
1204  usedVerts[tsi->i3]);
1205  }
1206  usedVerts.resize(0);
1207  } else {
1209  for (unsigned i = 0; i < triNum; ++i) {
1210  const cloudViewer::VerticesIndexes* tsi =
1212  cloneMesh->addTriangle(tsi->i1, tsi->i2, tsi->i3);
1213  }
1214  }
1215 
1216  // triangle normals
1218  // 1st: try to allocate per-triangle normals indexes
1220  // 2nd: clone the main array if not already done
1221  if (!clonedNormsTable) {
1222  clonedNormsTable =
1223  m_triNormals
1224  ->clone(); // TODO: keep only what's necessary!
1225  if (clonedNormsTable)
1226  cloneMesh->addChild(clonedNormsTable);
1227  else {
1229  "[ccMesh::clone] Not enough memory: failed to "
1230  "clone per-triangle normals!");
1231  cloneMesh->removePerTriangleNormalIndexes(); // don't need
1232  // this
1233  // anymore!
1234  }
1235  }
1236 
1237  // if we have both the main array and per-triangle normals indexes,
1238  // we can finish the job
1239  if (cloneMesh) {
1240  cloneMesh->setTriNormsTable(clonedNormsTable);
1241  assert(cloneMesh->m_triNormalIndexes);
1243  *cloneMesh->m_triNormalIndexes); // should be ok as
1244  // array is already
1245  // reserved!
1246  }
1247  } else {
1249  "[ccMesh::clone] Not enough memory: failed to clone "
1250  "per-triangle normal indexes!");
1251  }
1252  }
1253 
1254  // materials
1255  if (m_materials && m_triMtlIndexes) {
1256  // 1st: try to allocate per-triangle materials indexes
1258  // 2nd: clone the main array if not already done
1259  if (!clonedMaterials) {
1260  clonedMaterials =
1261  getMaterialSet()
1262  ->clone(); // TODO: keep only what's necessary!
1263  if (clonedMaterials) {
1264  cloneMesh->addChild(clonedMaterials);
1265  } else {
1267  "[ccMesh::clone] Not enough memory: failed to "
1268  "clone materials set!");
1269  cloneMesh->removePerTriangleMtlIndexes(); // don't need
1270  // this anymore!
1271  }
1272  }
1273 
1274  // if we have both the main array and per-triangle materials
1275  // indexes, we can finish the job
1276  if (clonedMaterials) {
1277  cloneMesh->setMaterialSet(clonedMaterials);
1278  assert(cloneMesh->m_triMtlIndexes);
1280  *cloneMesh->m_triMtlIndexes); // should be ok as array
1281  // is already reserved!
1282  }
1283  } else {
1285  "[ccMesh::clone] Not enough memory: failed to clone "
1286  "per-triangle materials!");
1287  }
1288  }
1289 
1290  // texture coordinates
1291  if (m_texCoords && m_texCoordIndexes) {
1292  // 1st: try to allocate per-triangle texture info
1294  // 2nd: clone the main array if not already done
1295  if (!cloneTexCoords) {
1296  cloneTexCoords =
1297  m_texCoords
1298  ->clone(); // TODO: keep only what's necessary!
1299  if (!cloneTexCoords) {
1301  "[ccMesh::clone] Not enough memory: failed to "
1302  "clone texture coordinates!");
1304  // need this
1305  // anymore!
1306  }
1307  }
1308 
1309  // if we have both the main array and per-triangle texture info, we
1310  // can finish the job
1311  if (cloneTexCoords) {
1312  cloneMesh->setTexCoordinatesTable(cloneTexCoords);
1313  assert(cloneMesh->m_texCoordIndexes);
1315  *cloneMesh
1316  ->m_texCoordIndexes); // should be ok as array
1317  // is already reserved!
1318  }
1319  } else {
1321  "[ccMesh::clone] Not enough memory: failed to clone "
1322  "per-triangle texture info!");
1323  }
1324  }
1325 
1326  if (!vertices) {
1327  if (hasNormals() && !cloneMesh->hasNormals())
1329  newVertices->setEnabled(false);
1330  // we link the mesh structure with the new vertex set
1331  cloneMesh->addChild(newVertices);
1332  // cloneMesh->setDisplay_recursive(getDisplay());
1333  }
1334 
1337  cloneMesh->showSF(sfShown());
1339  cloneMesh->setName(getName() + QString(".clone"));
1343 
1344  return cloneMesh;
1345 }
1346 
1347 ccMesh* ccMesh::partialClone(const std::vector<unsigned>& triangleIndices,
1348  int* warnings /*=nullptr*/) const {
1349  assert(m_associatedCloud);
1350 
1351  if (triangleIndices.empty()) {
1352  CVLog::Warning("[ccMesh::partialClone] Empty triangle selection");
1353  return nullptr;
1354  }
1355 
1356  unsigned triNum = size();
1357 
1358  // Verify all triangle indices are valid
1359  for (unsigned triIdx : triangleIndices) {
1360  if (triIdx >= triNum) {
1361  CVLog::Error(QString("[ccMesh::partialClone] Triangle index %1 out "
1362  "of range [0, %2)")
1363  .arg(triIdx)
1364  .arg(triNum));
1365  return nullptr;
1366  }
1367  }
1368 
1369  // Build set of unique vertices used by selected triangles
1370  std::set<unsigned> usedVerticesSet;
1371  for (unsigned triIdx : triangleIndices) {
1372  const cloudViewer::VerticesIndexes* tri =
1373  getTriangleVertIndexes(triIdx);
1374  usedVerticesSet.insert(tri->i1);
1375  usedVerticesSet.insert(tri->i2);
1376  usedVerticesSet.insert(tri->i3);
1377  }
1378 
1379  // Create mapping from old vertex indices to new vertex indices
1380  std::map<unsigned, unsigned> vertexIndexMap;
1381  unsigned newVertexIndex = 0;
1382  for (unsigned oldIdx : usedVerticesSet) {
1383  vertexIndexMap[oldIdx] = newVertexIndex++;
1384  }
1385 
1386  // Clone the used vertices
1388  if (!refCloud.reserve(static_cast<unsigned>(usedVerticesSet.size()))) {
1389  CVLog::Error(
1390  "[ccMesh::partialClone] Not enough memory for vertex reference "
1391  "cloud");
1392  return nullptr;
1393  }
1394 
1395  for (unsigned oldIdx : usedVerticesSet) {
1396  refCloud.addPointIndex(oldIdx);
1397  }
1398 
1399  ccGenericPointCloud* newVertices = nullptr;
1401  int cloneWarnings = 0;
1402  newVertices = static_cast<ccPointCloud*>(m_associatedCloud)
1403  ->partialClone(&refCloud, &cloneWarnings);
1404  if (warnings) {
1405  *warnings = cloneWarnings;
1406  }
1407  } else {
1408  newVertices = m_associatedCloud->clone();
1409  }
1410 
1411  if (!newVertices) {
1412  CVLog::Error("[ccMesh::partialClone] Failed to clone vertices");
1413  return nullptr;
1414  }
1415 
1416  // Create new mesh
1417  ccMesh* partialMesh = new ccMesh(newVertices);
1418  if (!partialMesh->reserve(static_cast<unsigned>(triangleIndices.size()))) {
1419  delete newVertices;
1420  delete partialMesh;
1421  CVLog::Error(
1422  "[ccMesh::partialClone] Not enough memory for mesh triangles");
1423  return nullptr;
1424  }
1425 
1426  // Add triangles with remapped vertex indices
1427  for (unsigned oldTriIdx : triangleIndices) {
1428  const cloudViewer::VerticesIndexes* tri =
1429  getTriangleVertIndexes(oldTriIdx);
1430  partialMesh->addTriangle(vertexIndexMap[tri->i1],
1431  vertexIndexMap[tri->i2],
1432  vertexIndexMap[tri->i3]);
1433  }
1434 
1435  // Clone triangle normals if present
1437  if (partialMesh->reservePerTriangleNormalIndexes()) {
1438  NormsIndexesTableType* clonedNormsTable = m_triNormals->clone();
1439  if (clonedNormsTable) {
1440  partialMesh->setTriNormsTable(clonedNormsTable, true);
1441  partialMesh->addChild(clonedNormsTable);
1442 
1443  // Copy normal indexes for selected triangles
1444  for (unsigned oldTriIdx : triangleIndices) {
1445  int i1, i2, i3;
1446  getTriangleNormalIndexes(oldTriIdx, i1, i2, i3);
1447  partialMesh->addTriangleNormalIndexes(i1, i2, i3);
1448  }
1449  } else {
1451  "[ccMesh::partialClone] Failed to clone triangle "
1452  "normals table");
1453  partialMesh->removePerTriangleNormalIndexes();
1454  }
1455  } else {
1457  "[ccMesh::partialClone] Failed to reserve triangle normal "
1458  "indexes");
1459  }
1460  }
1461 
1462  // Clone materials if present
1463  if (m_materials && m_triMtlIndexes) {
1464  if (partialMesh->reservePerTriangleMtlIndexes()) {
1465  ccMaterialSet* clonedMaterials = getMaterialSet()->clone();
1466  if (clonedMaterials) {
1467  partialMesh->setMaterialSet(clonedMaterials, true);
1468  partialMesh->addChild(clonedMaterials);
1469 
1470  // Copy material indexes for selected triangles
1471  for (unsigned oldTriIdx : triangleIndices) {
1472  int mtlIdx = getTriangleMtlIndex(oldTriIdx);
1473  partialMesh->addTriangleMtlIndex(mtlIdx);
1474  }
1475  } else {
1477  "[ccMesh::partialClone] Failed to clone materials");
1478  partialMesh->removePerTriangleMtlIndexes();
1479  }
1480  } else {
1482  "[ccMesh::partialClone] Failed to reserve triangle "
1483  "material indexes");
1484  }
1485  }
1486 
1487  // Clone texture coordinates if present
1488  if (m_texCoords && m_texCoordIndexes) {
1489  if (partialMesh->reservePerTriangleTexCoordIndexes()) {
1490  TextureCoordsContainer* clonedTexCoords = m_texCoords->clone();
1491  if (clonedTexCoords) {
1492  partialMesh->setTexCoordinatesTable(clonedTexCoords);
1493 
1494  // Copy texture coordinate indexes for selected triangles
1495  for (unsigned oldTriIdx : triangleIndices) {
1496  int t1, t2, t3;
1497  getTriangleTexCoordinatesIndexes(oldTriIdx, t1, t2, t3);
1498  partialMesh->addTriangleTexCoordIndexes(t1, t2, t3);
1499  }
1500  } else {
1502  "[ccMesh::partialClone] Failed to clone texture "
1503  "coordinates");
1504  partialMesh->removePerTriangleTexCoordIndexes();
1505  }
1506  } else {
1508  "[ccMesh::partialClone] Failed to reserve triangle texture "
1509  "coordinate indexes");
1510  }
1511  }
1512 
1513  // Set display properties
1514  newVertices->setEnabled(false);
1515  partialMesh->addChild(newVertices);
1516 
1517  if (hasNormals() && !partialMesh->hasNormals()) {
1518  partialMesh->computeNormals(!hasTriNormals());
1519  }
1520 
1521  partialMesh->showNormals(normalsShown());
1522  partialMesh->showColors(colorsShown());
1523  partialMesh->showSF(sfShown());
1524  partialMesh->showMaterials(materialsShown());
1525  partialMesh->setName(getName() + QString(".partial"));
1526  partialMesh->setVisible(isVisible());
1527  partialMesh->setEnabled(isEnabled());
1528  partialMesh->importParametersFrom(this);
1529 
1530  CVLog::Print(QString("[ccMesh::partialClone] Created partial mesh: %1 "
1531  "triangles, %2 vertices")
1532  .arg(triangleIndices.size())
1533  .arg(usedVerticesSet.size()));
1534 
1535  return partialMesh;
1536 }
1537 
1539  ccPolyline* p2,
1540  CCVector3* projectionDir /*=0*/) {
1541  if (!p1 || p1->size() == 0 || !p2 || p2->size() == 0) {
1542  assert(false);
1543  return nullptr;
1544  }
1545 
1546  ccPointCloud* vertices = new ccPointCloud("vertices");
1547  if (!vertices->reserve(p1->size() + p2->size())) {
1548  CVLog::Warning("[ccMesh::TriangulateTwoPolylines] Not enough memory");
1549  delete vertices;
1550  return nullptr;
1551  }
1552 
1553  // merge the two sets of vertices
1554  {
1555  for (unsigned i = 0; i < p1->size(); ++i)
1556  vertices->addPoint(*p1->getPoint(i));
1557  for (unsigned j = 0; j < p2->size(); ++j)
1558  vertices->addPoint(*p2->getPoint(j));
1559  }
1560  assert(vertices->size() != 0);
1561 
1562  cloudViewer::Neighbourhood N(vertices);
1563 
1564  // get plane coordinate system
1565  CCVector3 O = *N.getGravityCenter();
1566  CCVector3 X(1, 0, 0), Y(0, 1, 0);
1567  if (projectionDir) {
1568  // use the input projection dir.
1569  X = projectionDir->orthogonal();
1570  Y = projectionDir->cross(X);
1571  } else {
1572  // use the best fit plane (normal)
1573  if (!N.getLSPlane()) {
1575  "[ccMesh::TriangulateTwoPolylines] Failed to fit a plane "
1576  "through both polylines");
1577  delete vertices;
1578  return nullptr;
1579  }
1580 
1581  X = *N.getLSPlaneX();
1582  Y = *N.getLSPlaneY();
1583  }
1584 
1585  std::vector<CCVector2> points2D;
1586  std::vector<int> segments2D;
1587  try {
1588  points2D.reserve(p1->size() + p2->size());
1589  segments2D.reserve(p1->segmentCount() + p2->segmentCount());
1590  } catch (const std::bad_alloc&) {
1591  // not enough memory
1592  CVLog::Warning("[ccMesh::TriangulateTwoPolylines] Not enough memory");
1593  delete vertices;
1594  return nullptr;
1595  }
1596 
1597  // project the polylines on the best fitting plane
1598  {
1599  ccPolyline* polylines[2] = {p1, p2};
1600  for (size_t i = 0; i < 2; ++i) {
1601  ccPolyline* poly = polylines[i];
1602  unsigned vertCount = poly->size();
1603  int vertIndex0 = static_cast<int>(points2D.size());
1604  bool closed = poly->isClosed();
1605  for (unsigned v = 0; v < vertCount; ++v) {
1606  const CCVector3* P = poly->getPoint(v);
1607  int vertIndex = static_cast<int>(points2D.size());
1608 
1609  CCVector3 OP = *P - O;
1610  CCVector2 P2D(OP.dot(X), OP.dot(Y));
1611  points2D.emplace_back(P2D);
1612 
1613  if (v + 1 < vertCount) {
1614  segments2D.emplace_back(vertIndex);
1615  segments2D.emplace_back(vertIndex + 1);
1616  } else if (closed) {
1617  segments2D.emplace_back(vertIndex);
1618  segments2D.emplace_back(vertIndex0);
1619  }
1620  }
1621  }
1622  assert(points2D.size() == p1->size() + p2->size());
1623  assert(segments2D.size() ==
1624  (p1->segmentCount() + p2->segmentCount()) * 2);
1625  }
1626 
1628  std::string errorStr;
1629  if (!delaunayMesh->buildMesh(points2D, segments2D, errorStr)) {
1630  CVLog::Warning(QString("Third party library error: %1")
1631  .arg(QString::fromStdString(errorStr)));
1632  delete delaunayMesh;
1633  delete vertices;
1634  return nullptr;
1635  }
1636 
1637  delaunayMesh->linkMeshWith(vertices, false);
1638 
1639  // remove the points oustide of the 'concave' hull
1640  {
1641  // first compute the Convex hull
1642  std::vector<cloudViewer::PointProjectionTools::IndexedCCVector2>
1643  indexedPoints2D;
1644  try {
1645  indexedPoints2D.resize(points2D.size());
1646  for (size_t i = 0; i < points2D.size(); ++i) {
1647  indexedPoints2D[i] = points2D[i];
1648  indexedPoints2D[i].index = static_cast<unsigned>(i);
1649  }
1650 
1651  std::list<cloudViewer::PointProjectionTools::IndexedCCVector2*>
1652  hullPoints;
1654  indexedPoints2D, hullPoints)) {
1655  std::list<cloudViewer::PointProjectionTools::
1656  IndexedCCVector2*>::iterator A =
1657  hullPoints.begin();
1658  for (; A != hullPoints.end(); ++A) {
1659  // current hull segment
1660  std::list<cloudViewer::PointProjectionTools::
1661  IndexedCCVector2*>::iterator B = A;
1662  ++B;
1663  if (B == hullPoints.end()) {
1664  B = hullPoints.begin();
1665  }
1666 
1667  unsigned Aindex = (*A)->index;
1668  unsigned Bindex = (*B)->index;
1669  int Apoly = (Aindex < p1->size() ? 0 : 1);
1670  int Bpoly = (Bindex < p1->size() ? 0 : 1);
1671  // both vertices belong to the same polyline
1672  if (Apoly == Bpoly) {
1673  // if it creates an outer loop
1674  if (abs(static_cast<int>(Bindex) -
1675  static_cast<int>(Aindex)) > 1) {
1676  // create the corresponding contour
1677  unsigned iStart = std::min(Aindex, Bindex);
1678  unsigned iStop = std::max(Aindex, Bindex);
1679  std::vector<CCVector2> contour;
1680  contour.reserve(iStop - iStart + 1);
1681  for (unsigned j = iStart; j <= iStop; ++j) {
1682  contour.emplace_back(points2D[j]);
1683  }
1684  delaunayMesh->removeOuterTriangles(
1685  points2D, contour,
1686  /*remove inside = */ false);
1687  }
1688  }
1689  }
1690  } else {
1692  "[ccMesh::TriangulateTwoPolylines] Failed to compute "
1693  "the convex hull (can't clean the mesh borders)");
1694  }
1695  } catch (const std::bad_alloc&) {
1697  "[ccMesh::TriangulateTwoPolylines] Not enough memory to "
1698  "clean the mesh borders");
1699  }
1700  }
1701 
1702  ccMesh* mesh = new ccMesh(delaunayMesh, vertices);
1703  if (mesh->size() != delaunayMesh->size()) {
1704  // not enough memory (error will be issued later)
1705  delete mesh;
1706  mesh = nullptr;
1707  }
1708 
1709  // don't need this anymore
1710  delete delaunayMesh;
1711  delaunayMesh = nullptr;
1712 
1713  if (mesh) {
1714  mesh->addChild(vertices);
1715  mesh->setVisible(true);
1716  vertices->setEnabled(false);
1717 
1718  // global shift & scale (we copy it from the first polyline by default)
1719  vertices->setGlobalShift(p1->getGlobalShift());
1720  vertices->setGlobalScale(p1->getGlobalScale());
1721  // same thing for the display
1722  // mesh->setDisplay(p1->getDisplay());
1723  } else {
1724  CVLog::Warning("[ccMesh::TriangulateTwoPolylines] Not enough memory");
1725  delete vertices;
1726  vertices = nullptr;
1727  }
1728 
1729  return mesh;
1730 }
1731 
1734  bool updateNormals /*=false*/,
1735  PointCoordinateType maxEdgeLength /*=0*/,
1736  unsigned char dim /*=2*/) {
1737  if (!cloud || dim > 2) {
1738  CVLog::Warning("[ccMesh::Triangulate] Invalid input parameters!");
1739  return nullptr;
1740  }
1741  if (cloud->size() < 3) {
1742  CVLog::Warning("[ccMesh::Triangulate] Cloud has not enough points!");
1743  return nullptr;
1744  }
1745 
1746  // compute raw mesh
1747  std::string errorStr;
1748  cloudViewer::GenericIndexedMesh* dummyMesh =
1750  cloud, type, maxEdgeLength, dim, errorStr);
1751  if (!dummyMesh) {
1752  CVLog::Warning(QString("[ccMesh::Triangulate] Failed to construct "
1753  "Delaunay mesh (Triangle lib error: %1)")
1754  .arg(QString::fromStdString(errorStr)));
1755  return nullptr;
1756  }
1757 
1758  // convert raw mesh to ccMesh
1759  ccMesh* mesh = new ccMesh(dummyMesh, cloud);
1760 
1761  // don't need this anymore
1762  delete dummyMesh;
1763  dummyMesh = nullptr;
1764 
1765  if (!mesh) {
1767  "[ccMesh::Triangulate] An error occurred while computing mesh! "
1768  "(not enough memory?)");
1769  return nullptr;
1770  }
1771 
1772  mesh->setName(cloud->getName() + QString(".mesh"));
1773  // mesh->setDisplay(cloud->getDisplay());
1774  bool cloudHadNormals = cloud->hasNormals();
1775  // compute per-vertex normals if necessary
1776  if (!cloudHadNormals || updateNormals) {
1777  mesh->computeNormals(true);
1778  }
1779  mesh->showNormals(cloudHadNormals || !cloud->hasColors());
1780  if (mesh->getAssociatedCloud() && mesh->getAssociatedCloud() != cloud) {
1783  }
1784 
1785  return mesh;
1786 }
1787 
1788 bool ccMesh::merge(const ccMesh* mesh, bool createSubMesh) {
1789  if (!mesh) {
1790  assert(false);
1791  CVLog::Warning("[ccMesh::merge] Internal error: invalid input!");
1792  return false;
1793  }
1794  if (!mesh->getAssociatedCloud() ||
1797  assert(false);
1799  "[ccMesh::merge] Requires meshes with standard vertices!");
1800  return false;
1801  }
1802  ccPointCloud* vertices =
1803  static_cast<ccPointCloud*>(mesh->getAssociatedCloud());
1804 
1805  // vertices count (before merge)
1806  const unsigned vertNumBefore = m_associatedCloud->size();
1807  // triangles count (before merge)
1808  const unsigned triNumBefore = size();
1809 
1810  bool success = false;
1811 
1812  for (int iteration = 0; iteration < 1;
1813  ++iteration) // fake loop for easy breaking/cleaning
1814  {
1815  // merge vertices
1816  unsigned vertIndexShift = 0;
1817  if (mesh->getAssociatedCloud() != m_associatedCloud) {
1818  unsigned vertAdded = mesh->getAssociatedCloud()->size();
1819  static_cast<ccPointCloud*>(m_associatedCloud)
1820  ->append(vertices, m_associatedCloud->size(), true);
1821 
1822  // not enough memory?
1823  if (m_associatedCloud->size() < vertNumBefore + vertAdded) {
1824  CVLog::Warning("[ccMesh::merge] Not enough memory!");
1825  break;
1826  }
1827  vertIndexShift = vertNumBefore;
1828  if (vertNumBefore == 0) {
1829  // use the first merged cloud display properties
1830  m_associatedCloud->setVisible(vertices->isVisible());
1831  m_associatedCloud->setEnabled(vertices->isEnabled());
1832  }
1833  }
1834  showNormals(this->normalsShown() || mesh->normalsShown());
1835  showColors(this->colorsShown() || mesh->colorsShown());
1836  showSF(this->sfShown() || mesh->sfShown());
1837 
1838  // now for the triangles
1839  const unsigned triAdded = mesh->size();
1840  bool otherMeshHasMaterials =
1841  (mesh->m_materials && mesh->m_triMtlIndexes);
1842  bool otherMeshHasTexCoords =
1843  (mesh->m_texCoords && mesh->m_texCoordIndexes);
1844  bool otherMeshHasTriangleNormals =
1845  (mesh->m_triNormals && mesh->m_triNormalIndexes);
1846  {
1847  if (!reserve(triNumBefore + triAdded)) {
1848  CVLog::Warning("[ccMesh::merge] Not enough memory!");
1849  break;
1850  }
1851 
1852  // we'll need those arrays later
1853  if ((/*otherMeshHasMaterials && */ !m_triMtlIndexes &&
1855  (otherMeshHasTexCoords && !m_texCoordIndexes &&
1857  (otherMeshHasTriangleNormals && !m_triNormalIndexes &&
1859  CVLog::Warning("[ccMesh::merge] Not enough memory!");
1860  break;
1861  }
1862 
1863  for (unsigned i = 0; i < triAdded; ++i) {
1864  const cloudViewer::VerticesIndexes* tsi =
1865  mesh->getTriangleVertIndexes(i);
1866  addTriangle(vertIndexShift + tsi->i1, vertIndexShift + tsi->i2,
1867  vertIndexShift + tsi->i3);
1868  }
1869  }
1870 
1871  // triangle normals
1873  if (HasTriangleNormals || otherMeshHasTriangleNormals) {
1874  // 1st: does the other mesh has triangle normals
1875  if (otherMeshHasTriangleNormals) {
1876  size_t triIndexShift = 0;
1877  if (m_triNormals != mesh->m_triNormals) {
1878  // reserve mem for triangle normals
1879  if (!m_triNormals) {
1880  NormsIndexesTableType* normsTable =
1881  new NormsIndexesTableType();
1882  setTriNormsTable(normsTable);
1883  }
1884  assert(m_triNormals);
1885  size_t triNormalsCountBefore = m_triNormals->size();
1886  if (!m_triNormals->reserveSafe(
1887  triNormalsCountBefore +
1888  mesh->m_triNormals->size())) {
1889  CVLog::Warning("[ccMesh::merge] Not enough memory!");
1890  break;
1891  }
1892  // copy the values
1893  {
1894  for (unsigned i = 0; i < mesh->m_triNormals->size();
1895  ++i)
1896  m_triNormals->emplace_back(
1897  mesh->m_triNormals->getValue(i));
1898  }
1899  triIndexShift = triNormalsCountBefore;
1900  }
1901 
1902  // the indexes should have already been resized by the call to
1903  // 'reserve'!
1904  assert(m_triNormalIndexes->capacity() >=
1905  triNumBefore + triAdded);
1906  // copy the values
1907  {
1908  for (unsigned i = 0; i < mesh->m_triNormalIndexes->size();
1909  ++i) {
1910  const Tuple3i& indexes =
1911  mesh->m_triNormalIndexes->at(i);
1912  Tuple3i newIndexes(
1913  indexes.u[0] < 0
1914  ? -1
1915  : indexes.u[0] + static_cast<int>(
1916  triIndexShift),
1917  indexes.u[1] < 0
1918  ? -1
1919  : indexes.u[1] + static_cast<int>(
1920  triIndexShift),
1921  indexes.u[2] < 0
1922  ? -1
1923  : indexes.u[2] +
1924  static_cast<int>(
1925  triIndexShift));
1926  m_triNormalIndexes->emplace_back(newIndexes);
1927  }
1928  }
1929  } else {
1930  // the indexes should have already been resized by the call to
1931  // 'reserve'!
1932  assert(m_triNormalIndexes->capacity() >=
1933  triNumBefore + triAdded);
1934  // fill the indexes table with default values
1935  {
1936  Tuple3i defaultElement(-1, -1, -1);
1937  for (unsigned i = 0; i < mesh->size(); ++i)
1938  m_triNormalIndexes->emplace_back(defaultElement);
1939  }
1940  }
1941  showTriNorms(this->triNormsShown() || mesh->triNormsShown());
1942  }
1943 
1944  // materials
1946  if (hasMaterials || otherMeshHasMaterials) {
1947  // 1st: does the other mesh has materials?
1948  if (otherMeshHasMaterials) {
1949  std::vector<int> materialIndexMap;
1950  if (m_materials != mesh->m_materials) {
1951  // reserve mem for materials
1952  if (!m_materials) {
1953  ccMaterialSet* set = new ccMaterialSet("materials");
1954  setMaterialSet(set);
1955  }
1956  assert(m_materials);
1957 
1958  size_t otherMatSetSize = mesh->m_materials->size();
1959  try {
1960  materialIndexMap.resize(otherMatSetSize, -1);
1961  } catch (const std::bad_alloc&) {
1962  CVLog::Warning("[ccMesh::merge] Not enough memory!");
1963  break;
1964  }
1965  // update map table
1966  for (size_t m = 0; m != otherMatSetSize; ++m) {
1967  materialIndexMap[m] = m_materials->addMaterial(
1968  mesh->m_materials->at(m));
1969  }
1970  }
1971 
1972  // the indexes should have already been resized by the call to
1973  // 'reserve'!
1974  assert(m_triMtlIndexes->capacity() >= triNumBefore + triAdded);
1975  // copy the values
1976  {
1977  for (unsigned i = 0; i < mesh->m_triMtlIndexes->size();
1978  ++i) {
1979  int index = mesh->m_triMtlIndexes->getValue(i);
1980  assert(index <
1981  static_cast<int>(materialIndexMap.size()));
1982  int newIndex =
1983  (index < 0 ? -1 : materialIndexMap[index]);
1984  m_triMtlIndexes->emplace_back(newIndex);
1985  }
1986  }
1987  } else {
1988  // the indexes should have already been resized by the call to
1989  // 'reserve'!
1990  assert(m_triMtlIndexes->capacity() >= triNumBefore + triAdded);
1991  // fill the indexes table with default values
1992  {
1993  for (unsigned i = 0; i < mesh->size(); ++i)
1994  m_triMtlIndexes->emplace_back(-1);
1995  }
1996  }
1997  }
1998  showMaterials(this->materialsShown() || mesh->materialsShown());
1999 
2000  // texture coordinates
2001  bool hasTexCoords = m_texCoords && m_texCoordIndexes;
2002  if (hasTexCoords || otherMeshHasTexCoords) {
2003  // 1st: does the other mesh has texture coordinates?
2004  if (otherMeshHasTexCoords) {
2005  size_t texCoordIndexShift = 0;
2006  if (m_texCoords != mesh->m_texCoords) {
2007  // reserve mem for triangle normals
2008  if (!m_texCoords) {
2009  TextureCoordsContainer* texCoordsTable =
2011  setTexCoordinatesTable(texCoordsTable);
2012  }
2013  assert(m_texCoords);
2014  size_t texCoordCountBefore = m_texCoords->size();
2015  if (!m_texCoords->reserveSafe(texCoordCountBefore +
2016  mesh->m_texCoords->size())) {
2017  CVLog::Warning("[ccMesh::merge] Not enough memory!");
2018  break;
2019  }
2020  // copy the values
2021  {
2022  static const TexCoords2D TxDef(-1.0f, -1.0f);
2023  for (unsigned i = 0; i < mesh->m_texCoords->size();
2024  ++i) {
2025  const TexCoords2D& T = mesh->m_texCoords->at(i);
2026  m_texCoords->emplace_back(T);
2027  }
2028  }
2029  texCoordIndexShift = texCoordCountBefore;
2030  }
2031 
2032  // the indexes should have already been resized by the call to
2033  // 'reserve'!
2034  assert(m_texCoordIndexes->capacity() >=
2035  triNumBefore + triAdded);
2036  // copy the values
2037  {
2038  for (unsigned i = 0; i < mesh->m_texCoordIndexes->size();
2039  ++i) {
2040  const Tuple3i& indexes =
2041  mesh->m_texCoordIndexes->getValue(i);
2042  Tuple3i newIndexes(
2043  indexes.u[0] < 0
2044  ? -1
2045  : indexes.u[0] +
2046  static_cast<int>(
2047  texCoordIndexShift),
2048  indexes.u[1] < 0
2049  ? -1
2050  : indexes.u[1] +
2051  static_cast<int>(
2052  texCoordIndexShift),
2053  indexes.u[2] < 0
2054  ? -1
2055  : indexes.u[2] +
2056  static_cast<int>(
2057  texCoordIndexShift));
2058  m_texCoordIndexes->emplace_back(newIndexes);
2059  }
2060  }
2061  } else {
2062  // the indexes should have already been resized by the call to
2063  // 'reserve'!
2064  assert(m_texCoordIndexes->capacity() >=
2065  triNumBefore + triAdded);
2066  // fill the indexes table with default values
2067  {
2068  Tuple3i defaultElement(-1, -1, -1);
2069  for (unsigned i = 0; i < mesh->m_texCoordIndexes->size();
2070  ++i)
2071  m_texCoordIndexes->emplace_back(defaultElement);
2072  }
2073  }
2074  }
2075 
2076  // the end!
2077  showWired(this->isShownAsWire() || mesh->isShownAsWire());
2078  showPoints(this->isShownAsPoints() || mesh->isShownAsPoints());
2080  success = true;
2081  }
2082 
2083  if (createSubMesh) {
2084  // triangles count (after merge)
2085  const unsigned triNumAfter = size();
2086 
2087  ccSubMesh* subMesh = new ccSubMesh(this);
2088  if (subMesh->reserve(triNumAfter - triNumBefore)) {
2089  subMesh->addTriangleIndex(triNumBefore, triNumAfter);
2090  subMesh->setName(mesh->getName());
2091  subMesh->showMaterials(materialsShown());
2092  subMesh->showNormals(normalsShown());
2093  subMesh->showTriNorms(triNormsShown());
2094  subMesh->showColors(colorsShown());
2095  subMesh->showWired(isShownAsWire());
2096  subMesh->showPoints(isShownAsPoints());
2097  subMesh->enableStippling(stipplingEnabled());
2098  subMesh->setEnabled(false);
2099  addChild(subMesh);
2100  } else {
2101  CVLog::Warning(QString("[Merge] Not enough memory to create the "
2102  "sub-mesh corresponding to mesh '%1'!")
2103  .arg(mesh->getName()));
2104  delete subMesh;
2105  subMesh = nullptr;
2106  }
2107  }
2108 
2109  // textures and materials
2110  {
2111  textures_ = mesh->textures_;
2112  triangle_uvs_ = mesh->triangle_uvs_;
2115  }
2116 
2117  if (!success) {
2118  // revert to original state
2119  static_cast<ccPointCloud*>(m_associatedCloud)->resize(vertNumBefore);
2120  resize(triNumBefore);
2121  }
2122 
2123  return success;
2124 }
2125 
2128  assert(cloud);
2129  if (cloud && cloud->hasPoints()) {
2130  cloud->clear();
2131  }
2132 
2133  resize(0);
2134  clearTriNormals();
2135 
2136  adjacency_list_.clear();
2137  triangle_uvs_.clear();
2138  materials_.clear();
2139  triangle_material_ids_.clear();
2140  textures_.clear();
2141 }
2142 
2143 unsigned ccMesh::size() const {
2144  return static_cast<unsigned>(m_triVertIndexes->size());
2145 }
2146 
2147 unsigned ccMesh::capacity() const {
2148  return static_cast<unsigned>(m_triVertIndexes->capacity());
2149 }
2150 
2152  if (!m_associatedCloud) return;
2153 
2154  for (unsigned i = 0; i < m_triVertIndexes->size(); ++i) {
2155  const cloudViewer::VerticesIndexes& tri = m_triVertIndexes->at(i);
2159  action(m_currentTriangle);
2160  }
2161 }
2162 
2164 
2166  if (m_globalIterator < m_triVertIndexes->size()) {
2167  return _getTriangle(m_globalIterator++);
2168  }
2169 
2170  return nullptr;
2171 }
2172 
2174  unsigned triangleIndex) // temporary
2175 {
2176  assert(triangleIndex < m_triVertIndexes->size());
2177 
2178  const cloudViewer::VerticesIndexes& tri =
2179  m_triVertIndexes->getValue(triangleIndex);
2183 
2184  return &m_currentTriangle;
2185 }
2186 
2187 void ccMesh::getTriangleVertices(unsigned triangleIndex,
2188  CCVector3& A,
2189  CCVector3& B,
2190  CCVector3& C) const {
2191  assert(triangleIndex < m_triVertIndexes->size());
2192 
2193  const cloudViewer::VerticesIndexes& tri =
2194  m_triVertIndexes->getValue(triangleIndex);
2195  m_associatedCloud->getPoint(tri.i1, A);
2196  m_associatedCloud->getPoint(tri.i2, B);
2197  m_associatedCloud->getPoint(tri.i3, C);
2198 }
2199 
2200 void ccMesh::getTriangleVertices(unsigned triangleIndex,
2201  double A[3],
2202  double B[3],
2203  double C[3]) const {
2204  assert(triangleIndex < m_triVertIndexes->size());
2205 
2206  const cloudViewer::VerticesIndexes& tri =
2207  m_triVertIndexes->getValue(triangleIndex);
2208  m_associatedCloud->getPoint(tri.i1, A);
2209  m_associatedCloud->getPoint(tri.i2, B);
2210  m_associatedCloud->getPoint(tri.i3, C);
2211 }
2212 
2213 Eigen::Vector3d ccMesh::getVertice(size_t index) const {
2214  if (!getAssociatedCloud()) {
2215  return Eigen::Vector3d();
2216  }
2217 
2219  ->getEigenPoint(index);
2220 }
2221 
2222 void ccMesh::setVertice(size_t index, const Eigen::Vector3d& vertice) {
2223  if (!getAssociatedCloud()) {
2224  return;
2225  }
2227  ->setEigenPoint(index, vertice);
2228 }
2229 
2230 void ccMesh::addVertice(const Eigen::Vector3d& vertice) {
2231  if (!getAssociatedCloud()) {
2232  return;
2233  }
2235 }
2236 
2237 void ccMesh::setVertexNormal(size_t index, const Eigen::Vector3d& normal) {
2238  if (m_associatedCloud) {
2240  cloud->setPointNormal(index, normal);
2241  }
2242 }
2243 
2244 void ccMesh::addVertexNormal(const Eigen::Vector3d& normal) {
2245  if (m_associatedCloud) {
2247  cloud->addEigenNorm(normal);
2248  }
2249 }
2250 
2251 Eigen::Vector3d ccMesh::getVertexNormal(size_t index) const {
2252  if (!m_associatedCloud) {
2253  return Eigen::Vector3d();
2254  }
2256  ->getEigenNormal(index);
2257 }
2258 
2259 void ccMesh::setVertexNormals(const std::vector<Eigen::Vector3d>& normals) {
2260  if (m_associatedCloud && normals.size() == m_associatedCloud->size()) {
2262  cloud->setEigenNormals(normals);
2263  }
2264 }
2265 
2266 std::vector<Eigen::Vector3d> ccMesh::getVertexNormals() const {
2267  if (!m_associatedCloud) {
2268  return std::vector<Eigen::Vector3d>();
2269  }
2271 }
2272 
2273 void ccMesh::addVertexNormals(const std::vector<Eigen::Vector3d>& normals) {
2274  if (m_associatedCloud) {
2276  if (cloud->reserveTheNormsTable()) {
2277  cloud->addEigenNorms(normals);
2278  }
2279  }
2280 }
2281 
2282 void ccMesh::setVertexColor(size_t index, const Eigen::Vector3d& color) {
2283  if (m_associatedCloud) {
2285  ->setPointColor(index, color);
2286  }
2287 }
2288 
2289 void ccMesh::addVertexColor(const Eigen::Vector3d& color) {
2290  if (m_associatedCloud) {
2292  }
2293 }
2294 
2295 Eigen::Vector3d ccMesh::getVertexColor(size_t index) const {
2296  if (!m_associatedCloud) {
2297  return Eigen::Vector3d();
2298  }
2299 
2301  ->getEigenColor(index);
2302 }
2303 
2304 void ccMesh::setVertexColors(const std::vector<Eigen::Vector3d>& colors) {
2305  if (m_associatedCloud && colors.size() == m_associatedCloud->size()) {
2307  cloud->setEigenColors(colors);
2308  }
2309 }
2310 
2311 std::vector<Eigen::Vector3d> ccMesh::getVertexColors() const {
2312  if (!m_associatedCloud) {
2313  return std::vector<Eigen::Vector3d>();
2314  }
2315 
2317 }
2318 
2320  if (!m_associatedCloud) {
2321  return nullptr;
2322  }
2323 
2325 }
2326 
2327 void ccMesh::addVertexColors(const std::vector<Eigen::Vector3d>& colors) {
2328  if (m_associatedCloud) {
2330  if (cloud->reserveTheRGBTable()) {
2331  cloud->addEigenColors(colors);
2332  }
2333  }
2334 }
2335 
2336 unsigned int ccMesh::getVerticeSize() const {
2337  if (!getAssociatedCloud()) {
2338  return 0;
2339  }
2340 
2341  return getAssociatedCloud()->size();
2342 }
2343 
2344 std::vector<CCVector3>& ccMesh::getVerticesPtr() {
2345  if (!getAssociatedCloud()) {
2347  "[ccMesh] m_associatedCloud must be set before use!");
2348  }
2350 }
2351 
2352 const std::vector<CCVector3>& ccMesh::getVertices() const {
2353  if (!getAssociatedCloud()) {
2355  "[ccMesh] m_associatedCloud must be set before use!");
2356  }
2357 
2359 }
2360 
2361 void ccMesh::setEigenVertices(const std::vector<Eigen::Vector3d>& vertices) {
2362  if (m_associatedCloud && vertices.size() == m_associatedCloud->size()) {
2364  cloud->setEigenPoints(vertices);
2365  }
2366 }
2367 
2368 std::vector<Eigen::Vector3d> ccMesh::getEigenVertices() const {
2369  if (!getAssociatedCloud()) {
2370  return std::vector<Eigen::Vector3d>();
2371  }
2372 
2374  ->getEigenPoints();
2375 }
2376 
2377 void ccMesh::addEigenVertices(const std::vector<Eigen::Vector3d>& vertices) {
2378  if (m_associatedCloud) {
2380  if (cloud->reserveThePointsTable(
2381  static_cast<unsigned>(cloud->size() + vertices.size()))) {
2382  cloud->addPoints(vertices);
2383  }
2384  }
2385 }
2386 
2388  if (!m_associatedCloud || m_bBox.isValid()) return;
2389 
2390  m_bBox.clear();
2391 
2392  size_t count = m_triVertIndexes->size();
2393  for (size_t i = 0; i < count; ++i) {
2394  const cloudViewer::VerticesIndexes& tri = m_triVertIndexes->at(i);
2395  assert(tri.i1 < m_associatedCloud->size() &&
2396  tri.i2 < m_associatedCloud->size() &&
2397  tri.i3 < m_associatedCloud->size());
2401  }
2402 
2404 }
2405 
2407  refreshBB();
2408 
2409  bbMin = m_bBox.minCorner();
2410  bbMax = m_bBox.maxCorner();
2411 }
2412 
2413 ccBBox ccMesh::getOwnBB(bool withGLFeatures /*=false*/) {
2414  refreshBB();
2415 
2416  return m_bBox;
2417 }
2418 
2420  // DGM: it may happen that the vertices transformation history matrix is not
2421  // the same as the mesh (if applyGLTransformation is called directly on the
2422  // vertices). Therefore we prefer the cloud's by default.
2424  : m_glTransHistory;
2425 }
2426 
2427 // specific methods
2428 void ccMesh::addTriangle(unsigned i1, unsigned i2, unsigned i3) {
2429  m_triVertIndexes->emplace_back(cloudViewer::VerticesIndexes(i1, i2, i3));
2430 }
2431 
2433  m_triVertIndexes->emplace_back(triangle);
2434 }
2435 
2436 void ccMesh::setTriangle(size_t index, const Eigen::Vector3i& triangle) {
2437  if (index >= m_triVertIndexes->size()) {
2439  "[ccMesh::setTriangle] index out of range!");
2440  return;
2441  }
2442  m_triVertIndexes->at(index) =
2443  cloudViewer::VerticesIndexes(static_cast<unsigned>(triangle[0]),
2444  static_cast<unsigned>(triangle[1]),
2445  static_cast<unsigned>(triangle[2]));
2446 }
2447 
2449  const cloudViewer::VerticesIndexes* tsi =
2450  getTriangleVertIndexes(static_cast<unsigned int>(index));
2451 
2452  return Eigen::Vector3i(tsi->i1, tsi->i2, tsi->i3);
2453 }
2454 
2455 void ccMesh::setTriangles(const std::vector<Eigen::Vector3i>& triangles) {
2456  resize(triangles.size());
2457  for (unsigned int i = 0; i < size(); ++i) {
2458  setTriangle(i, triangles[i]);
2459  }
2460 }
2461 
2462 std::vector<Eigen::Vector3i> ccMesh::getTriangles() const {
2463  if (!hasTriangles()) {
2464  cloudViewer::utility::LogWarning("[getTriangles] has no triangles!");
2465  return std::vector<Eigen::Vector3i>();
2466  }
2467 
2468  std::vector<Eigen::Vector3i> triangles(size());
2469  for (size_t i = 0; i < size(); ++i) {
2470  triangles[i] = getTriangle(i);
2471  }
2472  return triangles;
2473 }
2474 
2475 bool ccMesh::reserve(size_t n) {
2476  if (m_triNormalIndexes)
2477  if (!m_triNormalIndexes->reserveSafe(n)) return false;
2478 
2479  if (m_triMtlIndexes)
2480  if (!m_triMtlIndexes->reserveSafe(n)) return false;
2481 
2482  if (m_texCoordIndexes)
2483  if (!m_texCoordIndexes->reserveSafe(n)) return false;
2484 
2485  return m_triVertIndexes->reserveSafe(n);
2486 }
2487 
2488 bool ccMesh::resize(size_t n) {
2489  m_bBox.setValidity(false);
2491 
2492  if (m_triMtlIndexes) {
2493  static const int s_defaultMtlIndex = -1;
2494  if (!m_triMtlIndexes->resizeSafe(n, true, &s_defaultMtlIndex))
2495  return false;
2496  }
2497 
2498  if (m_texCoordIndexes) {
2499  static const Tuple3i s_defaultTexCoords(-1, -1, -1);
2500  if (!m_texCoordIndexes->resizeSafe(n, true, &s_defaultTexCoords))
2501  return false;
2502  }
2503 
2504  if (m_triNormalIndexes) {
2505  static const Tuple3i s_defaultNormIndexes(-1, -1, -1);
2506  if (!m_triNormalIndexes->resizeSafe(n, true, &s_defaultNormIndexes))
2507  return false;
2508  }
2509 
2510  return m_triVertIndexes->resizeSafe(n);
2511 }
2512 
2513 bool ccMesh::resizeAssociatedCloud(std::size_t n) {
2514  if (!m_associatedCloud) {
2516  "Must call CreateInternalCloud first!");
2517  return false;
2518  }
2519  ccPointCloud* baseVertices =
2521 
2522  if (!baseVertices->resize(n)) {
2523  cloudViewer::utility::LogError("[resize] Not have enough memory! ");
2524  return false;
2525  }
2526  if (baseVertices->hasNormals() && !baseVertices->resizeTheNormsTable()) {
2528  "[resizeTheNormsTable] Not have enough memory! ");
2529  return false;
2530  }
2531  if (baseVertices->hasColors() && !baseVertices->resizeTheRGBTable()) {
2533  "[resizeTheRGBTable] Not have enough memory! ");
2534  return false;
2535  }
2536  return true;
2537 }
2538 
2540  bool init_color,
2541  bool init_normal) {
2542  if (!m_associatedCloud) {
2544  "Must call CreateInternalCloud first!");
2545  return false;
2546  }
2547  ccPointCloud* baseVertices =
2549 
2550  if (!baseVertices->reserveThePointsTable(n)) {
2552  "[reserveThePointsTable] Not have enough memory! ");
2553  return false;
2554  }
2555  if (init_normal && !baseVertices->reserveTheNormsTable()) {
2557  "[reserveTheNormsTable] Not have enough memory! ");
2558  return false;
2559  }
2560  if (init_color && !baseVertices->reserveTheRGBTable()) {
2562  "[reserveTheRGBTable] Not have enough memory! ");
2563  return false;
2564  }
2565  return true;
2566 }
2567 
2569  if (m_associatedCloud) {
2571  }
2572 }
2573 
2574 void ccMesh::swapTriangles(unsigned index1, unsigned index2) {
2575  assert(std::max(index1, index2) < size());
2576 
2577  m_triVertIndexes->swap(index1, index2);
2578  if (m_triMtlIndexes) m_triMtlIndexes->swap(index1, index2);
2579  if (m_texCoordIndexes) m_texCoordIndexes->swap(index1, index2);
2580  if (m_triNormalIndexes) m_triNormalIndexes->swap(index1, index2);
2581 }
2582 
2583 void ccMesh::removeTriangles(size_t index) {
2584  if (index >= size()) {
2586  "[ccMesh::removeTriangles] index out of range!");
2587  return;
2588  }
2589 
2590  m_triVertIndexes->erase(m_triVertIndexes->begin() + index);
2591  if (m_triMtlIndexes)
2592  m_triMtlIndexes->erase(m_triMtlIndexes->begin() + index);
2593  if (m_texCoordIndexes)
2594  m_texCoordIndexes->erase(m_texCoordIndexes->begin() + index);
2595  if (m_triNormalIndexes)
2596  m_triNormalIndexes->erase(m_triNormalIndexes->begin() + index);
2597 }
2598 
2600  unsigned triangleIndex) {
2601  return &m_triVertIndexes->at(triangleIndex);
2602 }
2603 
2605  unsigned triangleIndex) const {
2606  return &m_triVertIndexes->at(triangleIndex);
2607 }
2608 
2609 void ccMesh::getTriangleVertIndexes(size_t triangleIndex,
2610  Eigen::Vector3i& vertIndx) const {
2611  const cloudViewer::VerticesIndexes* tsi =
2612  getTriangleVertIndexes(static_cast<unsigned int>(triangleIndex));
2613  vertIndx(0) = tsi->i1;
2614  vertIndx(1) = tsi->i2;
2615  vertIndx(2) = tsi->i3;
2616 }
2617 
2619  if (m_globalIterator < m_triVertIndexes->size()) {
2621  }
2622 
2623  return nullptr;
2624 }
2625 
2627  if (m_parent && m_parent->getParent() &&
2629  return m_parent->getParent()->getUniqueID();
2630  } else {
2631  return getUniqueID();
2632  }
2633 }
2634 
2636  if (!m_associatedCloud) return;
2637 
2639 
2640  if (!ecvDisplayTools::GetMainWindow()) return;
2641 
2642  // 3D pass
2643  if (MACRO_Draw3D(context)) {
2644  // any triangle?
2645  size_t triNum = m_triVertIndexes->size();
2646  if (triNum == 0) return;
2647 
2648  // L.O.D.
2649  bool lodEnabled =
2650  (triNum > context.minLODTriangleCount &&
2651  context.decimateMeshOnMove && MACRO_LODActivated(context));
2652  unsigned decimStep =
2653  (lodEnabled ? static_cast<unsigned>(
2654  ceil(static_cast<double>(triNum * 3) /
2655  context.minLODTriangleCount))
2656  : 1);
2657 
2658  // display parameters
2659  glDrawParams glParams;
2660  getDrawingParameters(glParams);
2661  // no normals shading without light!
2662  if (!MACRO_LightIsEnabled(context)) glParams.showNorms = false;
2663 
2664  // wireframe ? (not compatible with LOD)
2665  bool showWired = isShownAsWire() && !lodEnabled;
2666  bool isShowPoints = isShownAsPoints() && !lodEnabled;
2667  if (showWired) {
2669  }
2670 
2671  if (isShowPoints) {
2672  context.meshRenderingMode = MESH_RENDERING_MODE::ECV_POINTS_MODE;
2673  }
2674  if (!showWired && !isShowPoints) {
2675  context.meshRenderingMode = MESH_RENDERING_MODE::ECV_SURFACE_MODE;
2676  }
2677 
2678  // per-triangle normals?
2679  bool showTriNormals = (hasTriNormals() && triNormsShown());
2680  // fix 'showNorms'
2681  glParams.showNorms =
2682  showTriNormals ||
2684 
2685  // materials & textures
2686  bool applyMaterials = (hasMaterials() && materialsShown());
2687  bool showTextures = (hasTextures() && materialsShown() && !lodEnabled);
2688 
2689  // GL name pushing
2690  bool entityPickingMode = MACRO_EntityPicking(context);
2691  if (entityPickingMode) {
2692  // not fast at all!
2693  if (MACRO_FastEntityPicking(context)) return;
2694  // minimal display for picking mode!
2695  glParams.showNorms = false;
2696  glParams.showColors = false;
2697  // glParams.showSF --> we keep it only if SF 'NaN' values are hidden
2698  showTriNormals = false;
2699  applyMaterials = false;
2700  showTextures = false;
2701  }
2702 
2703  bool greyForNanScalarValues = true;
2704  // unsigned colorRampSteps = 0;
2705  ccColorScale::Shared colorScale(nullptr);
2706 
2707  if (glParams.showSF) {
2709  ccPointCloud* cloud = static_cast<ccPointCloud*>(m_associatedCloud);
2710 
2711  greyForNanScalarValues = (cloud->getCurrentDisplayedScalarField() &&
2714  if (greyForNanScalarValues && entityPickingMode) {
2715  // in picking mode, no need to take SF into account if we don't
2716  // hide any points!
2717  glParams.showSF = false;
2718  }
2719  }
2720 
2721  if (glParams.showColors) {
2722  if (isColorOverridden()) {
2723  context.defaultMeshColor = m_tempColor;
2724  } else {
2726  context.defaultMeshColor =
2727  static_cast<ccPointCloud*>(m_associatedCloud)
2728  ->rgbColors()
2729  ->getValue(0);
2730  }
2731  } else {
2732  context.defaultMeshColor = ecvColor::lightGrey;
2733  }
2734 
2735  context.drawParam = glParams;
2736 
2737  // vertices visibility
2738  const ccGenericPointCloud::VisibilityTableType& verticesVisibility =
2740  bool visFiltering =
2741  (verticesVisibility.size() >= m_associatedCloud->size());
2742  context.visFiltering = visFiltering;
2744  }
2745 }
2746 
2748  bool removeSelectedTriangles,
2749  std::vector<int>* newIndexesOfRemainingTriangles /*=nullptr*/,
2750  bool withChildEntities /*=false*/) {
2751  if (!m_associatedCloud) {
2752  return nullptr;
2753  }
2754 
2755  size_t triCount = size();
2756 
2757  // we always need a map of the new triangle indexes
2758  std::vector<int> triangleIndexMap;
2759 
2760  // we create a new mesh with the current selection
2761  ccMesh* newMesh = nullptr;
2762  {
2763  // create a 'reference' cloud if none was provided
2765 
2766  // create vertices for the new mesh
2767  ccGenericPointCloud* newVertices =
2769  false, nullptr, nullptr, true, &rc);
2770  if (!newVertices) {
2772  "[ccMesh::createNewMeshFromSelection] Failed to create "
2773  "segmented mesh vertices! (not enough memory)");
2774  return nullptr;
2775  } else if (newVertices == m_associatedCloud) {
2776  // nothing to do
2777  return this;
2778  } else if (newVertices->size() == 0) {
2780  "[ccMesh::createNewMeshFromSelection] No visible point in "
2781  "selection");
2782  delete newVertices;
2783  return nullptr;
2784  }
2785  assert(newVertices);
2786 
2787  assert(rc.size() !=
2788  0); // otherwise 'newVertices->size() == 0' (see above)
2789  assert(rc.size() !=
2791  ->size()); // in this case
2792  // createNewCloudFromVisibilitySelection
2793  // would have return
2794  // 'm_associatedCloud' itself
2795 
2796  cloudViewer::GenericIndexedMesh* selection =
2798  this, &rc, true, nullptr, newVertices, 0,
2799  &triangleIndexMap);
2800  if (!selection) {
2802  "[ccMesh::createNewMeshFromSelection] Process failed: not "
2803  "enough memory?");
2804  return nullptr;
2805  }
2806 
2807  newMesh = new ccMesh(selection, newVertices);
2808 
2809  delete selection;
2810  selection = nullptr;
2811 
2812  if (!newMesh) {
2813  delete newVertices;
2814  newVertices = nullptr;
2816  "[ccMesh::createNewMeshFromSelection] An error occurred: "
2817  "not enough memory?");
2818  return nullptr;
2819  }
2820  newMesh->addChild(newVertices);
2821  newVertices->setEnabled(false);
2822  }
2823  assert(newMesh);
2824 
2825  // populate the new mesh
2826  {
2827  newMesh->setName(getName() + QString(".part"));
2828 
2829  // shall we add any advanced features?
2830  bool addFeatures = false;
2832  addFeatures |= newMesh->reservePerTriangleNormalIndexes();
2834  addFeatures |= newMesh->reservePerTriangleMtlIndexes();
2836  addFeatures |= newMesh->reservePerTriangleTexCoordIndexes();
2837 
2838  if (addFeatures) {
2839  // temporary structure for normal indexes mapping
2840  std::vector<int> newNormIndexes;
2841  NormsIndexesTableType* newTriNormals = nullptr;
2843  assert(m_triNormalIndexes->size() == triCount);
2844  // create new 'minimal' subset
2845  newTriNormals = new NormsIndexesTableType();
2846  newTriNormals->link();
2847  try {
2848  newNormIndexes.resize(m_triNormals->size(), -1);
2849  } catch (const std::bad_alloc&) {
2851  "[ccMesh::createNewMeshFromSelection] Failed to "
2852  "create new normals subset! (not enough memory)");
2853  newMesh->removePerTriangleNormalIndexes();
2854  newTriNormals->release();
2855  newTriNormals = nullptr;
2856  }
2857  }
2858 
2859  // temporary structure for texture indexes mapping
2860  std::vector<int> newTexIndexes;
2861  TextureCoordsContainer* newTriTexIndexes = nullptr;
2862  if (m_texCoords && m_texCoordIndexes) {
2863  assert(m_texCoordIndexes->size() == triCount);
2864  // create new 'minimal' subset
2865  newTriTexIndexes = new TextureCoordsContainer();
2866  newTriTexIndexes->link();
2867  try {
2868  newTexIndexes.resize(m_texCoords->size(), -1);
2869  } catch (const std::bad_alloc&) {
2871  "[ccMesh::createNewMeshFromSelection] Failed to "
2872  "create new texture indexes subset! (not enough "
2873  "memory)");
2875  newTriTexIndexes->release();
2876  newTriTexIndexes = nullptr;
2877  }
2878  }
2879 
2880  // temporary structure for material indexes mapping
2881  std::vector<int> newMatIndexes;
2882  ccMaterialSet* newMaterials = nullptr;
2883  if (m_materials && m_triMtlIndexes) {
2884  assert(m_triMtlIndexes->size() == triCount);
2885  // create new 'minimal' subset
2886  newMaterials = new ccMaterialSet(m_materials->getName() +
2887  QString(".subset"));
2888  newMaterials->link();
2889  try {
2890  newMatIndexes.resize(m_materials->size(), -1);
2891  } catch (const std::bad_alloc&) {
2893  "[ccMesh::createNewMeshFromSelection] Failed to "
2894  "create new material subset! (not enough memory)");
2895  newMesh->removePerTriangleMtlIndexes();
2896  newMaterials->release();
2897  newMaterials = nullptr;
2898  if (newTriTexIndexes) // we can release texture coordinates
2899  // as well (as they depend on
2900  // materials!)
2901  {
2903  newTriTexIndexes->release();
2904  newTriTexIndexes = nullptr;
2905  newTexIndexes.resize(0);
2906  }
2907  }
2908  }
2909 
2910  for (size_t i = 0; i < triCount; ++i) {
2911  if (triangleIndexMap[i] >= 0) // triangle should be copied over
2912  {
2913  // import per-triangle normals?
2914  if (newTriNormals) {
2915  assert(m_triNormalIndexes);
2916 
2917  // current triangle (compressed) normal indexes
2918  const Tuple3i& triNormIndexes =
2920 
2921  // for each triangle of this mesh, try to determine if
2922  // its normals are already in use (otherwise add them to
2923  // the new container and increase its index)
2924  for (unsigned j = 0; j < 3; ++j) {
2925  if (triNormIndexes.u[j] >= 0 &&
2926  newNormIndexes[triNormIndexes.u[j]] < 0) {
2927  if (newTriNormals->size() ==
2928  newTriNormals->capacity() &&
2929  !newTriNormals->reserveSafe(
2930  newTriNormals->size() +
2931  4096)) // auto expand
2932  {
2934  "[ccMesh::"
2935  "createNewMeshFromSelection] "
2936  "Failed to create new normals "
2937  "subset! (not enough memory)");
2938  newMesh->removePerTriangleNormalIndexes();
2939  newTriNormals->release();
2940  newTriNormals = nullptr;
2941  break;
2942  }
2943 
2944  // import old normal to new subset (create new
2945  // index)
2946  newNormIndexes[triNormIndexes.u[j]] =
2947  static_cast<int>(
2948  newTriNormals
2949  ->size()); // new
2950  // element
2951  // index =
2952  // new size
2953  // - 1 = old
2954  // size!
2955  newTriNormals->emplace_back(
2957  triNormIndexes.u[j]));
2958  }
2959  }
2960 
2961  if (newTriNormals) // structure still exists?
2962  {
2963  newMesh->addTriangleNormalIndexes(
2964  triNormIndexes.u[0] < 0
2965  ? -1
2966  : newNormIndexes[triNormIndexes
2967  .u[0]],
2968  triNormIndexes.u[1] < 0
2969  ? -1
2970  : newNormIndexes[triNormIndexes
2971  .u[1]],
2972  triNormIndexes.u[2] < 0
2973  ? -1
2974  : newNormIndexes[triNormIndexes
2975  .u[2]]);
2976  }
2977  }
2978 
2979  // import texture coordinates?
2980  if (newTriTexIndexes) {
2981  assert(m_texCoordIndexes);
2982 
2983  // current triangle texture coordinates indexes
2984  const Tuple3i& triTexIndexes =
2986 
2987  // for each triangle of this mesh, try to determine if
2988  // its textures coordinates are already in use
2989  //(otherwise add them to the new container and increase
2990  // its index)
2991  for (unsigned j = 0; j < 3; ++j) {
2992  if (triTexIndexes.u[j] >= 0 &&
2993  newTexIndexes[triTexIndexes.u[j]] < 0) {
2994  if (newTriTexIndexes->size() ==
2995  newTriTexIndexes->capacity() &&
2996  !newTriTexIndexes->reserveSafe(
2997  newTriTexIndexes->size() +
2998  4096)) // auto expand
2999  {
3001  "Failed to create new texture "
3002  "coordinates subset! (not enough "
3003  "memory)");
3005  newTriTexIndexes->release();
3006  newTriTexIndexes = nullptr;
3007  break;
3008  }
3009  // import old texture coordinate to new subset
3010  // (create new index)
3011  newTexIndexes[triTexIndexes.u[j]] = static_cast<
3012  int>(
3013  newTriTexIndexes
3014  ->size()); // new element index
3015  // = new size - 1 =
3016  // old size!
3017  newTriTexIndexes->emplace_back(
3019  triTexIndexes.u[j]));
3020  }
3021  }
3022 
3023  if (newTriTexIndexes) // structure still exists?
3024  {
3025  newMesh->addTriangleTexCoordIndexes(
3026  triTexIndexes.u[0] < 0
3027  ? -1
3028  : newTexIndexes[triTexIndexes.u[0]],
3029  triTexIndexes.u[1] < 0
3030  ? -1
3031  : newTexIndexes[triTexIndexes.u[1]],
3032  triTexIndexes.u[2] < 0
3033  ? -1
3034  : newTexIndexes[triTexIndexes
3035  .u[2]]);
3036  }
3037  }
3038 
3039  // import materials?
3040  if (newMaterials) {
3041  assert(m_triMtlIndexes);
3042 
3043  // current triangle material index
3044  const int triMatIndex = m_triMtlIndexes->getValue(i);
3045 
3046  // for each triangle of this mesh, try to determine if
3047  // its material is already in use (otherwise add it to
3048  // the new container and increase its index)
3049  if (triMatIndex >= 0 &&
3050  newMatIndexes[triMatIndex] < 0) {
3051  // import old material to new subset (create new
3052  // index)
3053  newMatIndexes[triMatIndex] = static_cast<int>(
3054  newMaterials->size()); // new element index
3055  // = new size - 1 =
3056  // old size!
3057  try {
3058  newMaterials->emplace_back(
3059  m_materials->at(triMatIndex));
3060  } catch (const std::bad_alloc&) {
3062  "[ccMesh::createNewMeshFromSelection] "
3063  "Failed to create new materials "
3064  "subset! (not enough memory)");
3065  newMesh->removePerTriangleMtlIndexes();
3066  newMaterials->release();
3067  newMaterials = nullptr;
3068  }
3069  }
3070 
3071  if (newMaterials) // structure still exists?
3072  {
3073  newMesh->addTriangleMtlIndex(
3074  triMatIndex < 0
3075  ? -1
3076  : newMatIndexes[triMatIndex]);
3077  }
3078  }
3079  }
3080  }
3081 
3082  if (newTriNormals) {
3083  newTriNormals->resize(
3084  newTriNormals->size()); // smaller so it should always
3085  // be ok!
3086  newMesh->setTriNormsTable(newTriNormals);
3087  newTriNormals->release();
3088  newTriNormals = nullptr;
3089  }
3090 
3091  if (newTriTexIndexes) {
3092  newMesh->setTexCoordinatesTable(newTriTexIndexes);
3093  newTriTexIndexes->release();
3094  newTriTexIndexes = nullptr;
3095  }
3096 
3097  if (newMaterials) {
3098  newMesh->setMaterialSet(newMaterials);
3099  newMaterials->release();
3100  newMaterials = nullptr;
3101  }
3102  }
3103 
3104  newMesh->showColors(colorsShown());
3105  newMesh->showNormals(normalsShown());
3106  newMesh->showMaterials(materialsShown());
3107  newMesh->showSF(sfShown());
3108  newMesh->importParametersFrom(this);
3109  }
3110 
3111  // we must update eventual sub-meshes
3112  ccHObject::Container subMeshes;
3113  if (filterChildren(subMeshes, false, CV_TYPES::SUB_MESH) != 0) {
3114  // create index map
3115  try {
3116  ccSubMesh::IndexMap newRemainingTriangleIndexes;
3117  if (removeSelectedTriangles) {
3118  newRemainingTriangleIndexes.resize(
3119  triCount, static_cast<unsigned>(triCount));
3120 
3121  unsigned newInvisibleIndex = 0;
3122  for (size_t i = 0; i < triCount; ++i) {
3123  if (triangleIndexMap[i] <
3124  0) // triangle is not used in the new mesh, it will be
3125  // kept in this one
3126  {
3127  newRemainingTriangleIndexes[i] = newInvisibleIndex++;
3128  }
3129  }
3130  }
3131 
3132  for (size_t i = 0; i < subMeshes.size(); ++i) {
3133  ccSubMesh* subMesh = static_cast<ccSubMesh*>(subMeshes[i]);
3134  ccSubMesh* newSubMesh = subMesh->createNewSubMeshFromSelection(
3135  removeSelectedTriangles, triangleIndexMap,
3136  removeSelectedTriangles ? &newRemainingTriangleIndexes
3137  : nullptr);
3138 
3139  if (newSubMesh) {
3140  if (newMesh) {
3141  newSubMesh->setEnabled(subMesh->isEnabled());
3142  newSubMesh->setVisible(subMesh->isVisible());
3143  newSubMesh->setAssociatedMesh(newMesh);
3144  newMesh->addChild(newSubMesh);
3145  } else {
3146  assert(false);
3147  delete newSubMesh;
3148  newSubMesh = nullptr;
3149  }
3150  }
3151 
3152  if (subMesh->size() ==
3153  0) // no triangle left in current sub-mesh?
3154  {
3155  removeChild(subMesh);
3156  subMeshes[i] = nullptr;
3157  subMesh = nullptr;
3158  }
3159  }
3160  } catch (const std::bad_alloc&) {
3161  CVLog::Warning("Not enough memory! Sub-meshes will be lost...");
3162  if (newMesh) {
3163  newMesh->setVisible(
3164  true); // force parent mesh visibility in this case!
3165  }
3166 
3167  for (size_t i = 0; i < subMeshes.size(); ++i) {
3168  removeChild(subMeshes[i]);
3169  }
3170  }
3171  }
3172 
3173  if (withChildEntities) {
3174  ccHObjectCaster::CloneChildren(this, newMesh, &triangleIndexMap);
3175  }
3176 
3177  // shall we remove the selected triangles from this mesh
3178  if (removeSelectedTriangles) {
3179  if (newIndexesOfRemainingTriangles) {
3180  if (newIndexesOfRemainingTriangles->empty()) {
3181  try {
3182  newIndexesOfRemainingTriangles->resize(triCount);
3183  } catch (const std::bad_alloc&) {
3185  "[ccMesh::createNewMeshFromSelection] Not enough "
3186  "memory");
3187  return nullptr;
3188  }
3189  } else if (newIndexesOfRemainingTriangles->size() != triCount) {
3191  "[ccMesh::createNewMeshFromSelection] Input 'new "
3192  "indexes of reamining triangles' vector has a wrong "
3193  "size");
3194  return nullptr;
3195  }
3196  }
3197  assert(!newIndexesOfRemainingTriangles ||
3198  newIndexesOfRemainingTriangles->size() == triCount);
3199 
3200  // we need to change the visibility status of some vertices that belong
3201  // to partially 'invisible' triangles
3202  auto& visArray = m_associatedCloud->getTheVisibilityArray();
3203  assert(visArray.size() == m_associatedCloud->size());
3204 
3205  size_t lastTri = 0;
3206  for (size_t i = 0; i < triCount; ++i) {
3207  if (triangleIndexMap[i] < 0) // triangle is not used in the new
3208  // mesh, it will be kept in this one
3209  {
3210  const cloudViewer::VerticesIndexes& tsi =
3211  m_triVertIndexes->at(i);
3212  for (unsigned j = 0; j < 3; ++j) {
3213  visArray[tsi.i[j]] = POINT_HIDDEN;
3214  }
3215 
3216  if (i != lastTri) {
3217  m_triVertIndexes->setValue(lastTri, tsi);
3218 
3219  if (m_triNormalIndexes)
3221  lastTri, m_triNormalIndexes->getValue(i));
3222  if (m_triMtlIndexes)
3223  m_triMtlIndexes->setValue(lastTri,
3225  if (m_texCoordIndexes)
3227  lastTri, m_texCoordIndexes->getValue(i));
3228  }
3229  if (newIndexesOfRemainingTriangles) {
3230  newIndexesOfRemainingTriangles->at(i) =
3231  static_cast<int>(lastTri);
3232  }
3233  ++lastTri;
3234  } else if (newIndexesOfRemainingTriangles) {
3235  newIndexesOfRemainingTriangles->at(i) = -1;
3236  }
3237  }
3238 
3239  // update the mesh size
3240  resize(lastTri);
3241  triCount = size();
3242 
3243  std::vector<int> newIndexes;
3244  if (m_associatedCloud->removeVisiblePoints(nullptr, &newIndexes)) {
3245  // warning: from this point on, verticesVisibility is not valid
3246  // anymore!
3247  for (size_t i = 0; i < m_triVertIndexes->size(); ++i) {
3249 
3250  // update each vertex index
3251  for (int j = 0; j < 3; ++j) {
3252  int oldVertexIndex = tsi.i[j];
3253  assert(oldVertexIndex < newIndexes.size());
3254  tsi.i[j] = newIndexes[oldVertexIndex];
3255  assert(tsi.i[j] < m_associatedCloud->size());
3256  }
3257  }
3258  } else {
3260  "[ccMesh::createNewMeshFromSelection] Failed to remove "
3261  "unused vertices");
3262  }
3263 
3265 
3266  // TODO: should we take care of the children here?
3267  } else if (newIndexesOfRemainingTriangles) {
3269  "[ccMesh::createNewMeshFromSelection] A 'new indexes of "
3270  "reamining triangles' vector was provided while no triangle "
3271  "shall be removed");
3272  }
3273 
3275 
3276  return newMesh;
3277 }
3278 
3279 void ccMesh::shiftTriangleIndexes(unsigned shift) {
3280  for (size_t i = 0; i < m_triVertIndexes->size(); ++i) {
3282  ti.i1 += shift;
3283  ti.i2 += shift;
3284  ti.i3 += shift;
3285  }
3286 }
3287 
3289  // per-triangle normals
3290  if (m_triNormals) {
3292  }
3293 
3294  // per-vertex normals
3295  ccPointCloud* pc = dynamic_cast<ccPointCloud*>(m_associatedCloud);
3296  if (pc && pc->hasNormals()) {
3297  pc->invertNormals();
3298  }
3299 }
3300 
3302  if (m_triNormals) {
3303  for (CompressedNormType& n : *m_triNormals) {
3305  }
3306  }
3307 }
3308 
3311  std::swap(ti.i2, ti.i3);
3312  }
3313 }
3314 
3315 /*********************************************************/
3316 /************** PER-TRIANGLE NORMALS ***************/
3317 /*********************************************************/
3318 
3321 }
3322 
3325  m_triNormalIndexes = nullptr;
3326 }
3327 
3329  assert(!m_triNormalIndexes); // try to avoid doing this twice!
3330  if (!m_triNormalIndexes) {
3333  }
3334 
3336 
3337  return m_triNormalIndexes->reserveSafe(m_triVertIndexes->capacity());
3338 }
3339 
3340 void ccMesh::addTriangleNormalIndexes(int i1, int i2, int i3) {
3342  m_triNormalIndexes->emplace_back(Tuple3i(i1, i2, i3));
3343 }
3344 
3345 void ccMesh::setTriangleNormalIndexes(unsigned triangleIndex,
3346  int i1,
3347  int i2,
3348  int i3) {
3349  assert(m_triNormalIndexes && m_triNormalIndexes->size() > triangleIndex);
3350  m_triNormalIndexes->setValue(triangleIndex, Tuple3i(i1, i2, i3));
3351 }
3352 
3353 void ccMesh::getTriangleNormalIndexes(unsigned triangleIndex,
3354  int& i1,
3355  int& i2,
3356  int& i3) const {
3357  if (m_triNormalIndexes && m_triNormalIndexes->size() > triangleIndex) {
3358  const Tuple3i& indexes = m_triNormalIndexes->getValue(triangleIndex);
3359  i1 = indexes.u[0];
3360  i2 = indexes.u[1];
3361  i3 = indexes.u[2];
3362  } else {
3363  i1 = i2 = i3 = -1;
3364  }
3365 }
3366 
3367 bool ccMesh::getTriangleNormals(unsigned triangleIndex,
3368  CCVector3& Na,
3369  CCVector3& Nb,
3370  CCVector3& Nc) const {
3372  m_triNormalIndexes->size() > triangleIndex) {
3373  const Tuple3i& indexes = m_triNormalIndexes->getValue(triangleIndex);
3374  if (indexes.u[0] >= 0)
3376  m_triNormals->getValue(indexes.u[0]));
3377  else
3378  Na = CCVector3(0, 0, 0);
3379  if (indexes.u[1] >= 0)
3381  m_triNormals->getValue(indexes.u[1]));
3382  else
3383  Nb = CCVector3(0, 0, 0);
3384  if (indexes.u[2] >= 0)
3386  m_triNormals->getValue(indexes.u[2]));
3387  else
3388  Nc = CCVector3(0, 0, 0);
3389 
3390  return true;
3391  }
3392 
3393  return false;
3394 }
3395 
3396 bool ccMesh::getTriangleNormals(unsigned triangleIndex,
3397  double Na[3],
3398  double Nb[3],
3399  double Nc[3]) const {
3401  m_triNormalIndexes->size() > triangleIndex) {
3402  const Tuple3i& indexes = m_triNormalIndexes->getValue(triangleIndex);
3403  if (indexes.u[0] >= 0) {
3404  const PointCoordinateType* n1 =
3406  ->getNormal(m_triNormals->getValue(indexes.u[0]))
3407  .u;
3408  Na[0] = static_cast<double>(n1[0]);
3409  Na[1] = static_cast<double>(n1[1]);
3410  Na[2] = static_cast<double>(n1[2]);
3411  } else {
3412  Na[0] = 0.0;
3413  Na[1] = 0.0;
3414  Na[2] = 0.0;
3415  }
3416  if (indexes.u[1] >= 0) {
3417  const PointCoordinateType* n2 =
3419  ->getNormal(m_triNormals->getValue(indexes.u[1]))
3420  .u;
3421  Nb[0] = static_cast<double>(n2[0]);
3422  Nb[1] = static_cast<double>(n2[1]);
3423  Nb[2] = static_cast<double>(n2[2]);
3424  } else {
3425  Nb[0] = 0.0;
3426  Nb[1] = 0.0;
3427  Nb[2] = 0.0;
3428  }
3429 
3430  if (indexes.u[2] >= 0) {
3431  const PointCoordinateType* n3 =
3433  ->getNormal(m_triNormals->getValue(indexes.u[2]))
3434  .u;
3435  Nc[0] = static_cast<double>(n3[0]);
3436  Nc[1] = static_cast<double>(n3[1]);
3437  Nc[2] = static_cast<double>(n3[2]);
3438  } else {
3439  Nc[0] = 0.0;
3440  Nc[1] = 0.0;
3441  Nc[2] = 0.0;
3442  }
3443 
3444  return true;
3445  }
3446  return false;
3447 }
3448 
3449 bool ccMesh::getTriangleNormals(unsigned triangleIndex,
3450  Eigen::Vector3d& Na,
3451  Eigen::Vector3d& Nb,
3452  Eigen::Vector3d& Nc) const {
3453  return getTriangleNormals(triangleIndex, Na.data(), Nb.data(), Nc.data());
3454 }
3455 
3456 std::vector<Eigen::Vector3d> ccMesh::getTriangleNormals() const {
3457  std::vector<Eigen::Vector3d> triangleNormals;
3458  triangleNormals.reserve(this->size());
3459  for (size_t i = 0; i < this->size(); i++) {
3460  triangleNormals.emplace_back(getTriangleNorm(i));
3461  }
3462  return triangleNormals;
3463 }
3464 
3465 std::vector<CCVector3*> ccMesh::getTriangleNormalsPtr() const {
3466  std::vector<CCVector3*> triNormals;
3467  for (size_t i = 0; i < this->size(); i++) {
3468  triNormals.push_back(&ccNormalVectors::GetUniqueInstance()->getNormal(
3469  m_triNormals->getValue(i)));
3470  }
3471  return triNormals;
3472 }
3473 
3474 Eigen::Vector3d ccMesh::getTriangleNorm(size_t index) const {
3475  if (index >= m_triNormals->size()) return Eigen::Vector3d(0.0, 0.0, 0.0);
3476  const PointCoordinateType* n1 =
3478  ->getNormal(m_triNormals->getValue(index))
3479  .u;
3480  return Eigen::Vector3d(n1[0], n1[1], n1[2]);
3481 }
3482 
3483 bool ccMesh::setTriangleNorm(size_t index,
3484  const Eigen::Vector3d& triangle_normal) {
3485  if (!hasTriNormals() || m_triNormals->size() <= index) {
3486  return false;
3487  }
3488 
3490  CCVector3::fromArray(triangle_normal));
3491  setTriangleNormalIndexes(index, nIndex);
3492  return true;
3493 }
3494 
3495 bool ccMesh::setTriangleNormalIndexes(size_t triangleIndex,
3496  CompressedNormType value) {
3497  if (!hasTriNormals() || m_triNormals->size() <= triangleIndex) {
3498  return false;
3499  }
3500 
3501  m_triNormals->setValue(triangleIndex, value);
3502  return true;
3503 }
3504 
3506  if (!hasTriNormals() || m_triNormals->size() <= triangleIndex) {
3507  assert(false);
3508  }
3509 
3510  return m_triNormals->getValue(triangleIndex);
3511 }
3512 
3515  NormsIndexesTableType* normsTable = new NormsIndexesTableType();
3517  normsTable->release();
3518  CVLog::Warning("[ccMesh::addTriangleNorm] Not enough memory!");
3519  return false;
3520  }
3521  setTriNormsTable(normsTable);
3522  }
3523 
3525  m_triNormals->push_back(nIndex);
3526  int normalIndex = static_cast<int>(m_triNormals->size() - 1);
3527  addTriangleNormalIndexes(normalIndex, normalIndex, normalIndex);
3528  return true;
3529 }
3530 
3531 bool ccMesh::addTriangleNorm(const Eigen::Vector3d& N) {
3533 }
3534 
3535 std::vector<Eigen::Vector3d> ccMesh::getTriangleNorms() const {
3536  if (!hasTriNormals()) {
3538  "[getTriangleNorms] has no triangle normals!");
3539  return std::vector<Eigen::Vector3d>();
3540  }
3541 
3542  std::vector<Eigen::Vector3d> triangle_normals(m_triNormals->size());
3543  for (size_t i = 0; i < m_triNormals->size(); ++i) {
3544  triangle_normals[i] = getTriangleNorm(i);
3545  }
3546 
3547  return triangle_normals;
3548 }
3549 
3551  const std::vector<Eigen::Vector3d>& triangle_normals) {
3552  bool success = true;
3553  if (resize(triangle_normals.size()) && m_triNormals &&
3554  m_triNormals->resizeSafe(triangle_normals.size())) {
3555  for (size_t i = 0; i < triangle_normals.size(); ++i) {
3556  if (!setTriangleNorm(i, triangle_normals[i])) {
3558  "[ccMesh::addTriangleNorms] add triangle normals "
3559  "failed!");
3560  success = false;
3561  break;
3562  }
3563  }
3564  } else {
3565  success = false;
3566  }
3567 
3568  return success;
3569 }
3570 
3572  const std::vector<Eigen::Vector3d>& triangle_normals) {
3573  bool success = true;
3574  if (reserve(size() + triangle_normals.size())) {
3575  for (const auto& normal : triangle_normals) {
3576  if (!addTriangleNorm(normal)) {
3578  "[ccMesh::addTriangleNorms] add triangle normals "
3579  "failed!");
3580  success = false;
3581  break;
3582  }
3583  }
3584  }
3585 
3586  return success;
3587 }
3588 
3591  (m_triNormalIndexes->size() == m_triVertIndexes->size());
3592 }
3593 
3594 /*********************************************************/
3595 /************ PER-TRIANGLE TEX COORDS **************/
3596 /*********************************************************/
3597 
3599  bool autoReleaseOldTable /*=true*/) {
3600  if (m_texCoords == texCoordsTable) return;
3601 
3602  if (m_texCoords && autoReleaseOldTable) {
3603  int childIndex = getChildIndex(m_texCoords);
3604  m_texCoords->release();
3605  m_texCoords = nullptr;
3606  if (childIndex >= 0) removeChild(childIndex);
3607  }
3608 
3609  m_texCoords = texCoordsTable;
3610  if (m_texCoords) {
3611  m_texCoords->link();
3612  int childIndex = getChildIndex(m_texCoords);
3613  if (childIndex < 0) addChild(m_texCoords);
3614  } else {
3615  removePerTriangleTexCoordIndexes(); // auto-remove per-triangle indexes
3616  // (we don't need them anymore)
3617  }
3618 }
3619 
3620 void ccMesh::getTriangleTexCoordinates(unsigned triIndex,
3621  TexCoords2D*& tx1,
3622  TexCoords2D*& tx2,
3623  TexCoords2D*& tx3) const {
3624  if (m_texCoords && m_texCoordIndexes) {
3625  const Tuple3i& txInd = m_texCoordIndexes->getValue(triIndex);
3626  unsigned int texCoordsSize = m_texCoords->size();
3627 
3628  // Add detailed logging for debugging
3629  if (txInd.u[0] >= static_cast<int>(texCoordsSize) ||
3630  txInd.u[1] >= static_cast<int>(texCoordsSize) ||
3631  txInd.u[2] >= static_cast<int>(texCoordsSize)) {
3633  "[ccMesh::getTriangleTexCoordinates] Triangle %u: texCoord "
3634  "indices [%d, %d, %d] out of range (size=%u)",
3635  triIndex, txInd.u[0], txInd.u[1], txInd.u[2],
3636  texCoordsSize);
3637  }
3638 
3639  tx1 = (txInd.u[0] >= 0 && txInd.u[0] < static_cast<int>(texCoordsSize)
3640  ? &m_texCoords->getValue(txInd.u[0])
3641  : nullptr);
3642  tx2 = (txInd.u[1] >= 0 && txInd.u[1] < static_cast<int>(texCoordsSize)
3643  ? &m_texCoords->getValue(txInd.u[1])
3644  : nullptr);
3645  tx3 = (txInd.u[2] >= 0 && txInd.u[2] < static_cast<int>(texCoordsSize)
3646  ? &m_texCoords->getValue(txInd.u[2])
3647  : nullptr);
3648  } else {
3649  tx1 = tx2 = tx3 = nullptr;
3650  }
3651 }
3652 
3653 void ccMesh::getTexCoordinates(unsigned index, TexCoords2D*& tx) const {
3654  if (m_texCoords && m_texCoords->size() > index) {
3655  tx = &m_texCoords->getValue(index);
3656  }
3657 }
3658 
3660  assert(!m_texCoordIndexes); // try to avoid doing this twice!
3661  if (!m_texCoordIndexes) {
3664  }
3665 
3667 
3668  return m_texCoordIndexes->reserveSafe(m_triVertIndexes->capacity());
3669 }
3670 
3672  triangleTexCoordIndexesSet* texCoordIndexes = m_texCoordIndexes;
3673  m_texCoordIndexes = nullptr;
3674 
3675  if (texCoordIndexes) texCoordIndexes->release();
3676 }
3677 
3678 void ccMesh::addTriangleTexCoordIndexes(int i1, int i2, int i3) {
3680  m_texCoordIndexes->emplace_back(Tuple3i(i1, i2, i3));
3681 }
3682 
3683 void ccMesh::setTriangleTexCoordIndexes(unsigned triangleIndex,
3684  int i1,
3685  int i2,
3686  int i3) {
3687  assert(m_texCoordIndexes && m_texCoordIndexes->size() > triangleIndex);
3688  m_texCoordIndexes->setValue(triangleIndex, Tuple3i(i1, i2, i3));
3689 }
3690 
3691 void ccMesh::getTriangleTexCoordinatesIndexes(unsigned triangleIndex,
3692  int& i1,
3693  int& i2,
3694  int& i3) const {
3695  assert(m_texCoordIndexes && m_texCoordIndexes->size() > triangleIndex);
3696 
3697  const Tuple3i& tci = m_texCoordIndexes->getValue(triangleIndex);
3698  i1 = tci.u[0];
3699  i2 = tci.u[1];
3700  i3 = tci.u[2];
3701 }
3702 
3703 bool ccMesh::hasTextures() const {
3704  return hasMaterials() && m_texCoords && m_texCoords->isAllocated() &&
3706  (m_texCoordIndexes->size() == m_triVertIndexes->size());
3707 }
3708 
3709 /*********************************************************/
3710 /************** PER-TRIANGLE MATERIALS *************/
3711 /*********************************************************/
3712 
3713 bool ccMesh::hasMaterials() const {
3714  return m_materials && !m_materials->empty() && m_triMtlIndexes &&
3715  (m_triMtlIndexes->size() == m_triVertIndexes->size());
3716 }
3717 
3719  triangleMaterialIndexesSet* matIndexesTable,
3720  bool autoReleaseOldTable /*=true*/) {
3721  if (m_triMtlIndexes == matIndexesTable) return;
3722 
3723  if (m_triMtlIndexes && autoReleaseOldTable) {
3725  m_triMtlIndexes = nullptr;
3726  }
3727 
3728  m_triMtlIndexes = matIndexesTable;
3729  if (m_triMtlIndexes) {
3730  m_triMtlIndexes->link();
3731  }
3732 }
3733 
3735  assert(!m_triMtlIndexes); // try to avoid doing this twice!
3736  if (!m_triMtlIndexes) {
3738  m_triMtlIndexes->link();
3739  }
3740 
3742 
3743  return m_triMtlIndexes->reserveSafe(m_triVertIndexes->capacity());
3744 }
3745 
3748  m_triMtlIndexes = nullptr;
3749 }
3750 
3751 void ccMesh::addTriangleMtlIndex(int mtlIndex) {
3753  m_triMtlIndexes->emplace_back(mtlIndex);
3754 }
3755 
3756 void ccMesh::setTriangleMtlIndex(unsigned triangleIndex, int mtlIndex) {
3757  assert(m_triMtlIndexes && m_triMtlIndexes->size() > triangleIndex);
3758  m_triMtlIndexes->setValue(triangleIndex, mtlIndex);
3759 }
3760 
3761 int ccMesh::getTriangleMtlIndex(unsigned triangleIndex) const {
3762  assert(m_triMtlIndexes && m_triMtlIndexes->size() > triangleIndex);
3763  return m_triMtlIndexes->at(triangleIndex);
3764 }
3765 
3766 bool ccMesh::toFile_MeOnly(QFile& out, short dataVersion) const {
3767  assert(out.isOpen() && (out.openMode() & QIODevice::WriteOnly));
3768  if (dataVersion < 29) {
3769  assert(false);
3770  return false;
3771  }
3772 
3773  if (!ccGenericMesh::toFile_MeOnly(out, dataVersion)) return false;
3774 
3775  // we can't save the associated cloud here (as it may be shared by multiple
3776  // meshes) so instead we save it's unique ID (dataVersion>=20) WARNING: the
3777  // cloud must be saved in the same BIN file! (responsibility of the caller)
3778  uint32_t vertUniqueID =
3780  ? static_cast<uint32_t>(m_associatedCloud->getUniqueID())
3781  : 0);
3782  if (out.write((const char*)&vertUniqueID, 4) < 0) return WriteError();
3783 
3784  // per-triangle normals array (dataVersion>=20)
3785  {
3786  // we can't save the normals array here (as it may be shared by multiple
3787  // meshes) so instead we save it's unique ID (dataVersion>=20) WARNING:
3788  // the normals array must be saved in the same BIN file! (responsibility
3789  // of the caller)
3790  uint32_t normArrayID =
3792  ? static_cast<uint32_t>(m_triNormals->getUniqueID())
3793  : 0);
3794  if (out.write((const char*)&normArrayID, 4) < 0) return WriteError();
3795  }
3796 
3797  // texture coordinates array (dataVersion>=20)
3798  {
3799  // we can't save the texture coordinates array here (as it may be shared
3800  // by multiple meshes) so instead we save it's unique ID
3801  // (dataVersion>=20) WARNING: the texture coordinates array must be
3802  // saved in the same BIN file! (responsibility of the caller)
3803  uint32_t texCoordArrayID =
3805  ? static_cast<uint32_t>(m_texCoords->getUniqueID())
3806  : 0);
3807  if (out.write((const char*)&texCoordArrayID, 4) < 0)
3808  return WriteError();
3809  }
3810 
3811  // materials
3812  {
3813  // we can't save the material set here (as it may be shared by multiple
3814  // meshes) so instead we save it's unique ID (dataVersion>=20) WARNING:
3815  // the material set must be saved in the same BIN file! (responsibility
3816  // of the caller)
3817  uint32_t matSetID =
3818  (m_materials ? static_cast<uint32_t>(m_materials->getUniqueID())
3819  : 0);
3820  if (out.write((const char*)&matSetID, 4) < 0) return WriteError();
3821  }
3822 
3823  // triangles indexes (dataVersion>=20)
3824  if (!m_triVertIndexes)
3825  return CVLog::Error(
3826  "Internal error: mesh has no triangles array! (not enough "
3827  "memory?)");
3829  3, unsigned>(
3830  *m_triVertIndexes, out))
3831  return false;
3832 
3833  // per-triangle materials (dataVersion>=20))
3834  bool hasTriMtlIndexes = hasPerTriangleMtlIndexes();
3835  if (out.write((const char*)&hasTriMtlIndexes, sizeof(bool)) < 0)
3836  return WriteError();
3837  if (hasTriMtlIndexes) {
3838  assert(m_triMtlIndexes);
3839  if (!ccSerializationHelper::GenericArrayToFile<int, 1, int>(
3840  *m_triMtlIndexes, out))
3841  return false;
3842  }
3843 
3844  // per-triangle texture coordinates indexes (dataVersion>=20))
3845  bool hasTexCoordIndexes = hasPerTriangleTexCoordIndexes();
3846  if (out.write((const char*)&hasTexCoordIndexes, sizeof(bool)) < 0)
3847  return WriteError();
3848  if (hasTexCoordIndexes) {
3849  assert(m_texCoordIndexes);
3850  if (!ccSerializationHelper::GenericArrayToFile<Tuple3i, 3, int>(
3851  *m_texCoordIndexes, out))
3852  return false;
3853  }
3854 
3855  // per-triangle normals indexes (dataVersion>=20))
3856  bool hasTriNormalIndexes =
3858  if (out.write((const char*)&hasTriNormalIndexes, sizeof(bool)) < 0)
3859  return WriteError();
3860  if (hasTriNormalIndexes) {
3861  assert(m_triNormalIndexes);
3862  if (!ccSerializationHelper::GenericArrayToFile<Tuple3i, 3, int>(
3863  *m_triNormalIndexes, out))
3864  return false;
3865  }
3866 
3867  return true;
3868 }
3869 
3871  short minVersion =
3872  std::max(static_cast<short>(29),
3874  return std::max(minVersion, ccGenericMesh::minimumFileVersion_MeOnly());
3875 }
3876 
3878  short dataVersion,
3879  int flags,
3880  LoadedIDMap& oldToNewIDMap) {
3881  CVLog::PrintVerbose(QString("Loading mesh %1...").arg(m_name));
3882  if (!ccGenericMesh::fromFile_MeOnly(in, dataVersion, flags, oldToNewIDMap))
3883  return false;
3884 
3885  // as the associated cloud (=vertices) can't be saved directly (as it may be
3886  // shared by multiple meshes) we only store its unique ID (dataVersion>=20)
3887  // --> we hope we will find it at loading time (i.e. this is the
3888  // responsibility of the caller to make sure that all dependencies are saved
3889  // together)
3890  uint32_t vertUniqueID = 0;
3891  if (in.read((char*)&vertUniqueID, 4) < 0) return ReadError();
3892  //[DIRTY] WARNING: temporarily, we set the vertices unique ID in the
3893  //'m_associatedCloud' pointer!!!
3894  *(uint32_t*)(&m_associatedCloud) = vertUniqueID;
3895 
3896  // per-triangle normals array (dataVersion>=20)
3897  {
3898  // as the associated normals array can't be saved directly (as it may be
3899  // shared by multiple meshes) we only store its unique ID
3900  // (dataVersion>=20) --> we hope we will find it at loading time (i.e.
3901  // this is the responsibility of the caller to make sure that all
3902  // dependencies are saved together)
3903  uint32_t normArrayID = 0;
3904  if (in.read((char*)&normArrayID, 4) < 0) return ReadError();
3905  //[DIRTY] WARNING: temporarily, we set the array unique ID in the
3906  //'m_triNormals' pointer!!!
3907  *(uint32_t*)(&m_triNormals) = normArrayID;
3908  }
3909 
3910  // texture coordinates array (dataVersion>=20)
3911  {
3912  // as the associated texture coordinates array can't be saved directly
3913  // (as it may be shared by multiple meshes) we only store its unique ID
3914  // (dataVersion>=20) --> we hope we will find it at loading time (i.e.
3915  // this is the responsibility of the caller to make sure that all
3916  // dependencies are saved together)
3917  uint32_t texCoordArrayID = 0;
3918  if (in.read((char*)&texCoordArrayID, 4) < 0) return ReadError();
3919  //[DIRTY] WARNING: temporarily, we set the array unique ID in the
3920  //'m_texCoords' pointer!!!
3921  *(uint32_t*)(&m_texCoords) = texCoordArrayID;
3922  }
3923 
3924  // materials
3925  {
3926  // as the associated materials can't be saved directly (as it may be
3927  // shared by multiple meshes) we only store its unique ID
3928  // (dataVersion>=20) --> we hope we will find it at loading time (i.e.
3929  // this is the responsibility of the caller to make sure that all
3930  // dependencies are saved together)
3931  uint32_t matSetID = 0;
3932  if (in.read((char*)&matSetID, 4) < 0) return ReadError();
3933  //[DIRTY] WARNING: temporarily, we set the array unique ID in the
3934  //'m_materials' pointer!!!
3935  *(uint32_t*)(&m_materials) = matSetID;
3936  }
3937 
3938  // triangles indexes (dataVersion>=20)
3939  if (!m_triVertIndexes) return false;
3941  cloudViewer::VerticesIndexes, 3, unsigned>(
3942  *m_triVertIndexes, in, dataVersion, "vertex indexes"))
3943  return false;
3944 
3945  // per-triangle materials (dataVersion>=20))
3946  bool hasTriMtlIndexes = false;
3947  if (in.read((char*)&hasTriMtlIndexes, sizeof(bool)) < 0) return ReadError();
3948  if (hasTriMtlIndexes) {
3949  if (!m_triMtlIndexes) {
3951  m_triMtlIndexes->link();
3952  }
3953  if (!ccSerializationHelper::GenericArrayFromFile<int, 1, int>(
3954  *m_triMtlIndexes, in, dataVersion, "material indexes")) {
3956  m_triMtlIndexes = nullptr;
3957  return false;
3958  }
3959  }
3960 
3961  // per-triangle texture coordinates indexes (dataVersion>=20))
3962  bool hasTexCoordIndexes = false;
3963  if (in.read((char*)&hasTexCoordIndexes, sizeof(bool)) < 0)
3964  return ReadError();
3965  if (hasTexCoordIndexes) {
3966  if (!m_texCoordIndexes) {
3969  }
3970  if (!ccSerializationHelper::GenericArrayFromFile<Tuple3i, 3, int>(
3971  *m_texCoordIndexes, in, dataVersion,
3972  "texture coordinates")) {
3974  m_texCoordIndexes = nullptr;
3975  return false;
3976  }
3977  }
3978 
3979  //'materials shown' state (dataVersion>=20 && dataVersion<29))
3980  if (dataVersion < 29) {
3981  bool materialsShown = false;
3982  if (in.read((char*)&materialsShown, sizeof(bool)) < 0)
3983  return ReadError();
3985  }
3986 
3987  // per-triangle normals indexes (dataVersion>=20))
3988  bool hasTriNormalIndexes = false;
3989  if (in.read((char*)&hasTriNormalIndexes, sizeof(bool)) < 0)
3990  return ReadError();
3991  if (hasTriNormalIndexes) {
3992  if (!m_triNormalIndexes) {
3995  }
3996  assert(m_triNormalIndexes);
3997  if (!ccSerializationHelper::GenericArrayFromFile<Tuple3i, 3, int>(
3998  *m_triNormalIndexes, in, dataVersion, "normal indexes")) {
4000  return false;
4001  }
4002  }
4003 
4004  if (dataVersion < 29) {
4005  //'per-triangle normals shown' state (dataVersion>=20 &&
4006  // dataVersion<29))
4007  bool triNormsShown = false;
4008  if (in.read((char*)&triNormsShown, sizeof(bool)) < 0)
4009  return ReadError();
4011 
4012  //'polygon stippling' state (dataVersion>=20 && dataVersion<29))
4013  bool stippling = false;
4014  if (in.read((char*)&stippling, sizeof(bool)) < 0) return ReadError();
4015  enableStippling(stippling);
4016  }
4017 
4019 
4020  return true;
4021 }
4022 
4024  const CCVector3& P,
4025  CCVector3d& weights) const {
4026  assert(triIndex < m_triVertIndexes->size());
4027 
4028  const cloudViewer::VerticesIndexes& tri = m_triVertIndexes->at(triIndex);
4029  return computeInterpolationWeights(tri, P, weights);
4030 }
4031 
4033  const cloudViewer::VerticesIndexes& vertIndexes,
4034  const CCVector3& P,
4035  CCVector3d& weights) const {
4036  const CCVector3* A = m_associatedCloud->getPoint(vertIndexes.i1);
4037  const CCVector3* B = m_associatedCloud->getPoint(vertIndexes.i2);
4038  const CCVector3* C = m_associatedCloud->getPoint(vertIndexes.i3);
4039 
4040  // barcyentric intepolation weights
4041  weights.x = sqrt(((P - *B).cross(*C - *B)).norm2d()) /*/2*/;
4042  weights.y = sqrt(((P - *C).cross(*A - *C)).norm2d()) /*/2*/;
4043  weights.z = sqrt(((P - *A).cross(*B - *A)).norm2d()) /*/2*/;
4044 
4045  // normalize weights
4046  double sum = weights.x + weights.y + weights.z;
4047  weights /= sum;
4048 }
4049 
4050 bool ccMesh::interpolateNormals(unsigned triIndex,
4051  const CCVector3& P,
4052  CCVector3& N) {
4053  assert(triIndex < size());
4054 
4055  if (!hasNormals()) return false;
4056 
4057  const cloudViewer::VerticesIndexes& tri =
4058  m_triVertIndexes->getValue(triIndex);
4059 
4060  return interpolateNormals(
4061  tri, P, N,
4062  hasTriNormals() ? &m_triNormalIndexes->at(triIndex) : nullptr);
4063 }
4064 
4066  const CCVector3d& w,
4067  CCVector3& N,
4068  const Tuple3i* triNormIndexes /*=0*/) {
4069  CCVector3d Nd(0, 0, 0);
4070  {
4071  if (!triNormIndexes || triNormIndexes->u[0] >= 0) {
4072  const CCVector3& N1 =
4073  triNormIndexes
4075  triNormIndexes->u[0]))
4076  : m_associatedCloud->getPointNormal(vertIndexes.i1);
4077  Nd += N1.toDouble() * w.u[0];
4078  }
4079 
4080  if (!triNormIndexes || triNormIndexes->u[1] >= 0) {
4081  const CCVector3& N2 =
4082  triNormIndexes
4084  triNormIndexes->u[1]))
4085  : m_associatedCloud->getPointNormal(vertIndexes.i2);
4086  Nd += N2.toDouble() * w.u[1];
4087  }
4088 
4089  if (!triNormIndexes || triNormIndexes->u[2] >= 0) {
4090  const CCVector3& N3 =
4091  triNormIndexes
4093  triNormIndexes->u[2]))
4094  : m_associatedCloud->getPointNormal(vertIndexes.i3);
4095  Nd += N3.toDouble() * w.u[2];
4096  }
4097  Nd.normalize();
4098  }
4099 
4100  N = Nd.toPC();
4101 
4102  return true;
4103 }
4104 
4105 bool ccMesh::interpolateNormalsBC(unsigned triIndex,
4106  const CCVector3d& w,
4107  CCVector3& N) {
4108  assert(triIndex < size());
4109 
4110  if (!hasNormals()) return false;
4111 
4112  const cloudViewer::VerticesIndexes& tri =
4113  m_triVertIndexes->getValue(triIndex);
4114 
4115  return interpolateNormals(
4116  tri, w, N,
4117  hasTriNormals() ? &m_triNormalIndexes->at(triIndex) : nullptr);
4118 }
4119 
4120 bool ccMesh::interpolateColors(unsigned triIndex,
4121  const CCVector3& P,
4122  ecvColor::Rgb& C) {
4123  assert(triIndex < size());
4124 
4125  if (!hasColors()) return false;
4126 
4127  const cloudViewer::VerticesIndexes& tri =
4128  m_triVertIndexes->getValue(triIndex);
4129 
4130  return interpolateColors(tri, P, C);
4131 }
4132 
4134  const CCVector3& P,
4135  ecvColor::Rgb& rgb) {
4136  // intepolation weights
4137  CCVector3d w;
4138  computeInterpolationWeights(vertIndexes, P, w);
4139 
4140  const ecvColor::Rgb& C1 = m_associatedCloud->getPointColor(vertIndexes.i1);
4141  const ecvColor::Rgb& C2 = m_associatedCloud->getPointColor(vertIndexes.i2);
4142  const ecvColor::Rgb& C3 = m_associatedCloud->getPointColor(vertIndexes.i3);
4143 
4144  rgb.r = static_cast<ColorCompType>(
4145  floor(C1.r * w.u[0] + C2.r * w.u[1] + C3.r * w.u[2]));
4146  rgb.g = static_cast<ColorCompType>(
4147  floor(C1.g * w.u[0] + C2.g * w.u[1] + C3.g * w.u[2]));
4148  rgb.b = static_cast<ColorCompType>(
4149  floor(C1.b * w.u[0] + C2.b * w.u[1] + C3.b * w.u[2]));
4150 
4151  return true;
4152 }
4153 
4154 bool ccMesh::getVertexColorFromMaterial(unsigned triIndex,
4155  unsigned char vertIndex,
4156  ecvColor::Rgb& rgb,
4157  bool returnColorIfNoTexture) {
4158  assert(triIndex < size());
4159 
4160  assert(vertIndex < 3);
4161  if (vertIndex > 2) {
4162  CVLog::Error(
4163  "[ccMesh::getVertexColorFromMaterial] Internal error: invalid "
4164  "vertex index!");
4165  return false;
4166  }
4167 
4168  int matIndex = -1;
4169 
4170  if (hasMaterials()) {
4171  assert(m_materials);
4172  matIndex = m_triMtlIndexes->getValue(triIndex);
4173  assert(matIndex < static_cast<int>(m_materials->size()));
4174  }
4175 
4176  const cloudViewer::VerticesIndexes& tri =
4177  m_triVertIndexes->getValue(triIndex);
4178 
4179  // do we need to change material?
4180  bool foundMaterial = false;
4181  if (matIndex >= 0) {
4182  ccMaterial::CShared material = (*m_materials)[matIndex];
4183  if (material->hasTexture()) {
4184  assert(m_texCoords && m_texCoordIndexes);
4185  const Tuple3i& txInd = m_texCoordIndexes->getValue(triIndex);
4186  const TexCoords2D* T =
4187  (txInd.u[vertIndex] >= 0
4188  ? &m_texCoords->getValue(txInd.u[vertIndex])
4189  : nullptr);
4190  if (T) {
4191  // get the texture coordinates between 0 and 1
4192  float temp;
4193  float tx = std::modf(T->tx, &temp);
4194  if (tx < 0) tx = 1.0f + tx;
4195  float ty = std::modf(T->ty, &temp);
4196  if (ty < 0) ty = 1.0f + ty;
4197 
4198  // get color from texture image
4199  const QImage texture = material->getTexture();
4200  int xPix =
4201  std::min(static_cast<int>(floor(tx * texture.width())),
4202  texture.width() - 1);
4203  int yPix =
4204  std::min(static_cast<int>(floor(ty * texture.height())),
4205  texture.height() - 1);
4206 
4207  QRgb pixel = texture.pixel(xPix, yPix);
4208 
4209  rgb = ecvColor::FromQRgb(pixel);
4210  foundMaterial = true;
4211  }
4212  } else {
4213  const ecvColor::Rgbaf& diffuse = material->getDiffuseFront();
4214  rgb = ecvColor::FromRgbafToRgb(diffuse);
4215 
4216  foundMaterial = true;
4217  }
4218  }
4219 
4220  if (!foundMaterial && returnColorIfNoTexture && hasColors()) {
4221  rgb = ecvColor::Rgb(m_associatedCloud->getPointColor(tri.i[vertIndex]));
4222  foundMaterial = true;
4223  }
4224 
4225  return foundMaterial;
4226 }
4227 
4228 bool ccMesh::getColorFromMaterial(unsigned triIndex,
4229  const CCVector3& P,
4230  ecvColor::Rgb& rgb,
4231  bool interpolateColorIfNoTexture) {
4232  assert(triIndex < size());
4233 
4234  int matIndex = -1;
4235 
4236  if (hasMaterials()) {
4237  assert(m_materials);
4238  matIndex = m_triMtlIndexes->getValue(triIndex);
4239  assert(matIndex < static_cast<int>(m_materials->size()));
4240  }
4241 
4242  // do we need to change material?
4243  if (matIndex < 0) {
4244  if (interpolateColorIfNoTexture)
4245  return interpolateColors(triIndex, P, rgb);
4246  return false;
4247  }
4248 
4249  ccMaterial::CShared material = (*m_materials)[matIndex];
4250 
4251  if (!material->hasTexture()) {
4252  const ecvColor::Rgbaf& diffuse = material->getDiffuseFront();
4253  rgb.r = static_cast<ColorCompType>(diffuse.r * ecvColor::MAX);
4254  rgb.g = static_cast<ColorCompType>(diffuse.g * ecvColor::MAX);
4255  rgb.b = static_cast<ColorCompType>(diffuse.b * ecvColor::MAX);
4256  return true;
4257  }
4258 
4259  assert(m_texCoords && m_texCoordIndexes);
4260  const Tuple3i& txInd = m_texCoordIndexes->getValue(triIndex);
4261  const TexCoords2D* T1 =
4262  (txInd.u[0] >= 0 ? &m_texCoords->getValue(txInd.u[0]) : nullptr);
4263  const TexCoords2D* T2 =
4264  (txInd.u[1] >= 0 ? &m_texCoords->getValue(txInd.u[1]) : nullptr);
4265  const TexCoords2D* T3 =
4266  (txInd.u[2] >= 0 ? &m_texCoords->getValue(txInd.u[2]) : nullptr);
4267 
4268  // intepolation weights
4269  CCVector3d w;
4270  computeInterpolationWeights(triIndex, P, w);
4271 
4272  if ((!T1 && w.u[0] > ZERO_TOLERANCE_D) ||
4273  (!T2 && w.u[1] > ZERO_TOLERANCE_D) ||
4274  (!T3 && w.u[2] > ZERO_TOLERANCE_D)) {
4275  // assert(false);
4276  if (interpolateColorIfNoTexture)
4277  return interpolateColors(triIndex, P, rgb);
4278  return false;
4279  }
4280 
4281  double x = (T1 ? T1->tx * w.u[0] : 0.0) + (T2 ? T2->tx * w.u[1] : 0.0) +
4282  (T3 ? T3->tx * w.u[2] : 0.0);
4283  double y = (T1 ? T1->ty * w.u[0] : 0.0) + (T2 ? T2->ty * w.u[1] : 0.0) +
4284  (T3 ? T3->ty * w.u[2] : 0.0);
4285 
4286  // DGM: we mut handle texture coordinates below 0 or above 1 (i.e.
4287  // repetition) if (x < 0 || x > 1.0 || y < 0 || y > 1.0)
4288  if (x > 1.0) {
4289  double xFrac, xInt;
4290  xFrac = std::modf(x, &xInt);
4291  x = xFrac;
4292  } else if (x < 0.0) {
4293  double xFrac, xInt;
4294  xFrac = std::modf(x, &xInt);
4295  x = 1.0 + xFrac;
4296  }
4297 
4298  // same thing for y
4299  if (y > 1.0) {
4300  double yFrac, yInt;
4301  yFrac = std::modf(y, &yInt);
4302  y = yFrac;
4303  } else if (y < 0.0) {
4304  double yFrac, yInt;
4305  yFrac = std::modf(y, &yInt);
4306  y = 1.0 + yFrac;
4307  }
4308 
4309  // get color from texture image
4310  {
4311  const QImage texture = material->getTexture();
4312  int xPix = std::min(static_cast<int>(floor(x * texture.width())),
4313  texture.width() - 1);
4314  int yPix = std::min(static_cast<int>(floor(y * texture.height())),
4315  texture.height() - 1);
4316 
4317  QRgb pixel = texture.pixel(xPix, yPix);
4318 
4319  const ecvColor::Rgbaf& diffuse = material->getDiffuseFront();
4320  rgb.r = static_cast<ColorCompType>(diffuse.r * qRed(pixel));
4321  rgb.g = static_cast<ColorCompType>(diffuse.g * qGreen(pixel));
4322  rgb.b = static_cast<ColorCompType>(diffuse.b * qBlue(pixel));
4323  }
4324 
4325  return true;
4326 }
4327 
4328 // we use as many static variables as we can to limit the size of the heap used
4329 // by each recursion...
4330 static const unsigned s_defaultSubdivideGrowRate = 50;
4332 static QMap<qint64, unsigned>
4333  s_alreadyCreatedVertices; // map to store already created edges middle
4334  // points
4335 
4336 static qint64 GenerateKey(unsigned edgeIndex1, unsigned edgeIndex2) {
4337  if (edgeIndex1 > edgeIndex2) std::swap(edgeIndex1, edgeIndex2);
4338 
4339  return (static_cast<qint64>(edgeIndex1) << 32) |
4340  static_cast<qint64>(edgeIndex2);
4341 }
4342 
4343 bool ccMesh::pushSubdivide(/*PointCoordinateType maxArea, */ unsigned indexA,
4344  unsigned indexB,
4345  unsigned indexC) {
4346  if (s_maxSubdivideArea /*maxArea*/ <= ZERO_TOLERANCE_F) {
4347  CVLog::Error("[ccMesh::pushSubdivide] Invalid input argument!");
4348  return false;
4349  }
4350 
4351  if (!getAssociatedCloud() ||
4353  CVLog::Error(
4354  "[ccMesh::pushSubdivide] Vertices set must be a true point "
4355  "cloud!");
4356  return false;
4357  }
4358  ccPointCloud* vertices = static_cast<ccPointCloud*>(getAssociatedCloud());
4359  assert(vertices);
4360  const CCVector3* A = vertices->getPoint(indexA);
4361  const CCVector3* B = vertices->getPoint(indexB);
4362  const CCVector3* C = vertices->getPoint(indexC);
4363 
4364  // do we need to sudivide this triangle?
4365  PointCoordinateType area = ((*B - *A) * (*C - *A)).norm() / 2;
4366  if (area > s_maxSubdivideArea /*maxArea*/) {
4367  // we will add 3 new vertices, so we must be sure to have enough memory
4368  if (vertices->size() + 2 >= vertices->capacity()) {
4369  assert(s_defaultSubdivideGrowRate > 2);
4370  if (!vertices->reserve(vertices->size() +
4372  CVLog::Error("[ccMesh::pushSubdivide] Not enough memory!");
4373  return false;
4374  }
4375  // We have to update pointers as they may have been wrangled by the
4376  // 'reserve' call
4377  A = vertices->getPoint(indexA);
4378  B = vertices->getPoint(indexB);
4379  C = vertices->getPoint(indexC);
4380  }
4381 
4382  // add new vertices
4383  unsigned indexG1 = 0;
4384  {
4385  qint64 key = GenerateKey(indexA, indexB);
4386  QMap<qint64, unsigned>::const_iterator it =
4387  s_alreadyCreatedVertices.constFind(key);
4388  if (it == s_alreadyCreatedVertices.constEnd()) {
4389  // generate new vertex
4390  indexG1 = vertices->size();
4391  CCVector3 G1 = (*A + *B) / 2;
4392  vertices->addPoint(G1);
4393  // interpolate other features?
4394  // if (vertices->hasNormals())
4395  //{
4396  // //vertices->reserveTheNormsTable();
4397  // CCVector3 N(0.0, 0.0, 1.0);
4398  // interpolateNormals(indexA, indexB, indexC, G1, N);
4399  // vertices->addNorm(N);
4400  // }
4401  if (vertices->hasColors()) {
4402  ecvColor::Rgb C;
4404  indexA, indexB, indexC),
4405  G1, C);
4406  vertices->addRGBColor(C);
4407  }
4408  // and add it to the map
4409  s_alreadyCreatedVertices.insert(key, indexG1);
4410  } else {
4411  indexG1 = it.value();
4412  }
4413  }
4414  unsigned indexG2 = 0;
4415  {
4416  qint64 key = GenerateKey(indexB, indexC);
4417  QMap<qint64, unsigned>::const_iterator it =
4418  s_alreadyCreatedVertices.constFind(key);
4419  if (it == s_alreadyCreatedVertices.constEnd()) {
4420  // generate new vertex
4421  indexG2 = vertices->size();
4422  CCVector3 G2 = (*B + *C) / 2;
4423  vertices->addPoint(G2);
4424  // interpolate other features?
4425  // if (vertices->hasNormals())
4426  //{
4427  // //vertices->reserveTheNormsTable();
4428  // CCVector3 N(0.0, 0.0, 1.0);
4429  // interpolateNormals(indexA, indexB, indexC, G2, N);
4430  // vertices->addNorm(N);
4431  // }
4432  if (vertices->hasColors()) {
4433  ecvColor::Rgb C;
4435  indexA, indexB, indexC),
4436  G2, C);
4437  vertices->addRGBColor(C);
4438  }
4439  // and add it to the map
4440  s_alreadyCreatedVertices.insert(key, indexG2);
4441  } else {
4442  indexG2 = it.value();
4443  }
4444  }
4445  unsigned indexG3 = vertices->size();
4446  {
4447  qint64 key = GenerateKey(indexC, indexA);
4448  QMap<qint64, unsigned>::const_iterator it =
4449  s_alreadyCreatedVertices.constFind(key);
4450  if (it == s_alreadyCreatedVertices.constEnd()) {
4451  // generate new vertex
4452  indexG3 = vertices->size();
4453  CCVector3 G3 = (*C + *A) / 2.0;
4454  vertices->addPoint(G3);
4455  // interpolate other features?
4456  // if (vertices->hasNormals())
4457  //{
4458  // //vertices->reserveTheNormsTable();
4459  // CCVector3 N(0.0, 0.0, 1.0);
4460  // interpolateNormals(indexA, indexB, indexC, G3, N);
4461  // vertices->addNorm(N);
4462  // }
4463  if (vertices->hasColors()) {
4464  ecvColor::Rgb C;
4466  indexA, indexB, indexC),
4467  G3, C);
4468  vertices->addRGBColor(C);
4469  }
4470  // and add it to the map
4471  s_alreadyCreatedVertices.insert(key, indexG3);
4472  } else {
4473  indexG3 = it.value();
4474  }
4475  }
4476 
4477  // add new triangles
4478  if (!pushSubdivide(/*maxArea, */ indexA, indexG1, indexG3))
4479  return false;
4480  if (!pushSubdivide(/*maxArea, */ indexB, indexG2, indexG1))
4481  return false;
4482  if (!pushSubdivide(/*maxArea, */ indexC, indexG3, indexG2))
4483  return false;
4484  if (!pushSubdivide(/*maxArea, */ indexG1, indexG2, indexG3))
4485  return false;
4486  } else {
4487  // we will add one triangle, so we must be sure to have enough memory
4488  if (size() == capacity()) {
4489  if (!reserve(size() + 3 * s_defaultSubdivideGrowRate)) {
4490  CVLog::Error("[ccMesh::pushSubdivide] Not enough memory!");
4491  return false;
4492  }
4493  }
4494 
4495  // we keep this triangle as is
4496  addTriangle(indexA, indexB, indexC);
4497  }
4498 
4499  return true;
4500 }
4501 
4503  if (cloudViewer::LessThanEpsilon(maxArea)) {
4504  CVLog::Error("[ccMesh::subdivide] Invalid input argument!");
4505  return nullptr;
4506  }
4507  s_maxSubdivideArea = maxArea;
4508 
4509  unsigned triCount = size();
4511  unsigned vertCount = (vertices ? vertices->size() : 0);
4512  if (!vertices || vertCount * triCount == 0) {
4513  CVLog::Error("[ccMesh::subdivide] Invalid mesh: no face or no vertex!");
4514  return nullptr;
4515  }
4516 
4517  ccPointCloud* resultVertices =
4518  vertices->isA(CV_TYPES::POINT_CLOUD)
4519  ? static_cast<ccPointCloud*>(vertices)->cloneThis()
4520  : ccPointCloud::From(vertices, vertices);
4521  if (!resultVertices) {
4522  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4523  return nullptr;
4524  }
4525 
4526  ccMesh* resultMesh = new ccMesh(resultVertices);
4527  resultMesh->addChild(resultVertices);
4528 
4529  if (!resultMesh->reserve(triCount)) {
4530  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4531  delete resultMesh;
4532  return nullptr;
4533  }
4534 
4535  s_alreadyCreatedVertices.clear();
4536 
4537  try {
4538  for (unsigned i = 0; i < triCount; ++i) {
4539  const cloudViewer::VerticesIndexes& tri =
4541  if (!resultMesh->pushSubdivide(/*maxArea,*/ tri.i1, tri.i2,
4542  tri.i3)) {
4543  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4544  delete resultMesh;
4545  return nullptr;
4546  }
4547  }
4548  } catch (...) {
4549  CVLog::Error("[ccMesh::subdivide] An error occurred!");
4550  delete resultMesh;
4551  return nullptr;
4552  }
4553 
4554  // we must also 'fix' the triangles that share (at least) an edge with a
4555  // subdivided triangle!
4556  try {
4557  unsigned newTriCount = resultMesh->size();
4558  for (unsigned i = 0; i < newTriCount; ++i) {
4560  resultMesh->m_triVertIndexes->getValue(
4561  i); // warning: array might change at each call to
4562  // reallocate!
4563  unsigned indexA = tri.i1;
4564  unsigned indexB = tri.i2;
4565  unsigned indexC = tri.i3;
4566 
4567  // test all edges
4568  int indexG1 = -1;
4569  {
4570  QMap<qint64, unsigned>::const_iterator it =
4571  s_alreadyCreatedVertices.constFind(
4572  GenerateKey(indexA, indexB));
4573  if (it != s_alreadyCreatedVertices.constEnd())
4574  indexG1 = (int)it.value();
4575  }
4576  int indexG2 = -1;
4577  {
4578  QMap<qint64, unsigned>::const_iterator it =
4579  s_alreadyCreatedVertices.constFind(
4580  GenerateKey(indexB, indexC));
4581  if (it != s_alreadyCreatedVertices.constEnd())
4582  indexG2 = (int)it.value();
4583  }
4584  int indexG3 = -1;
4585  {
4586  QMap<qint64, unsigned>::const_iterator it =
4587  s_alreadyCreatedVertices.constFind(
4588  GenerateKey(indexC, indexA));
4589  if (it != s_alreadyCreatedVertices.constEnd())
4590  indexG3 = (int)it.value();
4591  }
4592 
4593  // at least one edge is 'wrong'
4594  unsigned brokenEdges = (indexG1 < 0 ? 0 : 1) +
4595  (indexG2 < 0 ? 0 : 1) +
4596  (indexG3 < 0 ? 0 : 1);
4597 
4598  if (brokenEdges == 1) {
4599  int indexG = indexG1;
4600  unsigned char i1 = 2; // relative index facing the broken edge
4601  if (indexG2 >= 0) {
4602  indexG = indexG2;
4603  i1 = 0;
4604  } else if (indexG3 >= 0) {
4605  indexG = indexG3;
4606  i1 = 1;
4607  }
4608  assert(indexG >= 0);
4609  assert(i1 < 3);
4610 
4611  unsigned indexes[3] = {indexA, indexB, indexC};
4612 
4613  // replace current triangle by one half
4614  tri.i1 = indexes[i1];
4615  tri.i2 = indexG;
4616  tri.i3 = indexes[(i1 + 2) % 3];
4617  // and add the other half (we can use pushSubdivide as the area
4618  // should alredy be ok!)
4619  if (!resultMesh->pushSubdivide(/*maxArea,*/ indexes[i1],
4620  indexes[(i1 + 1) % 3], indexG)) {
4621  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4622  delete resultMesh;
4623  return nullptr;
4624  }
4625  } else if (brokenEdges == 2) {
4626  if (indexG1 < 0) // broken edges: BC and CA
4627  {
4628  // replace current triangle by the 'pointy' part
4629  tri.i1 = indexC;
4630  tri.i2 = indexG3;
4631  tri.i3 = indexG2;
4632  // split the remaining 'trapezoid' in 2
4633  if (!resultMesh->pushSubdivide(/*maxArea, */ indexA,
4634  indexG2, indexG3) ||
4635  !resultMesh->pushSubdivide(/*maxArea, */ indexA, indexB,
4636  indexG2)) {
4637  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4638  delete resultMesh;
4639  return nullptr;
4640  }
4641  } else if (indexG2 < 0) // broken edges: AB and CA
4642  {
4643  // replace current triangle by the 'pointy' part
4644  tri.i1 = indexA;
4645  tri.i2 = indexG1;
4646  tri.i3 = indexG3;
4647  // split the remaining 'trapezoid' in 2
4648  if (!resultMesh->pushSubdivide(/*maxArea, */ indexB,
4649  indexG3, indexG1) ||
4650  !resultMesh->pushSubdivide(/*maxArea, */ indexB, indexC,
4651  indexG3)) {
4652  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4653  delete resultMesh;
4654  return nullptr;
4655  }
4656  } else /*if (indexG3 < 0)*/ // broken edges: AB and BC
4657  {
4658  // replace current triangle by the 'pointy' part
4659  tri.i1 = indexB;
4660  tri.i2 = indexG2;
4661  tri.i3 = indexG1;
4662  // split the remaining 'trapezoid' in 2
4663  if (!resultMesh->pushSubdivide(/*maxArea, */ indexC,
4664  indexG1, indexG2) ||
4665  !resultMesh->pushSubdivide(/*maxArea, */ indexC, indexA,
4666  indexG1)) {
4667  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4668  delete resultMesh;
4669  return nullptr;
4670  }
4671  }
4672  } else if (brokenEdges ==
4673  3) // works just as a standard subdivision in fact!
4674  {
4675  // replace current triangle by one quarter
4676  tri.i1 = indexA;
4677  tri.i2 = indexG1;
4678  tri.i3 = indexG3;
4679  // and add the other 3 quarters (we can use pushSubdivide as the
4680  // area should alredy be ok!)
4681  if (!resultMesh->pushSubdivide(/*maxArea, */ indexB, indexG2,
4682  indexG1) ||
4683  !resultMesh->pushSubdivide(/*maxArea, */ indexC, indexG3,
4684  indexG2) ||
4685  !resultMesh->pushSubdivide(/*maxArea, */ indexG1, indexG2,
4686  indexG3)) {
4687  CVLog::Error("[ccMesh::subdivide] Not enough memory!");
4688  delete resultMesh;
4689  return nullptr;
4690  }
4691  }
4692  }
4693  } catch (...) {
4694  CVLog::Error("[ccMesh::subdivide] An error occurred!");
4695  delete resultMesh;
4696  return nullptr;
4697  }
4698 
4699  s_alreadyCreatedVertices.clear();
4700 
4701  resultMesh->shrinkToFit();
4702  resultVertices->shrinkToFit();
4703 
4704  // we import from the original mesh... what we can
4705  if (hasNormals()) {
4706  if (hasNormals()) // normals interpolation doesn't work well...
4707  resultMesh->computeNormals(!hasTriNormals());
4708  resultMesh->showNormals(normalsShown());
4709  }
4710  if (hasColors()) {
4711  resultMesh->showColors(colorsShown());
4712  }
4713  resultMesh->setVisible(isVisible());
4714 
4715  return resultMesh;
4716 }
4717 
4719  if (!hasMaterials()) {
4721  "[ccMesh::convertMaterialsToVertexColors] Mesh has no "
4722  "material!");
4723  return false;
4724  }
4725 
4728  "[ccMesh::convertMaterialsToVertexColors] Need a true point "
4729  "cloud as vertices!");
4730  return false;
4731  }
4732 
4733  ccPointCloud* cloud = static_cast<ccPointCloud*>(m_associatedCloud);
4734  if (!cloud->resizeTheRGBTable(true)) {
4736  "[ccMesh::convertMaterialsToVertexColors] Failed to resize "
4737  "vertices color table! (not enough memory?)");
4738  return false;
4739  }
4740 
4741  // now scan all faces and get the vertex color each time
4742  unsigned faceCount = size();
4743 
4745  for (unsigned i = 0; i < faceCount; ++i) {
4747  for (unsigned char j = 0; j < 3; ++j) {
4748  ecvColor::Rgb C;
4749  if (getVertexColorFromMaterial(i, j, C, true)) {
4750  // FIXME: could we be smarter? (we process each point several
4751  // times! And we assume the color is always the same...)
4752  cloud->setPointColor(tsi->i[j], C);
4753  }
4754  }
4755  }
4756 
4757  return true;
4758 }
constexpr float ZERO_TOLERANCE_F
Definition: CVConst.h:43
constexpr unsigned char POINT_HIDDEN
Definition: CVConst.h:94
constexpr double ZERO_TOLERANCE_D
Definition: CVConst.h:49
Tuple3Tpl< int > Tuple3i
Tuple of 3 int values.
Definition: CVGeom.h:217
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
double normal[3]
int count
char type
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 PrintVerbose(const char *format,...)
Prints out a verbose formatted message in console.
Definition: CVLog.cpp:103
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)
NormsIndexesTableType * clone() override
Duplicates array (overloaded from ccArray::clone)
Array of 2D texture coordinates.
TextureCoordsContainer * clone() override
Duplicates array (overloaded from ccArray::clone)
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
Vector3Tpl orthogonal() const
Returns a normalized vector which is orthogonal to this one.
Definition: CVGeom.h:433
Vector3Tpl< PointCoordinateType > toPC() const
Definition: CVGeom.h:263
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
Definition: CVGeom.h:412
Vector3Tpl< double > toDouble() const
Cast operator to a double vector (explicit call version)
Definition: CVGeom.h:255
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
Shareable array that can be properly inserted in the DB tree.
Definition: ecvArray.h:21
Type & getValue(size_t index)
Definition: ecvArray.h:100
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
void swap(size_t i1, size_t i2)
Definition: ecvArray.h:121
bool copy(Base &dest) const
Copies the content of this array in another one.
Definition: ecvArray.h:43
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 hasDisplayedScalarField() const
Returns whether an active scalar field is available 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 showNormals(bool state)
Sets normals visibility.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
virtual bool hasScalarFields() const
Returns whether one or more scalar fields are instantiated.
bool m_normalsDisplayed
Specifies whether normals should be displayed.
virtual void getDrawingParameters(glDrawParams &params) const
Returns main OpenGL parameters for this entity.
ecvColor::Rgb m_tempColor
Temporary (unique) color.
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.
void shiftRotationCenter(const Vector3Tpl< T > &vec)
Shifts rotation center.
void setRotation(const float Rt[9])
Sets Rotation from a float array.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Generic mesh interface.
virtual void showPoints(bool state)
virtual bool materialsShown() const
Sets whether textures/material should be displayed or not.
void showNormals(bool state) override
Sets normals visibility.
void importParametersFrom(const ccGenericMesh *mesh)
Imports the parameters from another mesh.
virtual void showWired(bool state)
Sets whether mesh should be displayed as a wire or with plain facets.
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
virtual bool triNormsShown() const
Returns whether per-triangle normals are shown or not.
virtual bool isShownAsWire() const
Returns whether the mesh is displayed as wired or with plain facets.
short minimumFileVersion_MeOnly() const override
void enableStippling(bool state)
Enables polygon stippling.
virtual void showMaterials(bool state)
Sets whether textures should be displayed or not.
virtual bool isShownAsPoints() const
Returns whether the mesh is displayed as wired or with plain facets.
void handleColorRamp(CC_DRAW_CONTEXT &context)
Handles the color ramp display.
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
virtual bool stipplingEnabled() const
Returns whether polygon stippling is enabled or not.
virtual void showTriNorms(bool state)
Sets whether to show or not per-triangle normals.
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 ccGenericPointCloud * createNewCloudFromVisibilitySelection(bool removeSelectedPoints=false, VisibilityTableType *visTable=nullptr, std::vector< int > *newIndexesOfRemainingPoints=nullptr, bool silent=false, cloudViewer::ReferenceCloud *selection=nullptr)=0
virtual const ecvColor::Rgb & getPointColor(unsigned pointIndex) const =0
Returns color corresponding to a given point.
virtual VisibilityTableType & getTheVisibilityArray()
Returns associated visibility array.
virtual ccGenericPointCloud * clone(ccGenericPointCloud *destCloud=nullptr, bool ignoreChildren=false)=0
Clones this entity.
std::vector< unsigned char > VisibilityTableType
Array of "visibility" information for each point.
virtual bool removeVisiblePoints(VisibilityTableType *visTable=nullptr, std::vector< int > *newIndexes=nullptr)=0
Removes all the 'visible' points (as defined by the visibility array)
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)
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual void notifyGeometryUpdate()
Definition: ecvHObject.cpp:104
static Eigen::Vector3d ComputeMinBound(const std::vector< Eigen::Vector3d > &points)
Compute min bound of a list points.
Definition: ecvHObject.cpp:272
virtual const ccGLMatrix & getGLTransformationHistory() const
Returns the transformation 'history' matrix.
Definition: ecvHObject.h:631
ccGLMatrix m_glTransHistory
Cumulative GL transformation.
Definition: ecvHObject.h:727
int getChildIndex(const ccHObject *aChild) const
Returns child index.
Definition: ecvHObject.cpp:639
static Eigen::Vector3d ComputeCenter(const std::vector< Eigen::Vector3d > &points)
Computer center of a list of points.
Definition: ecvHObject.cpp:296
virtual void onUpdateOf(ccHObject *obj)
This method is called when another object (geometry) is updated.
Definition: ecvHObject.h:706
void addDependency(ccHObject *otherObject, int flags, bool additive=true)
Adds a new dependence (additive or not)
Definition: ecvHObject.cpp:455
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
@ DP_NOTIFY_OTHER_ON_DELETE
Definition: ecvHObject.h:259
@ DP_NOTIFY_OTHER_ON_UPDATE
Definition: ecvHObject.h:261
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
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
virtual void onDeletionOf(const ccHObject *obj)
This method is called when another object is deleted.
Definition: ecvHObject.cpp:519
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
static Eigen::Vector3d ComputeMaxBound(const std::vector< Eigen::Vector3d > &points)
Compute max bound of a list points.
Definition: ecvHObject.cpp:284
Mesh (triangle) material.
int addMaterial(ccMaterial::CShared mat, bool allowDuplicateNames=false)
Adds a material.
ccMaterialSet * clone() const
Clones materials set.
QSharedPointer< const ccMaterial > CShared
Const + Shared type.
Definition: ecvMaterial.h:31
Triangular mesh.
Definition: ecvMesh.h:35
bool interpolateNormalsBC(unsigned triIndex, const CCVector3d &w, CCVector3 &N) override
Interpolates normal(s) inside a given triangle.
Definition: ecvMesh.cpp:4105
ccMesh * cloneMesh(ccGenericPointCloud *vertices=nullptr, ccMaterialSet *clonedMaterials=nullptr, NormsIndexesTableType *clonedNormsTable=nullptr, TextureCoordsContainer *cloneTexCoords=nullptr)
Clones this entity.
Definition: ecvMesh.cpp:1109
void getTriangleTexCoordinatesIndexes(unsigned triangleIndex, int &i1, int &i2, int &i3) const override
Returns the triplet of tex coords indexes for a given triangle.
Definition: ecvMesh.cpp:3691
cloudViewer::VerticesIndexes * getNextTriangleVertIndexes() override
Definition: ecvMesh.cpp:2618
virtual void showNormals_extended(bool p)
Definition: ecvMesh.h:1487
ccMaterialSet * m_materials
Materials.
Definition: ecvMesh.h:1499
const ccMaterialSet * getMaterialSet() const override
Definition: ecvMesh.h:412
std::vector< Eigen::Vector3d > getTriangleNorms() const
Definition: ecvMesh.cpp:3535
std::vector< Eigen::Vector3d > getVertexColors() const
Definition: ecvMesh.cpp:2311
bool merge(const ccMesh *mesh, bool createSubMesh)
Merges another mesh into this one.
Definition: ecvMesh.cpp:1788
cloudViewer::GenericTriangle * _getNextTriangle() override
Returns the next triangle (relatively to the global iterator position)
Definition: ecvMesh.cpp:2165
void addEigenVertices(const std::vector< Eigen::Vector3d > &vertices)
Definition: ecvMesh.cpp:2377
void getTriangleNormalIndexes(unsigned triangleIndex, int &i1, int &i2, int &i3) const override
Returns a triplet of normal indexes for a given triangle (if any)
Definition: ecvMesh.cpp:3353
static ccMesh * Triangulate(ccGenericPointCloud *cloud, cloudViewer::TRIANGULATION_TYPES type, bool updateNormals=false, PointCoordinateType maxEdgeLength=0, unsigned char dim=2)
Creates a Delaunay 2.5D mesh from a point cloud.
Definition: ecvMesh.cpp:1732
virtual ecvOrientedBBox GetOrientedBoundingBox() const override
Definition: ecvMesh.cpp:848
void drawMeOnly(CC_DRAW_CONTEXT &context) override
Enables (OpenGL) stipple mask.
Definition: ecvMesh.cpp:2635
bool hasMaterials() const override
Definition: ecvMesh.cpp:3713
bool hasDisplayedScalarField() const override
Returns whether an active scalar field is available or not.
Definition: ecvMesh.cpp:233
void invertPerTriangleNormals()
Invert per-triangle normals.
Definition: ecvMesh.cpp:3301
virtual ccMesh & 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...
Definition: ecvMesh.cpp:872
void applyGLTransformation(const ccGLMatrix &trans) override
Applies a GL transformation to the entity.
Definition: ecvMesh.cpp:514
ccMesh & operator=(const ccMesh &mesh)
Definition: ecvMesh.cpp:133
bool laplacianSmooth(unsigned nbIteration=100, PointCoordinateType factor=static_cast< PointCoordinateType >(0.01), ecvProgressDialog *progressCb=nullptr)
Laplacian smoothing.
Definition: ecvMesh.cpp:1013
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
Definition: ecvMesh.cpp:828
bool normalsShown() const override
Returns whether normals are shown or not.
Definition: ecvMesh.cpp:388
ccMesh operator+(const ccMesh &mesh) const
Definition: ecvMesh.cpp:165
bool reservePerTriangleMtlIndexes()
Reserves memory to store per-triangle material index.
Definition: ecvMesh.cpp:3734
void flipTriangles()
Flips the triangle.
Definition: ecvMesh.cpp:3309
void setTriangleMtlIndexesTable(triangleMaterialIndexesSet *matIndexesTable, bool autoReleaseOldTable=true)
Sets per-triangle material indexes array.
Definition: ecvMesh.cpp:3718
void setVertexNormal(size_t index, const Eigen::Vector3d &normal)
Definition: ecvMesh.cpp:2237
bool processScalarField(MESH_SCALAR_FIELD_PROCESS process)
Definition: ecvMesh.cpp:392
ccMesh * subdivide(PointCoordinateType maxArea) const
Definition: ecvMesh.cpp:4502
ccGenericPointCloud * m_associatedCloud
associated cloud (vertices)
Definition: ecvMesh.h:1490
bool setTriangleNorm(size_t index, const Eigen::Vector3d &triangle_normal)
Definition: ecvMesh.cpp:3483
triangleMaterialIndexesSet * m_triMtlIndexes
Per-triangle material indexes.
Definition: ecvMesh.h:1513
void removePerTriangleNormalIndexes()
Removes any per-triangle triplets of normal indexes.
Definition: ecvMesh.cpp:3323
std::vector< int > triangle_material_ids_
List of material ids.
Definition: ecvMesh.h:707
triangleTexCoordIndexesSet * m_texCoordIndexes
Mesh tex coords indexes (per-triangle)
Definition: ecvMesh.h:1518
bool convertMaterialsToVertexColors()
Converts materials to vertex colors.
Definition: ecvMesh.cpp:4718
short minimumFileVersion_MeOnly() const override
Definition: ecvMesh.cpp:3870
void addVertice(const Eigen::Vector3d &vertice)
Definition: ecvMesh.cpp:2230
bool toFile_MeOnly(QFile &out, short dataVersion) const override
Save own object data.
Definition: ecvMesh.cpp:3766
virtual unsigned size() const override
Returns the number of triangles.
Definition: ecvMesh.cpp:2143
void setTriangle(size_t index, const Eigen::Vector3i &triangle)
Definition: ecvMesh.cpp:2436
ccMesh(ccGenericPointCloud *vertices=nullptr)
Default ccMesh constructor.
Definition: ecvMesh.cpp:42
void getTriangleTexCoordinates(unsigned triIndex, TexCoords2D *&tx1, TexCoords2D *&tx2, TexCoords2D *&tx3) const override
Returns per-triangle texture coordinates (pointer to)
Definition: ecvMesh.cpp:3620
void setTriNormsTable(NormsIndexesTableType *triNormsTable, bool autoReleaseOldTable=true)
Sets per-triangle normals array (may be shared)
Definition: ecvMesh.cpp:470
std::vector< Eigen::Vector2d > triangle_uvs_
List of uv coordinates per triangle.
Definition: ecvMesh.h:625
void transformTriNormals(const ccGLMatrix &trans)
Transforms the mesh per-triangle normals.
Definition: ecvMesh.cpp:524
void swapTriangles(unsigned index1, unsigned index2)
Swaps two triangles.
Definition: ecvMesh.cpp:2574
Eigen::Vector3d getTriangleNorm(size_t index) const
Definition: ecvMesh.cpp:3474
ccBBox m_bBox
Bounding-box.
Definition: ecvMesh.h:1510
bool HasVertexNormals() const
Definition: ecvMesh.cpp:229
bool hasColors() const override
Returns whether colors are enabled or not.
Definition: ecvMesh.cpp:221
MESH_SCALAR_FIELD_PROCESS
Mesh scalar field processes.
Definition: ecvMesh.h:554
@ ENHANCE_MESH_SF
Definition: ecvMesh.h:556
@ SMOOTH_MESH_SF
Definition: ecvMesh.h:555
bool getColorFromMaterial(unsigned triIndex, const CCVector3 &P, ecvColor::Rgb &C, bool interpolateColorIfNoTexture) override
Definition: ecvMesh.cpp:4228
const ccGLMatrix & getGLTransformationHistory() const override
Returns the transformation 'history' matrix.
Definition: ecvMesh.cpp:2419
bool pushSubdivide(unsigned indexA, unsigned indexB, unsigned indexC)
Used internally by 'subdivide'.
Definition: ecvMesh.cpp:4343
void setAssociatedCloud(ccGenericPointCloud *cloud)
Sets the associated vertices cloud (warning)
Definition: ecvMesh.cpp:169
ccArray< Tuple3i, 3, int > triangleNormalsIndexesSet
Set of triplets of indexes referring to mesh normals.
Definition: ecvMesh.h:1521
std::vector< std::unordered_set< int > > adjacency_list_
Definition: ecvMesh.h:622
void addTriangleMtlIndex(int mtlIndex)
Adds triangle material index for next triangle.
Definition: ecvMesh.cpp:3751
void setTriangleMtlIndex(unsigned triangleIndex, int mtlIndex)
Sets triangle material indexes.
Definition: ecvMesh.cpp:3756
unsigned m_globalIterator
Iterator on the list of triangles.
Definition: ecvMesh.h:1505
void clearTriNormals()
Removes per-triangle normals.
Definition: ecvMesh.h:353
bool computePerTriangleNormals()
Computes per-triangle normals.
Definition: ecvMesh.cpp:331
void setEigenVertices(const std::vector< Eigen::Vector3d > &vertices)
Definition: ecvMesh.cpp:2361
const std::vector< CCVector3 > & getVertices() const
Definition: ecvMesh.cpp:2352
void setMaterialSet(ccMaterialSet *materialSet, bool autoReleaseOldMaterialSet=true)
Sets associated material set (may be shared)
Definition: ecvMesh.cpp:492
void setTriangles(const std::vector< Eigen::Vector3i > &triangles)
Definition: ecvMesh.cpp:2455
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 addVertexColors(const std::vector< Eigen::Vector3d > &colors)
Definition: ecvMesh.cpp:2327
std::shared_ptr< ccMesh > Crop(const ccBBox &bbox) const
Definition: ecvMesh.cpp:992
void addVertexNormal(const Eigen::Vector3d &normal)
Definition: ecvMesh.cpp:2244
void addTriangleTexCoordIndexes(int i1, int i2, int i3)
Adds a triplet of tex coords indexes for next triangle.
Definition: ecvMesh.cpp:3678
NormsIndexesTableType * m_triNormals
Per-triangle normals.
Definition: ecvMesh.h:1493
triangleNormalsIndexesSet * m_triNormalIndexes
Mesh normals indexes (per-triangle)
Definition: ecvMesh.h:1523
static ccMesh * TriangulateTwoPolylines(ccPolyline *p1, ccPolyline *p2, CCVector3 *projectionDir=nullptr)
Creates a Delaunay 2.5D mesh from two polylines.
Definition: ecvMesh.cpp:1538
bool hasTextures() const override
Returns whether textures are available for this mesh.
Definition: ecvMesh.cpp:3703
ccMesh & operator+=(const ccMesh &mesh)
Definition: ecvMesh.cpp:155
bool setTriangleNormalIndexes(size_t triangleIndex, CompressedNormType value)
Definition: ecvMesh.cpp:3495
void setVertexNormals(const std::vector< Eigen::Vector3d > &normals)
Definition: ecvMesh.cpp:2259
Eigen::Vector3d getVertice(size_t index) const
Definition: ecvMesh.cpp:2213
bool fromFile_MeOnly(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap) override
Loads own object data.
Definition: ecvMesh.cpp:3877
ccArray< Tuple3i, 3, int > triangleTexCoordIndexesSet
Set of triplets of indexes referring to mesh texture coordinates.
Definition: ecvMesh.h:1516
unsigned getUniqueIDForDisplay() const override
Returns object unqiue ID used for display.
Definition: ecvMesh.cpp:2626
void placeIteratorAtBeginning() override
Places the mesh iterator at the beginning.
Definition: ecvMesh.cpp:2163
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
Definition: ecvMesh.cpp:2428
virtual ccBBox GetAxisAlignedBoundingBox() const override
Returns an axis-aligned bounding box of the geometry.
Definition: ecvMesh.cpp:844
void removePerTriangleTexCoordIndexes()
Remove per-triangle tex coords indexes.
Definition: ecvMesh.cpp:3671
virtual void getTriangleVertices(unsigned triangleIndex, CCVector3 &A, CCVector3 &B, CCVector3 &C) const override
Returns the vertices of a given triangle.
Definition: ecvMesh.cpp:2187
bool reservePerTriangleNormalIndexes()
Reserves memory to store per-triangle triplets of normal indexes.
Definition: ecvMesh.cpp:3328
bool hasPerTriangleTexCoordIndexes() const override
Returns whether this mesh as per-triangle triplets of tex coords indexes.
Definition: ecvMesh.h:484
void setTexCoordinatesTable(TextureCoordsContainer *texCoordsTable, bool autoReleaseOldTable=true)
Sets per-triangle texture coordinates array (may be shared)
Definition: ecvMesh.cpp:3598
bool interpolateNormals(unsigned triIndex, const CCVector3 &P, CCVector3 &N) override
Interpolates normal(s) inside a given triangle.
Definition: ecvMesh.cpp:4050
std::vector< Eigen::Vector3i > getTriangles() const
Definition: ecvMesh.cpp:2462
std::vector< Eigen::Vector3d > getEigenVertices() const
Definition: ecvMesh.cpp:2368
unsigned int getVerticeSize() const
Definition: ecvMesh.cpp:2336
void removeTriangles(size_t index)
Definition: ecvMesh.cpp:2583
TextureCoordsContainer * m_texCoords
Texture coordinates.
Definition: ecvMesh.h:1496
std::vector< CCVector3 > & getVerticesPtr()
Definition: ecvMesh.cpp:2344
bool resize(size_t n)
Resizes the array of vertex indexes (3 per triangle)
Definition: ecvMesh.cpp:2488
void setVertexColors(const std::vector< Eigen::Vector3d > &colors)
Definition: ecvMesh.cpp:2304
Eigen::Vector3d getVertexColor(size_t index) const
Definition: ecvMesh.cpp:2295
bool resizeAssociatedCloud(std::size_t n)
Definition: ecvMesh.cpp:2513
bool arePerTriangleNormalsEnabled() const
Returns whether per triangle normals are enabled.
Definition: ecvMesh.cpp:3319
cloudViewer::SimpleRefTriangle m_currentTriangle
Dump triangle structure to transmit temporary data.
Definition: ecvMesh.h:1507
std::vector< Eigen::Vector3d > getVertexNormals() const
Definition: ecvMesh.cpp:2266
ccArray< cloudViewer::VerticesIndexes, 3, unsigned > triangleIndexesContainer
Container of per-triangle vertices indexes (3)
Definition: ecvMesh.h:278
void addTriangleNormalIndexes(int i1, int i2, int i3)
Adds a triplet of normal indexes for next triangle.
Definition: ecvMesh.cpp:3340
std::vector< Eigen::Vector3d > getTriangleNormals() const
Definition: ecvMesh.cpp:3456
void getTexCoordinates(unsigned index, TexCoords2D *&tx) const override
Definition: ecvMesh.cpp:3653
bool reserveAssociatedCloud(std::size_t n, bool init_color=false, bool init_normal=false)
Definition: ecvMesh.cpp:2539
bool reservePerTriangleTexCoordIndexes()
Reserves memory to store per-triangle triplets of tex coords indexes.
Definition: ecvMesh.cpp:3659
void setVertexColor(size_t index, const Eigen::Vector3d &color)
Definition: ecvMesh.cpp:2282
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
Definition: ecvMesh.cpp:2413
bool getVertexColorFromMaterial(unsigned triIndex, unsigned char vertIndex, ecvColor::Rgb &C, bool returnColorIfNoTexture) override
Definition: ecvMesh.cpp:4154
bool hasNormals() const override
Returns whether normals are enabled or not.
Definition: ecvMesh.cpp:225
bool computePerVertexNormals()
Computes per-vertex normals.
Definition: ecvMesh.cpp:246
void refreshBB() override
Forces bounding-box update.
Definition: ecvMesh.cpp:2387
void clear()
Definition: ecvMesh.cpp:2126
bool hasScalarFields() const override
Returns whether one or more scalar fields are instantiated.
Definition: ecvMesh.cpp:238
ccMesh * createNewMeshFromSelection(bool removeSelectedTriangles, std::vector< int > *newIndexesOfRemainingTriangles=nullptr, bool withChildEntities=false)
Creates a new mesh with the selected vertices only.
Definition: ecvMesh.cpp:2747
void onDeletionOf(const ccHObject *obj) override
This method is called when another object is deleted.
Definition: ecvMesh.cpp:215
void computeInterpolationWeights(unsigned triIndex, const CCVector3 &P, CCVector3d &weights) const override
Returns the (barycentric) interpolation weights for a given triangle.
Definition: ecvMesh.cpp:4023
~ccMesh() override
Default destructor.
Definition: ecvMesh.cpp:122
std::shared_ptr< ccMesh > SelectByIndex(const std::vector< size_t > &indices, bool cleanup=true) const
Definition: ecvMesh.cpp:898
void setTriangleTexCoordIndexes(unsigned triangleIndex, int i1, int i2, int i3)
Sets a triplet of tex coords indexes for a given triangle.
Definition: ecvMesh.cpp:3683
Eigen::Vector3i getTriangle(size_t index) const
Definition: ecvMesh.cpp:2448
virtual ccMesh & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
Definition: ecvMesh.cpp:863
void removePerTriangleMtlIndexes()
Removes any per-triangle material indexes.
Definition: ecvMesh.cpp:3746
bool setTriangleNorms(const std::vector< Eigen::Vector3d > &triangle_normals)
Definition: ecvMesh.cpp:3550
void forEach(genericTriangleAction action) override
Fast iteration mechanism.
Definition: ecvMesh.cpp:2151
int getTriangleMtlIndex(unsigned triangleIndex) const override
Returns a given triangle material indexes.
Definition: ecvMesh.cpp:3761
void setVertice(size_t index, const Eigen::Vector3d &vertice)
Definition: ecvMesh.cpp:2222
bool HasTriangleNormals() const
Definition: ecvMesh.h:314
virtual ccMesh & Rotate(const Eigen::Matrix3d &R, const Eigen::Vector3d &center) override
Apply rotation to the geometry coordinates and normals. Given a rotation matrix , and center ,...
Definition: ecvMesh.cpp:882
std::vector< cloudViewer::geometry::Image > textures_
Textures of the image.
Definition: ecvMesh.h:709
ccMesh * partialClone(const std::vector< unsigned > &triangleIndices, int *warnings=nullptr) const
Creates a new mesh from a selection of triangles (partial clone)
Definition: ecvMesh.cpp:1347
bool addTriangleNorm(const CCVector3 &N)
Definition: ecvMesh.cpp:3513
void shrinkVertexToFit()
Definition: ecvMesh.cpp:2568
std::vector< CCVector3 * > getTriangleNormalsPtr() const
Definition: ecvMesh.cpp:3465
virtual Eigen::Vector3d GetMinBound() const override
Returns min bounds for geometry coordinates.
Definition: ecvMesh.cpp:820
bool mergeDuplicatedVertices(unsigned char octreeLevel=DefaultMergeDulicateVerticesLevel, QWidget *parentWidget=nullptr)
Merges duplicated vertices.
Definition: ecvMesh.cpp:661
void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax) override
Returns the mesh bounding-box.
Definition: ecvMesh.cpp:2406
virtual Eigen::Vector3d GetCenter() const override
Returns the center of the geometry coordinates.
Definition: ecvMesh.cpp:836
Eigen::Vector3d getVertexNormal(size_t index) const
Definition: ecvMesh.cpp:2251
cloudViewer::GenericTriangle * _getTriangle(unsigned triangleIndex) override
Returns the ith triangle.
Definition: ecvMesh.cpp:2173
void onUpdateOf(ccHObject *obj) override
This method is called when another object (geometry) is updated.
Definition: ecvMesh.cpp:206
void addVertexNormals(const std::vector< Eigen::Vector3d > &normals)
Definition: ecvMesh.cpp:2273
void shrinkToFit()
Removes unused capacity.
Definition: ecvMesh.h:302
std::vector< std::pair< std::string, Material > > materials_
Definition: ecvMesh.h:704
void addVertexColor(const Eigen::Vector3d &color)
Definition: ecvMesh.cpp:2289
virtual ccMesh & Transform(const Eigen::Matrix4d &transformation) override
Apply transformation (4x4 matrix) to the geometry coordinates.
Definition: ecvMesh.cpp:852
bool addTriangleNorms(const std::vector< Eigen::Vector3d > &triangle_normals)
Definition: ecvMesh.cpp:3571
unsigned capacity() const override
Returns max capacity.
Definition: ecvMesh.cpp:2147
ccArray< int, 1, int > triangleMaterialIndexesSet
Container of per-triangle material descriptors.
Definition: ecvMesh.h:447
bool hasPerTriangleMtlIndexes() const
Returns whether this mesh as per-triangle material index.
Definition: ecvMesh.h:421
void invertNormals()
Inverts normals (if any)
Definition: ecvMesh.cpp:3288
bool hasTriangleUvs() const
Definition: ecvMesh.h:717
bool computeNormals(bool perVertex)
Computes normals.
Definition: ecvMesh.cpp:242
ColorsTableType * getVertexColorsPtr()
Definition: ecvMesh.cpp:2319
bool hasTriNormals() const override
Returns whether the mesh has per-triangle normals.
Definition: ecvMesh.cpp:3589
cloudViewer::VerticesIndexes * getTriangleVertIndexes(unsigned triangleIndex) override
Returns the indexes of the vertices of a given triangle.
Definition: ecvMesh.cpp:2599
triangleIndexesContainer * m_triVertIndexes
Triangles' vertices indexes (3 per triangle)
Definition: ecvMesh.h:1502
bool interpolateColors(unsigned triIndex, const CCVector3 &P, ecvColor::Rgb &C) override
Interpolates RGB colors inside a given triangle.
Definition: ecvMesh.cpp:4120
static void InvertNormal(CompressedNormType &code)
Inverts a (compressed) normal.
const CCVector3 & getNormal(unsigned normIndex) const
Returns the precomputed normal corresponding to a given compressed index.
static const CCVector3 & GetNormal(unsigned normIndex)
Static access to ccNormalVectors::getNormal.
static unsigned GetNumberOfVectors()
Returns the number of compressed normal vectors.
static ccNormalVectors * GetUniqueInstance()
Returns unique instance.
static CompressedNormType GetNormIndex(const PointCoordinateType N[])
Returns the compressed index corresponding to a normal vector.
virtual void setLocked(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:117
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
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
QString m_name
Object name.
Definition: ecvObject.h:219
Octree structure.
Definition: ecvOctree.h:27
QSharedPointer< ccOctree > Shared
Shared pointer.
Definition: ecvOctree.h:32
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void addEigenNorm(const Eigen::Vector3d &N)
std::vector< Eigen::Vector3d > getEigenNormals() const
ColorsTableType * rgbColors() const
Returns pointer on RGB colors table.
bool resizeTheNormsTable()
Resizes the compressed normals array.
void addEigenColor(const Eigen::Vector3d &color)
void addEigenNorms(const std::vector< Eigen::Vector3d > &normals)
void setEigenColors(const std::vector< Eigen::Vector3d > &colors)
Eigen::Vector3d getEigenNormal(size_t index) const
void addNorm(const CCVector3 &N)
Pushes a normal vector on stack (shortcut)
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool resizeTheRGBTable(bool fillWithWhite=false)
Resizes the RGB colors array.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
virtual ccPointCloud & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
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)
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
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 reserveTheRGBTable()
Reserves memory to store the RGB colors.
void clear() override
Clears the entity from all its points and features.
virtual void applyRigidTransformation(const ccGLMatrix &trans) override
Applies a rigid transformation (rotation + translation)
bool hasColors() const override
Returns whether colors are enabled or not.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
void setPointNormal(size_t pointIndex, const CCVector3 &N)
Sets a particular point normal (shortcut)
Eigen::Vector3d getEigenColor(size_t index) const
void addEigenColors(const std::vector< Eigen::Vector3d > &colors)
void shrinkToFit()
Removes unused capacity.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
std::vector< Eigen::Vector3d > getEigenColors() const
void invertNormals()
Inverts normals (if any)
Colored polyline.
Definition: ecvPolyline.h:24
unsigned segmentCount() const
Returns the number of segments.
bool areNaNValuesShownInGrey() const
Returns whether NaN values are displayed in gray or hidden.
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 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 short GenericArrayToFileMinVersion()
Returns the minimum file version to save/load a 'generic array'.
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.
A sub-mesh.
Definition: ecvSubMesh.h:19
void setAssociatedMesh(ccMesh *mesh, bool unlinkPreviousOne=true)
Sets the associated mesh.
Definition: ecvSubMesh.cpp:36
std::vector< unsigned int > IndexMap
Indexes map for createNewSubMeshFromSelection.
Definition: ecvSubMesh.h:175
ccSubMesh * createNewSubMeshFromSelection(bool removeSelectedTriangles, const std::vector< int > &selectedTriangleIndexes, IndexMap *newRemainingTriangleIndexes=nullptr)
Creates a new sub mesh with the visible vertices only.
Definition: ecvSubMesh.cpp:298
unsigned size() const override
Returns the number of triangles.
Definition: ecvSubMesh.h:85
bool reserve(size_t n)
Reserves some memory for hosting the triangle references.
Definition: ecvSubMesh.cpp:542
bool addTriangleIndex(unsigned globalIndex)
Triangle global index insertion mechanism.
Definition: ecvSubMesh.cpp:499
std::vector< std::size_t > GetPointIndicesWithinBoundingBox(const std::vector< Eigen::Vector3d > &points) const
Definition: BoundingBox.h:242
const Vector3Tpl< T > & maxCorner() const
Returns max corner (const)
Definition: BoundingBox.h:156
void setValidity(bool state)
Sets bonding box validity.
Definition: BoundingBox.h:200
void clear()
Resets the bounding box.
Definition: BoundingBox.h:125
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 add(const Vector3Tpl< T > &P)
'Enlarges' the bounding box with a point
Definition: BoundingBox.h:131
A class to compute and handle a Delaunay 2D mesh on a subset of points.
virtual unsigned size() const override
Returns the number of triangles.
virtual bool removeOuterTriangles(const std::vector< CCVector2 > &vertices2D, const std::vector< CCVector2 > &polygon2D, bool removeOutside=true)
Removes the triangles falling outside of a given (2D) polygon.
virtual bool buildMesh(const std::vector< CCVector2 > &points2D, std::size_t pointCountToUse, std::string &outputErrorStr)
Build the Delaunay mesh on top a set of 2D points.
virtual void linkMeshWith(GenericIndexedCloud *aCloud, bool passOwnership=false)
Associate this mesh to a point cloud.
void getCellPos(CellCode code, unsigned char level, Tuple3i &cellPos, bool isCodeTruncated) const
Definition: DgmOctree.cpp:498
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
const PointCoordinateType & getCellSize(unsigned char level) const
Returns the octree cells length for a given level of subdivision.
Definition: DgmOctree.h:494
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
int build(GenericProgressCallback *progressCb=nullptr)
Builds the structure.
Definition: DgmOctree.cpp:196
virtual unsigned size() const =0
Returns the number of points.
virtual ScalarType getPointScalarValue(unsigned pointIndex) const =0
Returns the ith point associated scalar value.
virtual bool hasPoints() const
Definition: GenericCloud.h:37
virtual void setPointScalarValue(unsigned pointIndex, ScalarType value)=0
Sets the ith point associated scalar value.
virtual bool isScalarFieldEnabled() const =0
Returns true if the scalar field is enabled, false otherwise.
virtual const CCVector3 * getPointPersistentPtr(unsigned index)=0
Returns the ith point as a persistent pointer.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
A generic mesh with index-based vertex access.
virtual VerticesIndexes * getNextTriangleVertIndexes()=0
virtual unsigned size() const =0
Returns the number of triangles.
std::function< void(GenericTriangle &)> genericTriangleAction
Generic function to apply to a triangle (used by foreach)
Definition: GenericMesh.h:53
virtual void placeIteratorAtBeginning()=0
Places the mesh iterator at the beginning.
virtual bool hasTriangles() const
Definition: GenericMesh.h:60
A generic triangle interface.
static GenericIndexedMesh * segmentMesh(GenericIndexedMesh *mesh, ReferenceCloud *selectedVertexIndexes, bool useSelectedVertices, GenericProgressCallback *progressCb=nullptr, GenericIndexedCloud *destCloud=nullptr, unsigned indexShift=0, std::vector< int > *triangleIndexMap=nullptr)
Segments a mesh knowing which vertices should be kept or not.
const CCVector3 * getLSPlaneY()
Returns best interpolating plane (Least-square) 'Y' base vector.
const PointCoordinateType * getLSPlane()
Returns best interpolating plane equation (Least-square)
const CCVector3 * getGravityCenter()
Returns gravity center.
const CCVector3 * getLSPlaneX()
Returns best interpolating plane (Least-square) 'X' base vector.
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.
void setEigenPoints(const std::vector< Eigen::Vector3d > &points)
void addEigenPoint(const Eigen::Vector3d &point)
std::vector< CCVector3 > & getPoints()
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
Definition: PointCloudTpl.h:38
void setEigenPoint(size_t index, const Eigen::Vector3d &point)
std::vector< Eigen::Vector3d > getEigenPoints() const
unsigned capacity() const
Returns cloud capacity (i.e. reserved size)
Eigen::Vector3d getEigenPoint(size_t index) const
const CCVector3 * getPoint(unsigned index) const override
void addPoints(const std::vector< CCVector3 > &points)
static bool extractConvexHull2D(std::vector< IndexedCCVector2 > &points, std::list< IndexedCCVector2 * > &hullPoints)
Determines the convex hull of a set of points.
static GenericIndexedMesh * computeTriangulation(GenericIndexedCloudPersist *cloud, TRIANGULATION_TYPES type, PointCoordinateType maxEdgeLength, unsigned char dim, std::string &outputErrorStr)
Applys a geometrical transformation to a single point.
bool isClosed() const
Returns whether the polyline is closed or not.
Definition: Polyline.h:26
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
const CCVector3 * getPointPersistentPtr(unsigned index) override
Returns the ith point as a persistent pointer.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
const CCVector3 * B
B vertex (ref)
const CCVector3 * C
C vertex (ref)
const CCVector3 * A
A vertex (ref)
RGB color structure.
Definition: ecvColorTypes.h:49
RGBA color structure.
static void Draw(const CC_DRAW_CONTEXT &context, const ccHObject *obj)
static QMainWindow * GetMainWindow()
virtual bool IsEmpty() const override
static ecvOrientedBBox CreateFromPoints(const std::vector< Eigen::Vector3d > &points)
Graphical progress indicator (thread-safe)
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual void start() override
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
double colors[3]
double normals[3]
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
#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_Draw3D(context)
@ ECV_POINTS_MODE
@ ECV_SURFACE_MODE
@ ECV_WIREFRAME_MODE
#define MACRO_LightIsEnabled(context)
#define MACRO_FastEntityPicking(context)
#define MACRO_EntityPicking(context)
#define MACRO_LODActivated(context)
static qint64 GenerateKey(unsigned edgeIndex1, unsigned edgeIndex2)
Definition: ecvMesh.cpp:4336
static bool TagDuplicatedVertices(const cloudViewer::DgmOctree::octreeCell &cell, void **additionalParameters, cloudViewer::NormalizedProgress *nProgress)
Definition: ecvMesh.cpp:582
static const unsigned s_defaultSubdivideGrowRate
Definition: ecvMesh.cpp:4330
static QMap< qint64, unsigned > s_alreadyCreatedVertices
Definition: ecvMesh.cpp:4333
static CCVector3 s_blankNorm(0, 0, 0)
static PointCoordinateType s_maxSubdivideArea
Definition: ecvMesh.cpp:4331
ImGuiContext * context
Definition: Window.cpp:76
normal_z y
normal_z rgb
normal_z x
@ MESH
Definition: CVTypes.h:105
@ POINT_CLOUD
Definition: CVTypes.h:104
@ FACET
Definition: CVTypes.h:109
@ SUB_MESH
Definition: CVTypes.h:106
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
TRIANGULATION_TYPES
Triangulation types.
bool LessThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:23
RgbTpl< ColorCompType > Rgb
3 components, default type
Rgb FromRgbafToRgb(const Rgbaf &color)
Conversion from Rgbaf.
constexpr ColorCompType MAX
Max value of a single color component (default type)
Definition: ecvColorTypes.h:34
constexpr Rgb lightGrey(static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8))
Rgb FromQRgb(QRgb qColor)
Conversion from QRgb.
Eigen::Matrix< Index, 3, 1 > Vector3i
Definition: knncpp.h:30
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
cloudViewer::NormalizedProgress * nProgress
cloudViewer::DgmOctree * octree
unsigned char octreeLevel
2D texture coordinates
Display context.
unsigned char level
Level of subdivision of the octree at which to start the search.
Definition: DgmOctree.h:171
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
Triangle described by the indexes of its 3 vertices.
Display parameters of a 3D entity.
bool showColors
Display colors.
bool showNorms
Display normals.
bool showSF
Display scalar field (prioritary on colors)