2018-10-26 10:41:22 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
//##########################################################################
|
|
|
|
|
//# #
|
|
|
|
|
//# CLOUDCOMPARE PLUGIN: q3DMASC #
|
|
|
|
|
//# #
|
|
|
|
|
//# This program is free software; you can redistribute it and/or modify #
|
|
|
|
|
//# it under the terms of the GNU General Public License as published by #
|
|
|
|
|
//# the Free Software Foundation; version 2 or later of the License. #
|
|
|
|
|
//# #
|
|
|
|
|
//# This program is distributed in the hope that it will be useful, #
|
|
|
|
|
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
|
|
|
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
|
|
|
//# GNU General Public License for more details. #
|
|
|
|
|
//# #
|
|
|
|
|
//# COPYRIGHT: Dimitri Lague / CNRS / UEB #
|
|
|
|
|
//# #
|
|
|
|
|
//##########################################################################
|
|
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
|
|
|
|
|
//Local
|
|
|
|
|
#include "CorePoints.h"
|
2019-01-19 00:49:29 +01:00
|
|
|
#include "ScalarFieldCollector.h"
|
2018-11-04 11:21:50 +01:00
|
|
|
|
2018-10-26 10:41:22 +02:00
|
|
|
//Qt
|
|
|
|
|
#include <QString>
|
|
|
|
|
|
|
|
|
|
//system
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
class ccPointCloud;
|
|
|
|
|
|
2018-12-01 22:40:04 +01:00
|
|
|
namespace CCLib
|
|
|
|
|
{
|
|
|
|
|
class ScalarField;
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
namespace masc
|
2018-10-26 10:41:22 +02:00
|
|
|
{
|
2018-11-04 11:21:50 +01:00
|
|
|
//! Generic feature descriptor
|
|
|
|
|
struct Feature
|
|
|
|
|
{
|
|
|
|
|
public: //shortcuts
|
2018-10-26 10:41:22 +02:00
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
//!Shared type
|
|
|
|
|
typedef QSharedPointer<Feature> Shared;
|
2018-10-26 10:41:22 +02:00
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
//! Set of features
|
|
|
|
|
typedef std::vector<Shared> Set;
|
2018-10-26 10:41:22 +02:00
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
public: //enumerators
|
2018-10-26 10:41:22 +02:00
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
//! Feature type
|
|
|
|
|
enum class Type
|
|
|
|
|
{
|
|
|
|
|
PointFeature, /*!< Point features (scalar field, etc.) */
|
|
|
|
|
NeighborhoodFeature, /*!< Neighborhood based features for a given scale */
|
|
|
|
|
ContextBasedFeature, /*!< Contextual based features */
|
|
|
|
|
DualCloudFeature, /*!< Dual Cloud features: requires 2 point clouds */
|
|
|
|
|
Invalid /*!< Invalid feature */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum Stat
|
|
|
|
|
{
|
|
|
|
|
NO_STAT,
|
|
|
|
|
MEAN,
|
|
|
|
|
MODE, //number with the highest frequency
|
2019-03-22 15:02:28 +01:00
|
|
|
MEDIAN,
|
2018-11-04 11:21:50 +01:00
|
|
|
STD,
|
|
|
|
|
RANGE,
|
|
|
|
|
SKEW //(SKEW = (MEAN - MODE)/STD)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static QString StatToString(Stat stat)
|
|
|
|
|
{
|
|
|
|
|
switch (stat)
|
|
|
|
|
{
|
|
|
|
|
case MEAN:
|
|
|
|
|
return "MEAN";
|
|
|
|
|
case MODE:
|
|
|
|
|
return "MODE";
|
2019-03-22 15:02:28 +01:00
|
|
|
case MEDIAN:
|
|
|
|
|
return "MEDIAN";
|
2018-11-04 11:21:50 +01:00
|
|
|
case STD:
|
|
|
|
|
return "STD";
|
|
|
|
|
case RANGE:
|
|
|
|
|
return "RANGE";
|
|
|
|
|
case SKEW:
|
|
|
|
|
return "SKEW";
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
};
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum Operation
|
|
|
|
|
{
|
|
|
|
|
NO_OPERATION, MINUS, PLUS, DIVIDE, MULTIPLY
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static QString OpToString(Operation op)
|
|
|
|
|
{
|
|
|
|
|
switch (op)
|
|
|
|
|
{
|
|
|
|
|
case MINUS:
|
|
|
|
|
return "MINUS";
|
|
|
|
|
case PLUS:
|
|
|
|
|
return "PLUS";
|
|
|
|
|
case DIVIDE:
|
|
|
|
|
return "DIVIDE";
|
|
|
|
|
case MULTIPLY:
|
|
|
|
|
return "MULTIPLY";
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
};
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Sources of values for this feature
|
|
|
|
|
enum Source
|
|
|
|
|
{
|
|
|
|
|
ScalarField, DimX, DimY, DimZ, Red, Green, Blue
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public: //methods
|
|
|
|
|
|
|
|
|
|
//! Default constructor
|
|
|
|
|
Feature(double p_scale = std::numeric_limits<double>::quiet_NaN(), Source p_source = ScalarField, QString p_sourceName = QString())
|
|
|
|
|
: scale(p_scale)
|
|
|
|
|
, cloud1(nullptr)
|
|
|
|
|
, cloud2(nullptr)
|
|
|
|
|
, source(p_source)
|
|
|
|
|
, sourceName(p_sourceName)
|
|
|
|
|
, stat(NO_STAT)
|
|
|
|
|
, op(NO_OPERATION)
|
|
|
|
|
{}
|
2018-10-26 10:41:22 +02:00
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
//! Returns the type (must be reimplemented by child struct)
|
|
|
|
|
virtual Type getType() const = 0;
|
|
|
|
|
|
|
|
|
|
//! Returns the formatted description
|
|
|
|
|
virtual QString toString() const = 0;
|
|
|
|
|
|
|
|
|
|
//! Clones this feature
|
|
|
|
|
virtual Feature::Shared clone() const = 0;
|
|
|
|
|
|
|
|
|
|
//! Prepares the feature (compute the scalar field, etc.)
|
2019-01-19 00:49:29 +01:00
|
|
|
virtual bool prepare(const CorePoints& corePoints, QString& error, CCLib::GenericProgressCallback* progressCb = nullptr, SFCollector* generatedScalarFields = nullptr) = 0;
|
2018-11-04 11:21:50 +01:00
|
|
|
|
2018-12-01 22:40:04 +01:00
|
|
|
//! Finishes the feature preparation (update the scalar field, etc.)
|
|
|
|
|
virtual bool finish(const CorePoints& corePoints, QString& error) { /* does nothing by default*/return true; }
|
|
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
//! Returns whether the feature has an associated scale
|
|
|
|
|
inline bool scaled() const { return std::isfinite(scale); }
|
|
|
|
|
|
|
|
|
|
//! Checks the feature definition validity
|
2018-11-04 18:15:49 +01:00
|
|
|
virtual bool checkValidity(QString &error) const
|
2018-11-04 11:21:50 +01:00
|
|
|
{
|
2018-11-04 18:15:49 +01:00
|
|
|
unsigned char cloudCount = (cloud1 ? (cloud2 ? 2 : 1) : 0);
|
|
|
|
|
if (cloudCount == 0)
|
|
|
|
|
{
|
|
|
|
|
error = "feature has no associated cloud";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-11-04 11:21:50 +01:00
|
|
|
|
2018-12-01 22:40:04 +01:00
|
|
|
if (stat != NO_STAT && getType() != Type::PointFeature)
|
2018-11-04 11:21:50 +01:00
|
|
|
{
|
2018-12-01 22:40:04 +01:00
|
|
|
error = "STAT measures can only be defined on Point features";
|
2018-11-04 11:21:50 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-01 22:40:04 +01:00
|
|
|
if (op != NO_OPERATION && cloudCount < 2)
|
2018-11-04 11:21:50 +01:00
|
|
|
{
|
2018-12-01 22:40:04 +01:00
|
|
|
error = "at least two clouds are required to apply math operations";
|
|
|
|
|
return false;
|
2018-11-04 11:21:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-01 22:40:04 +01:00
|
|
|
public: //helpers
|
|
|
|
|
|
|
|
|
|
//! Creates (or resets) a scalar field with the given name on the input core points cloud
|
2019-01-19 00:49:29 +01:00
|
|
|
static CCLib::ScalarField* PrepareSF(ccPointCloud* cloud, const char* resultSFName, SFCollector* generatedScalarFields = nullptr);
|
2018-12-01 22:40:04 +01:00
|
|
|
|
|
|
|
|
//! Performs a mathematical operation between two scalar fields (they must have the same size!)
|
|
|
|
|
static bool PerformMathOp(CCLib::ScalarField* sf1, const CCLib::ScalarField* sf2, Operation op);
|
|
|
|
|
|
2018-11-04 11:21:50 +01:00
|
|
|
public: //members
|
|
|
|
|
|
|
|
|
|
//! Scale (diameter)
|
|
|
|
|
double scale;
|
|
|
|
|
|
|
|
|
|
ccPointCloud *cloud1, *cloud2;
|
|
|
|
|
QString cloud1Label, cloud2Label;
|
|
|
|
|
|
|
|
|
|
Source source; //values source
|
|
|
|
|
QString sourceName; //feature source name (mandatory for scalar fields if the SF index is not set)
|
|
|
|
|
|
|
|
|
|
Stat stat; //only considered if a scale is defined
|
|
|
|
|
Operation op; //only considered if 2 clouds are defined
|
|
|
|
|
};
|
|
|
|
|
}
|