From 3e1741dfea31a54ca4ed8cb0f827c59aa6a7d43c Mon Sep 17 00:00:00 2001 From: marioalexis Date: Sun, 14 Sep 2025 00:14:52 -0300 Subject: [PATCH] Fem: Move FemFrameSourceAlgorithm class to its own source files --- src/Mod/Fem/App/CMakeLists.txt | 8 + src/Mod/Fem/App/FemPostPipeline.cpp | 143 +------------- src/Mod/Fem/App/FemPostPipeline.h | 35 +--- src/Mod/Fem/App/VTKExtensions/CMakeLists.txt | 6 + .../vtkFemFrameSourceAlgorithm.cpp | 180 ++++++++++++++++++ .../vtkFemFrameSourceAlgorithm.h | 66 +++++++ 6 files changed, 265 insertions(+), 173 deletions(-) create mode 100644 src/Mod/Fem/App/VTKExtensions/CMakeLists.txt create mode 100644 src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.cpp create mode 100644 src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.h diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index 24329f725e..6a8f02fb2e 100644 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -82,6 +82,12 @@ if(BUILD_FEM_VTK) FemVTKTools.cpp ) SOURCE_GROUP("PostObjects" FILES ${FemPost_SRCS}) + + SET(FemVTK_SRCS + VTKExtensions/vtkFemFrameSourceAlgorithm.h + VTKExtensions/vtkFemFrameSourceAlgorithm.cpp + ) + SOURCE_GROUP("VTKExtensions" FILES ${FemVTK_SRCS}) endif(BUILD_FEM_VTK) @@ -178,6 +184,7 @@ SET(Fem_SRCS ${FemConstraints_SRCS} ${FemPost_SRCS} ${FemSet_SRCS} + ${FemVTK_SRCS} ${Mod_SRCS} ${Python_SRCS} ) @@ -197,6 +204,7 @@ target_include_directories( ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} ) target_include_directories( diff --git a/src/Mod/Fem/App/FemPostPipeline.cpp b/src/Mod/Fem/App/FemPostPipeline.cpp index 70697e5bfe..953a2957bf 100644 --- a/src/Mod/Fem/App/FemPostPipeline.cpp +++ b/src/Mod/Fem/App/FemPostPipeline.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -61,145 +60,6 @@ using namespace Fem; using namespace App; -vtkStandardNewMacro(FemFrameSourceAlgorithm); - -FemFrameSourceAlgorithm::FemFrameSourceAlgorithm::FemFrameSourceAlgorithm() -{ - // we are a source - SetNumberOfInputPorts(0); - SetNumberOfOutputPorts(1); -} - - -FemFrameSourceAlgorithm::FemFrameSourceAlgorithm::~FemFrameSourceAlgorithm() -{} - -void FemFrameSourceAlgorithm::setDataObject(vtkSmartPointer data) -{ - m_data = data; - Modified(); - Update(); -} - -bool FemFrameSourceAlgorithm::isValid() -{ - return m_data.GetPointer() != nullptr; -} - -std::vector FemFrameSourceAlgorithm::getFrameValues() -{ - - // check if we have frame data - if (!m_data || !m_data->IsA("vtkMultiBlockDataSet")) { - return std::vector(); - } - - // we have multiple frames! let's check the amount and times - vtkSmartPointer multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data); - - unsigned long nblocks = multiblock->GetNumberOfBlocks(); - std::vector tFrames(nblocks); - - for (unsigned long i = 0; i < nblocks; i++) { - - vtkDataObject* block = multiblock->GetBlock(i); - // check if the TimeValue field is available - if (!block->GetFieldData()->HasArray("TimeValue")) { - // a frame with no valid value is a undefined state - return std::vector(); - } - - // store the time value! - vtkDataArray* TimeValue = block->GetFieldData()->GetArray("TimeValue"); - if (!TimeValue->IsA("vtkFloatArray") || TimeValue->GetNumberOfTuples() < 1) { - // a frame with no valid value is a undefined state - return std::vector(); - } - - tFrames[i] = vtkFloatArray::SafeDownCast(TimeValue)->GetValue(0); - } - - return tFrames; -} - -int FemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo, - vtkInformationVector** inVector, - vtkInformationVector* outVector) -{ - - // setup default information - if (!this->Superclass::RequestInformation(reqInfo, inVector, outVector)) { - return 0; - } - - if (!m_data) { - // for the no data case we would return a empty data set in RequestData. - return 1; - } - - std::vector frames = getFrameValues(); - - if (frames.empty()) { - // no frames, default info is sufficient - return 1; - } - - double tRange[2] = {frames.front(), frames.back()}; - - // finally set the time info! - vtkInformation* info = outVector->GetInformationObject(0); - info->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), tRange, 2); - info->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), &frames[0], frames.size()); - info->Set(CAN_HANDLE_PIECE_REQUEST(), 1); - - return 1; -} - -int FemFrameSourceAlgorithm::RequestData(vtkInformation*, - vtkInformationVector**, - vtkInformationVector* outVector) -{ - vtkInformation* outInfo = outVector->GetInformationObject(0); - vtkUnstructuredGrid* output = - vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); - - if (!output) { - return 0; - } - - if (!m_data) { - outInfo->Set(vtkDataObject::DATA_OBJECT(), vtkUnstructuredGrid::New()); - return 1; - } - - if (!m_data->IsA("vtkMultiBlockDataSet")) { - // no multi frame data, return directly - outInfo->Set(vtkDataObject::DATA_OBJECT(), m_data); - return 1; - } - - vtkSmartPointer multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data); - // find the block asked for (lazy implementation) - unsigned long idx = 0; - if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())) { - auto time = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()); - auto frames = getFrameValues(); - - // we have float values, so be aware of rounding errors. lets subtract the searched time and - // then use the smallest value - for (auto& frame : frames) { - frame = std::abs(frame - time); - } - - auto it = std::ranges::min_element(frames); - idx = std::distance(frames.begin(), it); - } - - auto block = multiblock->GetBlock(idx); - output->ShallowCopy(block); - return 1; -} - PROPERTY_SOURCE_WITH_EXTENSIONS(Fem::FemPostPipeline, Fem::FemPostObject) FemPostPipeline::FemPostPipeline() @@ -215,7 +75,8 @@ FemPostPipeline::FemPostPipeline() "set via pipeline object)."); // create our source algorithm - m_source_algorithm = vtkSmartPointer::New(); + m_source_algorithm = vtkSmartPointer::New(); + m_transform_filter->SetInputConnection(m_source_algorithm->GetOutputPort(0)); } diff --git a/src/Mod/Fem/App/FemPostPipeline.h b/src/Mod/Fem/App/FemPostPipeline.h index d590915cb4..3ba9108f5e 100644 --- a/src/Mod/Fem/App/FemPostPipeline.h +++ b/src/Mod/Fem/App/FemPostPipeline.h @@ -30,44 +30,14 @@ #include "FemPostFunction.h" #include "FemPostObject.h" #include "FemResultObject.h" +#include "VTKExtensions/vtkFemFrameSourceAlgorithm.h" #include -#include - -class vtkInformation; -class vtkInformationVector; namespace Fem { -// algorithm that allows multi frame handling: if data is stored in MultiBlock dataset -// this source enables the downstream filters to query the blocks as different time frames -class FemFrameSourceAlgorithm: public vtkUnstructuredGridAlgorithm -{ -public: - static FemFrameSourceAlgorithm* New(); - vtkTypeMacro(FemFrameSourceAlgorithm, vtkUnstructuredGridAlgorithm); - - bool isValid(); - void setDataObject(vtkSmartPointer data); - std::vector getFrameValues(); - -protected: - FemFrameSourceAlgorithm(); - ~FemFrameSourceAlgorithm() override; - - vtkSmartPointer m_data; - - int RequestInformation(vtkInformation* reqInfo, - vtkInformationVector** inVector, - vtkInformationVector* outVector) override; - int RequestData(vtkInformation* reqInfo, - vtkInformationVector** inVector, - vtkInformationVector* outVector) override; -}; - - class FemExport FemPostPipeline: public Fem::FemPostObject, public Fem::FemPostGroupExtension { PROPERTY_HEADER_WITH_EXTENSIONS(Fem::FemPostPipeline); @@ -136,7 +106,8 @@ protected: private: App::Enumeration m_frameEnum; - vtkSmartPointer m_source_algorithm; + + vtkSmartPointer m_source_algorithm; bool m_block_property = false; bool m_data_updated = false; diff --git a/src/Mod/Fem/App/VTKExtensions/CMakeLists.txt b/src/Mod/Fem/App/VTKExtensions/CMakeLists.txt new file mode 100644 index 0000000000..8e9fbd85f7 --- /dev/null +++ b/src/Mod/Fem/App/VTKExtensions/CMakeLists.txt @@ -0,0 +1,6 @@ +SET(FemVTK_SRCS + vtkFemFrameSourceAlgorithm.h + vtkFemFrameSourceAlgorithm.cpp + ) + +target_sources(Fem PRIVATE ${FemVTK_SRCS}) diff --git a/src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.cpp b/src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.cpp new file mode 100644 index 0000000000..91574cf9d2 --- /dev/null +++ b/src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.cpp @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Stefan Tröger * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "vtkFemFrameSourceAlgorithm.h" + +using namespace Fem; + + +vtkStandardNewMacro(vtkFemFrameSourceAlgorithm); + +vtkFemFrameSourceAlgorithm::vtkFemFrameSourceAlgorithm() +{ + // we are a source + SetNumberOfInputPorts(0); + SetNumberOfOutputPorts(1); +} + +vtkFemFrameSourceAlgorithm::~vtkFemFrameSourceAlgorithm() = default; + +void vtkFemFrameSourceAlgorithm::setDataObject(vtkSmartPointer data) +{ + m_data = data; + Modified(); + Update(); +} + +bool vtkFemFrameSourceAlgorithm::isValid() +{ + return m_data.GetPointer() ? true : false; +} + +std::vector vtkFemFrameSourceAlgorithm::getFrameValues() +{ + + // check if we have frame data + if (!m_data || !m_data->IsA("vtkMultiBlockDataSet")) { + return std::vector(); + } + + // we have multiple frames! let's check the amount and times + vtkSmartPointer multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data); + + unsigned long nblocks = multiblock->GetNumberOfBlocks(); + std::vector tFrames(nblocks); + + for (unsigned long i = 0; i < nblocks; i++) { + + vtkDataObject* block = multiblock->GetBlock(i); + // check if the TimeValue field is available + if (!block->GetFieldData()->HasArray("TimeValue")) { + // a frame with no valid value is a undefined state + return std::vector(); + } + + // store the time value! + vtkDataArray* TimeValue = block->GetFieldData()->GetArray("TimeValue"); + if (!TimeValue->IsA("vtkFloatArray") || TimeValue->GetNumberOfTuples() < 1) { + // a frame with no valid value is a undefined state + return std::vector(); + } + + tFrames[i] = vtkFloatArray::SafeDownCast(TimeValue)->GetValue(0); + } + + return tFrames; +} + +int vtkFemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo, + vtkInformationVector** inVector, + vtkInformationVector* outVector) +{ + + // setup default information + if (!this->Superclass::RequestInformation(reqInfo, inVector, outVector)) { + return 0; + } + + if (!m_data) { + // for the no data case we would return a empty data set in RequestData. + return 1; + } + + std::vector frames = getFrameValues(); + + if (frames.empty()) { + // no frames, default info is sufficient + return 1; + } + + double tRange[2] = {frames.front(), frames.back()}; + + // finally set the time info! + vtkInformation* info = outVector->GetInformationObject(0); + info->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), tRange, 2); + info->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), &frames[0], frames.size()); + info->Set(CAN_HANDLE_PIECE_REQUEST(), 1); + + return 1; +} + +int vtkFemFrameSourceAlgorithm::RequestData(vtkInformation*, + vtkInformationVector**, + vtkInformationVector* outVector) +{ + vtkInformation* outInfo = outVector->GetInformationObject(0); + vtkUnstructuredGrid* output = + vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + + if (!output) { + return 0; + } + + if (!m_data) { + outInfo->Set(vtkDataObject::DATA_OBJECT(), vtkUnstructuredGrid::New()); + return 1; + } + + if (!m_data->IsA("vtkMultiBlockDataSet")) { + // no multi frame data, return directly + outInfo->Set(vtkDataObject::DATA_OBJECT(), m_data); + return 1; + } + + vtkSmartPointer multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data); + // find the block asked for (lazy implementation) + unsigned long idx = 0; + if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())) { + auto time = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()); + auto frames = getFrameValues(); + + // we have float values, so be aware of rounding errors. lets subtract the searched time and + // then use the smallest value + for (auto& frame : frames) { + frame = std::abs(frame - time); + } + + auto it = std::ranges::min_element(frames); + idx = std::distance(frames.begin(), it); + } + + auto block = multiblock->GetBlock(idx); + output->ShallowCopy(block); + return 1; +} diff --git a/src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.h b/src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.h new file mode 100644 index 0000000000..62b309174a --- /dev/null +++ b/src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Stefan Tröger * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef Fem_VTK_vtkFemFrameSourceAlgorithm_H +#define Fem_VTK_vtkFemFrameSourceAlgorithm_H + +#include +#include + +class vtkInformation; +class vtkInformationVector; + + +namespace Fem +{ + +// algorithm that allows multi frame handling: if data is stored in MultiBlock dataset +// this source enables the downstream filters to query the blocks as different time frames +class vtkFemFrameSourceAlgorithm: public vtkUnstructuredGridAlgorithm +{ +public: + static vtkFemFrameSourceAlgorithm* New(); + vtkTypeMacro(vtkFemFrameSourceAlgorithm, vtkUnstructuredGridAlgorithm); + + bool isValid(); + void setDataObject(vtkSmartPointer data); + std::vector getFrameValues(); + +protected: + vtkFemFrameSourceAlgorithm(); + ~vtkFemFrameSourceAlgorithm() override; + + vtkSmartPointer m_data; + + int RequestInformation(vtkInformation* reqInfo, + vtkInformationVector** inVector, + vtkInformationVector* outVector) override; + int RequestData(vtkInformation* reqInfo, + vtkInformationVector** inVector, + vtkInformationVector* outVector) override; +}; + +} // namespace Fem + + +#endif // Fem_VTK_vtkFemFrameSourceAlgorithm_H