From 7f0ba3ed3d9454f2c6d0ce592d31fbd16c90dda0 Mon Sep 17 00:00:00 2001 From: ahmetd Date: Thu, 14 May 2026 10:30:00 +0200 Subject: [PATCH] feat: in-memory VTK I/O for lsVTKReader and lsVTKWriter Allow VTKReader/Writer to operate on vtkPolyData and vtkUnstructuredGrid objects directly, bypassing file I/O. Useful for VTK-based pipelines (e.g. ParaView plugins) where intermediate file writes are unnecessary. --- include/viennals/lsVTKReader.hpp | 108 ++++++++++++++++++++---- include/viennals/lsVTKWriter.hpp | 139 ++++++++++++++++--------------- 2 files changed, 165 insertions(+), 82 deletions(-) diff --git a/include/viennals/lsVTKReader.hpp b/include/viennals/lsVTKReader.hpp index c000a82c..414f7310 100644 --- a/include/viennals/lsVTKReader.hpp +++ b/include/viennals/lsVTKReader.hpp @@ -33,6 +33,12 @@ template class VTKReader { std::string fileName; std::unordered_map> metaData; +#ifdef VIENNALS_USE_VTK + // For memory-based reading + vtkPolyData *inputPolyData = nullptr; + vtkUnstructuredGrid *inputUnstructuredGrid = nullptr; +#endif + unsigned vtk_nodes_for_cell_type[15] = {0, 1, 0, 2, 0, 3, 0, 0, 4, 4, 4, 8, 8, 6, 5}; @@ -81,6 +87,21 @@ template class VTKReader { : mesh(passedMesh), fileFormat(passedFormat), fileName(std::move(passedFileName)) {} +#ifdef VIENNALS_USE_VTK + /// Constructor for reading from vtkPolyData in memory + VTKReader(SmartPointer> passedMesh, vtkPolyData *polyData) + : mesh(passedMesh), inputPolyData(polyData) { + fileFormat = FileFormatEnum::VTP; + } + + /// Constructor for reading from vtkUnstructuredGrid in memory + VTKReader(SmartPointer> passedMesh, + vtkUnstructuredGrid *unstructuredGrid) + : mesh(passedMesh), inputUnstructuredGrid(unstructuredGrid) { + fileFormat = FileFormatEnum::VTU; + } +#endif + /// set the mesh the file should be read into void setMesh(SmartPointer> passedMesh) { mesh = passedMesh; } @@ -92,6 +113,24 @@ template class VTKReader { fileName = std::move(passedFileName); } +#ifdef VIENNALS_USE_VTK + /// Set vtkPolyData to read from memory instead of file + void setPolyData(vtkPolyData *polyData) { + inputPolyData = polyData; + inputUnstructuredGrid = nullptr; + fileFormat = FileFormatEnum::VTP; + fileName.clear(); + } + + /// Set vtkUnstructuredGrid to read from memory instead of file + void setUnstructuredGrid(vtkUnstructuredGrid *unstructuredGrid) { + inputUnstructuredGrid = unstructuredGrid; + inputPolyData = nullptr; + fileFormat = FileFormatEnum::VTU; + fileName.clear(); + } +#endif + auto &getMetaData() { return metaData; } void apply() { @@ -102,6 +141,20 @@ template class VTKReader { .print(); return; } + +#ifdef VIENNALS_USE_VTK + // Check if we're reading from memory + if (inputPolyData != nullptr) { + buildFromPolyData(inputPolyData); + return; + } + + if (inputUnstructuredGrid != nullptr) { + buildFromUnstructuredGrid(inputUnstructuredGrid); + return; + } +#endif + // check filename if (fileName.empty()) { Logger::getInstance() @@ -164,16 +217,16 @@ template class VTKReader { private: #ifdef VIENNALS_USE_VTK - void readVTP(const std::string &filename) { + /// Build mesh from vtkPolyData + void buildFromPolyData(vtkPolyData *polyData) { + if (!polyData) { + Logger::getInstance() + .addError("Null vtkPolyData passed to VTKReader.") + .print(); + return; + } mesh->clear(); - vtkSmartPointer pReader = - vtkSmartPointer::New(); - pReader->SetFileName(filename.c_str()); - pReader->Update(); - - vtkSmartPointer polyData = vtkSmartPointer::New(); - polyData = pReader->GetOutput(); mesh->nodes.resize(polyData->GetNumberOfPoints()); for (unsigned i = 0; i < mesh->nodes.size(); ++i) { @@ -290,18 +343,28 @@ template class VTKReader { extractFieldData(polyData); } - void readVTU(const std::string &filename) { + void readVTP(const std::string &filename) { mesh->clear(); + vtkSmartPointer pReader = + vtkSmartPointer::New(); + pReader->SetFileName(filename.c_str()); + pReader->Update(); - vtkSmartPointer greader = - vtkSmartPointer::New(); - greader->SetFileName(filename.c_str()); - greader->Update(); + vtkSmartPointer polyData = pReader->GetOutput(); + buildFromPolyData(polyData); + } - vtkSmartPointer ugrid = - vtkSmartPointer::New(); - ugrid = greader->GetOutput(); + /// Build mesh from vtkUnstructuredGrid + void buildFromUnstructuredGrid(vtkUnstructuredGrid *ugrid) { + if (!ugrid) { + Logger::getInstance() + .addError("Null vtkUnstructuredGrid passed to VTKReader.") + .print(); + return; + } + + mesh->clear(); // get all points mesh->nodes.resize(ugrid->GetNumberOfPoints()); @@ -421,6 +484,19 @@ template class VTKReader { extractFieldData(ugrid); } + void readVTU(const std::string &filename) { + + mesh->clear(); + + vtkSmartPointer greader = + vtkSmartPointer::New(); + greader->SetFileName(filename.c_str()); + greader->Update(); + + vtkSmartPointer ugrid = greader->GetOutput(); + buildFromUnstructuredGrid(ugrid); + } + #endif // VIENNALS_USE_VTK void readVTKLegacy(const std::string &filename) { diff --git a/include/viennals/lsVTKWriter.hpp b/include/viennals/lsVTKWriter.hpp index 7f37ae1b..9f5f0c51 100644 --- a/include/viennals/lsVTKWriter.hpp +++ b/include/viennals/lsVTKWriter.hpp @@ -39,6 +39,10 @@ template class VTKWriter { MetaDataType metaData; #ifdef VIENNALS_USE_VTK + // Cached VTK objects for memory access + vtkSmartPointer cachedPolyData = nullptr; + vtkSmartPointer cachedUnstructuredGrid = nullptr; + template void addDataFromMesh(const In &inData, Out outData) const { // now add pointData @@ -133,6 +137,14 @@ template class VTKWriter { } } +#ifdef VIENNALS_USE_VTK + vtkPolyData *getPolyData() const { return cachedPolyData; } + + vtkUnstructuredGrid *getUnstructuredGrid() const { + return cachedUnstructuredGrid; + } +#endif // VIENNALS_USE_VTK + void apply() { // check mesh if (mesh == nullptr) { @@ -141,49 +153,58 @@ template class VTKWriter { .print(); return; } - // check filename - if (fileName.empty()) { - VIENNACORE_LOG_ERROR("No file name specified for VTKWriter."); - return; - } if (mesh->nodes.empty()) { VIENNACORE_LOG_WARNING("Writing empty mesh."); return; } if (fileFormat == FileFormatEnum::VTK_AUTO) { - auto dotPos = fileName.rfind('.'); - if (dotPos == std::string::npos) { - fileFormat = FileFormatEnum::VTP; - } else { - auto ending = fileName.substr(dotPos); - if (ending == ".vtk") { - fileFormat = FileFormatEnum::VTK_LEGACY; - } else if (ending == ".vtp") { + if (!fileName.empty()) { + auto dotPos = fileName.rfind('.'); + if (dotPos == std::string::npos) { fileFormat = FileFormatEnum::VTP; - } else if (ending == ".vtu") { - fileFormat = FileFormatEnum::VTU; } else { - Logger::getInstance() - .addError("No valid file format found based on the file ending " - "passed to VTKWriter.") - .print(); - return; + auto ending = fileName.substr(dotPos); + if (ending == ".vtk") { + fileFormat = FileFormatEnum::VTK_LEGACY; + } else if (ending == ".vtp") { + fileFormat = FileFormatEnum::VTP; + } else if (ending == ".vtu") { + fileFormat = FileFormatEnum::VTU; + } else { + Logger::getInstance() + .addError("No valid file format found based on the file ending " + "passed to VTKWriter.") + .print(); + return; + } } + } else { + // No filename: default to VTP memory-only mode + fileFormat = FileFormatEnum::VTP; } } - // check file format + // Build VTK object in memory, then write to file if filename is set switch (fileFormat) { case FileFormatEnum::VTK_LEGACY: + if (fileName.empty()) { + VIENNACORE_LOG_ERROR("VTK_LEGACY format requires a filename. Cannot " + "use memory-only mode."); + return; + } writeVTKLegacy(fileName); break; #ifdef VIENNALS_USE_VTK case FileFormatEnum::VTP: - writeVTP(fileName); + buildVTP(); + if (!fileName.empty()) + writeVTP(fileName); break; case FileFormatEnum::VTU: - writeVTU(fileName); + buildVTU(); + if (!fileName.empty()) + writeVTU(fileName); break; #else case FileFormatEnum::VTP: @@ -201,27 +222,14 @@ template class VTKWriter { private: #ifdef VIENNALS_USE_VTK - void writeVTP(std::string filename) const { - if (mesh == nullptr) { - Logger::getInstance() - .addError("No mesh was passed to VTKWriter.") - .print(); - return; - } + void buildVTP() { + cachedPolyData = vtkSmartPointer::New(); - if (filename.find(".vtp") != filename.size() - 4) - filename += ".vtp"; - vtkSmartPointer polyData = vtkSmartPointer::New(); - - // Points vtkSmartPointer polyPoints = vtkSmartPointer::New(); - for (auto it = mesh->getNodes().begin(); it != mesh->getNodes().end(); - ++it) { + for (auto it = mesh->getNodes().begin(); it != mesh->getNodes().end(); ++it) polyPoints->InsertNextPoint((*it)[0], (*it)[1], (*it)[2]); - } - polyData->SetPoints(polyPoints); + cachedPolyData->SetPoints(polyPoints); - // Vertices if (mesh->vertices.size() > 0) { vtkSmartPointer polyCells = vtkSmartPointer::New(); @@ -229,60 +237,53 @@ template class VTKWriter { polyCells->InsertNextCell(1); polyCells->InsertCellPoint((*it)[0]); } - polyData->SetVerts(polyCells); + cachedPolyData->SetVerts(polyCells); } - // Lines if (mesh->lines.size() > 0) { vtkSmartPointer polyCells = vtkSmartPointer::New(); for (auto it = mesh->lines.begin(); it != mesh->lines.end(); ++it) { polyCells->InsertNextCell(2); - for (unsigned i = 0; i < 2; ++i) { + for (unsigned i = 0; i < 2; ++i) polyCells->InsertCellPoint((*it)[i]); - } } - polyData->SetLines(polyCells); + cachedPolyData->SetLines(polyCells); } - // Triangles if (mesh->triangles.size() > 0) { vtkSmartPointer polyCells = vtkSmartPointer::New(); for (auto it = mesh->triangles.begin(); it != mesh->triangles.end(); ++it) { polyCells->InsertNextCell(3); - for (unsigned i = 0; i < 3; ++i) { + for (unsigned i = 0; i < 3; ++i) polyCells->InsertCellPoint((*it)[i]); - } } - polyData->SetPolys(polyCells); + cachedPolyData->SetPolys(polyCells); } - addDataFromMesh(mesh->pointData, polyData->GetPointData()); - addDataFromMesh(mesh->cellData, polyData->GetCellData()); - addMetaDataToVTK(polyData); + addDataFromMesh(mesh->pointData, cachedPolyData->GetPointData()); + addDataFromMesh(mesh->cellData, cachedPolyData->GetCellData()); + addMetaDataToVTK(cachedPolyData); + } + void writeVTP(std::string filename) const { + if (cachedPolyData == nullptr) + return; + if (filename.find(".vtp") != filename.size() - 4) + filename += ".vtp"; vtkSmartPointer pwriter = vtkSmartPointer::New(); pwriter->SetFileName(filename.c_str()); - pwriter->SetInputData(polyData); + pwriter->SetInputData(cachedPolyData); pwriter->Write(); } - void writeVTU(std::string filename) const { - if (mesh == nullptr) { - Logger::getInstance() - .addError("No mesh was passed to VTKWriter.") - .print(); - return; - } + void buildVTU() { + cachedUnstructuredGrid = vtkSmartPointer::New(); - if (filename.find(".vtu") != filename.size() - 4) - filename += ".vtu"; - - vtkSmartPointer uGrid = - vtkSmartPointer::New(); + vtkSmartPointer uGrid = cachedUnstructuredGrid; // Points vtkSmartPointer points = vtkSmartPointer::New(); @@ -386,11 +387,17 @@ template class VTKWriter { // } // uGrid->GetCellData()->AddArray(vectorData); // } + } + void writeVTU(std::string filename) const { + if (cachedUnstructuredGrid == nullptr) + return; + if (filename.find(".vtu") != filename.size() - 4) + filename += ".vtu"; vtkSmartPointer owriter = vtkSmartPointer::New(); owriter->SetFileName(filename.c_str()); - owriter->SetInputData(uGrid); + owriter->SetInputData(cachedUnstructuredGrid); owriter->Write(); }