ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qFacets.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 "qFacets.h"
9 
10 #include <exception>
11 
12 // Local
14 #include "disclaimerDialog.h"
15 #include "facetsClassifier.h"
16 #include "facetsExportDlg.h"
19 #include "stereogramDlg.h"
20 
21 // Qt
22 #include <QElapsedTimer>
23 #include <QFileInfo>
24 #include <QInputDialog>
25 #include <QMessageBox>
26 #include <QSettings>
27 #include <QtGui>
28 
29 // Qt5/Qt6 Compatibility
30 #include <QtCompat.h>
31 
32 // CV_CORE_LIB
33 #include <CVLog.h>
34 
35 // CV_DB_LIB
36 #include <ShpDBFFields.h>
37 #include <ecvDisplayTools.h>
38 #include <ecvFileUtils.h>
39 #include <ecvHObjectCaster.h>
40 #include <ecvMesh.h>
41 #include <ecvOctree.h> //for ComputeAverageNorm
42 #include <ecvProgressDialog.h>
43 #include <ecvScalarField.h>
44 
45 // CV_IO_LIB
46 #include <ShpFilter.h>
47 
48 // semi-persistent dialog values
49 static unsigned s_octreeLevel = 8;
50 static bool s_fmUseRetroProjectionError = false;
51 
52 static unsigned s_minPointsPerFacet = 10;
53 static double s_errorMaxPerFacet = 0.2;
54 static int s_errorMeasureType = 3; // max dist @ 99 %
55 static double s_maxEdgeLength = 1.0;
56 
57 static double s_kdTreeFusionMaxAngle_deg = 20.0;
59 
60 static double s_classifAngleStep = 30.0;
61 static double s_classifMaxDist = 1.0;
62 
63 static double s_stereogramAngleStep = 30.0;
64 static double s_stereogramResolution_deg = 5.0;
65 static ccPointCloud* s_lastCloud = nullptr;
66 
67 // persistent dialog
68 static StereogramDialog* s_fcDlg = nullptr;
69 
70 qFacets::qFacets(QObject* parent)
71  : QObject(parent),
72  ccStdPluginInterface(":/CC/plugin/qFacets/info.json"),
73  m_doFuseKdTreeCells(nullptr),
74  m_fastMarchingExtraction(nullptr),
75  m_doExportFacets(nullptr),
76  m_doExportFacetsInfo(nullptr),
77  m_doClassifyFacetsByAngle(nullptr),
78  m_doShowStereogram(nullptr) {}
79 
80 qFacets::~qFacets() {
81  if (s_fcDlg) {
82  try {
83  s_fcDlg->close();
84  s_fcDlg = nullptr;
85  } catch (std::exception& e) {
87  tr("closing facets dialog failed! [%1]").arg(e.what()));
88  }
89  }
90 }
91 
92 QList<QAction*> qFacets::getActions() {
93  // actions
94  if (!m_doFuseKdTreeCells) {
95  m_doFuseKdTreeCells = new QAction(tr("Extract facets (Kd-tree)"), this);
96  m_doFuseKdTreeCells->setToolTip(
97  tr("Detect planar facets by fusing Kd-tree cells"));
98  m_doFuseKdTreeCells->setIcon(QIcon(
99  QString::fromUtf8(":/CC/plugin/qFacets/images/extractKD.png")));
100  // connect signal
101  connect(m_doFuseKdTreeCells, &QAction::triggered, this,
103  }
104 
107  new QAction(tr("Extract facets (Fast Marching)"), this);
108  m_fastMarchingExtraction->setToolTip(
109  tr("Detect planar facets with Fast Marching"));
110  m_fastMarchingExtraction->setIcon(QIcon(
111  QString::fromUtf8(":/CC/plugin/qFacets/images/extractFM.png")));
112  // connect signal
113  connect(m_fastMarchingExtraction, &QAction::triggered, this,
115  }
116 
117  if (!m_doExportFacets) {
118  m_doExportFacets = new QAction(tr("Export facets (SHP)"), this);
119  m_doExportFacets->setToolTip(
120  tr("Exports one or several facets to a shapefile"));
121  m_doExportFacets->setIcon(QIcon(
122  QString::fromUtf8(":/CC/plugin/qFacets/images/shpFile.png")));
123  // connect signal
124  connect(m_doExportFacets, &QAction::triggered, this,
126  }
127 
128  if (!m_doExportFacetsInfo) {
130  new QAction(tr("Export facets info (CSV)"), this);
131  m_doExportFacetsInfo->setToolTip(
132  tr("Exports various information on a set of facets (ASCII CSV "
133  "file)"));
134  m_doExportFacetsInfo->setIcon(QIcon(
135  QString::fromUtf8(":/CC/plugin/qFacets/images/csvFile.png")));
136  // connect signal
137  connect(m_doExportFacetsInfo, &QAction::triggered, this,
139  }
140 
143  new QAction(tr("Classify facets by orientation"), this);
144  m_doClassifyFacetsByAngle->setToolTip(
145  tr("Classifies facets based on their orienation (dip & dip "
146  "direction)"));
147  m_doClassifyFacetsByAngle->setIcon(QIcon(QString::fromUtf8(
148  ":/CC/plugin/qFacets/images/classifIcon.png")));
149  // connect signal
150  connect(m_doClassifyFacetsByAngle, &QAction::triggered, this,
151  [=]() { classifyFacetsByAngle(); });
152  }
153 
154  if (!m_doShowStereogram) {
155  m_doShowStereogram = new QAction(tr("Show stereogram"), this);
156  m_doShowStereogram->setToolTip(
157  tr("Computes and displays a stereogram (+ interactive "
158  "filtering)"));
159  m_doShowStereogram->setIcon(QIcon(QString::fromUtf8(
160  ":/CC/plugin/qFacets/images/stereogram.png")));
161  // connect signal
162  connect(m_doShowStereogram, &QAction::triggered, this,
164  }
165 
166  return QList<QAction*>{
169  };
170 }
171 
172 void qFacets::onNewSelection(const ccHObject::Container& selectedEntities) {
174  m_doFuseKdTreeCells->setEnabled(
175  selectedEntities.size() == 1 &&
176  selectedEntities.back()->isA(CV_TYPES::POINT_CLOUD));
178  m_fastMarchingExtraction->setEnabled(
179  selectedEntities.size() == 1 &&
180  selectedEntities.back()->isA(CV_TYPES::POINT_CLOUD));
181  if (m_doExportFacets)
182  m_doExportFacets->setEnabled(selectedEntities.size() != 0);
184  m_doExportFacetsInfo->setEnabled(selectedEntities.size() != 0);
186  m_doClassifyFacetsByAngle->setEnabled(
187  selectedEntities.size() == 1 &&
188  selectedEntities.back()->isA(CV_TYPES::HIERARCHY_OBJECT));
189  if (m_doShowStereogram)
190  m_doShowStereogram->setEnabled(
191  selectedEntities.size() == 1 &&
192  (selectedEntities.back()->isA(CV_TYPES::HIERARCHY_OBJECT) ||
193  selectedEntities.back()->isA(CV_TYPES::POINT_CLOUD)));
194 }
195 
198 }
199 
201 
203  // disclaimer accepted?
204  if (!ShowDisclaimer(m_app)) return;
205 
206  assert(m_app);
207  if (!m_app) return;
208 
209  // we expect a unique cloud as input
210  const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
211  ccPointCloud* pc =
213  ? ccHObjectCaster::ToPointCloud(selectedEntities.back())
214  : nullptr);
215  if (!pc) {
216  m_app->dispToConsole(tr("Select one and only one point cloud!"),
218  return;
219  }
220 
223  m_app->dispToConsole(tr("Internal error: invalid algorithm type!"),
225  return;
226  }
227 
228  // first time: we compute the max edge length automatically
229  if (s_lastCloud != pc) {
231  static_cast<double>(pc->getOwnBB().getMinBoxDim()) / 50;
232  s_minPointsPerFacet = std::max<unsigned>(pc->size() / 100000, 10);
233  s_lastCloud = pc;
234  }
235 
236  CellsFusionDlg fusionDlg(algo, m_app->getMainWindow());
238  fusionDlg.octreeLevelSpinBox->setCloud(pc);
239 
240  fusionDlg.octreeLevelSpinBox->setValue(s_octreeLevel);
241  fusionDlg.useRetroProjectionCheckBox->setChecked(
243  fusionDlg.minPointsPerFacetSpinBox->setValue(s_minPointsPerFacet);
244  fusionDlg.errorMeasureComboBox->setCurrentIndex(s_errorMeasureType);
245  fusionDlg.maxRMSDoubleSpinBox->setValue(s_errorMaxPerFacet);
246  fusionDlg.maxAngleDoubleSpinBox->setValue(s_kdTreeFusionMaxAngle_deg);
247  fusionDlg.maxRelativeDistDoubleSpinBox->setValue(
249  fusionDlg.maxEdgeLengthDoubleSpinBox->setValue(s_maxEdgeLength);
250  //"no normal" warning
251  fusionDlg.noNormalWarningLabel->setVisible(!pc->hasNormals());
252 
253  if (!fusionDlg.exec()) return;
254 
255  s_octreeLevel = fusionDlg.octreeLevelSpinBox->value();
257  fusionDlg.useRetroProjectionCheckBox->isChecked();
258  s_minPointsPerFacet = fusionDlg.minPointsPerFacetSpinBox->value();
259  s_errorMeasureType = fusionDlg.errorMeasureComboBox->currentIndex();
260  s_errorMaxPerFacet = fusionDlg.maxRMSDoubleSpinBox->value();
261  s_kdTreeFusionMaxAngle_deg = fusionDlg.maxAngleDoubleSpinBox->value();
263  fusionDlg.maxRelativeDistDoubleSpinBox->value();
264  s_maxEdgeLength = fusionDlg.maxEdgeLengthDoubleSpinBox->value();
265 
266  // convert 'errorMeasureComboBox' index to enum
269  switch (s_errorMeasureType) {
270  case 0:
272  break;
273  case 1:
274  errorMeasure =
276  break;
277  case 2:
278  errorMeasure =
280  break;
281  case 3:
282  errorMeasure =
284  break;
285  case 4:
287  break;
288  default:
289  assert(false);
290  break;
291  }
292 
293  // create scalar field to host the fusion result
294  const char c_defaultSFName[] = "facet indexes";
295  int sfIdx = pc->getScalarFieldIndexByName(c_defaultSFName);
296  if (sfIdx < 0) sfIdx = pc->addScalarField(c_defaultSFName);
297  if (sfIdx < 0) {
299  tr("Couldn't allocate a new scalar field for computing fusion "
300  "labels! Try to free some memory ..."),
302  return;
303  }
304  pc->setCurrentScalarField(sfIdx);
305 
306  // computation
307  QElapsedTimer eTimer;
308  eTimer.start();
309  ecvProgressDialog pDlg(true, m_app->getActiveWindow());
310 
311  bool success = true;
312  if (algo == CellsFusionDlg::ALGO_KD_TREE) {
313  // we need a kd-tree
314  QElapsedTimer eTimer;
315  eTimer.start();
316  ccKdTree kdtree(pc);
317 
318  if (kdtree.build(s_errorMaxPerFacet / 2, errorMeasure,
319  s_minPointsPerFacet, 1000, &pDlg)) {
320  qint64 elapsedTime_ms = eTimer.elapsed();
322  tr("[qFacets] Kd-tree construction timing: %1 s")
323  .arg(static_cast<double>(elapsedTime_ms) / 1.0e3, 0,
324  'f', 3),
326 
328  &kdtree, s_errorMaxPerFacet, errorMeasure,
330  static_cast<PointCoordinateType>(
332  true, &pDlg);
333  } else {
335  tr("Failed to build Kd-tree! (not enough memory?)"),
337  success = false;
338  }
339  } else if (algo == CellsFusionDlg::ALGO_FAST_MARCHING) {
341  pc, static_cast<unsigned char>(s_octreeLevel),
342  static_cast<ScalarType>(s_errorMaxPerFacet), errorMeasure,
343  s_fmUseRetroProjectionError, &pDlg, pc->getOctree().data());
344 
345  success = (result >= 0);
346  }
347 
348  if (success) {
350  sfIdx); // for
351  // AutoSegmentationTools::extractConnectedComponents
352 
355  pc, components)) {
356  m_app->dispToConsole(tr("Failed to extract fused components! (not "
357  "enough memory?)"),
359  } else {
360  // we remove the temporary scalar field (otherwise it will be copied
361  // to the sub-clouds!)
362  ccScalarField* indexSF =
363  static_cast<ccScalarField*>(pc->getScalarField(sfIdx));
364  indexSF->link(); // to prevent deletion below
365  pc->deleteScalarField(sfIdx);
366  sfIdx = -1;
367 
368  bool error = false;
369  ccHObject* group = createFacets(pc, components, s_minPointsPerFacet,
370  s_maxEdgeLength, false, error);
371 
372  if (group) {
373  switch (algo) {
375  group->setName(
376  group->getName() +
377  tr(" [Kd-tree][error < %1][angle < %2 deg.]")
378  .arg(s_errorMaxPerFacet)
380  break;
382  group->setName(group->getName() +
383  tr(" [FM][level %2][error < %1]")
384  .arg(s_octreeLevel)
385  .arg(s_errorMaxPerFacet));
386  break;
387  default:
388  break;
389  }
390 
391  unsigned count = group->getChildrenNumber();
392  m_app->dispToConsole(tr("[qFacets] %1 facet(s) where created "
393  "from cloud '%2'")
394  .arg(count)
395  .arg(pc->getName()));
396 
397  if (error) {
399  tr("Error(s) occurred during the generation of "
400  "facets! Result may be incomplete"),
402  } else {
403  // we but back the scalar field
404  if (indexSF) sfIdx = pc->addScalarField(indexSF);
405  }
406 
407  m_app->addToDB(group);
408  } else if (error) {
409  m_app->dispToConsole(tr("An error occurred during the "
410  "generation of facets!"),
412  } else {
413  m_app->dispToConsole(tr("No facet remains! Check the "
414  "parameters (min size, etc.)"),
416  }
417  }
418  } else {
419  m_app->dispToConsole(tr("An error occurred during the fusion process!"),
421  }
422 
423  if (sfIdx >= 0) {
424  pc->getScalarField(sfIdx)->computeMinAndMax();
425 #ifdef _DEBUG
427  pc->showSF(true);
428 #endif
429  }
430 
431  // currently selected entities appearance may have changed!
432  // m_app->refreshAll();
433 }
434 
436  ccPointCloud* cloud,
438  unsigned minPointsPerComponent,
439  double maxEdgeLength,
440  bool randomColors,
441  bool& error) {
442  if (!cloud) {
443  return 0;
444  }
445 
446  // we create a new group to store all input CCs as 'facets'
447  ccHObject* ccGroup = new ccHObject(cloud->getName() + tr(" [facets]"));
448  // ccGroup->setDisplay(cloud->getDisplay());
449  ccGroup->setVisible(true);
450 
451  bool cloudHasNormal = cloud->hasNormals();
452 
453  // number of input components
454  size_t componentCount = components.size();
455 
456  // progress notification
457  ecvProgressDialog pDlg(true, m_app->getMainWindow());
458  pDlg.setMethodTitle(tr("Facets creation"));
459  pDlg.setInfo(tr("Components: %1").arg(componentCount));
460  pDlg.setMaximum(static_cast<int>(componentCount));
461  pDlg.show();
462  QApplication::processEvents();
463 
464  // for each component
465  error = false;
466  while (!components.empty()) {
467  cloudViewer::ReferenceCloud* compIndexes = components.back();
468  components.pop_back();
469 
470  // if it has enough points
471  if (compIndexes && compIndexes->size() >= minPointsPerComponent) {
472  ccPointCloud* facetCloud = cloud->partialClone(compIndexes);
473  if (!facetCloud) {
474  // not enough memory!
475  error = true;
476  delete facetCloud;
477  facetCloud = 0;
478  } else {
479  ccFacet* facet = ccFacet::Create(
480  facetCloud,
481  static_cast<PointCoordinateType>(maxEdgeLength), true);
482  if (facet) {
483  QString facetName =
484  tr("facet %1 (rms=%2)")
485  .arg(ccGroup->getChildrenNumber())
486  .arg(facet->getRMS());
487  facet->setName(facetName);
488  if (facet->getPolygon()) {
489  facet->getPolygon()->enableStippling(false);
490  facet->getPolygon()->showNormals(false);
491  }
492  if (facet->getContour()) {
493  facet->getContour()->setGlobalScale(
494  facetCloud->getGlobalScale());
495  facet->getContour()->setGlobalShift(
496  facetCloud->getGlobalShift());
497  }
498 
499  // check the facet normal sign
500  if (cloudHasNormal) {
501  CCVector3 N = ccOctree::ComputeAverageNorm(compIndexes,
502  cloud);
503 
504  if (N.dot(facet->getNormal()) < 0)
505  facet->invertNormal();
506  }
507 
508 #ifdef _DEBUG
509  facet->showNormalVector(true);
510 #endif
511 
512  // shall we colorize it with a random color?
513  ecvColor::Rgb col, darkCol;
514  if (randomColors) {
516  assert(c_darkColorRatio <= 1.0);
517  darkCol.r = static_cast<ColorCompType>(
518  static_cast<double>(col.r) * c_darkColorRatio);
519  darkCol.g = static_cast<ColorCompType>(
520  static_cast<double>(col.g) * c_darkColorRatio);
521  darkCol.b = static_cast<ColorCompType>(
522  static_cast<double>(col.b) * c_darkColorRatio);
523  } else {
524  // use normal-based HSV coloring
525  CCVector3 N = facet->getNormal();
526  PointCoordinateType dip, dipDir;
528  dipDir);
530  col, dip, dipDir, 0, 1, &darkCol);
531  }
532  facet->setColor(col);
533  if (facet->getContour()) {
534  facet->getContour()->setColor(darkCol);
535  facet->getContour()->setWidth(2);
536  }
537  ccGroup->addChild(facet);
538  }
539  }
540 
541  delete compIndexes;
542  compIndexes = nullptr;
543  }
544 
545  pDlg.setValue(static_cast<int>(componentCount - components.size()));
546  // QApplication::processEvents();
547  }
548 
549  if (ccGroup->getChildrenNumber() == 0) {
550  delete ccGroup;
551  ccGroup = nullptr;
552  }
553 
554  return ccGroup;
555 }
556 
558  facets.clear();
559 
560  // look for potential facets
561  for (ccHObject* entity : m_app->getSelectedEntities()) {
562  if (entity->isA(CV_TYPES::FACET)) {
563  ccFacet* facet = static_cast<ccFacet*>(entity);
564  if (facet->getContour()) // if no contour, we won't be able to save
565  // it?!
566  facets.insert(facet);
567  } else // if (entity->isA(CV_TYPES::HIERARCHY_OBJECT)) //recursively
568  // tests group's children
569  {
570  ccHObject::Container childFacets;
571  entity->filterChildren(childFacets, true, CV_TYPES::FACET);
572 
573  for (ccHObject* childFacet : childFacets) {
574  ccFacet* facet = static_cast<ccFacet*>(childFacet);
575  if (facet->getContour()) // if no contour, we won't be able to
576  // save it?!
577  {
578  facets.insert(facet);
579  }
580  }
581  }
582  }
583 }
584 
585 // standard meta-data for the qFacets plugin
590  double surface;
591  int dip_deg;
593  double rms;
596 
599  : facetIndex(-1),
600  center(0, 0, 0),
601  normal(0, 0, 1),
602  surface(0.0),
603  dip_deg(0),
604  dipDir_deg(0),
605  rms(0.0),
606  familyIndex(0),
607  subfamilyIndex(0) {}
608 };
609 
610 // helper: extract all meta-data information form a facet
612  // try to get the facet index from the facet name!
613  {
614  QStringList tokens =
615  facet->getName().split(" ", QtCompat::SkipEmptyParts);
616  if (tokens.size() > 1 && tokens[0] == QString("facet")) {
617  bool ok = true;
618  data.facetIndex = tokens[1].toInt(&ok);
619  if (!ok) data.facetIndex = -1;
620  }
621  }
622 
623  data.center = facet->getCenter();
624  data.normal = facet->getNormal();
625  data.surface = facet->getSurface();
626  data.rms = facet->getRMS();
627 
628  // family and subfamily indexes
629  QVariant fi = facet->getMetaData(s_OriFamilyKey);
630  if (fi.isValid()) data.familyIndex = fi.toInt();
631  QVariant sfi = facet->getMetaData(s_OriSubFamilyKey);
632  if (sfi.isValid()) data.subfamilyIndex = sfi.toInt();
633 
634  // compute dip direction & dip
635  {
636  PointCoordinateType dipDir = 0, dip = 0;
638  data.dipDir_deg = static_cast<int>(dipDir);
639  data.dip_deg = static_cast<int>(dip);
640  }
641 }
642 
643 // helper: computes a facet horizontal and vertical extensions
645  ccPolyline* facetContour,
646  double& horizExt,
647  double& vertExt) {
648  // horizontal and vertical extensions
649  horizExt = vertExt = 0;
650 
652  facetContour->getAssociatedCloud();
653  if (vertCloud) {
654  // oriRotMat.applyRotation(N); //DGM: oriRotMat is only for display!
655  // we assume that at this point the "up" direction is always (0,0,1)
656  CCVector3 Xf(1, 0, 0), Yf(0, 1, 0);
657  // we get the horizontal vector on the plane
658  CCVector3 D = CCVector3(0, 0, 1).cross(N);
660  D.norm2())) // otherwise the facet is horizontal!
661  {
662  Yf = D;
663  Yf.normalize();
664  Xf = N.cross(Yf);
665  }
666 
667  const CCVector3* G =
669 
670  ccBBox box;
671  for (unsigned i = 0; i < vertCloud->size(); ++i) {
672  const CCVector3 P = *(vertCloud->getPoint(i)) - *G;
673  CCVector3 p(P.dot(Xf), P.dot(Yf), 0);
674  box.add(p);
675  }
676 
677  vertExt = box.getDiagVec().x;
678  horizExt = box.getDiagVec().y;
679  }
680 }
681 
683  assert(m_app);
684  if (!m_app) return;
685 
686  // disclaimer accepted?
687  if (!ShowDisclaimer(m_app)) return;
688 
689  // Retrive selected facets
690  FacetSet facets;
692 
693  if (facets.empty()) {
695  tr("Couldn't find any facet in the current selection!"),
697  return;
698  }
699  assert(!facets.empty());
700 
702  m_app->getMainWindow());
703 
704  // persistent settings (default export path)
705  QSettings settings;
706  settings.beginGroup("qFacets");
707  QString facetsSavePath =
708  settings.value("exportPath", ecvFileUtils::defaultDocPath())
709  .toString();
710  fDlg.destinationPathLineEdit->setText(facetsSavePath +
711  QString("/facets.shp"));
712 
713  if (!fDlg.exec()) return;
714 
715  QString filename = fDlg.destinationPathLineEdit->text();
716 
717  // save current export path to persistent settings
718  settings.setValue("exportPath", QFileInfo(filename).absolutePath());
719 
720  if (QFile(filename).exists()) {
721  // if the file already exists, ask for confirmation!
722  if (QMessageBox::warning(
723  m_app->getMainWindow(), tr("File already exists!"),
724  tr("File already exists! Are you sure you want to "
725  "overwrite it?"),
726  QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
727  return;
728  }
729 
730  // fields (shapefile) - WARNING names must not have more than 10 chars!
731  IntegerDBFField facetIndex(tr("index"));
732  DoubleDBFField facetSurface(tr("surface"));
733  DoubleDBFField facetRMS(tr("rms"));
734  IntegerDBFField facetDipDir(tr("dip_dir"));
735  IntegerDBFField facetDip(tr("dip"));
736  IntegerDBFField familyIndex(tr("family_ind"));
737  IntegerDBFField subfamilyIndex(tr("subfam_ind"));
738  DoubleDBFField3D facetNormal(tr("normal"));
739  DoubleDBFField3D facetBarycenter(tr("center"));
740  DoubleDBFField horizExtension(tr("horiz_ext"));
741  DoubleDBFField vertExtension(tr("vert_ext"));
742  DoubleDBFField surfaceExtension(tr("surf_ext"));
743 
744  size_t facetCount = facets.size();
745  assert(facetCount != 0);
746  try {
747  facetIndex.values.reserve(facetCount);
748  facetSurface.values.reserve(facetCount);
749  facetRMS.values.reserve(facetCount);
750  facetDipDir.values.reserve(facetCount);
751  facetDip.values.reserve(facetCount);
752  familyIndex.values.reserve(facetCount);
753  subfamilyIndex.values.reserve(facetCount);
754  facetNormal.values.reserve(facetCount);
755  facetBarycenter.values.reserve(facetCount);
756  horizExtension.values.reserve(facetCount);
757  vertExtension.values.reserve(facetCount);
758  surfaceExtension.values.reserve(facetCount);
759  } catch (const std::bad_alloc&) {
760  m_app->dispToConsole(tr("Not enough memory!"),
762  return;
763  }
764 
765  ccHObject toSave(tr("facets"));
766 
767  // depending on the 'main orientation', the job is more or less easy ;)
768  bool useNativeOrientation = fDlg.nativeOriRadioButton->isChecked();
769  bool useGlobalOrientation = fDlg.verticalOriRadioButton->isChecked();
770  bool useCustomOrientation = fDlg.customOriRadioButton->isChecked();
771 
772  // Default base
773  CCVector3 X(1, 0, 0), Y(0, 1, 0), Z(0, 0, 1);
774 
775  //'vertical' orientation (potentially specified by the user)
776  if (!useNativeOrientation) {
777  if (useCustomOrientation) {
778  Z = CCVector3(static_cast<PointCoordinateType>(
779  fDlg.nXLineEdit->text().toDouble()),
780  static_cast<PointCoordinateType>(
781  fDlg.nYLineEdit->text().toDouble()),
782  static_cast<PointCoordinateType>(
783  fDlg.nZLineEdit->text().toDouble()));
784  Z.normalize();
785  } else if (useGlobalOrientation) {
786  // we compute the mean orientation (weighted by each facet's
787  // surface)
788  CCVector3d Nsum(0, 0, 0);
789  for (FacetSet::iterator it = facets.begin(); it != facets.end();
790  ++it) {
791  double surf = (*it)->getSurface();
792  CCVector3 N = (*it)->getNormal();
793  Nsum.x += static_cast<double>(N.x) * surf;
794  Nsum.y += static_cast<double>(N.y) * surf;
795  Nsum.z += static_cast<double>(N.z) * surf;
796  }
797  Nsum.normalize();
798 
799  Z = CCVector3(static_cast<PointCoordinateType>(Nsum.x),
800  static_cast<PointCoordinateType>(Nsum.y),
801  static_cast<PointCoordinateType>(Nsum.z));
802  }
803 
804  // update X & Y
805  CCVector3 D = Z.cross(CCVector3(0, 0, 1));
807  D.norm2())) // otherwise the vertical dir hasn't changed!
808  {
809  X = -D;
810  X.normalize();
811  Y = Z.cross(X);
812  }
813  }
814 
815  // we compute the mean center (weighted by each facet's surface)
816  CCVector3 C(0, 0, 0);
817  {
818  double weightSum = 0;
819  for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it) {
820  double surf = (*it)->getSurface();
821  CCVector3 Ci = (*it)->getCenter();
822  C += Ci * static_cast<PointCoordinateType>(surf);
823  weightSum += surf;
824  }
825  if (weightSum) C /= static_cast<PointCoordinateType>(weightSum);
826  }
827 
828  // determine the 'global' orientation matrix
829  ccGLMatrix oriRotMat;
830  oriRotMat.toIdentity();
831  if (!useNativeOrientation) {
832  oriRotMat.getColumn(0)[0] = static_cast<float>(X.x);
833  oriRotMat.getColumn(0)[1] = static_cast<float>(X.y);
834  oriRotMat.getColumn(0)[2] = static_cast<float>(X.z);
835  oriRotMat.getColumn(1)[0] = static_cast<float>(Y.x);
836  oriRotMat.getColumn(1)[1] = static_cast<float>(Y.y);
837  oriRotMat.getColumn(1)[2] = static_cast<float>(Y.z);
838  oriRotMat.getColumn(2)[0] = static_cast<float>(Z.x);
839  oriRotMat.getColumn(2)[1] = static_cast<float>(Z.y);
840  oriRotMat.getColumn(2)[2] = static_cast<float>(Z.z);
841  oriRotMat.invert();
842 
843  ccGLMatrix transMat;
844  transMat.setTranslation(-C);
845  oriRotMat = oriRotMat * transMat;
846  oriRotMat.setTranslation(oriRotMat.getTranslationAsVec3D() + C);
847  }
848 
849  // for each facet
850  for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it) {
851  ccFacet* facet = *it;
852  ccPolyline* poly = facet->getContour();
853 
854  // if necessary, we create a (temporary) new facet
855  if (!useNativeOrientation) {
857  poly->getAssociatedCloud();
858  if (!vertices || vertices->size() < 3) continue;
859 
860  // create (temporary) new polyline
861  ccPolyline* newPoly = new ccPolyline(*poly);
862  ccPointCloud* pc = (newPoly ? dynamic_cast<ccPointCloud*>(
863  newPoly->getAssociatedCloud())
864  : 0);
865  if (pc) {
866  pc->applyGLTransformation_recursive(&oriRotMat);
867  } else {
868  m_app->dispToConsole(tr("Failed to change the orientation of "
869  "polyline '%1'! (not enough memory)")
870  .arg(poly->getName()),
872  continue;
873  }
874 
875  newPoly->set2DMode(true);
876  poly = newPoly;
877  }
878 
879  toSave.addChild(poly, useNativeOrientation
882 
883  // save associated meta-data as 'shapefile' fields
884  {
885  // main parameters
887  GetFacetMetaData(facet, data);
888 
889  // horizontal and vertical extensions
890  double horizExt = 0, vertExt = 0;
891  ComputeFacetExtensions(data.normal, poly, horizExt, vertExt);
892 
893  facetIndex.values.push_back(data.facetIndex);
894  facetSurface.values.push_back(data.surface);
895  facetRMS.values.push_back(data.rms);
896  facetDipDir.values.push_back(data.dipDir_deg);
897  facetDip.values.push_back(data.dip_deg);
898  familyIndex.values.push_back(data.familyIndex);
899  subfamilyIndex.values.push_back(data.subfamilyIndex);
900  facetNormal.values.push_back(
901  CCVector3d(data.normal.x, data.normal.y, data.normal.z));
902  facetBarycenter.values.push_back(
903  CCVector3d(data.center.x, data.center.y, data.center.z));
904  vertExtension.values.push_back(vertExt);
905  horizExtension.values.push_back(horizExt);
906  surfaceExtension.values.push_back(horizExt * vertExt);
907  }
908  }
909 
910  // save entities
911  if (toSave.getChildrenNumber()) {
912  std::vector<GenericDBFField*> fields;
913  fields.push_back(&facetIndex);
914  fields.push_back(&facetBarycenter);
915  fields.push_back(&facetNormal);
916  fields.push_back(&facetRMS);
917  fields.push_back(&horizExtension);
918  fields.push_back(&vertExtension);
919  fields.push_back(&surfaceExtension);
920  fields.push_back(&facetSurface);
921  fields.push_back(&facetDipDir);
922  fields.push_back(&facetDip);
923  fields.push_back(&familyIndex);
924  fields.push_back(&subfamilyIndex);
925  ShpFilter filter;
926  filter.treatClosedPolylinesAsPolygons(true);
927  ShpFilter::SaveParameters params;
928  params.alwaysDisplaySaveDialog = false;
929  if (filter.saveToFile(&toSave, fields, filename, params) ==
932  tr("[qFacets] File '%1' successfully saved").arg(filename),
934  } else {
936  tr("[qFacets] Failed to save file '%1'!").arg(filename),
938  }
939  }
940 }
941 
943  assert(m_app);
944  if (!m_app) return;
945 
946  // disclaimer accepted?
947  if (!ShowDisclaimer(m_app)) return;
948 
949  // we expect a facet group or a cloud
950  const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
951  if (!m_app->haveOneSelection() ||
952  (!selectedEntities.back()->isA(CV_TYPES::HIERARCHY_OBJECT) &&
953  !selectedEntities.back()->isA(CV_TYPES::POINT_CLOUD))) {
954  m_app->dispToConsole(tr("Select a group of facets or a point cloud!"));
955  return;
956  }
957 
958  StereogramParamsDlg stereogramParamsDlg(m_app->getMainWindow());
959  stereogramParamsDlg.angleStepDoubleSpinBox->setValue(s_stereogramAngleStep);
960  stereogramParamsDlg.resolutionDoubleSpinBox->setValue(
962  if (!stereogramParamsDlg.exec()) return;
963 
964  s_stereogramAngleStep = stereogramParamsDlg.angleStepDoubleSpinBox->value();
966  stereogramParamsDlg.resolutionDoubleSpinBox->value();
967 
968  if (!s_fcDlg) s_fcDlg = new StereogramDialog(m_app);
969  if (s_fcDlg->init(s_stereogramAngleStep, selectedEntities.back(),
971  s_fcDlg->show();
972  s_fcDlg->raise();
973  }
974 }
975 
977  assert(m_app);
978  if (!m_app) return;
979 
980  // disclaimer accepted?
981  if (!ShowDisclaimer(m_app)) return;
982 
983  // we expect a facet group
984  const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
985  if (!m_app->haveOneSelection() ||
986  !selectedEntities.back()->isA(CV_TYPES::HIERARCHY_OBJECT)) {
987  m_app->dispToConsole(tr("Select a group of facets!"));
988  return;
989  }
990 
991  ClassificationParamsDlg classifParamsDlg(m_app->getMainWindow());
992  classifParamsDlg.angleStepDoubleSpinBox->setValue(s_classifAngleStep);
993  classifParamsDlg.maxDistDoubleSpinBox->setValue(s_classifMaxDist);
994  if (!classifParamsDlg.exec()) return;
995 
996  s_classifAngleStep = classifParamsDlg.angleStepDoubleSpinBox->value();
998  s_classifAngleStep; // we automatically copy it to the stereogram's
999  // equivalent parameter
1000  s_classifMaxDist = classifParamsDlg.maxDistDoubleSpinBox->value();
1001 
1002  ccHObject* group = selectedEntities.back();
1004 }
1005 
1007  double angleStep_deg,
1008  double maxDist) {
1009  assert(m_app);
1010  if (!m_app) return;
1011 
1012  assert(group);
1013 
1014  if (group->isA(CV_TYPES::HIERARCHY_OBJECT)) {
1015  if (group->getParent()) {
1016  m_app->removeFromDB(group, false);
1017  }
1018 
1019  bool success =
1020  FacetsClassifier::ByOrientation(group, angleStep_deg, maxDist);
1021  m_app->addToDB(group);
1022 
1023  if (!success) {
1024  m_app->dispToConsole(tr("An error occurred while classifying the "
1025  "facets! (not enough memory?)"),
1027  return;
1028  }
1029  }
1030 
1031  // m_app->refreshAll();
1032 }
1033 
1035  assert(m_app);
1036  if (!m_app) return;
1037 
1038  // disclaimer accepted?
1039  if (!ShowDisclaimer(m_app)) return;
1040 
1041  // Retrive selected facets
1042  FacetSet facets;
1044 
1045  if (facets.empty()) {
1047  tr("Couldn't find any facet in the current selection!"),
1049  return;
1050  }
1051  assert(!facets.empty());
1052 
1054  m_app->getMainWindow());
1055  fDlg.orientationGroupBox->setEnabled(false);
1056 
1057  // persistent settings (default export path)
1058  QSettings settings;
1059  settings.beginGroup("qFacets");
1060  QString facetsSavePath =
1061  settings.value("exportPath", ecvFileUtils::defaultDocPath())
1062  .toString();
1063  fDlg.destinationPathLineEdit->setText(facetsSavePath +
1064  QString("/facets.csv"));
1065 
1066  if (!fDlg.exec()) return;
1067 
1068  QString filename = fDlg.destinationPathLineEdit->text();
1069 
1070  // save current export path to persistent settings
1071  settings.setValue("exportPath", QFileInfo(filename).absolutePath());
1072 
1073  QFile outFile(filename);
1074  if (outFile.exists()) {
1075  // if the file already exists, ask for confirmation!
1076  if (QMessageBox::warning(m_app->getMainWindow(), tr("Overwrite"),
1077  tr("File already exists! Are you sure you "
1078  "want to overwrite it?"),
1079  QMessageBox::Yes,
1080  QMessageBox::No) == QMessageBox::No)
1081  return;
1082  }
1083 
1084  // open CSV file
1085  if (!outFile.open(QFile::WriteOnly | QFile::Text)) {
1086  m_app->dispToConsole(tr("Failed to open file for writing! Check "
1087  "available space and access rights"),
1089  return;
1090  }
1091 
1092  // write header
1093  QTextStream outStream(&outFile);
1094  outStream << " Index,";
1095  outStream << " CenterX,";
1096  outStream << " CenterY,";
1097  outStream << " CenterZ,";
1098  outStream << " NormalX,";
1099  outStream << " NormalY,";
1100  outStream << " NormalZ,";
1101  outStream << " RMS,";
1102  outStream << " Horiz_ext,";
1103  outStream << " Vert_ext,";
1104  outStream << " Surf_ext,";
1105  outStream << " Surface,";
1106  outStream << " Dip dir.,";
1107  outStream << " Dip,";
1108  outStream << " Family ind.,";
1109  outStream << " Subfamily ind.,";
1110  outStream << " \n";
1111 
1112  // write data (one line per facet)
1113  for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it) {
1114  ccFacet* facet = *it;
1116  GetFacetMetaData(facet, data);
1117  // horizontal and vertical extensions
1118  double horizExt = 0, vertExt = 0;
1119  ComputeFacetExtensions(data.normal, facet->getContour(), horizExt,
1120  vertExt);
1121 
1122  outStream << data.facetIndex << ",";
1123  outStream << data.center.x << "," << data.center.y << ","
1124  << data.center.z << ",";
1125  outStream << data.normal.x << "," << data.normal.y << ","
1126  << data.normal.z << ",";
1127  outStream << data.rms << ",";
1128  outStream << horizExt << ",";
1129  outStream << vertExt << ",";
1130  outStream << horizExt * vertExt << ",";
1131  outStream << data.surface << ",";
1132  outStream << data.dipDir_deg << ",";
1133  outStream << data.dip_deg << ",";
1134  outStream << data.familyIndex << ",";
1135  outStream << data.subfamilyIndex << ",";
1136  outStream << "\n";
1137  }
1138 
1139  outFile.close();
1140 
1142  tr("[qFacets] File '%1' successfully saved").arg(filename),
1144 }
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Definition: CVGeom.h:804
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
std::string filename
std::vector< PCLPointField > fields
int count
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
void * X
Definition: SmallVector.cpp:45
core::Tensor result
Definition: VtkUtils.cpp:76
virtual void link()
Increase counter.
Definition: CVShareable.cpp:33
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
Algorithm
Cell fusion algorithm.
Dialog for orientation-based classification of facets (qFacets plugin)
static void GenerateSubfamilyColor(ecvColor::Rgb &col, double dip, double dipDir, unsigned subFamilyIndex, unsigned subFamilyCount, ecvColor::Rgb *darkCol=0)
Generates a given sub-family color.
static bool ByOrientation(ccHObject *facetGroup, double angularStep_deg, double maxDist)
Classifies the facets based on their orientation.
Dialog for exporting facets or facets info (qFacets plugin)
static int ExtractPlanarFacets(ccPointCloud *theCloud, unsigned char octreeLevel, ScalarType maxError, cloudViewer::DistanceComputationTools::ERROR_MEASURES errorMeasure, bool useRetroProjectionError=true, cloudViewer::GenericProgressCallback *progressCb=0, cloudViewer::DgmOctree *_theOctree=0)
Static entry point (helper)
Dialog for displaying the angular repartition of facets (qFacets plugin)
bool init(double angularStep_deg, ccHObject *facetGroup, double resolution_deg=2.0)
Inits dialog.
Dialog for stereogram parameters (qFacets plugin)
Definition: stereogramDlg.h:29
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
Type dot(const Vector3Tpl &v) const
Dot product.
Definition: CVGeom.h:408
Type norm2() const
Returns vector square norm.
Definition: CVGeom.h:417
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
Definition: CVGeom.h:412
Bounding box structure.
Definition: ecvBBox.h:25
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
Facet.
Definition: ecvFacet.h:25
ccMesh * getPolygon()
Returns polygon mesh (if any)
Definition: ecvFacet.h:81
double getSurface() const
Returns associated surface area.
Definition: ecvFacet.h:70
ccPolyline * getContour()
Returns contour polyline (if any)
Definition: ecvFacet.h:86
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvFacet.h:63
void invertNormal()
Inverts the facet normal.
Definition: ecvFacet.cpp:653
double getRMS() const
Returns associated RMS.
Definition: ecvFacet.h:68
void setColor(const ecvColor::Rgb &rgb)
Sets the facet unique color.
Definition: ecvFacet.cpp:413
const CCVector3 & getCenter() const
Returns the facet center.
Definition: ecvFacet.h:78
static ccFacet * Create(cloudViewer::GenericIndexedCloudPersist *cloud, PointCoordinateType maxEdgeLength=0, bool transferOwnership=false, const PointCoordinateType *planeEquation=nullptr)
Creates a facet from a set of points.
Definition: ecvFacet.cpp:167
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
void invert()
Inverts transformation.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
virtual void toIdentity()
Sets matrix to identity.
T * getColumn(unsigned index)
Returns a pointer to a given column.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
void showNormals(bool state) override
Sets normals visibility.
void enableStippling(bool state)
Enables polygon stippling.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
Definition: ecvHObject.cpp:948
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
@ DP_PARENT_OF_OTHER
Definition: ecvHObject.h:266
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
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
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
static bool FuseCells(ccKdTree *kdTree, double maxError, cloudViewer::DistanceComputationTools::ERROR_MEASURES errorMeasure, double maxAngle_deg, PointCoordinateType overlapCoef=1, bool closestFirst=true, cloudViewer::GenericProgressCallback *progressCb=0)
Fuses cells.
KD-tree structure.
Definition: ecvKdTree.h:25
static void ConvertNormalToDipAndDipDir(const CCVector3 &N, PointCoordinateType &dip_deg, PointCoordinateType &dipDir_deg)
Converts a normal vector to geological 'dip direction & dip' parameters.
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
Definition: ecvObject.cpp:208
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
static CCVector3 ComputeAverageNorm(cloudViewer::ReferenceCloud *subset, ccGenericPointCloud *sourceCloud)
Computes the average normal of a set of points.
Definition: ecvOctree.cpp:299
void showNormalVector(bool state)
Show normal vector.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool hasNormals() const override
Returns whether normals are enabled or not.
void deleteScalarField(int index) override
Deletes a specific scalar field.
Colored polyline.
Definition: ecvPolyline.h:24
void set2DMode(bool state)
Defines if the polyline is considered as 2D or 3D.
virtual void setGlobalScale(double scale) override
virtual void setGlobalShift(const CCVector3d &shift) override
Sets shift applied to original coordinates (information storage only)
void setColor(const ecvColor::Rgb &col)
Sets the polyline color.
Definition: ecvPolyline.h:81
void setWidth(PointCoordinateType width)
Sets the width of the line.
A scalar field associated to display-related parameters.
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
Standard ECV plugin interface.
ecvMainAppInterface * m_app
Main application interface.
static bool extractConnectedComponents(GenericIndexedCloudPersist *theCloud, ReferenceCloudContainer &ccc)
Extracts connected components from a point cloud.
Vector3Tpl< T > getDiagVec() const
Returns diagonal vector.
Definition: BoundingBox.h:169
T getMinBoxDim() const
Returns minimal box dimension.
Definition: BoundingBox.h:178
void add(const Vector3Tpl< T > &P)
'Enlarges' the bounding box with a point
Definition: BoundingBox.h:131
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based and presistent access to points.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
const CCVector3 * getGravityCenter()
Returns gravity center.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
void setCurrentScalarField(int index)
Sets both the INPUT & OUTPUT scalar field.
unsigned size() const override
Definition: PointCloudTpl.h:38
A very simple point cloud (no point duplication)
virtual GenericIndexedCloudPersist * getAssociatedCloud()
Returns the associated (source) cloud.
unsigned size() const override
Returns the number of points.
virtual void computeMinAndMax()
Determines the min and max values.
Definition: ScalarField.h:123
bool build(double maxError, DistanceComputationTools::ERROR_MEASURES errorMeasure=DistanceComputationTools::RMS, unsigned minPointCountPerCell=3, unsigned maxPointCountPerCell=0, GenericProgressCallback *progressCb=nullptr)
Builds KD-tree.
Definition: TrueKdTree.cpp:247
static Rgb Random(bool lightOnly=true)
Generates a random color.
RGB color structure.
Definition: ecvColorTypes.h:49
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual QWidget * getActiveWindow()=0
virtual const ccHObject::Container & getSelectedEntities() const =0
Returns currently selected entities ("read only")
bool haveOneSelection() const
Checks if we have exactly one selection.
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
virtual void removeFromDB(ccHObject *obj, bool autoDelete=true)=0
Removes an entity from main db tree.
Graphical progress indicator (thread-safe)
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
void extractFacets(CellsFusionDlg::Algorithm algo)
Uses the given algorithm to detect planar facets.
Definition: qFacets.cpp:202
void showStereogram()
Displays the selected entity stereogram.
Definition: qFacets.cpp:942
QAction * m_doShowStereogram
Associated action.
Definition: qFacets.h:107
QAction * m_doExportFacets
Associated action.
Definition: qFacets.h:101
QAction * m_doFuseKdTreeCells
Associated action.
Definition: qFacets.h:97
void exportFacets()
Exports facets (as shapefiles)
Definition: qFacets.cpp:682
void exportFacetsInfo()
Exports statistics on a set of facets.
Definition: qFacets.cpp:1034
QAction * m_doExportFacetsInfo
Associated action.
Definition: qFacets.h:103
QAction * m_fastMarchingExtraction
Associated action.
Definition: qFacets.h:99
void classifyFacetsByAngle()
Classifies facets by orientation.
Definition: qFacets.cpp:976
ccHObject * createFacets(ccPointCloud *cloud, cloudViewer::ReferenceCloudContainer &components, unsigned minPointsPerComponent, double maxEdgeLength, bool randomColors, bool &error)
Creates facets from components.
Definition: qFacets.cpp:435
void getFacetsInCurrentSelection(FacetSet &facets) const
Returns all the facets in the current selection.
Definition: qFacets.cpp:557
void extractFacetsWithFM()
Uses Fast Marching to detect planar facets.
Definition: qFacets.cpp:196
void fuseKdTreeCells()
Fuses the cells of a kd-tree to produces planar facets.
Definition: qFacets.cpp:200
std::unordered_set< ccFacet * > FacetSet
Set of facets (pointers)
Definition: qFacets.h:86
QAction * m_doClassifyFacetsByAngle
Associated action.
Definition: qFacets.h:105
static bool ShowDisclaimer(ecvMainAppInterface *app)
unsigned char ColorCompType
Default color components type (R,G and B)
Definition: ecvColorTypes.h:29
const double * e
static const QString s_OriSubFamilyKey
static const QString s_OriFamilyKey
const double c_darkColorRatio
GraphType data
Definition: graph_cut.cc:138
@ HIERARCHY_OBJECT
Definition: CVTypes.h:103
@ POINT_CLOUD
Definition: CVTypes.h:104
@ FACET
Definition: CVTypes.h:109
constexpr Qt::SplitBehavior SkipEmptyParts
Definition: QtCompat.h:302
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:37
std::vector< ReferenceCloud * > ReferenceCloudContainer
A standard container to store several subsets of points.
QString defaultDocPath()
Shortcut for getting the documents location path.
Definition: ecvFileUtils.h:30
static StereogramDialog * s_fcDlg
Definition: qFacets.cpp:68
static double s_classifMaxDist
Definition: qFacets.cpp:61
static bool s_fmUseRetroProjectionError
Definition: qFacets.cpp:50
static double s_errorMaxPerFacet
Definition: qFacets.cpp:53
static int s_errorMeasureType
Definition: qFacets.cpp:54
static double s_stereogramAngleStep
Definition: qFacets.cpp:63
static double s_stereogramResolution_deg
Definition: qFacets.cpp:64
static ccPointCloud * s_lastCloud
Definition: qFacets.cpp:65
static double s_classifAngleStep
Definition: qFacets.cpp:60
void GetFacetMetaData(ccFacet *facet, FacetMetaData &data)
Definition: qFacets.cpp:611
static unsigned s_minPointsPerFacet
Definition: qFacets.cpp:52
void ComputeFacetExtensions(CCVector3 &N, ccPolyline *facetContour, double &horizExt, double &vertExt)
Definition: qFacets.cpp:644
static double s_kdTreeFusionMaxRelativeDistance
Definition: qFacets.cpp:58
static unsigned s_octreeLevel
Definition: qFacets.cpp:49
static double s_kdTreeFusionMaxAngle_deg
Definition: qFacets.cpp:57
static double s_maxEdgeLength
Definition: qFacets.cpp:55
FacetMetaData()
Default constructor.
Definition: qFacets.cpp:598
CCVector3 center
Definition: qFacets.cpp:588
int subfamilyIndex
Definition: qFacets.cpp:595
CCVector3 normal
Definition: qFacets.cpp:589
double rms
Definition: qFacets.cpp:593
double surface
Definition: qFacets.cpp:590