diff --git a/ContextBasedFeature.cpp b/ContextBasedFeature.cpp index 270b8c9..ad5f498 100644 --- a/ContextBasedFeature.cpp +++ b/ContextBasedFeature.cpp @@ -43,7 +43,7 @@ bool ContextBasedFeature::prepare( const CorePoints& corePoints, return false; } - if (!checkValidity(error)) + if (!checkValidity(corePoints.role, error)) { assert(false); return false; @@ -248,9 +248,9 @@ bool ContextBasedFeature::finish(const CorePoints& corePoints, QString& error) return success; } -bool ContextBasedFeature::checkValidity(QString &error) const +bool ContextBasedFeature::checkValidity(QString corePointRole, QString &error) const { - if (!Feature::checkValidity(error)) + if (!Feature::checkValidity(corePointRole, error)) { return false; } diff --git a/ContextBasedFeature.h b/ContextBasedFeature.h index 3f1b5a2..f56bd2e 100644 --- a/ContextBasedFeature.h +++ b/ContextBasedFeature.h @@ -84,7 +84,7 @@ namespace masc virtual Feature::Shared clone() const override { return Feature::Shared(new ContextBasedFeature(*this)); } virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; virtual bool finish(const CorePoints& corePoints, QString& error) override; - virtual bool checkValidity(QString &error) const override; + virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override; //! Compute the feature value on a set of points diff --git a/CorePoints.h b/CorePoints.h index 07eb1ed..ee43555 100644 --- a/CorePoints.h +++ b/CorePoints.h @@ -32,12 +32,15 @@ namespace masc //! Core points descriptor struct CorePoints { - //origin cloud + //! Origin cloud ccPointCloud* origin = nullptr; - //core points cloud + //! Core points cloud ccPointCloud* cloud = nullptr; + //! Core points 'role' + QString role; + //! Return the size inline unsigned size() const { return (cloud ? cloud->size() : 0); } //! Return the point index diff --git a/DualCloudFeature.cpp b/DualCloudFeature.cpp index 62fdd12..b0ffc8f 100644 --- a/DualCloudFeature.cpp +++ b/DualCloudFeature.cpp @@ -28,9 +28,9 @@ bool DualCloudFeature::prepare( const CorePoints& corePoints, return false; } -bool DualCloudFeature::checkValidity(QString &error) const +bool DualCloudFeature::checkValidity(QString corePointRole, QString &error) const { - if (!Feature::checkValidity(error)) + if (!Feature::checkValidity(corePointRole, error)) { return false; } diff --git a/DualCloudFeature.h b/DualCloudFeature.h index d617810..d5fb5a4 100644 --- a/DualCloudFeature.h +++ b/DualCloudFeature.h @@ -68,7 +68,7 @@ namespace masc virtual Type getType() const override { return Type::DualCloudFeature; } virtual Feature::Shared clone() const override { return Feature::Shared(new DualCloudFeature(*this)); } virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; - virtual bool checkValidity(QString &error) const override; + virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override { //use the default keyword + "_SC" + the scale diff --git a/FeaturesInterface.h b/FeaturesInterface.h index 31d1be4..a9abceb 100644 --- a/FeaturesInterface.h +++ b/FeaturesInterface.h @@ -155,7 +155,7 @@ namespace masc inline bool scaled() const { return std::isfinite(scale); } //! Checks the feature definition validity - virtual bool checkValidity(QString &error) const + virtual bool checkValidity(QString corePointRole, QString &error) const { unsigned char cloudCount = (cloud1 ? (cloud2 ? 2 : 1) : 0); if (cloudCount == 0) diff --git a/NeighborhoodFeature.cpp b/NeighborhoodFeature.cpp index 7971cdb..c3648b0 100644 --- a/NeighborhoodFeature.cpp +++ b/NeighborhoodFeature.cpp @@ -23,9 +23,9 @@ using namespace masc; -bool NeighborhoodFeature::checkValidity(QString &error) const +bool NeighborhoodFeature::checkValidity(QString corePointRole, QString &error) const { - if (!Feature::checkValidity(error)) + if (!Feature::checkValidity(corePointRole, error)) { return false; } @@ -65,7 +65,7 @@ bool NeighborhoodFeature::prepare( const CorePoints& corePoints, return false; } - if (!checkValidity(error)) + if (!checkValidity(corePoints.role, error)) { assert(false); return false; diff --git a/NeighborhoodFeature.h b/NeighborhoodFeature.h index 9914e35..b853b57 100644 --- a/NeighborhoodFeature.h +++ b/NeighborhoodFeature.h @@ -160,7 +160,7 @@ namespace masc virtual Feature::Shared clone() const override { return Feature::Shared(new NeighborhoodFeature(*this)); } virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; virtual bool finish(const CorePoints& corePoints, QString& error) override; - virtual bool checkValidity(QString &error) const override; + virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override; //! Compute the feature value on a set of points diff --git a/PointFeature.cpp b/PointFeature.cpp index 8601b8c..d27b6cb 100644 --- a/PointFeature.cpp +++ b/PointFeature.cpp @@ -46,9 +46,9 @@ static const char* s_normDipDirSFName = "Norm dip dir."; using namespace masc; -bool PointFeature::checkValidity(QString &error) const +bool PointFeature::checkValidity(QString corePointRole, QString &error) const { - if (!Feature::checkValidity(error)) + if (!Feature::checkValidity(corePointRole, error)) { return false; } @@ -62,6 +62,12 @@ bool PointFeature::checkValidity(QString &error) const assert(cloud1); + if (!corePointRole.isEmpty() && !scaled() && cloud1Label != corePointRole) //in some cases, we don't know the role of the core points yet! + { + error = "Scale-less features can only be computed on the core points / classified cloud"; + return false; + } + if (scaled() && stat == NO_STAT) { error = "scaled point features need a STAT measure to be defined"; diff --git a/PointFeature.h b/PointFeature.h index 9bef06d..4c71151 100644 --- a/PointFeature.h +++ b/PointFeature.h @@ -189,7 +189,7 @@ namespace masc virtual Feature::Shared clone() const override { return Feature::Shared(new PointFeature(*this)); } virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) override; virtual bool finish(const CorePoints& corePoints, QString& error) override; - virtual bool checkValidity(QString &error) const override; + virtual bool checkValidity(QString corePointRole, QString &error) const override; virtual QString toString() const override; //! Compute the associated 'stat' on a set of points (and with a given field) diff --git a/q3DMASC.cpp b/q3DMASC.cpp index b772542..f8048e5 100644 --- a/q3DMASC.cpp +++ b/q3DMASC.cpp @@ -170,6 +170,7 @@ void q3DMASCPlugin::doClassifyAction() //the 'main cloud' is the cloud that should be classified masc::CorePoints corePoints; corePoints.origin = corePoints.cloud = clouds[mainCloudLabel]; + corePoints.role = mainCloudLabel; //prepare the main cloud ccProgressDialog progressDlg(true, m_app->getMainWindow()); @@ -279,6 +280,7 @@ void q3DMASCPlugin::doTrainAction() classifDlg.getClouds(loadedClouds, mainCloudLabel); m_app->dispToConsole("Training cloud: " + mainCloudLabel, ccMainAppInterface::STD_CONSOLE_MESSAGE); corePoints.origin = loadedClouds[mainCloudLabel]; + corePoints.role = mainCloudLabel; } static masc::TrainParameters s_params; diff --git a/q3DMASCCommands.h b/q3DMASCCommands.h index 9d9d93d..01b4374 100644 --- a/q3DMASCCommands.h +++ b/q3DMASCCommands.h @@ -155,6 +155,7 @@ struct Command3DMASCClassif : public ccCommandLineInterface::Command //the 'main cloud' is the cloud that should be classified masc::CorePoints corePoints; corePoints.origin = corePoints.cloud = cloudPerRole[mainCloudRole]; + corePoints.role = mainCloudRole; //prepare the main cloud QScopedPointer pDlg; diff --git a/q3DMASCTools.cpp b/q3DMASCTools.cpp index 218628b..cc027dd 100644 --- a/q3DMASCTools.cpp +++ b/q3DMASCTools.cpp @@ -441,7 +441,7 @@ static bool CreateFeaturesFromCommand(const QString& command, int lineNumber, co //now check the consistency of the rule QString errorMessage; - if (!feature->checkValidity(errorMessage)) + if (!feature->checkValidity(QString(), errorMessage)) { ccLog::Warning("Malformed feature: " + errorMessage + QString(" (line %1)").arg(lineNumber)); return false; @@ -459,7 +459,7 @@ static bool CreateFeaturesFromCommand(const QString& command, int lineNumber, co newFeature->scale = scales.at(i); //as we only change the scale value, all the duplicated features should be valid - assert(newFeature->checkValidity(errorMessage)); + assert(newFeature->checkValidity(QString(), errorMessage)); rawFeatures.push_back(newFeature); } @@ -542,6 +542,7 @@ static bool ReadCorePoints(const QString& command, const Tools::NamedClouds& clo return false; } corePoints.origin = clouds[pcName]; + corePoints.role = pcName; //should we sub-sample the origin cloud? if (tokens.size() > 1) @@ -928,7 +929,8 @@ bool Tools::PrepareFeatures(const CorePoints& corePoints, Feature::Set& features for (const Feature::Shared& feature : features) { QString errorMessage("invalid pointer"); - if (!feature || !feature->checkValidity(errorMessage)) + assert(!corePoints.role.isEmpty()); + if (!feature || !feature->checkValidity(corePoints.role, errorMessage)) { errorStr = "Invalid rule/feature: " + errorMessage; return false;