diff --git a/Base/Geometry/Reader/imstkMeshIO.cpp b/Base/Geometry/Reader/imstkMeshIO.cpp index 28d106ed2a6e438aa6beb0c07f898ee8a7ee9316..9415fc9948788762494abba38971d65012c8a1f1 100644 --- a/Base/Geometry/Reader/imstkMeshIO.cpp +++ b/Base/Geometry/Reader/imstkMeshIO.cpp @@ -149,6 +149,12 @@ MeshIO::write(const std::shared_ptr<imstk::Mesh> imstkMesh, const std::string& f case MeshFileType::VEG: return VegaMeshIO::write(imstkMesh, filePath, meshType); break; + case MeshFileType::VTU: + case MeshFileType::VTP: + case MeshFileType::STL: + case MeshFileType::PLY: + return VTKMeshIO::write(imstkMesh, filePath, meshType); + break; } LOG(WARNING) << "MeshIO::write error: file type not supported"; diff --git a/Base/Geometry/Reader/imstkVTKMeshIO.cpp b/Base/Geometry/Reader/imstkVTKMeshIO.cpp index 2b1c4cdd376d1d5faedd27737f87117d586569e5..508a348faefd63dacbe99c18d33d55a57f942cef 100644 --- a/Base/Geometry/Reader/imstkVTKMeshIO.cpp +++ b/Base/Geometry/Reader/imstkVTKMeshIO.cpp @@ -28,6 +28,10 @@ #include "vtkPLYReader.h" #include "vtkOBJReader.h" #include "vtkSTLReader.h" +#include "vtkUnstructuredGridWriter.h" +#include "vtkXMLPolyDataWriter.h" +#include "vtkPLYWriter.h" +#include "vtkSTLWriter.h" #include "vtkFloatArray.h" #include "vtkTriangleFilter.h" @@ -44,42 +48,71 @@ VTKMeshIO::read(const std::string& filePath, MeshFileType meshType) case MeshFileType::VTK : { return VTKMeshIO::readVtkGenericFormatData<vtkGenericDataObjectReader>(filePath); - break; } case MeshFileType::VTU : { return VTKMeshIO::readVtkUnstructuredGrid<vtkXMLUnstructuredGridReader>(filePath); - break; } case MeshFileType::VTP : { return VTKMeshIO::readVtkPolyData<vtkXMLPolyDataReader>(filePath); - break; } case MeshFileType::STL : { return VTKMeshIO::readVtkPolyData<vtkSTLReader>(filePath); - break; } case MeshFileType::PLY : { return VTKMeshIO::readVtkPolyData<vtkPLYReader>(filePath); - break; } case MeshFileType::OBJ : { return VTKMeshIO::readVtkPolyData<vtkOBJReader>(filePath); - break; } default : { LOG(WARNING) << "VTKMeshIO::read error: file type not supported"; return nullptr; - break; } } } +bool +VTKMeshIO::write(const std::shared_ptr<Mesh> imstkMesh, const std::string & filePath, const MeshFileType meshType) +{ + if (auto vMesh = std::dynamic_pointer_cast<VolumetricMesh>(imstkMesh)) + { + switch (meshType) + { + case MeshFileType::VTU: + return VTKMeshIO::writeVtkUnstructuredGrid(vMesh, filePath); + default: + LOG(WARNING) << "VTKMeshIO::write error: file type not supported for volumetric mesh."; + return false; + } + } + else if (auto sMesh = std::dynamic_pointer_cast<SurfaceMesh>(imstkMesh)) + { + switch (meshType) + { + case MeshFileType::VTP: + return VTKMeshIO::writeVtkPolyData<vtkXMLPolyDataWriter>(sMesh, filePath); + case MeshFileType::STL: + return VTKMeshIO::writeVtkPolyData<vtkSTLWriter>(sMesh, filePath); + case MeshFileType::PLY: + return VTKMeshIO::writeVtkPolyData<vtkPLYWriter>(sMesh, filePath); + default: + LOG(WARNING) << "VTKMeshIO::write error: file type not supported for surface mesh."; + return false; + } + } + else + { + LOG(WARNING) << "VTKMeshIO::write error: the provided mesh is not a surface or volumetric mesh."; + return false; + } +} + template<typename ReaderType> std::shared_ptr<Mesh> VTKMeshIO::readVtkGenericFormatData(const std::string& filePath) @@ -119,6 +152,25 @@ VTKMeshIO::readVtkPolyData(const std::string& filePath) return VTKMeshIO::convertVtkPolyDataToSurfaceMesh(vtkMesh); } +template<typename WriterType> +bool +VTKMeshIO::writeVtkPolyData(const std::shared_ptr<SurfaceMesh> imstkMesh, const std::string & filePath) +{ + vtkPolyData* vtkMesh = convertSurfaceMeshToVtkPolyData(imstkMesh); + if (!vtkMesh) //conversion unsuccessful + { + return false; + } + + auto writer = vtkSmartPointer<WriterType>::New(); + writer->SetInputData(vtkMesh); + writer->SetFileName(filePath.c_str()); + writer->Update(); + vtkMesh->Delete(); + + return true; +} + template<typename ReaderType> std::shared_ptr<VolumetricMesh> VTKMeshIO::readVtkUnstructuredGrid(const std::string& filePath) @@ -131,6 +183,41 @@ VTKMeshIO::readVtkUnstructuredGrid(const std::string& filePath) return VTKMeshIO::convertVtkUnstructuredGridToVolumetricMesh(vtkMesh); } +bool +VTKMeshIO::writeVtkUnstructuredGrid(const std::shared_ptr<VolumetricMesh> imstkMesh, const std::string & filePath) +{ + auto tMesh = std::dynamic_pointer_cast<TetrahedralMesh>(imstkMesh); + auto hMesh = std::dynamic_pointer_cast<HexahedralMesh>(imstkMesh); + vtkUnstructuredGrid* vtkMesh = nullptr; + if (tMesh) + { + vtkMesh = convertTetrahedralMeshToVtkUnstructuredGrid(tMesh); + } + else if (hMesh) + { + vtkMesh = convertHexahedralMeshToVtkUnstructuredGrid(hMesh); + } + else + { + LOG(WARNING) << "VTKMeshIO::writeVtkUnstructuredGrid error: mesh is neither tetrahedral nor hexahedral"; + return false; + } + + if (!vtkMesh) + { + LOG(WARNING) << "VTKMeshIO::writeVtkUnstructuredGrid error: conversion unsuccessful"; + return false; + } + + auto writer = vtkSmartPointer<vtkUnstructuredGridWriter>::New(); + writer->SetInputData(vtkMesh); + writer->SetFileName(filePath.c_str()); + writer->Update(); + vtkMesh->Delete(); + + return true; +} + std::shared_ptr<SurfaceMesh> VTKMeshIO::convertVtkPolyDataToSurfaceMesh(vtkPolyData* vtkMesh) { @@ -141,10 +228,10 @@ VTKMeshIO::convertVtkPolyDataToSurfaceMesh(vtkPolyData* vtkMesh) } StdVectorOfVec3d vertices; - VTKMeshIO::copyVertices(vtkMesh->GetPoints(), vertices); + VTKMeshIO::copyVerticesFromVtk(vtkMesh->GetPoints(), vertices); std::vector<SurfaceMesh::TriangleArray> triangles; - VTKMeshIO::copyCells<3>(vtkMesh->GetPolys(), triangles); + VTKMeshIO::copyCellsFromVtk<3>(vtkMesh->GetPolys(), triangles); auto mesh = std::make_shared<SurfaceMesh>(); mesh->initialize(vertices, triangles, true); @@ -167,6 +254,51 @@ VTKMeshIO::convertVtkPolyDataToSurfaceMesh(vtkPolyData* vtkMesh) return mesh; } +vtkPolyData* +VTKMeshIO::convertSurfaceMeshToVtkPolyData(std::shared_ptr<SurfaceMesh> imstkMesh) +{ + vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); + VTKMeshIO::copyVerticesToVtk(imstkMesh->getVertexPositions(), points.Get()); + + vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New(); + VTKMeshIO::copyCellsToVtk<3>(imstkMesh->getTrianglesVertices(), polys.Get()); + + vtkPolyData *polydata = vtkPolyData::New(); + polydata->SetPoints(points); + polydata->SetPolys(polys); + return polydata; +} + +vtkUnstructuredGrid* +VTKMeshIO::convertTetrahedralMeshToVtkUnstructuredGrid(std::shared_ptr<TetrahedralMesh> imstkMesh) +{ + vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); + VTKMeshIO::copyVerticesToVtk(imstkMesh->getVertexPositions(), points.Get()); + + vtkSmartPointer<vtkCellArray> tetras = vtkSmartPointer<vtkCellArray>::New(); + VTKMeshIO::copyCellsToVtk<4>(imstkMesh->getTetrahedraVertices(), tetras.Get()); + + vtkUnstructuredGrid *ug = vtkUnstructuredGrid::New(); + ug->SetPoints(points); + ug->SetCells(VTK_TETRA, tetras); + return ug; +} + +vtkUnstructuredGrid* +VTKMeshIO::convertHexahedralMeshToVtkUnstructuredGrid(std::shared_ptr<HexahedralMesh> imstkMesh) +{ + vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); + VTKMeshIO::copyVerticesToVtk(imstkMesh->getVertexPositions(), points.Get()); + + vtkSmartPointer<vtkCellArray> bricks = vtkSmartPointer<vtkCellArray>::New(); + VTKMeshIO::copyCellsToVtk<8>(imstkMesh->getHexahedraVertices(), bricks.Get()); + + vtkUnstructuredGrid *ug = vtkUnstructuredGrid::New(); + ug->SetPoints(points); + ug->SetCells(VTK_HEXAHEDRON, bricks); + return ug; +} + std::shared_ptr<VolumetricMesh> VTKMeshIO::convertVtkUnstructuredGridToVolumetricMesh(vtkUnstructuredGrid* vtkMesh) { @@ -177,13 +309,13 @@ VTKMeshIO::convertVtkUnstructuredGridToVolumetricMesh(vtkUnstructuredGrid* vtkMe } StdVectorOfVec3d vertices; - VTKMeshIO::copyVertices(vtkMesh->GetPoints(), vertices); + VTKMeshIO::copyVerticesFromVtk(vtkMesh->GetPoints(), vertices); int cellType = vtkMesh->GetCellType(vtkMesh->GetNumberOfCells()-1); if( cellType == VTK_TETRA ) { std::vector<TetrahedralMesh::TetraArray> cells; - VTKMeshIO::copyCells<4>(vtkMesh->GetCells(), cells); + VTKMeshIO::copyCellsFromVtk<4>(vtkMesh->GetCells(), cells); auto mesh = std::make_shared<TetrahedralMesh>(); mesh->initialize(vertices, cells, false); @@ -192,7 +324,7 @@ VTKMeshIO::convertVtkUnstructuredGridToVolumetricMesh(vtkUnstructuredGrid* vtkMe else if( cellType == VTK_HEXAHEDRON ) { std::vector<HexahedralMesh::HexaArray> cells; - VTKMeshIO::copyCells<8>(vtkMesh->GetCells(), cells); + VTKMeshIO::copyCellsFromVtk<8>(vtkMesh->GetCells(), cells); auto mesh = std::make_shared<HexahedralMesh>(); mesh->initialize(vertices, cells, false); @@ -207,14 +339,15 @@ VTKMeshIO::convertVtkUnstructuredGridToVolumetricMesh(vtkUnstructuredGrid* vtkMe } void -VTKMeshIO::copyVertices(vtkPoints* points, StdVectorOfVec3d& vertices) +VTKMeshIO::copyVerticesFromVtk(vtkPoints* points, StdVectorOfVec3d& vertices) { if(!points) { - LOG(WARNING) << "VTKMeshIO::copyVertices error: No points found."; + LOG(WARNING) << "VTKMeshIO::copyVerticesFromVtk error: No points found."; return; } + vertices.reserve(points->GetNumberOfPoints()); for(vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i) { double pos[3]; @@ -223,16 +356,52 @@ VTKMeshIO::copyVertices(vtkPoints* points, StdVectorOfVec3d& vertices) } } +void VTKMeshIO::copyVerticesToVtk(const StdVectorOfVec3d & vertices, vtkPoints * points) +{ + if (!points) + { + LOG(WARNING) << "VTKMeshIO::copyVerticesToVtk error: No points found."; + return; + } + + points->SetNumberOfPoints(vertices.size()); + for (size_t i = 0; i < vertices.size(); i++) + { + points->SetPoint(i, vertices[i][0], vertices[i][1], vertices[i][2]); + } +} + +template<size_t dim> +void VTKMeshIO::copyCellsToVtk(const std::vector<std::array<size_t, dim>>& cells, vtkCellArray * vtkCells) +{ + if (!vtkCells) + { + LOG(WARNING) << "VTKMeshIO::copyCellsToVtk error: No cells found."; + return; + } + + for (size_t i = 0; i < cells.size(); i++) + { + + vtkCells->InsertNextCell(dim); + for (size_t k = 0; k < dim; k++) + { + vtkCells->InsertCellPoint(cells[i][k]); + } + } +} + template<size_t dim> void -VTKMeshIO::copyCells(vtkCellArray* vtkCells, std::vector<std::array<size_t,dim>>& cells) +VTKMeshIO::copyCellsFromVtk(vtkCellArray* vtkCells, std::vector<std::array<size_t,dim>>& cells) { if(!vtkCells) { - LOG(WARNING) << "VTKMeshIO::copyCells error: No cells found."; + LOG(WARNING) << "VTKMeshIO::copyCellsFromVtk error: No cells found."; return; } + cells.reserve(vtkCells->GetNumberOfCells()); vtkCells->InitTraversal(); auto vtkCell = vtkSmartPointer<vtkIdList>::New(); std::array<size_t, dim> cell; @@ -258,19 +427,19 @@ VTKMeshIO::copyPointData(vtkPointData* pointData, std::map<std::string, StdVecto return; } - for (unsigned int i = 0; i < pointData->GetNumberOfArrays(); ++i) + for (int i = 0; i < pointData->GetNumberOfArrays(); ++i) { vtkDataArray* array = pointData->GetArray(i); std::string name = array->GetName(); int nbrOfComp = array->GetNumberOfComponents(); - int nbrOfTuples = array->GetNumberOfTuples(); + vtkIdType nbrOfTuples = array->GetNumberOfTuples(); StdVectorOfVectorf data; - for(unsigned int j = 0; j < nbrOfTuples; ++j) + for(vtkIdType j = 0; j < nbrOfTuples; ++j) { double* tupleData = new double [nbrOfComp]; array->GetTuple(j, tupleData); Vectorf tuple(nbrOfComp); - for (unsigned int k = 0; k < nbrOfComp; k++) + for (int k = 0; k < nbrOfComp; k++) { tuple[k] = tupleData[k]; } diff --git a/Base/Geometry/Reader/imstkVTKMeshIO.h b/Base/Geometry/Reader/imstkVTKMeshIO.h index 0ca2f6d7d4a67425def246417c534a49d9fb7a70..ca0f0d5469976caa9283c41fe01a1a8f0815d4d0 100644 --- a/Base/Geometry/Reader/imstkVTKMeshIO.h +++ b/Base/Geometry/Reader/imstkVTKMeshIO.h @@ -62,6 +62,11 @@ public: /// static std::shared_ptr<Mesh> read(const std::string& filePath, MeshFileType meshType); + /// + /// \brief Writes the given mesh to the specified file path. + /// + static bool write(const std::shared_ptr<Mesh> imstkMesh, const std::string& filePath, const MeshFileType meshType); + protected: /// @@ -76,6 +81,17 @@ protected: template<typename ReaderType> static std::shared_ptr<SurfaceMesh> readVtkPolyData(const std::string& filePath); + /// + /// \brief Writes the given surfase mesh to given file path using the provided writer type + /// + template<typename WriterType> + static bool writeVtkPolyData(const std::shared_ptr<SurfaceMesh> imstkMesh, const std::string& filePath); + + /// + /// \brief Writes the given volumetric mesh to given file path + /// + static bool writeVtkUnstructuredGrid(const std::shared_ptr<VolumetricMesh> imstkMesh, const std::string& filePath); + /// /// \brief /// @@ -87,6 +103,21 @@ protected: /// static std::shared_ptr<SurfaceMesh> convertVtkPolyDataToSurfaceMesh(vtkPolyData* vtkMesh); + /// + /// \brief Converts imstk surface mesh into a vtk polydata suitable for writing to file + /// + static vtkPolyData* convertSurfaceMeshToVtkPolyData(std::shared_ptr<SurfaceMesh> imstkMesh); + + /// + /// \brief Converts imstk tetrahedral mesh into a vtk unstructured grid suitable for writing to file + /// + static vtkUnstructuredGrid* convertTetrahedralMeshToVtkUnstructuredGrid(std::shared_ptr<TetrahedralMesh> imstkMesh); + + /// + /// \brief Converts imstk hexahedral mesh into a vtk unstructured grid suitable for writing to file + /// + static vtkUnstructuredGrid * convertHexahedralMeshToVtkUnstructuredGrid(std::shared_ptr<HexahedralMesh> imstkMesh); + /// /// \brief /// @@ -95,13 +126,24 @@ protected: /// /// \brief /// - static void copyVertices(vtkPoints* points, StdVectorOfVec3d& vertices); + static void copyVerticesFromVtk(vtkPoints* points, StdVectorOfVec3d& vertices); + + /// + /// \brief Copies vertices from imstk structure to VTK one + /// + static void copyVerticesToVtk(const StdVectorOfVec3d& vertices, vtkPoints* points); + + /// + /// \brief Copies cells of the given dimension from imstk structure to VTK one + /// + template<size_t dim> + static void copyCellsToVtk(const std::vector<std::array<size_t, dim>>& cells, vtkCellArray* vtkCells); /// /// \brief /// template<size_t dim> - static void copyCells(vtkCellArray* vtkCells, std::vector<std::array<size_t,dim>>& cells); + static void copyCellsFromVtk(vtkCellArray* vtkCells, std::vector<std::array<size_t,dim>>& cells); /// /// \brief