22 #include <QElapsedTimer>
24 #include <QInputDialog>
25 #include <QMessageBox>
70 qFacets::qFacets(QObject* parent)
73 m_doFuseKdTreeCells(nullptr),
74 m_fastMarchingExtraction(nullptr),
75 m_doExportFacets(nullptr),
76 m_doExportFacetsInfo(nullptr),
77 m_doClassifyFacetsByAngle(nullptr),
78 m_doShowStereogram(nullptr) {}
85 }
catch (std::exception& e) {
87 tr(
"closing facets dialog failed! [%1]").arg(e.what()));
92 QList<QAction*> qFacets::getActions() {
97 tr(
"Detect planar facets by fusing Kd-tree cells"));
99 QString::fromUtf8(
":/CC/plugin/qFacets/images/extractKD.png")));
107 new QAction(tr(
"Extract facets (Fast Marching)"),
this);
109 tr(
"Detect planar facets with Fast Marching"));
111 QString::fromUtf8(
":/CC/plugin/qFacets/images/extractFM.png")));
120 tr(
"Exports one or several facets to a shapefile"));
122 QString::fromUtf8(
":/CC/plugin/qFacets/images/shpFile.png")));
130 new QAction(tr(
"Export facets info (CSV)"),
this);
132 tr(
"Exports various information on a set of facets (ASCII CSV "
135 QString::fromUtf8(
":/CC/plugin/qFacets/images/csvFile.png")));
143 new QAction(tr(
"Classify facets by orientation"),
this);
145 tr(
"Classifies facets based on their orienation (dip & dip "
148 ":/CC/plugin/qFacets/images/classifIcon.png")));
157 tr(
"Computes and displays a stereogram (+ interactive "
160 ":/CC/plugin/qFacets/images/stereogram.png")));
166 return QList<QAction*>{
175 selectedEntities.size() == 1 &&
179 selectedEntities.size() == 1 &&
187 selectedEntities.size() == 1 &&
191 selectedEntities.size() == 1 &&
238 fusionDlg.octreeLevelSpinBox->setCloud(pc);
241 fusionDlg.useRetroProjectionCheckBox->setChecked(
247 fusionDlg.maxRelativeDistDoubleSpinBox->setValue(
251 fusionDlg.noNormalWarningLabel->setVisible(!pc->
hasNormals());
253 if (!fusionDlg.exec())
return;
257 fusionDlg.useRetroProjectionCheckBox->isChecked();
263 fusionDlg.maxRelativeDistDoubleSpinBox->value();
294 const char c_defaultSFName[] =
"facet indexes";
299 tr(
"Couldn't allocate a new scalar field for computing fusion "
300 "labels! Try to free some memory ..."),
307 QElapsedTimer eTimer;
314 QElapsedTimer eTimer;
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,
335 tr(
"Failed to build Kd-tree! (not enough memory?)"),
377 tr(
" [Kd-tree][error < %1][angle < %2 deg.]")
383 tr(
" [FM][level %2][error < %1]")
399 tr(
"Error(s) occurred during the generation of "
400 "facets! Result may be incomplete"),
410 "generation of facets!"),
414 "parameters (min size, etc.)"),
438 unsigned minPointsPerComponent,
439 double maxEdgeLength,
454 size_t componentCount = components.size();
459 pDlg.
setInfo(tr(
"Components: %1").arg(componentCount));
460 pDlg.setMaximum(
static_cast<int>(componentCount));
462 QApplication::processEvents();
466 while (!components.empty()) {
468 components.pop_back();
471 if (compIndexes && compIndexes->
size() >= minPointsPerComponent) {
484 tr(
"facet %1 (rms=%2)")
500 if (cloudHasNormal) {
530 col, dip, dipDir, 0, 1, &darkCol);
542 compIndexes =
nullptr;
545 pDlg.setValue(
static_cast<int>(componentCount - components.size()));
566 facets.insert(facet);
573 for (
ccHObject* childFacet : childFacets) {
578 facets.insert(facet);
616 if (tokens.size() > 1 && tokens[0] == QString(
"facet")) {
639 data.
dip_deg =
static_cast<int>(dip);
649 horizExt = vertExt = 0;
671 for (
unsigned i = 0; i < vertCloud->
size(); ++i) {
693 if (facets.empty()) {
695 tr(
"Couldn't find any facet in the current selection!"),
699 assert(!facets.empty());
706 settings.beginGroup(
"qFacets");
707 QString facetsSavePath =
710 fDlg.destinationPathLineEdit->setText(facetsSavePath +
711 QString(
"/facets.shp"));
713 if (!fDlg.exec())
return;
715 QString
filename = fDlg.destinationPathLineEdit->text();
718 settings.setValue(
"exportPath", QFileInfo(
filename).absolutePath());
722 if (QMessageBox::warning(
724 tr(
"File already exists! Are you sure you want to "
726 QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
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"));
744 size_t facetCount = facets.size();
745 assert(facetCount != 0);
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&) {
768 bool useNativeOrientation = fDlg.nativeOriRadioButton->isChecked();
769 bool useGlobalOrientation = fDlg.verticalOriRadioButton->isChecked();
770 bool useCustomOrientation = fDlg.customOriRadioButton->isChecked();
776 if (!useNativeOrientation) {
777 if (useCustomOrientation) {
779 fDlg.nXLineEdit->text().toDouble()),
781 fDlg.nYLineEdit->text().toDouble()),
783 fDlg.nZLineEdit->text().toDouble()));
785 }
else if (useGlobalOrientation) {
789 for (FacetSet::iterator it = facets.begin(); it != facets.end();
791 double surf = (*it)->getSurface();
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;
818 double weightSum = 0;
819 for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it) {
820 double surf = (*it)->getSurface();
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);
845 oriRotMat = oriRotMat * transMat;
850 for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it) {
855 if (!useNativeOrientation) {
858 if (!vertices || vertices->
size() < 3)
continue;
869 "polyline '%1'! (not enough memory)")
879 toSave.
addChild(poly, useNativeOrientation
890 double horizExt = 0, vertExt = 0;
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);
900 facetNormal.values.push_back(
902 facetBarycenter.values.push_back(
904 vertExtension.values.push_back(vertExt);
905 horizExtension.values.push_back(horizExt);
906 surfaceExtension.values.push_back(horizExt * vertExt);
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);
926 filter.treatClosedPolylinesAsPolygons(
true);
927 ShpFilter::SaveParameters
params;
928 params.alwaysDisplaySaveDialog =
false;
932 tr(
"[qFacets] File '%1' successfully saved").arg(
filename),
936 tr(
"[qFacets] Failed to save file '%1'!").arg(
filename),
960 stereogramParamsDlg.resolutionDoubleSpinBox->setValue(
962 if (!stereogramParamsDlg.exec())
return;
966 stereogramParamsDlg.resolutionDoubleSpinBox->value();
994 if (!classifParamsDlg.exec())
return;
1002 ccHObject* group = selectedEntities.back();
1007 double angleStep_deg,
1025 "facets! (not enough memory?)"),
1045 if (facets.empty()) {
1047 tr(
"Couldn't find any facet in the current selection!"),
1051 assert(!facets.empty());
1055 fDlg.orientationGroupBox->setEnabled(
false);
1059 settings.beginGroup(
"qFacets");
1060 QString facetsSavePath =
1063 fDlg.destinationPathLineEdit->setText(facetsSavePath +
1064 QString(
"/facets.csv"));
1066 if (!fDlg.exec())
return;
1068 QString
filename = fDlg.destinationPathLineEdit->text();
1071 settings.setValue(
"exportPath", QFileInfo(
filename).absolutePath());
1074 if (outFile.exists()) {
1077 tr(
"File already exists! Are you sure you "
1078 "want to overwrite it?"),
1080 QMessageBox::No) == QMessageBox::No)
1085 if (!outFile.open(QFile::WriteOnly | QFile::Text)) {
1087 "available space and access rights"),
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.,";
1113 for (FacetSet::iterator it = facets.begin(); it != facets.end(); ++it) {
1118 double horizExt = 0, vertExt = 0;
1127 outStream << data.
rms <<
",";
1128 outStream << horizExt <<
",";
1129 outStream << vertExt <<
",";
1130 outStream << horizExt * vertExt <<
",";
1131 outStream << data.
surface <<
",";
1133 outStream << data.
dip_deg <<
",";
1142 tr(
"[qFacets] File '%1' successfully saved").arg(
filename),
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
std::vector< PCLPointField > fields
cmdLineReadable * params[]
virtual void link()
Increase counter.
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
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)
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)
void normalize()
Sets vector norm to unity.
Type dot(const Vector3Tpl &v) const
Dot product.
Type norm2() const
Returns vector square norm.
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
ccMesh * getPolygon()
Returns polygon mesh (if any)
double getSurface() const
Returns associated surface area.
ccPolyline * getContour()
Returns contour polyline (if any)
CCVector3 getNormal() const override
Returns the entity normal.
void invertNormal()
Inverts the facet normal.
double getRMS() const
Returns associated RMS.
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.
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.
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.
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
unsigned getChildrenNumber() const
Returns the number of children.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
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.)
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.
bool isA(CV_CLASS_ENUM type) const
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
virtual void setName(const QString &name)
Sets object name.
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)
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.
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.
Vector3Tpl< T > getDiagVec() const
Returns diagonal vector.
T getMinBoxDim() const
Returns minimal box dimension.
void add(const Vector3Tpl< T > &P)
'Enlarges' the bounding box with a point
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
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.
bool build(double maxError, DistanceComputationTools::ERROR_MEASURES errorMeasure=DistanceComputationTools::RMS, unsigned minPointCountPerCell=3, unsigned maxPointCountPerCell=0, GenericProgressCallback *progressCb=nullptr)
Builds KD-tree.
static Rgb Random(bool lightOnly=true)
Generates a random color.
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.
void showStereogram()
Displays the selected entity stereogram.
QAction * m_doShowStereogram
Associated action.
QAction * m_doExportFacets
Associated action.
QAction * m_doFuseKdTreeCells
Associated action.
void exportFacets()
Exports facets (as shapefiles)
void exportFacetsInfo()
Exports statistics on a set of facets.
QAction * m_doExportFacetsInfo
Associated action.
QAction * m_fastMarchingExtraction
Associated action.
void classifyFacetsByAngle()
Classifies facets by orientation.
ccHObject * createFacets(ccPointCloud *cloud, cloudViewer::ReferenceCloudContainer &components, unsigned minPointsPerComponent, double maxEdgeLength, bool randomColors, bool &error)
Creates facets from components.
void getFacetsInCurrentSelection(FacetSet &facets) const
Returns all the facets in the current selection.
void extractFacetsWithFM()
Uses Fast Marching to detect planar facets.
void fuseKdTreeCells()
Fuses the cells of a kd-tree to produces planar facets.
std::unordered_set< ccFacet * > FacetSet
Set of facets (pointers)
QAction * m_doClassifyFacetsByAngle
Associated action.
static bool ShowDisclaimer(ecvMainAppInterface *app)
unsigned char ColorCompType
Default color components type (R,G and B)
static const QString s_OriSubFamilyKey
static const QString s_OriFamilyKey
const double c_darkColorRatio
static void error(char *msg)
constexpr Qt::SplitBehavior SkipEmptyParts
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
std::vector< ReferenceCloud * > ReferenceCloudContainer
A standard container to store several subsets of points.
QString defaultDocPath()
Shortcut for getting the documents location path.
static StereogramDialog * s_fcDlg
static double s_classifMaxDist
static bool s_fmUseRetroProjectionError
static double s_errorMaxPerFacet
static int s_errorMeasureType
static double s_stereogramAngleStep
static double s_stereogramResolution_deg
static ccPointCloud * s_lastCloud
static double s_classifAngleStep
void GetFacetMetaData(ccFacet *facet, FacetMetaData &data)
static unsigned s_minPointsPerFacet
void ComputeFacetExtensions(CCVector3 &N, ccPolyline *facetContour, double &horizExt, double &vertExt)
static double s_kdTreeFusionMaxRelativeDistance
static unsigned s_octreeLevel
static double s_kdTreeFusionMaxAngle_deg
static double s_maxEdgeLength