From 0612be9c5bbbbdc49854ae2c1438d0d88c6de525 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 12:27:01 -0600 Subject: [PATCH] Move VTKDataSetReaderBase code to vtkm_io Most of this code is not templated methods. Rather, it implements over several types to call templated functions, which creates quite a bit of code. Rather than have all code using a reader recompile the code, just compile it once and put it in a library. --- benchmarking/CMakeLists.txt | 2 +- examples/mesh_quality/CMakeLists.txt | 2 +- examples/particle_advection/CMakeLists.txt | 2 +- examples/redistribute_points/CMakeLists.txt | 2 +- examples/temporal_advection/CMakeLists.txt | 2 +- vtkm/filter/testing/CMakeLists.txt | 2 +- vtkm/io/CMakeLists.txt | 1 + vtkm/io/VTKDataSetReaderBase.cxx | 826 +++++++++++++++++++ vtkm/io/VTKDataSetReaderBase.h | 869 ++------------------ vtkm/io/VTKPolyDataReader.h | 1 + vtkm/io/testing/CMakeLists.txt | 16 +- 11 files changed, 912 insertions(+), 813 deletions(-) create mode 100644 vtkm/io/VTKDataSetReaderBase.cxx diff --git a/benchmarking/CMakeLists.txt b/benchmarking/CMakeLists.txt index a504d395e..bbfbd8c2a 100644 --- a/benchmarking/CMakeLists.txt +++ b/benchmarking/CMakeLists.txt @@ -52,7 +52,7 @@ set(VTKm_BENCHS_RANGE_UPPER_BOUNDARY 134217728 CACHE STRING "Biggest sample for mark_as_advanced(VTKm_BENCHS_RANGE_LOWER_BOUNDARY VTKm_BENCHS_RANGE_UPPER_BOUNDARY) foreach (benchmark ${benchmarks}) - add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter) + add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter vtkm_io) endforeach () target_compile_definitions(BenchmarkDeviceAdapter PUBLIC VTKm_BENCHS_RANGE_LOWER_BOUNDARY=${VTKm_BENCHS_RANGE_LOWER_BOUNDARY}) diff --git a/examples/mesh_quality/CMakeLists.txt b/examples/mesh_quality/CMakeLists.txt index 3987f3da2..6308b37b6 100644 --- a/examples/mesh_quality/CMakeLists.txt +++ b/examples/mesh_quality/CMakeLists.txt @@ -26,7 +26,7 @@ project(MeshQuality CXX) find_package(VTKm REQUIRED QUIET) add_executable(MeshQuality MeshQuality.cxx) -target_link_libraries(MeshQuality PRIVATE vtkm_filter) +target_link_libraries(MeshQuality PRIVATE vtkm_filter vtkm_io) if(TARGET vtkm::tbb) target_compile_definitions(MeshQuality PRIVATE BUILDING_TBB_VERSION) diff --git a/examples/particle_advection/CMakeLists.txt b/examples/particle_advection/CMakeLists.txt index 5c0b9a006..7866c56c6 100644 --- a/examples/particle_advection/CMakeLists.txt +++ b/examples/particle_advection/CMakeLists.txt @@ -14,7 +14,7 @@ project(ParticleAdvection CXX) find_package(VTKm REQUIRED QUIET) add_executable(Particle_Advection ParticleAdvection.cxx) -target_link_libraries(Particle_Advection PRIVATE vtkm_filter) +target_link_libraries(Particle_Advection PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Particle_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES ParticleAdvection.cxx) diff --git a/examples/redistribute_points/CMakeLists.txt b/examples/redistribute_points/CMakeLists.txt index 8a2ff67e9..59948d79f 100644 --- a/examples/redistribute_points/CMakeLists.txt +++ b/examples/redistribute_points/CMakeLists.txt @@ -13,7 +13,7 @@ project(RedistributePoints CXX) #Find the VTK-m package find_package(VTKm REQUIRED QUIET) add_executable(RedistributePoints RedistributePoints.cxx RedistributePoints.h) -target_link_libraries(RedistributePoints PRIVATE vtkm_filter) +target_link_libraries(RedistributePoints PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(RedistributePoints DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES RedistributePoints.cxx) diff --git a/examples/temporal_advection/CMakeLists.txt b/examples/temporal_advection/CMakeLists.txt index 027a24f7a..eeed9ff44 100644 --- a/examples/temporal_advection/CMakeLists.txt +++ b/examples/temporal_advection/CMakeLists.txt @@ -19,4 +19,4 @@ add_executable(Temporal_Advection TemporalAdvection.cxx) vtkm_add_target_information(Temporal_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES TemporalAdvection.cxx) -target_link_libraries(Temporal_Advection PRIVATE vtkm_filter) +target_link_libraries(Temporal_Advection PRIVATE vtkm_filter vtkm_io) diff --git a/vtkm/filter/testing/CMakeLists.txt b/vtkm/filter/testing/CMakeLists.txt index 72a572eeb..ab23e36be 100644 --- a/vtkm/filter/testing/CMakeLists.txt +++ b/vtkm/filter/testing/CMakeLists.txt @@ -69,7 +69,7 @@ set(unit_tests vtkm_unit_tests( SOURCES ${unit_tests} - LIBRARIES vtkm_filter vtkm_source + LIBRARIES vtkm_filter vtkm_source vtkm_io ALL_BACKENDS USE_VTKM_JOB_POOL ) diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 7d8bc7660..32d9d9ae5 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -35,6 +35,7 @@ set(template_sources set(sources DecodePNG.cxx EncodePNG.cxx + VTKDataSetReaderBase.cxx ) vtkm_declare_headers( diff --git a/vtkm/io/VTKDataSetReaderBase.cxx b/vtkm/io/VTKDataSetReaderBase.cxx new file mode 100644 index 000000000..dcbace6e5 --- /dev/null +++ b/vtkm/io/VTKDataSetReaderBase.cxx @@ -0,0 +1,826 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace +{ + +inline void PrintVTKDataFileSummary(const vtkm::io::internal::VTKDataSetFile& df, std::ostream& out) +{ + out << "\tFile: " << df.FileName << std::endl; + out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; + out << "\tTitle: " << df.Title << std::endl; + out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; + out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) + << std::endl; +} + +// Since Fields and DataSets store data in the default VariantArrayHandle, convert +// the data to the closest type supported by default. The following will +// need to be updated if VariantArrayHandle or TypeListCommon changes. +template +struct ClosestCommonType +{ + using Type = T; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; + +template +struct ClosestFloat +{ + using Type = T; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; + +template +vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) +{ + switch (vtkm::VecTraits::NUM_COMPONENTS) + { + case 1: + { + using CommonType = typename ClosestCommonType::Type; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() + << " is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + portal.Set(i, static_cast(vec[static_cast(i)])); + } + + return vtkm::cont::VariantArrayHandle(output); + } + case 2: + case 3: + case 9: + { + constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; + + using InComponentType = typename vtkm::VecTraits::ComponentType; + using OutComponentType = typename ClosestFloat::Type; + using CommonType = vtkm::Vec; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() << "[" + << vtkm::VecTraits::GetNumberOfComponents(T()) + << "] " + << "is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "[" + << numComps + << "]."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + CommonType outval = CommonType(); + for (vtkm::IdComponent j = 0; j < numComps; ++j) + { + outval[j] = static_cast( + vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); + } + portal.Set(i, outval); + } + + return vtkm::cont::VariantArrayHandle(output); + } + default: + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); + return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); + } + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +VTKDataSetReaderBase::VTKDataSetReaderBase(const char* fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::VTKDataSetReaderBase(const std::string& fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::~VTKDataSetReaderBase() +{ +} + +const vtkm::cont::DataSet& VTKDataSetReaderBase::ReadDataSet() +{ + if (!this->Loaded) + { + try + { + this->OpenFile(); + this->ReadHeader(); + this->Read(); + this->CloseFile(); + this->Loaded = true; + } + catch (std::ifstream::failure& e) + { + std::string message("IO Error: "); + throw vtkm::io::ErrorIO(message + e.what()); + } + } + + return this->DataSet; +} + +void VTKDataSetReaderBase::PrintSummary(std::ostream& out) const +{ + out << "VTKDataSetReader" << std::endl; + PrintVTKDataFileSummary(*this->DataFile.get(), out); + this->DataSet.PrintSummary(out); +} + +void VTKDataSetReaderBase::ReadPoints() +{ + std::string dataType; + std::size_t numPoints; + this->DataFile->Stream >> numPoints >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle points = + this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + + this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); +} + +void VTKDataSetReaderBase::ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices) +{ + vtkm::Id numCells, numInts; + this->DataFile->Stream >> numCells >> numInts >> std::ws; + + connectivity.Allocate(numInts - numCells); + numIndices.Allocate(numCells); + + std::vector buffer(static_cast(numInts)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto connectivityPortal = connectivity.WritePortal(); + auto numIndicesPortal = numIndices.WritePortal(); + for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) + { + vtkm::IdComponent numInds = static_cast(*buffp++); + numIndicesPortal.Set(i, numInds); + for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) + { + connectivityPortal.Set(connInd, static_cast(*buffp++)); + } + } +} + +void VTKDataSetReaderBase::ReadShapes(vtkm::cont::ArrayHandle& shapes) +{ + std::string tag; + vtkm::Id numCells; + this->DataFile->Stream >> tag >> numCells >> std::ws; + internal::parseAssert(tag == "CELL_TYPES"); + + shapes.Allocate(numCells); + std::vector buffer(static_cast(numCells)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto shapesPortal = shapes.WritePortal(); + for (vtkm::Id i = 0; i < numCells; ++i) + { + shapesPortal.Set(i, static_cast(*buffp++)); + } +} + +void VTKDataSetReaderBase::ReadAttributes() +{ + if (this->DataFile->Stream.eof()) + { + return; + } + + vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; + std::size_t size; + + std::string tag; + this->DataFile->Stream >> tag; + while (!this->DataFile->Stream.eof()) + { + if (tag == "POINT_DATA") + { + association = vtkm::cont::Field::Association::POINTS; + } + else if (tag == "CELL_DATA") + { + association = vtkm::cont::Field::Association::CELL_SET; + } + else + { + internal::parseAssert(false); + } + + this->DataFile->Stream >> size; + while (!this->DataFile->Stream.eof()) + { + this->DataFile->Stream >> tag; + if (tag == "SCALARS") + { + this->ReadScalars(association, size); + } + else if (tag == "COLOR_SCALARS") + { + this->ReadColorScalars(association, size); + } + else if (tag == "LOOKUP_TABLE") + { + this->ReadLookupTable(); + } + else if (tag == "VECTORS" || tag == "NORMALS") + { + this->ReadVectors(association, size); + } + else if (tag == "TEXTURE_COORDINATES") + { + this->ReadTextureCoordinates(association, size); + } + else if (tag == "TENSORS") + { + this->ReadTensors(association, size); + } + else if (tag == "FIELD") + { + this->ReadFields(association, size); + } + else + { + break; + } + } + } +} + +void VTKDataSetReaderBase::CloseFile() +{ + this->DataFile->Stream.close(); +} + +void VTKDataSetReaderBase::OpenFile() +{ + this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try + { + this->DataFile->Stream.open(this->DataFile->FileName.c_str(), + std::ios_base::in | std::ios_base::binary); + } + catch (std::ifstream::failure&) + { + std::string message("could not open file \"" + this->DataFile->FileName + "\""); + throw vtkm::io::ErrorIO(message); + } +} + +void VTKDataSetReaderBase::ReadHeader() +{ + char vstring[] = "# vtk DataFile Version"; + const std::size_t vlen = sizeof(vstring); + + // Read version line + char vbuf[vlen]; + this->DataFile->Stream.read(vbuf, vlen - 1); + vbuf[vlen - 1] = '\0'; + if (std::string(vbuf) != std::string(vstring)) + { + throw vtkm::io::ErrorIO("Incorrect file format."); + } + + char dot; + this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; + // skip rest of the line + std::string skip; + std::getline(this->DataFile->Stream, skip); + + if ((this->DataFile->Version[0] > 4) || + (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Reader may not correctly read >v4.2 files. Reading version " + << this->DataFile->Version[0] + << "." + << this->DataFile->Version[1] + << ".\n"); + } + + // Read title line + std::getline(this->DataFile->Stream, this->DataFile->Title); + + // Read format line + this->DataFile->IsBinary = false; + std::string format; + this->DataFile->Stream >> format >> std::ws; + if (format == "BINARY") + { + this->DataFile->IsBinary = true; + } + else if (format != "ASCII") + { + throw vtkm::io::ErrorIO("Unsupported Format."); + } + + // Read structure line + std::string tag, structStr; + this->DataFile->Stream >> tag >> structStr >> std::ws; + internal::parseAssert(tag == "DATASET"); + + this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); + if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) + { + throw vtkm::io::ErrorIO("Unsupported DataSet type."); + } +} + + +void VTKDataSetReaderBase::AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data) +{ + if (data.GetNumberOfValues() > 0) + { + switch (association) + { + case vtkm::cont::Field::Association::POINTS: + case vtkm::cont::Field::Association::WHOLE_MESH: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + case vtkm::cont::Field::Association::CELL_SET: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + default: + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Not recording field '" << name << "' because it has an unknown association"); + break; + } + } +} + +void VTKDataSetReaderBase::ReadScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName, dataType, lookupTableName; + vtkm::IdComponent numComponents = 1; + this->DataFile->Stream >> dataName >> dataType; + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "LOOKUP_TABLE") + { + try + { + numComponents = std::stoi(tag); + } + catch (std::invalid_argument&) + { + internal::parseAssert(false); + } + this->DataFile->Stream >> tag; + } + + internal::parseAssert(tag == "LOOKUP_TABLE"); + this->DataFile->Stream >> lookupTableName >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for COLOR_SCALARS is not implemented. Skipping."); + + std::string dataName; + vtkm::IdComponent numComponents; + this->DataFile->Stream >> dataName >> numComponents >> std::ws; + std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadLookupTable() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for LOOKUP_TABLE is not implemented. Skipping."); + + std::string dataName; + std::size_t numEntries; + this->DataFile->Stream >> dataName >> numEntries >> std::ws; + this->SkipArray(numEntries, vtkm::Vec()); +} + +void VTKDataSetReaderBase::ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + vtkm::IdComponent numComponents; + std::string dataType; + this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadVectors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 3); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadTensors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 9); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (numTuples == expectedNumElements) + { + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numTuples, numComponents); + this->AddField(arrayName, association, data); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Field " << arrayName + << "'s size does not match expected number of elements. Skipping"); + } + } +} + +void VTKDataSetReaderBase::ReadGlobalFields(std::vector* visitBounds) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (arrayName == "avtOriginalBounds" && visitBounds) + { + visitBounds->resize(6); + internal::parseAssert(numComponents == 1 && numTuples == 6); + // parse the bounds and fill the bounds vector + this->ReadArray(*visitBounds); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Support for global field " << arrayName << " not implemented. Skipping."); + this->DoSkipArrayVariant(dataType, numTuples, numComponents); + } + } +} + +class VTKDataSetReaderBase::SkipArrayVariant +{ +public: + SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) + : Reader(reader) + , NumElements(numElements) + { + } + + template + void operator()(T) const + { + this->Reader->SkipArray(this->NumElements, T()); + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); + } + +protected: + VTKDataSetReaderBase* Reader; + std::size_t NumElements; +}; + +class VTKDataSetReaderBase::ReadArrayVariant : public SkipArrayVariant +{ +public: + ReadArrayVariant(VTKDataSetReaderBase* reader, + vtkm::cont::Field::Association association, + std::size_t numElements, + vtkm::cont::VariantArrayHandle& data) + : SkipArrayVariant(reader, numElements) + , Association(association) + , Data(&data) + { + } + + template + void operator()(T) const + { + std::vector buffer(this->NumElements); + this->Reader->ReadArray(buffer); + if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || + (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) + { + *this->Data = CreateVariantArrayHandle(buffer); + } + else + { + // If we are reading data associated with a cell set, we need to (sometimes) permute the + // data due to differences between VTK and VTK-m cell shapes. + auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); + vtkm::Id outSize = permutation.GetNumberOfValues(); + std::vector permutedBuffer(static_cast(outSize)); + for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) + { + std::size_t inIndex = static_cast(permutation.Get(outIndex)); + permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; + } + *this->Data = CreateVariantArrayHandle(permutedBuffer); + } + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for " << numComponents << " components not implemented. Skipping."); + SkipArrayVariant::operator()(numComponents, T()); + } + +private: + vtkm::cont::Field::Association Association; + vtkm::cont::VariantArrayHandle* Data; +}; + +void VTKDataSetReaderBase::DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // string is unsupported for SkipArrayVariant, so it requires some + // special handling + if (dataType == "string") + { + const vtkm::Id stringCount = numComponents * static_cast(numElements); + for (vtkm::Id i = 0; i < stringCount; ++i) + { + std::string trash; + this->DataFile->Stream >> trash; + } + } + else + { + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, SkipArrayVariant(this, numElements)); + } +} + +vtkm::cont::VariantArrayHandle VTKDataSetReaderBase::DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // Create empty data to start so that the return can check if data were actually read + vtkm::cont::ArrayHandle empty; + vtkm::cont::VariantArrayHandle data(empty); + + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); + + return data; +} + +void VTKDataSetReaderBase::ReadArray(std::vector& buffer) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for data type 'bit' is not implemented. Skipping."); + this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); + buffer.clear(); +} + +void VTKDataSetReaderBase::SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents) +{ + if (this->DataFile->IsBinary) + { + numElements = (numElements + 7) / 8; + this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); + } + else + { + for (std::size_t i = 0; i < numElements; ++i) + { + vtkm::UInt16 val; + this->DataFile->Stream >> val; + } + } + this->DataFile->Stream >> std::ws; + this->SkipArrayMetaData(numComponents); +} + +void VTKDataSetReaderBase::SkipArrayMetaData(vtkm::IdComponent numComponents) +{ + if (!this->DataFile->Stream.good()) + { + return; + } + + auto begining = this->DataFile->Stream.tellg(); + + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "METADATA") + { + this->DataFile->Stream.seekg(begining); + return; + } + + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); + + this->DataFile->Stream >> tag >> std::ws; + if (tag == "COMPONENT_NAMES") + { + std::string name; + for (vtkm::IdComponent i = 0; i < numComponents; ++i) + { + this->DataFile->Stream >> name >> std::ws; + } + } + else if (tag == "INFORMATION") + { + int numKeys = 0; + this->DataFile->Stream >> numKeys >> std::ws; + + // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the + // information, which is not provided in the file. + // Here we will just skip until an empty line is found. + // However, if there are no keys, then there is nothing to read (and the stream tends + // to skip over empty lines. + if (numKeys > 0) + { + std::string line; + do + { + std::getline(this->DataFile->Stream, line); + } while (this->DataFile->Stream.good() && !line.empty()); + + // Eat any remaining whitespace after the INFORMATION to be ready to read the next token + this->DataFile->Stream >> std::ws; + } + } + else + { + internal::parseAssert(false); + } +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetReaderBase.h b/vtkm/io/VTKDataSetReaderBase.h index 70292392c..62a935315 100644 --- a/vtkm/io/VTKDataSetReaderBase.h +++ b/vtkm/io/VTKDataSetReaderBase.h @@ -10,25 +10,16 @@ #ifndef vtk_m_io_VTKDataSetReaderBase_h #define vtk_m_io_VTKDataSetReaderBase_h +#include +#include +#include +#include + #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include #include -#include -#include namespace vtkm { @@ -48,16 +39,6 @@ struct VTKDataSetFile std::ifstream Stream; }; -inline void PrintVTKDataFileSummary(const VTKDataSetFile& df, std::ostream& out) -{ - out << "\tFile: " << df.FileName << std::endl; - out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; - out << "\tTitle: " << df.Title << std::endl; - out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; - out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) - << std::endl; -} - inline void parseAssert(bool condition) { if (!condition) @@ -82,166 +63,6 @@ struct StreamIOType using Type = vtkm::UInt16; }; -// Since Fields and DataSets store data in the default VariantArrayHandle, convert -// the data to the closest type supported by default. The following will -// need to be updated if VariantArrayHandle or TypeListCommon changes. -template -struct ClosestCommonType -{ - using Type = T; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; - -template -struct ClosestFloat -{ - using Type = T; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; - -template -vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) -{ - switch (vtkm::VecTraits::NUM_COMPONENTS) - { - case 1: - { - using CommonType = typename ClosestCommonType::Type; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() - << " is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - portal.Set(i, static_cast(vec[static_cast(i)])); - } - - return vtkm::cont::VariantArrayHandle(output); - } - case 2: - case 3: - case 9: - { - constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; - - using InComponentType = typename vtkm::VecTraits::ComponentType; - using OutComponentType = typename ClosestFloat::Type; - using CommonType = vtkm::Vec; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() << "[" - << vtkm::VecTraits::GetNumberOfComponents(T()) - << "] " - << "is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "[" - << numComps - << "]."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - CommonType outval = CommonType(); - for (vtkm::IdComponent j = 0; j < numComps; ++j) - { - outval[j] = static_cast( - vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); - } - portal.Set(i, outval); - } - - return vtkm::cont::VariantArrayHandle(output); - } - default: - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); - return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); - } - } -} - inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) { if (dim[0] > 1 && dim[1] > 1 && dim[2] > 1) @@ -272,564 +93,102 @@ inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) } // namespace internal -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKDataSetReaderBase { +protected: + std::unique_ptr DataFile; + vtkm::cont::DataSet DataSet; + +private: + bool Loaded; + vtkm::cont::ArrayHandle CellsPermutation; + + friend class VTKDataSetReader; + public: - explicit VTKDataSetReaderBase(const char* fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const char* fileName); - explicit VTKDataSetReaderBase(const std::string& fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const std::string& fileName); - virtual ~VTKDataSetReaderBase() {} + virtual VTKM_CONT ~VTKDataSetReaderBase(); - const vtkm::cont::DataSet& ReadDataSet() - { - if (!this->Loaded) - { - try - { - this->OpenFile(); - this->ReadHeader(); - this->Read(); - this->CloseFile(); - this->Loaded = true; - } - catch (std::ifstream::failure& e) - { - std::string message("IO Error: "); - throw vtkm::io::ErrorIO(message + e.what()); - } - } + VTKDataSetReaderBase(const VTKDataSetReaderBase&) = delete; + void operator=(const VTKDataSetReaderBase&) = delete; - return this->DataSet; - } + const VTKM_CONT vtkm::cont::DataSet& ReadDataSet(); const vtkm::cont::DataSet& GetDataSet() const { return this->DataSet; } - virtual void PrintSummary(std::ostream& out) const - { - out << "VTKDataSetReader" << std::endl; - PrintVTKDataFileSummary(*this->DataFile.get(), out); - this->DataSet.PrintSummary(out); - } + virtual VTKM_CONT void PrintSummary(std::ostream& out) const; protected: - void ReadPoints() - { - std::string dataType; - std::size_t numPoints; - this->DataFile->Stream >> numPoints >> dataType >> std::ws; + VTKM_CONT void ReadPoints(); - vtkm::cont::VariantArrayHandle points = - this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + VTKM_CONT void ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices); - this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); - } + VTKM_CONT void ReadShapes(vtkm::cont::ArrayHandle& shapes); - void ReadCells(vtkm::cont::ArrayHandle& connectivity, - vtkm::cont::ArrayHandle& numIndices) - { - vtkm::Id numCells, numInts; - this->DataFile->Stream >> numCells >> numInts >> std::ws; - - connectivity.Allocate(numInts - numCells); - numIndices.Allocate(numCells); - - std::vector buffer(static_cast(numInts)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto connectivityPortal = connectivity.WritePortal(); - auto numIndicesPortal = numIndices.WritePortal(); - for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) - { - vtkm::IdComponent numInds = static_cast(*buffp++); - numIndicesPortal.Set(i, numInds); - for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) - { - connectivityPortal.Set(connInd, static_cast(*buffp++)); - } - } - } - - void ReadShapes(vtkm::cont::ArrayHandle& shapes) - { - std::string tag; - vtkm::Id numCells; - this->DataFile->Stream >> tag >> numCells >> std::ws; - internal::parseAssert(tag == "CELL_TYPES"); - - shapes.Allocate(numCells); - std::vector buffer(static_cast(numCells)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto shapesPortal = shapes.WritePortal(); - for (vtkm::Id i = 0; i < numCells; ++i) - { - shapesPortal.Set(i, static_cast(*buffp++)); - } - } - - void ReadAttributes() - { - if (this->DataFile->Stream.eof()) - { - return; - } - - vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; - std::size_t size; - - std::string tag; - this->DataFile->Stream >> tag; - while (!this->DataFile->Stream.eof()) - { - if (tag == "POINT_DATA") - { - association = vtkm::cont::Field::Association::POINTS; - } - else if (tag == "CELL_DATA") - { - association = vtkm::cont::Field::Association::CELL_SET; - } - else - { - internal::parseAssert(false); - } - - this->DataFile->Stream >> size; - while (!this->DataFile->Stream.eof()) - { - this->DataFile->Stream >> tag; - if (tag == "SCALARS") - { - this->ReadScalars(association, size); - } - else if (tag == "COLOR_SCALARS") - { - this->ReadColorScalars(association, size); - } - else if (tag == "LOOKUP_TABLE") - { - this->ReadLookupTable(); - } - else if (tag == "VECTORS" || tag == "NORMALS") - { - this->ReadVectors(association, size); - } - else if (tag == "TEXTURE_COORDINATES") - { - this->ReadTextureCoordinates(association, size); - } - else if (tag == "TENSORS") - { - this->ReadTensors(association, size); - } - else if (tag == "FIELD") - { - this->ReadFields(association, size); - } - else - { - break; - } - } - } - } + VTKM_CONT void ReadAttributes(); void SetCellsPermutation(const vtkm::cont::ArrayHandle& permutation) { this->CellsPermutation = permutation; } - vtkm::cont::ArrayHandle GetCellsPermutation() const { return this->CellsPermutation; } + VTKM_CONT vtkm::cont::ArrayHandle GetCellsPermutation() const + { + return this->CellsPermutation; + } - void TransferDataFile(VTKDataSetReaderBase& reader) + VTKM_CONT void TransferDataFile(VTKDataSetReaderBase& reader) { reader.DataFile.swap(this->DataFile); this->DataFile.reset(nullptr); } - virtual void CloseFile() { this->DataFile->Stream.close(); } + VTKM_CONT virtual void CloseFile(); + + VTKM_CONT virtual void Read() = 0; private: - void OpenFile() - { - this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try - { - this->DataFile->Stream.open(this->DataFile->FileName.c_str(), - std::ios_base::in | std::ios_base::binary); - } - catch (std::ifstream::failure&) - { - std::string message("could not open file \"" + this->DataFile->FileName + "\""); - throw vtkm::io::ErrorIO(message); - } - } - - void ReadHeader() - { - char vstring[] = "# vtk DataFile Version"; - const std::size_t vlen = sizeof(vstring); - - // Read version line - char vbuf[vlen]; - this->DataFile->Stream.read(vbuf, vlen - 1); - vbuf[vlen - 1] = '\0'; - if (std::string(vbuf) != std::string(vstring)) - { - throw vtkm::io::ErrorIO("Incorrect file format."); - } - - char dot; - this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; - // skip rest of the line - std::string skip; - std::getline(this->DataFile->Stream, skip); - - if ((this->DataFile->Version[0] > 4) || - (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Reader may not correctly read >v4.2 files. Reading version " - << this->DataFile->Version[0] - << "." - << this->DataFile->Version[1] - << ".\n"); - } - - // Read title line - std::getline(this->DataFile->Stream, this->DataFile->Title); - - // Read format line - this->DataFile->IsBinary = false; - std::string format; - this->DataFile->Stream >> format >> std::ws; - if (format == "BINARY") - { - this->DataFile->IsBinary = true; - } - else if (format != "ASCII") - { - throw vtkm::io::ErrorIO("Unsupported Format."); - } - - // Read structure line - std::string tag, structStr; - this->DataFile->Stream >> tag >> structStr >> std::ws; - internal::parseAssert(tag == "DATASET"); - - this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); - if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) - { - throw vtkm::io::ErrorIO("Unsupported DataSet type."); - } - } - - virtual void Read() = 0; - - void AddField(const std::string& name, - vtkm::cont::Field::Association association, - vtkm::cont::VariantArrayHandle& data) - { - if (data.GetNumberOfValues() > 0) - { - switch (association) - { - case vtkm::cont::Field::Association::POINTS: - case vtkm::cont::Field::Association::WHOLE_MESH: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - case vtkm::cont::Field::Association::CELL_SET: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - default: - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Not recording field '" << name << "' because it has an unknown association"); - break; - } - } - } - - void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName, dataType, lookupTableName; - vtkm::IdComponent numComponents = 1; - this->DataFile->Stream >> dataName >> dataType; - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "LOOKUP_TABLE") - { - try - { - numComponents = std::stoi(tag); - } - catch (std::invalid_argument&) - { - internal::parseAssert(false); - } - this->DataFile->Stream >> tag; - } - - internal::parseAssert(tag == "LOOKUP_TABLE"); - this->DataFile->Stream >> lookupTableName >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadColorScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for COLOR_SCALARS is not implemented. Skipping."); - - std::string dataName; - vtkm::IdComponent numComponents; - this->DataFile->Stream >> dataName >> numComponents >> std::ws; - std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadLookupTable() - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for LOOKUP_TABLE is not implemented. Skipping."); - - std::string dataName; - std::size_t numEntries; - this->DataFile->Stream >> dataName >> numEntries >> std::ws; - this->SkipArray(numEntries, vtkm::Vec()); - } - - void ReadTextureCoordinates(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - vtkm::IdComponent numComponents; - std::string dataType; - this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 3); - this->AddField(dataName, association, data); - } - - void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 9); - this->AddField(dataName, association, data); - } - - void ReadFields(vtkm::cont::Field::Association association, std::size_t expectedNumElements) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (numTuples == expectedNumElements) - { - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numTuples, numComponents); - this->AddField(arrayName, association, data); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Field " << arrayName - << "'s size does not match expected number of elements. Skipping"); - } - } - } + VTKM_CONT void OpenFile(); + VTKM_CONT void ReadHeader(); + VTKM_CONT void AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data); + VTKM_CONT void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadLookupTable(); + VTKM_CONT void ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements); protected: - void ReadGlobalFields(std::vector* visitBounds = nullptr) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (arrayName == "avtOriginalBounds" && visitBounds) - { - visitBounds->resize(6); - internal::parseAssert(numComponents == 1 && numTuples == 6); - // parse the bounds and fill the bounds vector - this->ReadArray(*visitBounds); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Support for global field " << arrayName << " not implemented. Skipping."); - this->DoSkipArrayVariant(dataType, numTuples, numComponents); - } - } - } + VTKM_CONT void ReadGlobalFields(std::vector* visitBounds = nullptr); private: - class SkipArrayVariant - { - public: - SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) - : Reader(reader) - , NumElements(numElements) - { - } - - template - void operator()(T) const - { - this->Reader->SkipArray(this->NumElements, T()); - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); - } - - protected: - VTKDataSetReaderBase* Reader; - std::size_t NumElements; - }; - - class ReadArrayVariant : public SkipArrayVariant - { - public: - ReadArrayVariant(VTKDataSetReaderBase* reader, - vtkm::cont::Field::Association association, - std::size_t numElements, - vtkm::cont::VariantArrayHandle& data) - : SkipArrayVariant(reader, numElements) - , Association(association) - , Data(&data) - { - } - - template - void operator()(T) const - { - std::vector buffer(this->NumElements); - this->Reader->ReadArray(buffer); - if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || - (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) - { - *this->Data = internal::CreateVariantArrayHandle(buffer); - } - else - { - // If we are reading data associated with a cell set, we need to (sometimes) permute the - // data due to differences between VTK and VTK-m cell shapes. - auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); - vtkm::Id outSize = permutation.GetNumberOfValues(); - std::vector permutedBuffer(static_cast(outSize)); - for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) - { - std::size_t inIndex = static_cast(permutation.Get(outIndex)); - permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; - } - *this->Data = internal::CreateVariantArrayHandle(permutedBuffer); - } - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for " << numComponents << " components not implemented. Skipping."); - SkipArrayVariant::operator()(numComponents, T()); - } - - private: - vtkm::cont::Field::Association Association; - vtkm::cont::VariantArrayHandle* Data; - }; + class SkipArrayVariant; + class ReadArrayVariant; //Make the Array parsing methods protected so that derived classes //can call the methods. protected: - void DoSkipArrayVariant(std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // string is unsupported for SkipArrayVariant, so it requires some - // special handling - if (dataType == "string") - { - const vtkm::Id stringCount = numComponents * static_cast(numElements); - for (vtkm::Id i = 0; i < stringCount; ++i) - { - std::string trash; - this->DataFile->Stream >> trash; - } - } - else - { - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, SkipArrayVariant(this, numElements)); - } - } - - vtkm::cont::VariantArrayHandle DoReadArrayVariant(vtkm::cont::Field::Association association, - std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // Create empty data to start so that the return can check if data were actually read - vtkm::cont::ArrayHandle empty; - vtkm::cont::VariantArrayHandle data(empty); - - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); - - return data; - } + VTKM_CONT void DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); + VTKM_CONT vtkm::cont::VariantArrayHandle DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); template - void ReadArray(std::vector& buffer) + VTKM_CONT void ReadArray(std::vector& buffer) { using ComponentType = typename vtkm::VecTraits::ComponentType; constexpr vtkm::IdComponent numComponents = vtkm::VecTraits::NUM_COMPONENTS; @@ -861,7 +220,8 @@ protected: } template - void ReadArray(std::vector>& buffer) + VTKM_CONT void ReadArray( + std::vector>& buffer) { VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for data type 'bit' is not implemented. Skipping."); @@ -869,13 +229,7 @@ protected: buffer.clear(); } - void ReadArray(std::vector& buffer) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for data type 'bit' is not implemented. Skipping."); - this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); - buffer.clear(); - } + VTKM_CONT void ReadArray(std::vector& buffer); template void SkipArray(std::size_t numElements, T) @@ -912,95 +266,12 @@ protected: NumComponents); } - void SkipArray(std::size_t numElements, - vtkm::io::internal::DummyBitType, - vtkm::IdComponent numComponents = 1) - { - if (this->DataFile->IsBinary) - { - numElements = (numElements + 7) / 8; - this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); - } - else - { - for (std::size_t i = 0; i < numElements; ++i) - { - vtkm::UInt16 val; - this->DataFile->Stream >> val; - } - } - this->DataFile->Stream >> std::ws; - this->SkipArrayMetaData(numComponents); - } + VTKM_CONT void SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents = 1); - void SkipArrayMetaData(vtkm::IdComponent numComponents) - { - if (!this->DataFile->Stream.good()) - { - return; - } - - auto begining = this->DataFile->Stream.tellg(); - - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "METADATA") - { - this->DataFile->Stream.seekg(begining); - return; - } - - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); - - this->DataFile->Stream >> tag >> std::ws; - if (tag == "COMPONENT_NAMES") - { - std::string name; - for (vtkm::IdComponent i = 0; i < numComponents; ++i) - { - this->DataFile->Stream >> name >> std::ws; - } - } - else if (tag == "INFORMATION") - { - int numKeys = 0; - this->DataFile->Stream >> numKeys >> std::ws; - - // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the - // information, which is not provided in the file. - // Here we will just skip until an empty line is found. - // However, if there are no keys, then there is nothing to read (and the stream tends - // to skip over empty lines. - if (numKeys > 0) - { - std::string line; - do - { - std::getline(this->DataFile->Stream, line); - } while (this->DataFile->Stream.good() && !line.empty()); - - // Eat any remaining whitespace after the INFORMATION to be ready to read the next token - this->DataFile->Stream >> std::ws; - } - } - else - { - internal::parseAssert(false); - } - } - -protected: - std::unique_ptr DataFile; - vtkm::cont::DataSet DataSet; - -private: - bool Loaded; - vtkm::cont::ArrayHandle CellsPermutation; - - friend class VTKDataSetReader; + VTKM_CONT void SkipArrayMetaData(vtkm::IdComponent numComponents); }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // vtkm::io diff --git a/vtkm/io/VTKPolyDataReader.h b/vtkm/io/VTKPolyDataReader.h index 3629f62ca..dcc0623f7 100644 --- a/vtkm/io/VTKPolyDataReader.h +++ b/vtkm/io/VTKPolyDataReader.h @@ -11,6 +11,7 @@ #define vtk_m_io_VTKPolyDataReader_h #include +#include #include diff --git a/vtkm/io/testing/CMakeLists.txt b/vtkm/io/testing/CMakeLists.txt index f17628c77..33ee8762e 100644 --- a/vtkm/io/testing/CMakeLists.txt +++ b/vtkm/io/testing/CMakeLists.txt @@ -15,14 +15,14 @@ set(unit_tests UnitTestVTKDataSetWriter.cxx ) -vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES vtkm_lodepng) +set(unit_test_libraries vtkm_lodepng vtkm_io) -if(NOT VTKm_ENABLE_RENDERING) - return() +if(VTKm_ENABLE_RENDERING) + set(unit_tests ${unit_tests} + UnitTestImageWriter.cxx + ) + + set(unit_test_libraries ${unit_test_libraries} vtkm_rendering) endif() -set(image_unit_tests - UnitTestImageWriter.cxx -) - -vtkm_unit_tests(NAME UnitTests_vtkm_io_image_testing SOURCES ${image_unit_tests} ALL_BACKENDS LIBRARIES vtkm_rendering vtkm_lodepng) +vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES ${unit_test_libraries})