Skip to content

Commit 0c8cbd5

Browse files
authored
E57Simple: Simplify & clarify node types when setting up Data3D (asmaloney#178)
Note that Data3D's "intensity" has been changed to a double so we can allow writing them as doubles. Addresses some of asmaloney#157 and asmaloney#160
1 parent f554fc3 commit 0c8cbd5

9 files changed

+548
-252
lines changed

include/E57Exception.h

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ namespace e57
9595
ErrorBadConfiguration = 49, //!< bad configuration string
9696
ErrorInvarianceViolation = 50, //!< class invariance constraint violation in debug mode
9797

98+
ErrorInvalidNodeType = 51, ///< an invalid note type was passed in Data3D pointFields
99+
98100
/// @deprecated Will be removed in 4.0. Use e57::Success.
99101
E57_SUCCESS [[deprecated( "Will be removed in 4.0. Use Success." )]] = Success,
100102
/// @deprecated Will be removed in 4.0. Use e57::ErrorBadCVHeader.

include/E57SimpleData.h

+76-58
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,6 @@
3737

3838
namespace e57
3939
{
40-
//! Indicates to use FloatNode instead of ScaledIntegerNode in fields that can use both.
41-
constexpr double E57_NOT_SCALED_USE_FLOAT = 0.0;
42-
43-
//! Indicates to use ScaledIntegerNode instead of FloatNode in fields that can use both.
44-
constexpr double E57_NOT_SCALED_USE_INTEGER = -1.0;
45-
4640
//! @cond documentNonPublic The following isn't part of the API, and isn't documented.
4741
class ReaderImpl;
4842
class WriterImpl;
@@ -305,6 +299,15 @@ namespace e57
305299
GroupingByLine groupingByLine; //!< Grouping information by row or column index
306300
};
307301

302+
//! @brief Used to set the type of node in some PointStandardizedFieldsAvailable fields.
303+
enum class NumericalNodeType
304+
{
305+
Integer = 0, ///< Use IntegerNode
306+
ScaledInteger, ///< Use ScaledIntegerNode
307+
Float, ///< Use FloatNode with floats
308+
Double, ///< Use FloatNode with doubles
309+
};
310+
308311
//! @brief Used to interrogate if standardized fields are available
309312
struct E57_DLL PointStandardizedFieldsAvailable
310313
{
@@ -326,14 +329,15 @@ namespace e57
326329
//! E57_FLOAT_MAX or E57_DOUBLE_MAX. If using a ScaledIntegerNode then this needs to be a maximum range value.
327330
double pointRangeMaximum = DOUBLE_MAX;
328331

329-
//! @brief Controls the type of Node used for the PointRecord cartesian and range fields
330-
//! @details The value determines which type of Node to use and whether to use floats or doubles.
331-
//! Value | Node Type
332-
//! -- | --
333-
//! &lt; 0.0 | FloatNode using doubles
334-
//! == 0.0 (e57::E57_NOT_SCALED_USE_FLOAT) | FloatNode using floats (@em default)
335-
//! &gt; 0.0 | ScaledIntegerNode with the value as the scale setting
336-
double pointRangeScaledInteger = E57_NOT_SCALED_USE_FLOAT;
332+
/// @brief Controls the type of Node used for the PointRecord cartesian and range fields
333+
/// @details Accepts NumericalNodeType::ScaledInteger, NumericalNodeType::Float, and
334+
/// NumericalNodeType::Double.
335+
NumericalNodeType pointRangeNodeType = NumericalNodeType::Float;
336+
337+
/// @brief Sets the scale if using scaled integers for point fields
338+
/// @details If pointRangeNodeType == NumericalNodeType::ScaledInteger, it will use this value
339+
/// to scale the numbers and it must be > 0.0.
340+
double pointRangeScale = 0.0;
337341

338342
//! Indicates that the PointRecord angle fields should be configured with this minimum value E57_FLOAT_MIN or
339343
//! E57_DOUBLE_MIN. If using a ScaledIntegerNode then this needs to be a minimum angle value.
@@ -343,14 +347,15 @@ namespace e57
343347
//! E57_DOUBLE_MAX. If using a ScaledIntegerNode then this needs to be a maximum angle value.
344348
double angleMaximum = DOUBLE_MAX;
345349

346-
//! @brief Controls the type of Node used for the PointRecord angle fields
347-
//! @details The value determines which type of Node to use and whether to use floats or doubles.
348-
//! Value | Node Type
349-
//! -- | --
350-
//! &lt; 0.0 | FloatNode using doubles
351-
//! == 0.0 (e57::E57_NOT_SCALED_USE_FLOAT) | FloatNode using floats (@em default)
352-
//! &gt; 0.0 | ScaledIntegerNode with the value as the scale setting
353-
double angleScaledInteger = E57_NOT_SCALED_USE_FLOAT;
350+
/// @brief Controls the type of Node used for the PointRecord angle fields
351+
/// @details Accepts NumericalNodeType::ScaledInteger, NumericalNodeType::Float, and
352+
/// NumericalNodeType::Double.
353+
NumericalNodeType angleNodeType = NumericalNodeType::Float;
354+
355+
/// @brief Sets the scale if using scaled integers for angle fields
356+
/// @details If angleNodeType == NumericalNodeType::ScaledInteger, it will use this value
357+
/// to scale the numbers and it must be > 0.0.
358+
double angleScale = 0.0;
354359

355360
bool rowIndexField = false; //!< Indicates that the PointRecord @a rowIndex field is active
356361

@@ -381,36 +386,46 @@ namespace e57
381386
//! E57_UINT32_MAX, E57_DOUBLE_MAX or E57_DOUBLE_MAX.
382387
double timeMaximum = DOUBLE_MAX;
383388

384-
//! @brief Controls the type of Node used for the PointRecord @a timeStamp fields
385-
//! @details The value determines which type of Node to use and whether to use floats or doubles.
386-
//! Value | Node Type
387-
//! -- | --
388-
//! &lt; 0.0 | IntegerNode
389-
//! == 0.0 (e57::E57_NOT_SCALED_USE_FLOAT) | FloatNode using floats if (::timeMaximum == E57_FLOAT_MAX)
390-
//! == 0.0 | FloatNode using doubles if (::timeMaximum == E57_DOUBLE_MAX) (@em default)
391-
//! &gt; 0.0 | ScaledIntegerNode with the value as the scale setting
392-
double timeScaledInteger = E57_NOT_SCALED_USE_FLOAT;
393-
394-
bool intensityField = false; //!< Indicates that the PointRecord @a intensity field is active
395-
bool isIntensityInvalidField = false; //!< Indicates that the PointRecord @a isIntensityInvalid field is active
396-
397-
//! @brief Controls the type of Node used for the PointRecord @a intensity fields
398-
//! @details The value determines which type of Node to use.
399-
//! Value | Node Type
400-
//! -- | --
401-
//! &lt; 0.0 | IntegerNode
402-
//! == 0.0 (e57::E57_NOT_SCALED_USE_FLOAT) | FloatNode using floats (@em default)
403-
//! &gt; 0.0 | ScaledIntegerNode with the value as the scale setting
404-
double intensityScaledInteger = E57_NOT_SCALED_USE_INTEGER;
405-
406-
bool colorRedField = false; //!< Indicates that the PointRecord @a colorRed field is active
407-
bool colorGreenField = false; //!< Indicates that the PointRecord @a colorGreen field is active
408-
bool colorBlueField = false; //!< Indicates that the PointRecord @a colorBlue field is active
409-
bool isColorInvalidField = false; //!< Indicates that the PointRecord @a isColorInvalid field is active
410-
411-
bool normalXField = false; //!< Indicates that the PointRecord @a nor:normalX field is active
412-
bool normalYField = false; //!< Indicates that the PointRecord @a nor:normalY field is active
413-
bool normalZField = false; //!< Indicates that the PointRecord @a nor:normalZ field is active
389+
/// @brief Controls the type of Node used for the PointRecord time fields
390+
/// @details Accepts NumericalNodeType::Integer, NumericalNodeType::ScaledInteger, NumericalNodeType::Float, and
391+
/// NumericalNodeType::Double.
392+
NumericalNodeType timeNodeType = NumericalNodeType::Float;
393+
394+
/// @brief Sets the scale if using scaled integers for time fields
395+
/// @details If timeNodeType == NumericalNodeType::ScaledInteger, it will use this value
396+
/// to scale the numbers and it must be > 0.0.
397+
double timeScale = 0.0;
398+
399+
/// Indicates that the PointRecord @a intensity field is active
400+
bool intensityField = false;
401+
/// Indicates that the PointRecord @a isIntensityInvalid field is active
402+
bool isIntensityInvalidField = false;
403+
404+
/// @brief Controls the type of Node used for the PointRecord intensity fields
405+
/// @details Accepts NumericalNodeType::Integer, NumericalNodeType::ScaledInteger, NumericalNodeType::Float, and
406+
/// NumericalNodeType::Double.
407+
NumericalNodeType intensityNodeType = NumericalNodeType::Float;
408+
409+
/// @brief Sets the scale if using scaled integers for intensity fields
410+
/// @details If intensityNodeType == NumericalNodeType::ScaledInteger, it will use this value
411+
/// to scale the numbers and it must be > 0.0.
412+
double intensityScale = 0.0;
413+
414+
/// Indicates that the PointRecord @a colorRed field is active
415+
bool colorRedField = false;
416+
/// Indicates that the PointRecord @a colorGreen field is active
417+
bool colorGreenField = false;
418+
/// Indicates that the PointRecord @a colorBlue field is active
419+
bool colorBlueField = false;
420+
/// Indicates that the PointRecord @a isColorInvalid field is active
421+
bool isColorInvalidField = false;
422+
423+
/// Indicates that the PointRecord @a nor:normalX field is active
424+
bool normalXField = false;
425+
/// Indicates that the PointRecord @a nor:normalY field is active
426+
bool normalYField = false;
427+
/// Indicates that the PointRecord @a nor:normalZ field is active
428+
bool normalZField = false;
414429
};
415430

416431
//! @brief Stores the top-level information for a single lidar scan
@@ -478,12 +493,15 @@ namespace e57
478493
{
479494
static_assert( std::is_floating_point<COORDTYPE>::value, "Floating point type required." );
480495

481-
//! @brief Default constructor does not manage any memory or adjust min/max for floats.
496+
//! @brief Default constructor does not manage any memory, adjust min/max for floats, or validate data.
482497
Data3DPointsData_t() = default;
483498

484-
//! @brief Constructor which allocates buffers for all valid fields in the given Data3D header
485-
//! This constructor will also adjust the min/max fields in the data3D pointFields if we are using floats.
486-
//! @param [in] data3D Completed header which indicates the fields we are using
499+
/// @brief Constructor which allocates buffers for all valid fields in the given Data3D header.
500+
/// @details This constructor will also adjust the min/max fields in the data3D pointFields if we are using
501+
/// floats, and run some validation on the Data3D.
502+
/// @param [in] data3D Completed header which indicates the fields we are using
503+
/// @throw ::ErrorValueOutOfBounds
504+
/// @throw ::ErrorInvalidNodeType
487505
explicit Data3DPointsData_t( e57::Data3D &data3D );
488506

489507
//! @brief Destructor will delete any memory allocated using the Data3DPointsData_t( const e57::Data3D & )
@@ -502,8 +520,8 @@ namespace e57
502520
//! Value = 0 if the point is considered valid, 1 otherwise
503521
int8_t *cartesianInvalidState = nullptr;
504522

505-
//! Pointer to a buffer with the Point response intensity. Unit is unspecified.
506-
float *intensity = nullptr;
523+
/// @brief Pointer to a buffer with the Point response intensity. Unit is unspecified.
524+
double *intensity = nullptr;
507525

508526
//! Value = 0 if the intensity is considered valid, 1 otherwise
509527
int8_t *isIntensityInvalid = nullptr;

src/E57Exception.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,9 @@ namespace e57
447447
case ErrorInvarianceViolation:
448448
return "class invariance constraint violation in debug mode "
449449
"(ErrorInvarianceViolation)";
450+
case ErrorInvalidNodeType:
451+
return "an invalid node type was passed in Data3D pointFields";
452+
450453
default:
451454
return "unknown error (" + std::to_string( ecode ) + ")";
452455
}

src/E57SimpleData.cpp

+39-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@
1414

1515
namespace e57
1616
{
17+
/// Validates a Data3D and throws on error.
18+
void _validateData3D( const Data3D &inData3D )
19+
{
20+
if ( inData3D.pointCount < 1 )
21+
{
22+
throw E57_EXCEPTION2( ErrorValueOutOfBounds, "pointCount=" + toString( inData3D.pointCount ) + " minimum=1" );
23+
}
24+
25+
if ( inData3D.pointFields.pointRangeNodeType == NumericalNodeType::Integer )
26+
{
27+
throw E57_EXCEPTION2( ErrorInvalidNodeType, "pointRangeNodeType cannot be Integer" );
28+
}
29+
30+
if ( inData3D.pointFields.angleNodeType == NumericalNodeType::Integer )
31+
{
32+
throw E57_EXCEPTION2( ErrorInvalidNodeType, "angleNodeType cannot be Integer" );
33+
}
34+
}
35+
1736
// To avoid exposing M_PI, we define the constructor here.
1837
SphericalBounds::SphericalBounds()
1938
{
@@ -31,15 +50,12 @@ namespace e57
3150
template <typename COORDTYPE>
3251
Data3DPointsData_t<COORDTYPE>::Data3DPointsData_t( Data3D &data3D ) : _selfAllocated( true )
3352
{
34-
const auto cPointCount = data3D.pointCount;
53+
_validateData3D( data3D );
3554

36-
if ( cPointCount < 1 )
37-
{
38-
throw E57_EXCEPTION2( ErrorValueOutOfBounds, "pointCount=" + toString( cPointCount ) + " minimum=1" );
39-
}
55+
constexpr bool cIsFloat = std::is_same<COORDTYPE, float>::value;
4056

4157
// We need to adjust min/max for floats.
42-
if ( std::is_same<COORDTYPE, float>::value )
58+
if ( cIsFloat )
4359
{
4460
data3D.pointFields.pointRangeMinimum = FLOAT_MIN;
4561
data3D.pointFields.pointRangeMaximum = FLOAT_MAX;
@@ -49,6 +65,22 @@ namespace e57
4965
data3D.pointFields.timeMaximum = FLOAT_MAX;
5066
}
5167

68+
// IF point range node type is not ScaledInteger
69+
// THEN make sure the proper floating point type is set
70+
if ( data3D.pointFields.pointRangeNodeType != NumericalNodeType::ScaledInteger )
71+
{
72+
data3D.pointFields.pointRangeNodeType = ( cIsFloat ? NumericalNodeType::Float : NumericalNodeType::Double );
73+
}
74+
75+
// IF angle node type is not ScaledInteger
76+
// THEN make sure the proper floating point type is set
77+
if ( data3D.pointFields.angleNodeType != NumericalNodeType::ScaledInteger )
78+
{
79+
data3D.pointFields.angleNodeType = ( cIsFloat ? NumericalNodeType::Float : NumericalNodeType::Double );
80+
}
81+
82+
const auto cPointCount = data3D.pointCount;
83+
5284
if ( data3D.pointFields.cartesianXField )
5385
{
5486
cartesianX = new COORDTYPE[cPointCount];
@@ -71,7 +103,7 @@ namespace e57
71103

72104
if ( data3D.pointFields.intensityField )
73105
{
74-
intensity = new float[cPointCount];
106+
intensity = new double[cPointCount];
75107
}
76108

77109
if ( data3D.pointFields.isIntensityInvalidField )

src/E57SimpleWriter.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace
5454
auto pointRangeMinimum = cMax;
5555
auto pointRangeMaximum = cMin;
5656

57-
const bool writePointRange = ( pointFields.pointRangeScaledInteger != 0.0 ) &&
57+
const bool writePointRange = ( pointFields.pointRangeNodeType == e57::NumericalNodeType::ScaledInteger ) &&
5858
( pointFields.pointRangeMinimum == cMin ) &&
5959
( pointFields.pointRangeMaximum == cMax );
6060

@@ -64,14 +64,14 @@ namespace
6464
auto angleMinimum = cMax;
6565
auto angleMaximum = cMin;
6666

67-
const bool writeAngle = ( pointFields.angleScaledInteger != 0.0 ) && ( pointFields.angleMinimum == cMin ) &&
68-
( pointFields.angleMaximum == cMax );
67+
const bool writeAngle = ( pointFields.angleNodeType == e57::NumericalNodeType::ScaledInteger ) &&
68+
( pointFields.angleMinimum == cMin ) && ( pointFields.angleMaximum == cMax );
6969

7070
// IF we are using intesity
7171
// AND we haven't set either min or max
7272
// THEN calculate them from the points
73-
float intensityMinimum = std::numeric_limits<float>::max();
74-
float intensityMaximum = std::numeric_limits<float>::lowest();
73+
double intensityMinimum = std::numeric_limits<double>::max();
74+
double intensityMaximum = std::numeric_limits<double>::lowest();
7575

7676
const bool writeIntensity =
7777
pointFields.intensityField && ( ioData3DHeader.intensityLimits == e57::IntensityLimits{} );
@@ -82,7 +82,8 @@ namespace
8282
double timeMinimum = std::numeric_limits<double>::max();
8383
double timeMaximum = std::numeric_limits<double>::lowest();
8484

85-
const bool writeTimeStamp = pointFields.timeStampField && ( pointFields.timeScaledInteger != 0.0 ) &&
85+
const bool writeTimeStamp = pointFields.timeStampField &&
86+
( pointFields.timeNodeType == e57::NumericalNodeType::ScaledInteger ) &&
8687
( pointFields.timeMinimum == cMin ) && ( pointFields.timeMaximum == cMax );
8788

8889
// Now run through the points and set the things we need to

0 commit comments

Comments
 (0)