/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkVTKPolyDataMeshIO_h #define itkVTKPolyDataMeshIO_h #include "ITKIOMeshVTKExport.h" #include "itkByteSwapper.h" #include "itkMetaDataObject.h" #include "itkMeshIOBase.h" #include "itkVectorContainer.h" #include "itkNumberToString.h" #include #include namespace itk { /** *\class VTKPolyDataMeshIO * \brief This class defines how to read and write vtk legacy file format. * * \author Wanlin Zhu. Uviversity of New South Wales, Australia. * \ingroup IOFilters * \ingroup ITKIOMeshVTK */ class ITKIOMeshVTK_EXPORT VTKPolyDataMeshIO : public MeshIOBase { public: ITK_DISALLOW_COPY_AND_MOVE(VTKPolyDataMeshIO); /** Standard "Self" type alias. */ using Self = VTKPolyDataMeshIO; using Superclass = MeshIOBase; using Pointer = SmartPointer; using ConstPointer = SmartPointer; using SizeValueType = Superclass::SizeValueType; using StringType = std::string; using StringVectorType = std::vector; using StringStreamType = std::stringstream; using PointIdVector = std::vector; using PolylinesContainerType = VectorContainer; using PolylinesContainerPointer = PolylinesContainerType::Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(VTKPolyDataMeshIO, MeshIOBase); /**-------- This part of the interfaces deals with reading data. ----- */ /** Determine if the file can be read with this MeshIO implementation. * \param FileNameToRead The name of the file to test for reading. * \post Sets classes MeshIOBase::m_FileName variable to be FileNameToWrite * \return Returns true if this MeshIO can read the file specified. */ bool CanReadFile(const char * fileName) override; /** Set the spacing and dimension information for the set filename. */ void ReadMeshInformation() override; /** Reads the data from disk into the memory buffer provided. */ void ReadPoints(void * buffer) override; void ReadCells(void * buffer) override; void ReadPointData(void * buffer) override; void ReadCellData(void * buffer) override; /*-------- This part of the interfaces deals with writing data. ----- */ /** Determine if the file can be written with this MeshIO implementation. * \param FileNameToWrite The name of the file to test for writing. * \post Sets classes MeshIOBase::m_FileName variable to be FileNameToWrite * \return Returns true if this MeshIO can write the file specified. */ bool CanWriteFile(const char * fileName) override; /** Set the spacing and dimension information for the set filename. */ void WriteMeshInformation() override; /** Writes the data to disk from the memory buffer provided. Make sure * that the IORegions has been set properly. */ void WritePoints(void * buffer) override; void WriteCells(void * buffer) override; void WritePointData(void * buffer) override; void WriteCellData(void * buffer) override; void Write() override; protected: VTKPolyDataMeshIO(); ~VTKPolyDataMeshIO() override; void PrintSelf(std::ostream & os, Indent indent) const override; // Internal function to get next line from a given file (*.vtk) int GetNextLine(std::ifstream & ifs, std::string & line, bool lowerCase = true, SizeValueType count = 0); template void UpdateCellInformation(T * buffer) { unsigned int numberOfVertices = 0; unsigned int numberOfVertexIndices = 0; unsigned int numberOfLines = 0; SizeValueType numberOfLineIndices = 0; unsigned int numberOfPolygons = 0; unsigned int numberOfPolygonIndices = 0; SizeValueType index = 0; for (SizeValueType ii = 0; ii < this->m_NumberOfCells; ii++) { auto cellType = static_cast(static_cast(buffer[index++])); auto nn = static_cast(buffer[index++]); switch (cellType) { case CellGeometryEnum::VERTEX_CELL: numberOfVertices++; numberOfVertexIndices += nn + 1; break; case CellGeometryEnum::LINE_CELL: numberOfLines++; numberOfLineIndices += nn + 1; break; case CellGeometryEnum::TRIANGLE_CELL: numberOfPolygons++; numberOfPolygonIndices += nn + 1; break; case CellGeometryEnum::POLYGON_CELL: numberOfPolygons++; numberOfPolygonIndices += nn + 1; break; case CellGeometryEnum::QUADRILATERAL_CELL: numberOfPolygons++; numberOfPolygonIndices += nn + 1; break; default: itkExceptionMacro(<< "Currently we dont support this cell type"); } index += nn; } MetaDataDictionary & metaDic = this->GetMetaDataDictionary(); EncapsulateMetaData(metaDic, "numberOfVertices", numberOfVertices); EncapsulateMetaData(metaDic, "numberOfVertexIndices", numberOfVertexIndices); EncapsulateMetaData(metaDic, "numberOfLines", numberOfLines); EncapsulateMetaData(metaDic, "numberOfLineIndices", numberOfLineIndices); EncapsulateMetaData(metaDic, "numberOfPolygons", numberOfPolygons); EncapsulateMetaData(metaDic, "numberOfPolygonIndices", numberOfPolygonIndices); return; } template void ReadPointsBufferAsASCII(std::ifstream & inputFile, T * buffer) { std::string line; while (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("POINTS") != std::string::npos) { /** Load the point coordinates into the itk::Mesh */ SizeValueType numberOfComponents = this->m_NumberOfPoints * this->m_PointDimension; for (SizeValueType ii = 0; ii < numberOfComponents; ii++) { inputFile >> buffer[ii]; } } } } template void ReadPointsBufferAsBINARY(std::ifstream & inputFile, T * buffer) { std::string line; while (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("POINTS") != std::string::npos) { /** Load the point coordinates into the itk::Mesh */ SizeValueType numberOfComponents = this->m_NumberOfPoints * this->m_PointDimension; inputFile.read(reinterpret_cast(buffer), numberOfComponents * sizeof(T)); if (itk::ByteSwapper::SystemIsLittleEndian()) { itk::ByteSwapper::SwapRangeFromSystemToBigEndian(buffer, numberOfComponents); } } } } void ReadCellsBufferAsASCII(std::ifstream & inputFile, void * buffer); void ReadCellsBufferAsBINARY(std::ifstream & inputFile, void * buffer); template void ReadPointDataBufferAsASCII(std::ifstream & inputFile, T * buffer) { StringType line; while (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("POINT_DATA") != std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); } else { itkExceptionMacro("UnExpected end of line while trying to read POINT_DATA"); } /** For scalars we have to read the next line of LOOKUP_TABLE */ if (line.find("SCALARS") != std::string::npos && line.find("COLOR_SCALARS") == std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("LOOKUP_TABLE") == std::string::npos) { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } else { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } /** for VECTORS or NORMALS or TENSORS, we could read them directly */ SizeValueType numberOfComponents = this->m_NumberOfPointPixels * this->m_NumberOfPointPixelComponents; for (SizeValueType ii = 0; ii < numberOfComponents; ii++) { inputFile >> buffer[ii]; } } } } template void ReadPointDataBufferAsBINARY(std::ifstream & inputFile, T * buffer) { StringType line; while (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("POINT_DATA") != std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); } else { itkExceptionMacro("UnExpected end of line while trying to read POINT_DATA"); } /** For scalars we have to read the next line of LOOKUP_TABLE */ if (line.find("SCALARS") != std::string::npos && line.find("COLOR_SCALARS") == std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("LOOKUP_TABLE") == std::string::npos) { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } else { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } /** for VECTORS or NORMALS or TENSORS, we could read them directly */ SizeValueType numberOfComponents = this->m_NumberOfPointPixels * this->m_NumberOfPointPixelComponents; inputFile.read(reinterpret_cast(buffer), numberOfComponents * sizeof(T)); if (itk::ByteSwapper::SystemIsLittleEndian()) { itk::ByteSwapper::SwapRangeFromSystemToBigEndian(buffer, numberOfComponents); } } } } template void ReadCellDataBufferAsASCII(std::ifstream & inputFile, T * buffer) { StringType line; while (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("CELL_DATA") != std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); } else { itkExceptionMacro("UnExpected end of line while trying to read CELL_DATA"); } /** For scalars we have to read the next line of LOOKUP_TABLE */ if (line.find("SCALARS") != std::string::npos && line.find("COLOR_SCALARS") == std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("LOOKUP_TABLE") == std::string::npos) { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } else { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } /** for VECTORS or NORMALS or TENSORS, we could read them directly */ SizeValueType numberOfComponents = this->m_NumberOfCellPixels * this->m_NumberOfCellPixelComponents; for (SizeValueType ii = 0; ii < numberOfComponents; ii++) { inputFile >> buffer[ii]; } } } } template void ReadCellDataBufferAsBINARY(std::ifstream & inputFile, T * buffer) { StringType line; while (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("POINT_DATA") != std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); } else { itkExceptionMacro("UnExpected end of line while trying to read POINT_DATA"); } /** For scalars we have to read the next line of LOOKUP_TABLE */ if (line.find("SCALARS") != std::string::npos && line.find("COLOR_SCALARS") == std::string::npos) { if (!inputFile.eof()) { std::getline(inputFile, line, '\n'); if (line.find("LOOKUP_TABLE") == std::string::npos) { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } else { itkExceptionMacro("UnExpected end of line while trying to read LOOKUP_TABLE"); } } /** For VECTORS or NORMALS or TENSORS, we could read them directly */ SizeValueType numberOfComponents = this->m_NumberOfCellPixels * this->m_NumberOfCellPixelComponents; inputFile.read(reinterpret_cast(buffer), numberOfComponents * sizeof(T)); if (itk::ByteSwapper::SystemIsLittleEndian()) { itk::ByteSwapper::SwapRangeFromSystemToBigEndian(buffer, numberOfComponents); } } } } template void WritePointsBufferAsASCII(std::ofstream & outputFile, T * buffer, const StringType & pointComponentType) { NumberToString convert; /** 1. Write number of points */ outputFile << "POINTS " << this->m_NumberOfPoints; outputFile << pointComponentType << '\n'; for (SizeValueType ii = 0; ii < this->m_NumberOfPoints; ii++) { for (unsigned int jj = 0; jj < this->m_PointDimension - 1; jj++) { outputFile << convert(buffer[ii * this->m_PointDimension + jj]) << " "; } outputFile << convert(buffer[ii * this->m_PointDimension + this->m_PointDimension - 1]) << '\n'; } return; } template void WritePointsBufferAsBINARY(std::ofstream & outputFile, T * buffer, const StringType & pointComponentType) { /** 1. Write number of points */ outputFile << "POINTS " << this->m_NumberOfPoints; outputFile << pointComponentType << "\n"; itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian( buffer, this->m_NumberOfPoints * this->m_PointDimension, &outputFile); outputFile << "\n"; return; } template void WriteCellsBufferAsASCII(std::ofstream & outputFile, T * buffer) { MetaDataDictionary & metaDic = this->GetMetaDataDictionary(); unsigned int numberOfVertices = 0; unsigned int numberOfVertexIndices = 0; unsigned int numberOfLines = 0; unsigned int numberOfLineIndices = 0; unsigned int numberOfPolygons = 0; unsigned int numberOfPolygonIndices = 0; /** Write vertices */ SizeValueType index = 0; ExposeMetaData(metaDic, "numberOfVertices", numberOfVertices); if (numberOfVertices) { ExposeMetaData(metaDic, "numberOfVertexIndices", numberOfVertexIndices); outputFile << "VERTICES " << numberOfVertices << " " << numberOfVertexIndices << '\n'; for (SizeValueType ii = 0; ii < this->m_NumberOfCells; ii++) { auto cellType = static_cast(static_cast(buffer[index++])); auto nn = static_cast(buffer[index++]); if (cellType == CellGeometryEnum::VERTEX_CELL) { outputFile << nn; for (unsigned int jj = 0; jj < nn; jj++) { outputFile << " " << buffer[index++]; } outputFile << '\n'; } else { index += nn; } } } /** Write lines */ index = 0; ExposeMetaData(metaDic, "numberOfLines", numberOfLines); if (numberOfLines) { numberOfLineIndices = 0; SizeValueType numberOfPolylines = 0; PolylinesContainerPointer polylines = PolylinesContainerType::New(); PointIdVector pointIds; for (SizeValueType ii = 0; ii < this->m_NumberOfCells; ii++) { auto cellType = static_cast(static_cast(buffer[index++])); auto nn = static_cast(buffer[index++]); if (cellType == CellGeometryEnum::LINE_CELL) { if (pointIds.size() >= nn) { SizeValueType id = pointIds.back(); if (id == static_cast(buffer[index])) { pointIds.push_back(static_cast(buffer[index + 1])); } else if (id == static_cast(buffer[index + 1])) { pointIds.push_back(static_cast(buffer[index])); } else { polylines->InsertElement(numberOfPolylines++, pointIds); numberOfLineIndices += pointIds.size(); pointIds.clear(); for (unsigned int jj = 0; jj < nn; jj++) { pointIds.push_back(static_cast(buffer[index + jj])); } } } else { for (unsigned int jj = 0; jj < nn; jj++) { pointIds.push_back(static_cast(buffer[index + jj])); } } } index += nn; } polylines->InsertElement(numberOfPolylines++, pointIds); numberOfLineIndices += pointIds.size(); pointIds.clear(); numberOfLines = polylines->Size(); numberOfLineIndices += numberOfLines; EncapsulateMetaData(metaDic, "numberOfLines", numberOfLines); EncapsulateMetaData(metaDic, "numberOfLineIndices", numberOfLineIndices); outputFile << "LINES " << numberOfLines << " " << numberOfLineIndices << '\n'; for (SizeValueType ii = 0; ii < polylines->Size(); ++ii) { auto nn = static_cast(polylines->ElementAt(ii).size()); outputFile << nn; for (unsigned int jj = 0; jj < nn; ++jj) { outputFile << " " << polylines->ElementAt(ii)[jj]; } outputFile << '\n'; } } /** Write polygons */ index = 0; ExposeMetaData(metaDic, "numberOfPolygons", numberOfPolygons); if (numberOfPolygons) { ExposeMetaData(metaDic, "numberOfPolygonIndices", numberOfPolygonIndices); outputFile << "POLYGONS " << numberOfPolygons << " " << numberOfPolygonIndices << '\n'; for (SizeValueType ii = 0; ii < this->m_NumberOfCells; ii++) { auto cellType = static_cast(static_cast(buffer[index++])); auto nn = static_cast(buffer[index++]); if (cellType == CellGeometryEnum::POLYGON_CELL || cellType == CellGeometryEnum::TRIANGLE_CELL || cellType == CellGeometryEnum::QUADRILATERAL_CELL) { outputFile << nn; for (unsigned int jj = 0; jj < nn; jj++) { outputFile << " " << buffer[index++]; } outputFile << '\n'; } else { index += nn; } } } } template void WriteCellsBufferAsBINARY(std::ofstream & outputFile, T * buffer) { MetaDataDictionary & metaDic = this->GetMetaDataDictionary(); unsigned int numberOfVertices = 0; unsigned int numberOfVertexIndices = 0; unsigned int numberOfLines = 0; unsigned int numberOfLineIndices = 0; unsigned int numberOfPolygons = 0; unsigned int numberOfPolygonIndices = 0; /** Write vertices */ SizeValueType index = 0; ExposeMetaData(metaDic, "numberOfVertices", numberOfVertices); if (numberOfVertices) { ExposeMetaData(metaDic, "numberOfVertexIndices", numberOfVertexIndices); outputFile << "VERTICES " << numberOfVertices << " " << numberOfVertexIndices << '\n'; auto * data = new unsigned int[numberOfVertexIndices]; ReadCellsBuffer(buffer, data); itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(data, numberOfVertexIndices, &outputFile); outputFile << "\n"; delete[] data; } /** Write lines */ index = 0; ExposeMetaData(metaDic, "numberOfLines", numberOfLines); if (numberOfLines) { numberOfLineIndices = 0; SizeValueType numberOfPolylines = 0; PolylinesContainerPointer polylines = PolylinesContainerType::New(); PointIdVector pointIds; for (SizeValueType ii = 0; ii < this->m_NumberOfCells; ii++) { auto cellType = static_cast(static_cast(buffer[index++])); auto nn = static_cast(buffer[index++]); if (cellType == CellGeometryEnum::LINE_CELL) { if (pointIds.size() >= nn) { SizeValueType id = pointIds.back(); if (id == static_cast(buffer[index])) { pointIds.push_back(static_cast(buffer[index + 1])); } else if (id == static_cast(buffer[index + 1])) { pointIds.push_back(static_cast(buffer[index])); } else { polylines->InsertElement(numberOfPolylines++, pointIds); numberOfLineIndices += pointIds.size(); pointIds.clear(); for (unsigned int jj = 0; jj < nn; jj++) { pointIds.push_back(static_cast(buffer[index + jj])); } } } else { for (unsigned int jj = 0; jj < nn; jj++) { pointIds.push_back(static_cast(buffer[index + jj])); } } } index += nn; } polylines->InsertElement(numberOfPolylines++, pointIds); numberOfLineIndices += pointIds.size(); pointIds.clear(); numberOfLines = polylines->Size(); numberOfLineIndices += numberOfLines; EncapsulateMetaData(metaDic, "numberOfLines", numberOfLines); EncapsulateMetaData(metaDic, "numberOfLineIndices", numberOfLineIndices); outputFile << "LINES " << numberOfLines << " " << numberOfLineIndices << '\n'; auto * data = new unsigned int[numberOfLineIndices]; unsigned long outputIndex = 0; for (SizeValueType ii = 0; ii < polylines->Size(); ++ii) { auto nn = static_cast(polylines->ElementAt(ii).size()); data[outputIndex++] = nn; for (unsigned int jj = 0; jj < nn; ++jj) { data[outputIndex++] = polylines->ElementAt(ii)[jj]; } } itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(data, numberOfLineIndices, &outputFile); outputFile << "\n"; delete[] data; } /** Write polygons */ index = 0; ExposeMetaData(metaDic, "numberOfPolygons", numberOfPolygons); if (numberOfPolygons) { ExposeMetaData(metaDic, "numberOfPolygonIndices", numberOfPolygonIndices); outputFile << "POLYGONS " << numberOfPolygons << " " << numberOfPolygonIndices << '\n'; auto * data = new unsigned int[numberOfPolygonIndices]; ReadCellsBuffer(buffer, data); itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(data, numberOfPolygonIndices, &outputFile); outputFile << "\n"; delete[] data; } } template void WritePointDataBufferAsASCII(std::ofstream & outputFile, T * buffer, const StringType & pointPixelComponentName) { NumberToString convert; MetaDataDictionary & metaDic = this->GetMetaDataDictionary(); StringType dataName; outputFile << "POINT_DATA " << this->m_NumberOfPointPixels << '\n'; switch (this->m_PointPixelType) { case IOPixelEnum::SCALAR: { outputFile << "SCALARS "; ExposeMetaData(metaDic, "pointScalarDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::OFFSET: case IOPixelEnum::POINT: case IOPixelEnum::COVARIANTVECTOR: case IOPixelEnum::VECTOR: { outputFile << "VECTORS "; ExposeMetaData(metaDic, "pointVectorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::SYMMETRICSECONDRANKTENSOR: case IOPixelEnum::DIFFUSIONTENSOR3D: { outputFile << "TENSORS "; ExposeMetaData(metaDic, "pointTensorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::ARRAY: case IOPixelEnum::VARIABLELENGTHVECTOR: { outputFile << "COLOR_SCALARS "; ExposeMetaData(metaDic, "pointColorScalarDataName", dataName); outputFile << dataName << " "; WriteColorScalarBufferAsASCII( outputFile, buffer, this->m_NumberOfPointPixelComponents, this->m_NumberOfPointPixels); return; } default: { itkExceptionMacro(<< "Unknown point pixel type"); } } outputFile << pointPixelComponentName << '\n'; if (this->m_PointPixelType == IOPixelEnum::SCALAR) { outputFile << "LOOKUP_TABLE default" << '\n'; } Indent indent(2); if (this->m_PointPixelType == IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { T * ptr = buffer; SizeValueType i = 0; const SizeValueType num = this->m_NumberOfPointPixelComponents * this->m_NumberOfPointPixels; // Note that only the 3D tensors are supported in the VTK File Format // documentation. if (this->m_NumberOfPointPixelComponents == 3) { T zero(itk::NumericTraits::ZeroValue()); T e12; while (i < num) { // row 1 outputFile << convert(*ptr++) << indent; e12 = *ptr++; outputFile << convert(e12) << indent; outputFile << convert(zero) << '\n'; // row 2 outputFile << convert(e12) << indent; outputFile << convert(*ptr++) << indent; outputFile << convert(zero) << '\n'; // row 3 outputFile << convert(zero) << indent << convert(zero) << indent << convert(zero) << "\n\n"; i += 3; } } else if (this->m_NumberOfPointPixelComponents == 6) { T e12; T e13; T e23; while (i < num) { // row 1 outputFile << convert(*ptr++) << indent; e12 = *ptr++; outputFile << convert(e12) << indent; e13 = *ptr++; outputFile << convert(e13) << '\n'; // row 2 outputFile << convert(e12) << indent; outputFile << convert(*ptr++) << indent; e23 = *ptr++; outputFile << convert(e23) << '\n'; // row 3 outputFile << convert(e13) << indent; outputFile << convert(e23) << indent; outputFile << convert(*ptr++) << "\n\n"; i += 6; } } else { ::itk::ExceptionObject e_( __FILE__, __LINE__, "itk::ERROR: VTKImageIO2: Unsupported number of components in tensor.", ITK_LOCATION); throw e_; } } else // not tensor { unsigned int jj; for (SizeValueType ii = 0; ii < this->m_NumberOfPointPixels; ii++) { for (jj = 0; jj < this->m_NumberOfPointPixelComponents - 1; jj++) { outputFile << convert(buffer[ii * this->m_NumberOfPointPixelComponents + jj]) << indent; } outputFile << convert(buffer[ii * this->m_NumberOfPointPixelComponents + jj]); outputFile << '\n'; } } return; } template void WritePointDataBufferAsBINARY(std::ofstream & outputFile, T * buffer, const StringType & pointPixelComponentName) { MetaDataDictionary & metaDic = this->GetMetaDataDictionary(); StringType dataName; outputFile << "POINT_DATA " << this->m_NumberOfPointPixels << "\n"; switch (this->m_PointPixelType) { case IOPixelEnum::SCALAR: { outputFile << "SCALARS "; ExposeMetaData(metaDic, "pointScalarDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::OFFSET: case IOPixelEnum::POINT: case IOPixelEnum::COVARIANTVECTOR: case IOPixelEnum::VECTOR: { outputFile << "VECTORS "; ExposeMetaData(metaDic, "pointVectorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::SYMMETRICSECONDRANKTENSOR: case IOPixelEnum::DIFFUSIONTENSOR3D: { outputFile << "TENSORS "; ExposeMetaData(metaDic, "pointTensorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::ARRAY: case IOPixelEnum::VARIABLELENGTHVECTOR: { outputFile << "COLOR_SCALARS "; ExposeMetaData(metaDic, "pointColorScalarDataName", dataName); outputFile << dataName << " "; WriteColorScalarBufferAsBINARY( outputFile, buffer, this->m_NumberOfPointPixelComponents, this->m_NumberOfPointPixels); return; } default: { itkExceptionMacro(<< "Unknown point pixel type"); } } outputFile << pointPixelComponentName << "\n"; if (this->m_PointPixelType == IOPixelEnum::SCALAR) { outputFile << "LOOKUP_TABLE default\n"; } itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian( buffer, this->m_NumberOfPointPixels * this->m_NumberOfPointPixelComponents, &outputFile); outputFile << "\n"; return; } template void WriteCellDataBufferAsASCII(std::ofstream & outputFile, T * buffer, const StringType & cellPixelComponentName) { MetaDataDictionary & metaDic = this->GetMetaDataDictionary(); StringType dataName; outputFile << "CELL_DATA " << this->m_NumberOfCellPixels << '\n'; switch (this->m_CellPixelType) { case IOPixelEnum::SCALAR: { outputFile << "SCALARS "; ExposeMetaData(metaDic, "cellScalarDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::OFFSET: case IOPixelEnum::POINT: case IOPixelEnum::COVARIANTVECTOR: case IOPixelEnum::VECTOR: { outputFile << "VECTORS "; ExposeMetaData(metaDic, "cellVectorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::SYMMETRICSECONDRANKTENSOR: case IOPixelEnum::DIFFUSIONTENSOR3D: { outputFile << "TENSORS "; ExposeMetaData(metaDic, "cellTensorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::ARRAY: case IOPixelEnum::VARIABLELENGTHVECTOR: { outputFile << "COLOR_SCALARS "; ExposeMetaData(metaDic, "cellColorScalarDataName", dataName); outputFile << dataName << " "; WriteColorScalarBufferAsASCII( outputFile, buffer, this->m_NumberOfCellPixelComponents, this->m_NumberOfCellPixels); return; } default: { itkExceptionMacro(<< "Unknown cell pixel type"); } } outputFile << cellPixelComponentName << '\n'; if (this->m_CellPixelType == IOPixelEnum::SCALAR) { outputFile << "LOOKUP_TABLE default" << '\n'; } Indent indent(2); if (this->m_CellPixelType == IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { T * ptr = buffer; SizeValueType i = 0; const SizeValueType num = this->m_NumberOfCellPixelComponents * this->m_NumberOfCellPixels; if (this->m_NumberOfCellPixelComponents == 2) { T zero(itk::NumericTraits::ZeroValue()); T e12; while (i < num) { // row 1 outputFile << *ptr++ << indent; e12 = *ptr++; outputFile << e12 << indent; outputFile << zero << '\n'; // row 2 outputFile << e12 << indent; outputFile << *ptr++ << indent; outputFile << zero << '\n'; // row 3 outputFile << zero << indent << zero << indent << zero << "\n\n"; i += 3; } } else if (this->m_NumberOfCellPixelComponents == 3) { T e12; T e13; T e23; while (i < num) { // row 1 outputFile << *ptr++ << indent; e12 = *ptr++; outputFile << e12 << indent; e13 = *ptr++; outputFile << e13 << '\n'; // row 2 outputFile << e12 << indent; outputFile << *ptr++ << indent; e23 = *ptr++; outputFile << e23 << '\n'; // row 3 outputFile << e13 << indent; outputFile << e23 << indent; outputFile << *ptr++ << "\n\n"; i += 6; } } else { ExceptionObject e_(__FILE__, __LINE__, "itk::ERROR: VTKPolyDataMeshIO: Unsupported number of components in tensor.", ITK_LOCATION); throw e_; } } else // not tensor { unsigned int jj; for (SizeValueType ii = 0; ii < this->m_NumberOfCellPixels; ii++) { for (jj = 0; jj < this->m_NumberOfCellPixelComponents - 1; jj++) { outputFile << buffer[ii * this->m_NumberOfCellPixelComponents + jj] << indent; } outputFile << buffer[ii * this->m_NumberOfCellPixelComponents + jj]; outputFile << '\n'; } } return; } template void WriteCellDataBufferAsBINARY(std::ofstream & outputFile, T * buffer, const StringType & cellPixelComponentName) { MetaDataDictionary & metaDic = this->GetMetaDataDictionary(); StringType dataName; outputFile << "CELL_DATA " << this->m_NumberOfCellPixels << "\n"; switch (this->m_CellPixelType) { case IOPixelEnum::SCALAR: { outputFile << "SCALARS "; ExposeMetaData(metaDic, "cellScalarDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::OFFSET: case IOPixelEnum::POINT: case IOPixelEnum::COVARIANTVECTOR: case IOPixelEnum::VECTOR: { outputFile << "VECTORS "; ExposeMetaData(metaDic, "cellVectorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::SYMMETRICSECONDRANKTENSOR: case IOPixelEnum::DIFFUSIONTENSOR3D: { outputFile << "TENSORS "; ExposeMetaData(metaDic, "cellTensorDataName", dataName); outputFile << dataName << " "; break; } case IOPixelEnum::ARRAY: case IOPixelEnum::VARIABLELENGTHVECTOR: { outputFile << "COLOR_SCALARS "; ExposeMetaData(metaDic, "cellColorScalarDataName", dataName); outputFile << dataName << " "; WriteColorScalarBufferAsBINARY( outputFile, buffer, this->m_NumberOfCellPixelComponents, this->m_NumberOfCellPixels); return; } default: { itkExceptionMacro(<< "Unknown cell pixel type"); } } outputFile << cellPixelComponentName << "\n"; if (this->m_CellPixelType == IOPixelEnum::SCALAR) { outputFile << "LOOKUP_TABLE default\n"; } itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian( buffer, this->m_NumberOfCells * this->m_NumberOfCellPixelComponents, &outputFile); outputFile << "\n"; return; } template void WriteColorScalarBufferAsASCII(std::ofstream & outputFile, T * buffer, unsigned int numberOfPixelComponents, SizeValueType numberOfPixels) { NumberToString convert; outputFile << numberOfPixelComponents << "\n"; Indent indent(2); for (SizeValueType ii = 0; ii < numberOfPixels; ++ii) { for (unsigned int jj = 0; jj < numberOfPixelComponents; ++jj) { outputFile << convert(static_cast(buffer[ii * numberOfPixelComponents + jj])) << indent; } outputFile << "\n"; } return; } template void WriteColorScalarBufferAsBINARY(std::ofstream & outputFile, T * buffer, unsigned int numberOfPixelComponents, SizeValueType numberOfPixels) { outputFile << numberOfPixelComponents << "\n"; SizeValueType numberOfElements = numberOfPixelComponents * numberOfPixels; auto * data = new unsigned char[numberOfElements]; for (SizeValueType ii = 0; ii < numberOfElements; ++ii) { data[ii] = static_cast(buffer[ii]); } outputFile.write(reinterpret_cast(data), numberOfElements); delete[] data; outputFile << "\n"; return; } /** Convert cells buffer for output cells buffer, it's user's responsibility to make sure the input cells don't contain any cell type that coule not be written as polygon cell */ template void ReadCellsBuffer(TInput * input, TOutput * output) { SizeValueType inputIndex = 0; SizeValueType outputIndex = 0; if (input && output) { for (SizeValueType ii = 0; ii < this->m_NumberOfCells; ii++) { inputIndex++; auto nn = static_cast(input[inputIndex++]); output[outputIndex++] = nn; for (unsigned int jj = 0; jj < nn; jj++) { output[outputIndex++] = static_cast(input[inputIndex++]); } } } } /** Convenience method returns the IOComponentEnum corresponding to a string. */ IOComponentEnum GetComponentTypeFromString(const std::string & pointType); }; } // end namespace itk #endif // itkVTKPolyDataMeshIO_h