/*========================================================================= * * 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 * * https://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 itkMeshFileWriter_hxx #define itkMeshFileWriter_hxx #include "itkCommand.h" #include "itkDataObject.h" #include "itkMeshConvertPixelTraits.h" #include "itkMeshIOFactory.h" #include "itkObjectFactoryBase.h" #include "itkMakeUniqueForOverwrite.h" #include "vnl/vnl_vector.h" namespace itk { template MeshFileWriter::MeshFileWriter() { m_MeshIO = nullptr; m_UseCompression = false; m_FactorySpecifiedMeshIO = false; m_UserSpecifiedMeshIO = false; m_FileTypeIsBINARY = false; } template void MeshFileWriter::SetInput(const InputMeshType * input) { this->ProcessObject::SetNthInput(0, const_cast(input)); } template auto MeshFileWriter::GetInput() -> const InputMeshType * { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(this->ProcessObject::GetInput(0)); } template auto MeshFileWriter::GetInput(unsigned int idx) -> const InputMeshType * { return static_cast(this->ProcessObject::GetInput(idx)); } template void MeshFileWriter::Write() { const InputMeshType * input = this->GetInput(); itkDebugMacro("Writing an mesh file"); // Make sure input is available if (input == nullptr) { itkExceptionMacro("No input to writer!"); } // Make sure that we can write the file given the name if (m_FileName.empty()) { throw MeshFileWriterException(__FILE__, __LINE__, "FileName must be specified", ITK_LOCATION); } if (!(m_UserSpecifiedMeshIO && !m_MeshIO.IsNull())) { // Try creating via factory if (m_MeshIO.IsNull()) { itkDebugMacro("Attempting factory creation of MeshIO for file: " << m_FileName); m_MeshIO = MeshIOFactory::CreateMeshIO(m_FileName.c_str(), MeshIOFactory::IOFileModeEnum::WriteMode); m_FactorySpecifiedMeshIO = true; } else { if (m_FactorySpecifiedMeshIO && !m_MeshIO->CanWriteFile(m_FileName.c_str())) { itkDebugMacro("MeshIO exists but doesn't know how to write file:" << m_FileName); itkDebugMacro("Attempting creation of MeshIO with a factory for file:" << m_FileName); m_MeshIO = MeshIOFactory::CreateMeshIO(m_FileName.c_str(), MeshIOFactory::IOFileModeEnum::WriteMode); m_FactorySpecifiedMeshIO = true; } } } if (m_MeshIO.IsNull()) { MeshFileWriterException e(__FILE__, __LINE__); std::ostringstream msg; msg << " Could not create IO object for file " << m_FileName.c_str() << std::endl << " Tried to create one of the following:" << std::endl; { for (auto & allobject : ObjectFactoryBase::CreateAllInstance("itkMeshIOBase")) { auto * io = dynamic_cast(allobject.GetPointer()); msg << " " << io->GetNameOfClass() << std::endl; } } msg << " You probably failed to set a file suffix, or" << std::endl << " set the suffix to an unsupported type." << std::endl; e.SetDescription(msg.str().c_str()); e.SetLocation(ITK_LOCATION); throw e; } // NOTE: this const_cast<> is due to the lack of const-correctness // of the ProcessObject. auto * nonConstInput = const_cast(input); // Update the input. // Streaming is not supported at this time. nonConstInput->SetRequestedRegionToLargestPossibleRegion(); nonConstInput->Update(); if (m_FileTypeIsBINARY) { m_MeshIO->SetFileType(IOFileEnum::BINARY); } else { m_MeshIO->SetFileType(IOFileEnum::ASCII); } if (m_UseCompression) { m_MeshIO->UseCompressionOn(); } else { m_MeshIO->UseCompressionOff(); } // Setup the MeshIO m_MeshIO->SetFileName(m_FileName.c_str()); // Whether write points if (input->GetPoints() && input->GetNumberOfPoints()) { m_MeshIO->SetUpdatePoints(true); m_MeshIO->SetNumberOfPoints(input->GetNumberOfPoints()); m_MeshIO->SetPointDimension(TInputMesh::PointDimension); m_MeshIO->SetPointComponentType(MeshIOBase::MapComponentType::CType); } // Whether write cells if (input->GetCells() && input->GetNumberOfCells()) { SizeValueType cellsBufferSize = 2 * input->GetNumberOfCells(); for (typename TInputMesh::CellsContainerConstIterator ct = input->GetCells()->Begin(); ct != input->GetCells()->End(); ++ct) { cellsBufferSize += ct->Value()->GetNumberOfPoints(); } m_MeshIO->SetCellBufferSize(cellsBufferSize); m_MeshIO->SetUpdateCells(true); m_MeshIO->SetNumberOfCells(input->GetNumberOfCells()); m_MeshIO->SetCellComponentType(MeshIOBase::MapComponentType::CType); } // Whether write point data if (input->GetPointData() && input->GetPointData()->Size()) { m_MeshIO->SetUpdatePointData(true); m_MeshIO->SetNumberOfPointPixels(input->GetPointData()->Size()); // m_MeshIO->SetNumberOfPointPixelComponents(MeshConvertPixelTraits::GetNumberOfComponents()); m_MeshIO->SetPixelType(input->GetPointData()->Begin().Value(), true); } // Whether write cell data if (input->GetCellData() && input->GetCellData()->Size()) { m_MeshIO->SetUpdateCellData(true); m_MeshIO->SetNumberOfCellPixels(input->GetCellData()->Size()); // m_MeshIO->SetNumberOfCellPixelComponents(MeshConvertPixelTraits::GetNumberOfComponents()); m_MeshIO->SetPixelType(input->GetCellData()->Begin().Value(), false); } this->InvokeEvent(StartEvent()); // Write mesh information m_MeshIO->WriteMeshInformation(); // write points if (input->GetPoints() && input->GetNumberOfPoints()) { WritePoints(); } // Write cells if (input->GetCells() && input->GetNumberOfCells()) { WriteCells(); } // Write point data if (input->GetPointData() && input->GetPointData()->Size()) { WritePointData(); } // Write cell data if (input->GetCellData() && input->GetCellData()->Size()) { WriteCellData(); } // Write to disk m_MeshIO->Write(); // Notify end event observers this->InvokeEvent(EndEvent()); // Release upstream data if requested this->ReleaseInputs(); } template void MeshFileWriter::WritePoints() { const InputMeshType * input = this->GetInput(); itkDebugMacro("Writing points: " << m_FileName); SizeValueType pointsBufferSize = input->GetNumberOfPoints() * TInputMesh::PointDimension; using ValueType = typename TInputMesh::PointType::ValueType; const auto buffer = make_unique_for_overwrite(pointsBufferSize); CopyPointsToBuffer(buffer.get()); m_MeshIO->WritePoints(buffer.get()); } template void MeshFileWriter::WriteCells() { itkDebugMacro("Writing cells: " << m_FileName); SizeValueType cellsBufferSize = m_MeshIO->GetCellBufferSize(); using PointIdentifierType = typename TInputMesh::PointIdentifier; const auto buffer = make_unique_for_overwrite(cellsBufferSize); CopyCellsToBuffer(buffer.get()); m_MeshIO->WriteCells(buffer.get()); } template void MeshFileWriter::WritePointData() { const InputMeshType * input = this->GetInput(); itkDebugMacro("Writing point data: " << m_FileName); if (input->GetPointData()->Size()) { const SizeValueType numberOfComponents = input->GetPointData()->Size() * MeshConvertPixelTraits::GetNumberOfComponents( input->GetPointData()->Begin().Value()); using ValueType = typename itk::NumericTraits::ValueType; const auto buffer = make_unique_for_overwrite(numberOfComponents); CopyPointDataToBuffer(buffer.get()); m_MeshIO->WritePointData(buffer.get()); } } template void MeshFileWriter::WriteCellData() { const InputMeshType * input = this->GetInput(); itkDebugMacro("Writing cell data: " << m_FileName); if (input->GetCellData()->Size()) { const SizeValueType numberOfComponents = input->GetCellData()->Size() * MeshConvertPixelTraits::GetNumberOfComponents( input->GetCellData()->Begin().Value()); using ValueType = typename itk::NumericTraits::ValueType; const auto buffer = make_unique_for_overwrite(numberOfComponents); CopyCellDataToBuffer(buffer.get()); m_MeshIO->WriteCellData(buffer.get()); } } template template void MeshFileWriter::CopyPointsToBuffer(Output * data) { const typename InputMeshType::PointsContainer * points = this->GetInput()->GetPoints(); SizeValueType index{}; typename TInputMesh::PointsContainerConstIterator pter = points->Begin(); while (pter != points->End()) { for (unsigned int jj = 0; jj < TInputMesh::PointDimension; ++jj) { data[index++] = static_cast(pter.Value()[jj]); } ++pter; } } template template void MeshFileWriter::CopyCellsToBuffer(Output * data) { // Get input mesh pointer const typename InputMeshType::CellsContainer * cells = this->GetInput()->GetCells(); // Define required variables typename TInputMesh::PointIdentifier const * ptIds; typename TInputMesh::CellType * cellPtr; // For each cell SizeValueType index{}; typename TInputMesh::CellsContainerConstIterator cter = cells->Begin(); while (cter != cells->End()) { cellPtr = cter.Value(); // Write the cell type switch (cellPtr->GetType()) { case CellGeometryEnum::VERTEX_CELL: data[index++] = static_cast(CellGeometryEnum::VERTEX_CELL); break; case CellGeometryEnum::LINE_CELL: data[index++] = static_cast(CellGeometryEnum::LINE_CELL); break; case CellGeometryEnum::POLYLINE_CELL: data[index++] = static_cast(CellGeometryEnum::POLYLINE_CELL); break; case CellGeometryEnum::TRIANGLE_CELL: data[index++] = static_cast(CellGeometryEnum::TRIANGLE_CELL); break; case CellGeometryEnum::QUADRILATERAL_CELL: data[index++] = static_cast(CellGeometryEnum::QUADRILATERAL_CELL); break; case CellGeometryEnum::POLYGON_CELL: data[index++] = static_cast(CellGeometryEnum::POLYGON_CELL); break; case CellGeometryEnum::TETRAHEDRON_CELL: data[index++] = static_cast(CellGeometryEnum::TETRAHEDRON_CELL); break; case CellGeometryEnum::HEXAHEDRON_CELL: data[index++] = static_cast(CellGeometryEnum::HEXAHEDRON_CELL); break; case CellGeometryEnum::QUADRATIC_EDGE_CELL: data[index++] = static_cast(CellGeometryEnum::QUADRATIC_EDGE_CELL); break; case CellGeometryEnum::QUADRATIC_TRIANGLE_CELL: data[index++] = static_cast(CellGeometryEnum::QUADRATIC_TRIANGLE_CELL); break; default: itkExceptionMacro("Unknown mesh cell"); } // The second element is number of points for each cell data[index++] = cellPtr->GetNumberOfPoints(); // Others are point identifiers in the cell ptIds = cellPtr->GetPointIds(); unsigned int numberOfPoints = cellPtr->GetNumberOfPoints(); for (unsigned int ii = 0; ii < numberOfPoints; ++ii) { data[index++] = static_cast(ptIds[ii]); } ++cter; } } template template void MeshFileWriter::CopyPointDataToBuffer(Output * data) { const typename InputMeshType::PointDataContainer * pointData = this->GetInput()->GetPointData(); // typename TInputMesh::PixelType value = NumericTraits< typename // TInputMesh::PixelType >::ZeroValue(); // TODO? NumericTraitsVariableLengthVectorPixel should define ZeroValue() // Should define NumericTraitsArrayPixel unsigned int numberOfComponents = MeshConvertPixelTraits::GetNumberOfComponents(pointData->Begin().Value()); SizeValueType index = 0; typename TInputMesh::PointDataContainer::ConstIterator pter = pointData->Begin(); while (pter != pointData->End()) { for (unsigned int jj = 0; jj < numberOfComponents; ++jj) { data[index++] = static_cast(MeshConvertPixelTraits::GetNthComponent(jj, pter.Value())); } ++pter; } } template template void MeshFileWriter::CopyCellDataToBuffer(Output * data) { const typename InputMeshType::CellDataContainer * cellData = this->GetInput()->GetCellData(); // typename TInputMesh::CellPixelType value = NumericTraits< typename // TInputMesh::CellPixelType >::ZeroValue(); // TODO? NumericTraitsVariableLengthVectorPixel should define ZeroValue() // Should define NumericTraitsArrayPixel unsigned int numberOfComponents = MeshConvertPixelTraits::GetNumberOfComponents(cellData->Begin().Value()); SizeValueType index = 0; typename TInputMesh::CellDataContainer::ConstIterator cter = cellData->Begin(); while (cter != cellData->End()) { for (unsigned int jj = 0; jj < numberOfComponents; ++jj) { data[index++] = static_cast( MeshConvertPixelTraits::GetNthComponent(jj, cter.Value())); } ++cter; } } template void MeshFileWriter::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "File Name: " << (m_FileName.data() ? m_FileName.data() : "(none)") << std::endl; os << indent << "Mesh IO: "; if (m_MeshIO.IsNull()) { os << "(none)\n"; } else { os << m_MeshIO << '\n'; } if (m_UseCompression) { os << indent << "Compression: On\n"; } else { os << indent << "Compression: Off\n"; } if (m_FactorySpecifiedMeshIO) { os << indent << "FactorySpecifiedMeshIO: On\n"; } else { os << indent << "FactorySpecifiedMeshIO: Off\n"; } } } // end namespace itk #endif