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
886  FacetMetaData data;
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;
1115  FacetMetaData data;
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
cmdLineReadable * params[]
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.
double getRMS() const
Returns associated RMS.
Definition: ecvFacet.h:68
void setColor(const ecvColor::Rgb &rgb)
Sets the facet unique color.
static ccFacet * Create(cloudViewer::GenericIndexedCloudPersist *cloud, PointCoordinateType maxEdgeLength=0, bool transferOwnership=false, const PointCoordinateType *planeEquation=nullptr)
Creates a facet from a set of points.
const CCVector3 & getCenter() const
Returns the facet center.
Definition: ecvFacet.h:78
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.
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
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)
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
@ 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.
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.
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.
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.
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.
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
Colored polyline.
Definition: ecvPolyline.h:24
virtual void setGlobalShift(const CCVector3d &shift) override
Sets shift applied to original coordinates (information storage only)
void set2DMode(bool state)
Defines if the polyline is considered as 2D or 3D.
virtual void setGlobalScale(double scale) override
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
static const QString s_OriSubFamilyKey
static const QString s_OriFamilyKey
const double c_darkColorRatio
static void error(char *msg)
Definition: lsd.c:159
@ 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