From fbd07ebc3aa2d22038a6d95b117cd743e4725c31 Mon Sep 17 00:00:00 2001 From: Andy Maloney Date: Mon, 31 Oct 2022 22:25:00 -0400 Subject: [PATCH] E57SimpleReader: Ensure scaled integer fields are set as best we can when reading See #157 Related to #126 --- src/ReaderImpl.cpp | 25 ++++-- src/WriterImpl.cpp | 4 +- test/src/test_SimpleData.cpp | 159 +++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 11 deletions(-) diff --git a/src/ReaderImpl.cpp b/src/ReaderImpl.cpp index c2461a7..3a55a31 100644 --- a/src/ReaderImpl.cpp +++ b/src/ReaderImpl.cpp @@ -834,7 +834,6 @@ namespace e57 data3DHeader.pointFields.cartesianZField = proto.isDefined( "cartesianZ" ); data3DHeader.pointFields.cartesianInvalidStateField = proto.isDefined( "cartesianInvalidState" ); - data3DHeader.pointFields.pointRangeScaledInteger = E57_NOT_SCALED_USE_FLOAT; // FloatNode data3DHeader.pointFields.pointRangeMinimum = 0.0; data3DHeader.pointFields.pointRangeMaximum = 0.0; @@ -860,7 +859,11 @@ namespace e57 data3DHeader.pointFields.pointRangeMinimum = floatCartesianX.minimum(); data3DHeader.pointFields.pointRangeMaximum = floatCartesianX.maximum(); - data3DHeader.pointFields.pointRangeScaledInteger = E57_NOT_SCALED_USE_FLOAT; + + if ( floatCartesianX.precision() == E57_DOUBLE ) + { + data3DHeader.pointFields.pointRangeScaledInteger = -1.0; + } } } else if ( proto.isDefined( "sphericalRange" ) ) @@ -885,7 +888,11 @@ namespace e57 data3DHeader.pointFields.pointRangeMinimum = floatSphericalRange.minimum(); data3DHeader.pointFields.pointRangeMaximum = floatSphericalRange.maximum(); - data3DHeader.pointFields.pointRangeScaledInteger = E57_NOT_SCALED_USE_FLOAT; + + if ( floatSphericalRange.precision() == E57_DOUBLE ) + { + data3DHeader.pointFields.pointRangeScaledInteger = -1.0; + } } } @@ -894,7 +901,6 @@ namespace e57 data3DHeader.pointFields.sphericalElevationField = proto.isDefined( "sphericalElevation" ); data3DHeader.pointFields.sphericalInvalidStateField = proto.isDefined( "sphericalInvalidState" ); - data3DHeader.pointFields.angleScaledInteger = E57_NOT_SCALED_USE_FLOAT; // FloatNode data3DHeader.pointFields.angleMinimum = 0.0; data3DHeader.pointFields.angleMaximum = 0.0; @@ -920,7 +926,11 @@ namespace e57 data3DHeader.pointFields.angleMinimum = floatSphericalAzimuth.minimum(); data3DHeader.pointFields.angleMaximum = floatSphericalAzimuth.maximum(); - data3DHeader.pointFields.angleScaledInteger = E57_NOT_SCALED_USE_FLOAT; + + if ( floatSphericalAzimuth.precision() == E57_DOUBLE ) + { + data3DHeader.pointFields.angleScaledInteger = -1.0; + } } } @@ -955,7 +965,6 @@ namespace e57 data3DHeader.pointFields.isTimeStampInvalidField = proto.isDefined( "isTimeStampInvalid" ); data3DHeader.pointFields.timeMaximum = 0.0; data3DHeader.pointFields.timeMinimum = 0.0; - data3DHeader.pointFields.timeScaledInteger = E57_NOT_SCALED_USE_FLOAT; if ( proto.isDefined( "timeStamp" ) ) { @@ -967,7 +976,7 @@ namespace e57 data3DHeader.pointFields.timeMaximum = static_cast( integerTimeStamp.maximum() ); data3DHeader.pointFields.timeMinimum = static_cast( integerTimeStamp.minimum() ); - data3DHeader.pointFields.timeScaledInteger = E57_NOT_SCALED_USE_FLOAT; + data3DHeader.pointFields.timeScaledInteger = -1.0; } else if ( timeStampProto.type() == E57_SCALED_INTEGER ) { @@ -988,7 +997,6 @@ namespace e57 data3DHeader.pointFields.timeMinimum = floatTimeStamp.minimum(); data3DHeader.pointFields.timeMaximum = floatTimeStamp.maximum(); - data3DHeader.pointFields.timeScaledInteger = E57_NOT_SCALED_USE_FLOAT; } } @@ -996,7 +1004,6 @@ namespace e57 data3DHeader.pointFields.isIntensityInvalidField = proto.isDefined( "isIntensityInvalid" ); data3DHeader.intensityLimits.intensityMinimum = 0.0; data3DHeader.intensityLimits.intensityMaximum = 0.0; - data3DHeader.pointFields.intensityScaledInteger = E57_NOT_SCALED_USE_FLOAT; if ( scan.isDefined( "intensityLimits" ) ) { diff --git a/src/WriterImpl.cpp b/src/WriterImpl.cpp index 5148c03..e833efa 100644 --- a/src/WriterImpl.cpp +++ b/src/WriterImpl.cpp @@ -526,7 +526,7 @@ namespace e57 const double intensityMin = data3DHeader.intensityLimits.intensityMinimum; const double intensityMax = data3DHeader.intensityLimits.intensityMaximum; - if ( data3DHeader.pointFields.intensityScaledInteger > 0.0 ) + if ( data3DHeader.pointFields.intensityScaledInteger > E57_NOT_SCALED_USE_FLOAT ) { const double scale = data3DHeader.pointFields.intensityScaledInteger; const double offset = 0.0; @@ -539,7 +539,7 @@ namespace e57 intbox.set( "intensityMaximum", ScaledIntegerNode( imf_, rawIntegerMaximum, rawIntegerMinimum, rawIntegerMaximum, scale, offset ) ); } - else if ( data3DHeader.pointFields.intensityScaledInteger == 0.0 ) + else if ( data3DHeader.pointFields.intensityScaledInteger == E57_NOT_SCALED_USE_FLOAT ) { intbox.set( "intensityMinimum", FloatNode( imf_, intensityMin ) ); intbox.set( "intensityMaximum", FloatNode( imf_, intensityMax ) ); diff --git a/test/src/test_SimpleData.cpp b/test/src/test_SimpleData.cpp index c852a0f..fd0de24 100644 --- a/test/src/test_SimpleData.cpp +++ b/test/src/test_SimpleData.cpp @@ -4,8 +4,11 @@ #include "gtest/gtest.h" #include "E57SimpleData.h" +#include "E57SimpleReader.h" +#include "E57SimpleWriter.h" #include "Helpers.h" +#include "TestData.h" TEST( SimpleDataHeader, InvalidPointSize ) { @@ -61,3 +64,159 @@ TEST( SimpleDataHeader, HeaderMinMaxDouble ) EXPECT_EQ( dataHeader.pointFields.timeMinimum, e57::E57_DOUBLE_MIN ); EXPECT_EQ( dataHeader.pointFields.timeMaximum, e57::E57_DOUBLE_MAX ); } + +// Checks that the Data3D header and the the cartesianX FloatNode data are the same when read, written, and read again. +// https://github.com/asmaloney/libE57Format/issues/126 +TEST( SimpleData, ReadWrite ) +{ + e57::Reader *originalReader = nullptr; + e57::E57Root originalFileHeader; + e57::Data3D originalData3DHeader; + e57::Data3DPointsData_d *originalPointsData = nullptr; + + // 1. Read in original file + { + E57_ASSERT_NO_THROW( originalReader = new e57::Reader( TestData::Path() + "/self/ColouredCubeDouble.e57", {} ) ); + + ASSERT_TRUE( originalReader->GetE57Root( originalFileHeader ) ); + ASSERT_TRUE( originalReader->ReadData3D( 0, originalData3DHeader ) ); + + const uint64_t cNumPoints = originalData3DHeader.pointCount; + + originalPointsData = new e57::Data3DPointsData_d( originalData3DHeader ); + + auto vectorReader = originalReader->SetUpData3DPointsData( 0, cNumPoints, *originalPointsData ); + + const uint64_t cNumRead = vectorReader.read(); + + vectorReader.close(); + + EXPECT_EQ( cNumRead, cNumPoints ); + } + + // 2. Write it out again using the Data3D header from the reader + { + e57::WriterOptions options; + options.guid = originalFileHeader.guid; + + e57::Writer *writer = nullptr; + E57_ASSERT_NO_THROW( writer = new e57::Writer( "./ColouredCubeDoubleCopy.e57", options ) ); + + const int64_t cScanIndex1 = writer->NewData3D( originalData3DHeader ); + const uint16_t cNumPoints = originalData3DHeader.pointCount; + + auto dataWriter = writer->SetUpData3DPointsData( cScanIndex1, cNumPoints, *originalPointsData ); + + dataWriter.write( cNumPoints ); + dataWriter.close(); + + delete writer; + } + + e57::Reader *copyReader = nullptr; + e57::E57Root copyFileHeader; + e57::Data3D copyData3DHeader; + + // 3. Read in what we just wrote + { + E57_ASSERT_NO_THROW( copyReader = new e57::Reader( "./ColouredCubeDoubleCopy.e57", {} ) ); + + ASSERT_TRUE( copyReader->GetE57Root( copyFileHeader ) ); + ASSERT_TRUE( copyReader->ReadData3D( 0, copyData3DHeader ) ); + + const uint64_t cNumPoints = copyData3DHeader.pointCount; + + e57::Data3DPointsData_d copyPointsData( copyData3DHeader ); + + auto vectorReader = copyReader->SetUpData3DPointsData( 0, cNumPoints, copyPointsData ); + + const uint64_t cNumRead = vectorReader.read(); + + vectorReader.close(); + + EXPECT_EQ( cNumRead, cNumPoints ); + } + + // 4. Compare the header data + EXPECT_EQ( originalData3DHeader.name, copyData3DHeader.name ); + EXPECT_EQ( originalData3DHeader.guid, copyData3DHeader.guid ); + + EXPECT_EQ( originalData3DHeader.description, copyData3DHeader.description ); + + EXPECT_EQ( originalData3DHeader.colorLimits, copyData3DHeader.colorLimits ); + + EXPECT_EQ( originalData3DHeader.pointFields.cartesianXField, copyData3DHeader.pointFields.cartesianXField ); + EXPECT_EQ( originalData3DHeader.pointFields.cartesianYField, copyData3DHeader.pointFields.cartesianYField ); + EXPECT_EQ( originalData3DHeader.pointFields.cartesianZField, copyData3DHeader.pointFields.cartesianZField ); + EXPECT_EQ( originalData3DHeader.pointFields.cartesianInvalidStateField, + copyData3DHeader.pointFields.cartesianInvalidStateField ); + + EXPECT_EQ( originalData3DHeader.pointFields.sphericalRangeField, copyData3DHeader.pointFields.sphericalRangeField ); + EXPECT_EQ( originalData3DHeader.pointFields.sphericalAzimuthField, + copyData3DHeader.pointFields.sphericalAzimuthField ); + EXPECT_EQ( originalData3DHeader.pointFields.sphericalElevationField, + copyData3DHeader.pointFields.sphericalElevationField ); + EXPECT_EQ( originalData3DHeader.pointFields.sphericalInvalidStateField, + copyData3DHeader.pointFields.sphericalInvalidStateField ); + + EXPECT_EQ( originalData3DHeader.pointFields.pointRangeMinimum, copyData3DHeader.pointFields.pointRangeMinimum ); + EXPECT_EQ( originalData3DHeader.pointFields.pointRangeMaximum, copyData3DHeader.pointFields.pointRangeMaximum ); + EXPECT_EQ( originalData3DHeader.pointFields.pointRangeScaledInteger, + copyData3DHeader.pointFields.pointRangeScaledInteger ); + + EXPECT_EQ( originalData3DHeader.pointFields.angleMinimum, copyData3DHeader.pointFields.angleMinimum ); + EXPECT_EQ( originalData3DHeader.pointFields.angleMaximum, copyData3DHeader.pointFields.angleMaximum ); + EXPECT_EQ( originalData3DHeader.pointFields.angleScaledInteger, copyData3DHeader.pointFields.angleScaledInteger ); + + EXPECT_EQ( originalData3DHeader.pointFields.rowIndexField, copyData3DHeader.pointFields.rowIndexField ); + EXPECT_EQ( originalData3DHeader.pointFields.rowIndexMaximum, copyData3DHeader.pointFields.rowIndexMaximum ); + + EXPECT_EQ( originalData3DHeader.pointFields.columnIndexField, copyData3DHeader.pointFields.columnIndexField ); + EXPECT_EQ( originalData3DHeader.pointFields.columnIndexMaximum, copyData3DHeader.pointFields.columnIndexMaximum ); + + EXPECT_EQ( originalData3DHeader.pointFields.returnIndexField, copyData3DHeader.pointFields.returnIndexField ); + EXPECT_EQ( originalData3DHeader.pointFields.returnCountField, copyData3DHeader.pointFields.returnCountField ); + EXPECT_EQ( originalData3DHeader.pointFields.returnMaximum, copyData3DHeader.pointFields.returnMaximum ); + + EXPECT_EQ( originalData3DHeader.pointFields.timeStampField, copyData3DHeader.pointFields.timeStampField ); + EXPECT_EQ( originalData3DHeader.pointFields.isTimeStampInvalidField, + copyData3DHeader.pointFields.isTimeStampInvalidField ); + EXPECT_EQ( originalData3DHeader.pointFields.timeMinimum, copyData3DHeader.pointFields.timeMinimum ); + EXPECT_EQ( originalData3DHeader.pointFields.timeMaximum, copyData3DHeader.pointFields.timeMaximum ); + + EXPECT_EQ( originalData3DHeader.pointFields.intensityField, copyData3DHeader.pointFields.intensityField ); + EXPECT_EQ( originalData3DHeader.pointFields.isIntensityInvalidField, + copyData3DHeader.pointFields.isIntensityInvalidField ); + EXPECT_EQ( originalData3DHeader.pointFields.intensityScaledInteger, + copyData3DHeader.pointFields.intensityScaledInteger ); + + EXPECT_EQ( originalData3DHeader.pointFields.colorRedField, copyData3DHeader.pointFields.colorRedField ); + EXPECT_EQ( originalData3DHeader.pointFields.colorGreenField, copyData3DHeader.pointFields.colorGreenField ); + EXPECT_EQ( originalData3DHeader.pointFields.colorBlueField, copyData3DHeader.pointFields.colorBlueField ); + EXPECT_EQ( originalData3DHeader.pointFields.isColorInvalidField, copyData3DHeader.pointFields.isColorInvalidField ); + + EXPECT_EQ( originalData3DHeader.pointFields.normalXField, copyData3DHeader.pointFields.normalXField ); + EXPECT_EQ( originalData3DHeader.pointFields.normalYField, copyData3DHeader.pointFields.normalYField ); + EXPECT_EQ( originalData3DHeader.pointFields.normalZField, copyData3DHeader.pointFields.normalZField ); + + EXPECT_EQ( originalData3DHeader.pointCount, copyData3DHeader.pointCount ); + + // 5. Compare the cartesianX FloatNode data + e57::CompressedVectorNode originalPointsStructureNode( originalReader->GetRawData3D().get( "/data3D/0/points" ) ); + e57::CompressedVectorNode copyPointsStructureNode( copyReader->GetRawData3D().get( "/data3D/0/points" ) ); + + const e57::StructureNode originalProto( originalPointsStructureNode.prototype() ); + const e57::StructureNode copyProto( copyPointsStructureNode.prototype() ); + + e57::FloatNode originalCartesianXNode( originalProto.get( "cartesianX" ) ); + e57::FloatNode copyCartesianXNode( copyProto.get( "cartesianX" ) ); + + EXPECT_EQ( originalCartesianXNode.precision(), copyCartesianXNode.precision() ); + EXPECT_EQ( originalCartesianXNode.minimum(), copyCartesianXNode.minimum() ); + EXPECT_EQ( originalCartesianXNode.maximum(), copyCartesianXNode.maximum() ); + + // 6. Cleanup + delete originalPointsData; + delete originalReader; + delete copyReader; +}