Skip to content

Commit

Permalink
Scalar fields now rely on an internal offset to properly manage doubl…
Browse files Browse the repository at this point in the history
…e values (as long as the min and max values are not too far from each other!)
  • Loading branch information
dgirardeau committed Nov 9, 2024
1 parent 411d11b commit cebe198
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 116 deletions.
2 changes: 1 addition & 1 deletion include/CCConst.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ namespace CCCoreLib
};

//! Min number of points to compute local models (see CC_LOCAL_MODEL_TYPES)
constexpr unsigned CC_LOCAL_MODEL_MIN_SIZE[] = {
constexpr unsigned CC_LOCAL_MODEL_MIN_SIZE[] {
1, //!< for single point model (i.e. no model ;)
3, //!< for least Square best fitting plane
3, //!< for Delaunay triangulation (2.5D)
Expand Down
6 changes: 0 additions & 6 deletions include/CCTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,4 @@
using PointCoordinateType = float;

//! Type of a single scalar field value
#if defined CC_CORE_LIB_USES_DOUBLE
using ScalarType = double;
#elif defined CC_CORE_LIB_USES_FLOAT
using ScalarType = float;
#else
static_assert(false, "type for ScalarType has not been declared");
#endif
34 changes: 27 additions & 7 deletions include/PointCloudTpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,27 @@ namespace CCCoreLib
}

unsigned n = size();
for (unsigned i = 0; i < n; ++i)

if (0 != n)
{
action(m_points[i], (*currentOutScalarFieldArray)[i]);
double previousOffset = currentOutScalarFieldArray->getOffset();
if (n == currentOutScalarFieldArray->size())
{
// if we are going to change ALL the values, we can also apply the functor on the offset
double firstValue = currentOutScalarFieldArray->getValue(0);
action(m_points.front(), firstValue);
if (ScalarField::ValidValue(firstValue))
{
currentOutScalarFieldArray->setOffset(firstValue);
}
}

for (unsigned i = 0; i < n; ++i)
{
ScalarType value = previousOffset + currentOutScalarFieldArray->getLocalValue(i); // warning, the offset has been changed, we can't use getValue anymore
action(m_points[i], value);
currentOutScalarFieldArray->setValue(i, value);
}
}
}

Expand Down Expand Up @@ -244,7 +262,7 @@ namespace CCCoreLib
//if something fails, we restore the previous size for already processed SFs!
for (std::size_t j = 0; j < i; ++j)
{
m_scalarFields[j]->resize(oldCount);
m_scalarFields[j]->resizeSafe(oldCount);
m_scalarFields[j]->computeMinAndMax();
}
//we can assume that newNumberOfPoints > oldCount, so it should always be ok
Expand Down Expand Up @@ -348,23 +366,25 @@ namespace CCCoreLib
/** \param index a scalar field index
\return a pointer to a string structure (null-terminated array of characters), or 0 if the index is invalid.
**/
const char* getScalarFieldName(int index) const
const std::string getScalarFieldName(int index) const
{
return (index >= 0 && index < static_cast<int>(m_scalarFields.size()) ? m_scalarFields[index]->getName() : 0);
return (index >= 0 && index < static_cast<int>(m_scalarFields.size()) ? m_scalarFields[index]->getName() : std::string{});
}

//! Returns the index of a scalar field represented by its name
/** \param name a scalar field name
\return an index (-1 if the scalar field couldn't be found)
**/
int getScalarFieldIndexByName(const char* name) const
int getScalarFieldIndexByName(const std::string& name) const
{
std::size_t sfCount = m_scalarFields.size();
for (std::size_t i = 0; i < sfCount; ++i)
{
//we don't accept two SF with the same name!
if (strcmp(m_scalarFields[i]->getName(), name) == 0)
if (0 == m_scalarFields[i]->getName().compare(name))
{
return static_cast<int>(i);
}
}

return -1;
Expand Down
146 changes: 91 additions & 55 deletions include/ScalarField.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,31 @@

//System
#include <vector>
#include <string>

namespace CCCoreLib
{
//! A simple scalar field (to be associated to a point cloud)
/** A monodimensional array of scalar values. It has also specific
parameters for display purposes.
It is now using a base offset value of type double, and internally
stores the values as float to limit the memory consumption.
Invalid values can be represented by CCCoreLib::NAN_VALUE.
**/
class ScalarField : public std::vector<ScalarType>, public CCShareable
class ScalarField : protected std::vector<float>, public CCShareable
{
public:

//! Shortcut to the (protected) std::vector::size() method
using std::vector<float>::size;

//! Default constructor
/** [SHAREABLE] Call 'link' when associating this structure to an object.
\param name scalar field name
**/
CC_CORE_LIB_API explicit ScalarField(const char* name = nullptr);
CC_CORE_LIB_API explicit ScalarField(const std::string& name = std::string());

//! Copy constructor
/** \param sf scalar field to copy
Expand All @@ -35,19 +42,32 @@ namespace CCCoreLib
CC_CORE_LIB_API ScalarField(const ScalarField& sf);

//! Sets scalar field name
CC_CORE_LIB_API void setName(const char* name);
CC_CORE_LIB_API void setName(const std::string& name);

//! Returns scalar field name
inline const char* getName() const { return m_name; }
inline const std::string& getName() const { return m_name; }

//! Returns the specific NaN value
static inline ScalarType NaN() { return NAN_VALUE; }

//! Returns the offset
inline double getOffset() const { return m_offset; }

//! Sets the offset
/** \warning Dangerous. All values inside the vector are relative to the offset!
\param offset the new offset value
**/
inline void setOffset(double offset)
{
m_offsetHasBeenSet = true;
m_offset = offset;
}

//! Computes the mean value (and optionally the variance value) of the scalar field
/** \param mean a field to store the mean value
\param variance if not void, the variance will be computed and stored here
**/
CC_CORE_LIB_API void computeMeanAndVariance(ScalarType &mean, ScalarType* variance = nullptr) const;
CC_CORE_LIB_API void computeMeanAndVariance(ScalarType& mean, ScalarType* variance = nullptr) const;

//! Determines the min and max values
CC_CORE_LIB_API virtual void computeMinAndMax();
Expand All @@ -56,30 +76,79 @@ namespace CCCoreLib
static inline bool ValidValue(ScalarType value) { return std::isfinite(value); }

//! Sets the value as 'invalid' (i.e. CCCoreLib::NAN_VALUE)
inline void flagValueAsInvalid(std::size_t index) { at(index) = NaN(); }
inline void flagValueAsInvalid(std::size_t index) { (*this)[index] = std::numeric_limits<float>::quiet_NaN(); }

//! Returns the number of valid values in this scalar field
CC_CORE_LIB_API std::size_t countValidValues() const;

//! Returns the minimum value
inline ScalarType getMin() const { return m_minVal; }
inline ScalarType getMin() const { return m_offset + m_localMinVal; }
//! Returns the maximum value
inline ScalarType getMax() const { return m_maxVal; }
inline ScalarType getMax() const { return m_offset + m_localMaxVal; }

//! Fills the array with a particular value
inline void fill(ScalarType fillValue = 0) { if (empty()) resize(capacity(), fillValue); else std::fill(begin(), end(), fillValue); }
inline void fill(ScalarType fillValue = 0)
{
float fillValueF = 0.0f;
if (m_offsetHasBeenSet)
{
fillValueF = static_cast<float>(fillValue - m_offset);
}
else
{
setOffset(fillValue);
}

if (empty())
{
resize(capacity(), fillValueF);
}
else
{
std::fill(begin(), end(), fillValueF);
}
}

//! Reserves memory (no exception thrown)
CC_CORE_LIB_API bool reserveSafe(std::size_t count);
//! Resizes memory (no exception thrown)
CC_CORE_LIB_API bool resizeSafe(std::size_t count, bool initNewElements = false, ScalarType valueForNewElements = 0);

//Shortcuts (for backward compatibility)
inline ScalarType& getValue(std::size_t index) { return at(index); }
inline const ScalarType& getValue(std::size_t index) const { return at(index); }
inline void setValue(std::size_t index, ScalarType value) { at(index) = value; }
inline void addElement(ScalarType value) { push_back(value); }
inline ScalarType getValue(std::size_t index) const { return m_offset + (*this)[index]; }

inline float getLocalValue(std::size_t index) const { return (*this)[index]; }
inline void setLocalValue(std::size_t index, float value) { (*this)[index] = value; }

inline void setValue(std::size_t index, ScalarType value)
{
if (m_offsetHasBeenSet)
{
(*this)[index] = static_cast<float>(value - m_offset);
}
else
{
setOffset(value);
(*this)[index] = 0.0f;
}
}

inline void addElement(ScalarType value)
{
if (m_offsetHasBeenSet)
{
push_back(static_cast<float>(value - m_offset));
}
else
{
// if the offset has not been set yet, we use the first value by default
setOffset(value);
push_back(0.0f);
}
}

inline unsigned currentSize() const { return static_cast<unsigned>(size()); }

inline void swap(std::size_t i1, std::size_t i2) { std::swap(at(i1), at(i2)); }

protected: //methods
Expand All @@ -92,49 +161,16 @@ namespace CCCoreLib
protected: //members

//! Scalar field name
char m_name[256];
std::string m_name;

private:
//! Minimum value
ScalarType m_minVal;
//! Maximum value
ScalarType m_maxVal;
//! Offset value (local to global)
double m_offset;
//! Whether the offset has been set or not
bool m_offsetHasBeenSet;
//! Minimum value (local)
float m_localMinVal;
//! Maximum value (local)
float m_localMaxVal;
};

inline void ScalarField::computeMinAndMax()
{
ScalarType minVal, maxVal;

bool minMaxInitialized = false;
for (std::size_t i = 0; i < size(); ++i)
{
const ScalarType& val = at(i);
if (ValidValue(val))
{
if (minMaxInitialized)
{
if (val < minVal)
minVal = val;
else if (val > maxVal)
maxVal = val;
}
else
{
//first valid value is used to init min and max
minVal = maxVal = val;
minMaxInitialized = true;
}
}
}

if (minMaxInitialized)
{
m_minVal = minVal;
m_maxVal = maxVal;
}
else //particular case: zero valid values
{
m_minVal = m_maxVal = 0;
}
}
}
Loading

0 comments on commit cebe198

Please sign in to comment.