Fem: Move FemFrameSourceAlgorithm class to its own source files

This commit is contained in:
marioalexis
2025-09-14 00:14:52 -03:00
parent 3ffb861d11
commit 3e1741dfea
6 changed files with 265 additions and 173 deletions

View File

@@ -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(

View File

@@ -40,7 +40,6 @@
#include <vtkXMLUnstructuredGridReader.h>
#include <vtkXMLMultiBlockDataReader.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkStreamingDemandDrivenPipeline.h>
#include <vtkFloatArray.h>
#include <vtkStringArray.h>
#include <vtkInformation.h>
@@ -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<vtkDataObject> data)
{
m_data = data;
Modified();
Update();
}
bool FemFrameSourceAlgorithm::isValid()
{
return m_data.GetPointer() != nullptr;
}
std::vector<double> FemFrameSourceAlgorithm::getFrameValues()
{
// check if we have frame data
if (!m_data || !m_data->IsA("vtkMultiBlockDataSet")) {
return std::vector<double>();
}
// we have multiple frames! let's check the amount and times
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
unsigned long nblocks = multiblock->GetNumberOfBlocks();
std::vector<double> 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<double>();
}
// 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<double>();
}
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<double> 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<vtkMultiBlockDataSet> 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<FemFrameSourceAlgorithm>::New();
m_source_algorithm = vtkSmartPointer<vtkFemFrameSourceAlgorithm>::New();
m_transform_filter->SetInputConnection(m_source_algorithm->GetOutputPort(0));
}

View File

@@ -30,44 +30,14 @@
#include "FemPostFunction.h"
#include "FemPostObject.h"
#include "FemResultObject.h"
#include "VTKExtensions/vtkFemFrameSourceAlgorithm.h"
#include <vtkSmartPointer.h>
#include <vtkUnstructuredGridAlgorithm.h>
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<vtkDataObject> data);
std::vector<double> getFrameValues();
protected:
FemFrameSourceAlgorithm();
~FemFrameSourceAlgorithm() override;
vtkSmartPointer<vtkDataObject> 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<FemFrameSourceAlgorithm> m_source_algorithm;
vtkSmartPointer<vtkFemFrameSourceAlgorithm> m_source_algorithm;
bool m_block_property = false;
bool m_data_updated = false;

View File

@@ -0,0 +1,6 @@
SET(FemVTK_SRCS
vtkFemFrameSourceAlgorithm.h
vtkFemFrameSourceAlgorithm.cpp
)
target_sources(Fem PRIVATE ${FemVTK_SRCS})

View File

@@ -0,0 +1,180 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <cmath>
#include <algorithm>
#include <iterator>
#include <vector>
#include <vtkUnstructuredGrid.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkFieldData.h>
#include <vtkStreamingDemandDrivenPipeline.h>
#include <vtkFloatArray.h>
#include <vtkInformation.h>
#include <vtkInformationVector.h>
#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<vtkDataObject> data)
{
m_data = data;
Modified();
Update();
}
bool vtkFemFrameSourceAlgorithm::isValid()
{
return m_data.GetPointer() ? true : false;
}
std::vector<double> vtkFemFrameSourceAlgorithm::getFrameValues()
{
// check if we have frame data
if (!m_data || !m_data->IsA("vtkMultiBlockDataSet")) {
return std::vector<double>();
}
// we have multiple frames! let's check the amount and times
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
unsigned long nblocks = multiblock->GetNumberOfBlocks();
std::vector<double> 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<double>();
}
// 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<double>();
}
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<double> 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<vtkMultiBlockDataSet> 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;
}

View File

@@ -0,0 +1,66 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef Fem_VTK_vtkFemFrameSourceAlgorithm_H
#define Fem_VTK_vtkFemFrameSourceAlgorithm_H
#include <vtkSmartPointer.h>
#include <vtkUnstructuredGridAlgorithm.h>
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<vtkDataObject> data);
std::vector<double> getFrameValues();
protected:
vtkFemFrameSourceAlgorithm();
~vtkFemFrameSourceAlgorithm() override;
vtkSmartPointer<vtkDataObject> 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