From 713feb5f27dd4a44bdf1c26b6d2ecd423b525750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Sun, 9 Mar 2025 18:25:11 +0100 Subject: [PATCH] FEM: Multistep test fix for vtk 7.x --- src/Mod/Fem/App/FemPostFilter.cpp | 112 +++++++++++++++------ src/Mod/Fem/App/FemPostFilter.h | 3 + src/Mod/Fem/App/FemPostPipeline.cpp | 14 ++- src/Mod/Fem/App/PropertyPostDataObject.cpp | 1 - src/Mod/Fem/femtest/app/test_object.py | 2 +- 5 files changed, 97 insertions(+), 35 deletions(-) diff --git a/src/Mod/Fem/App/FemPostFilter.cpp b/src/Mod/Fem/App/FemPostFilter.cpp index f4d2dd00db..a6d88dec44 100644 --- a/src/Mod/Fem/App/FemPostFilter.cpp +++ b/src/Mod/Fem/App/FemPostFilter.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #endif #include @@ -164,9 +165,14 @@ void FemPostFilter::onChanged(const App::Property* prop) DocumentObjectExecReturn* FemPostFilter::execute() { - // the pipelines are setup correctly, all we need to do is to update and take out the data. if (!m_pipelines.empty() && !m_activePipeline.empty()) { + + auto active = m_pipelines[m_activePipeline]; + if (active.source->GetNumberOfInputConnections(0) == 0) { + return nullptr; + } + auto output = getFilterOutput(); if (output->GetNumberOfInputConnections(0) == 0) { @@ -179,14 +185,15 @@ DocumentObjectExecReturn* FemPostFilter::execute() else { output->Update(); } + Data.setValue(output->GetOutputDataObject(0)); } return StdReturn; } -vtkSmartPointer FemPostFilter::getInputData() { - +vtkSmartPointer FemPostFilter::getInputData() +{ auto active = m_pipelines[m_activePipeline]; if (active.source->GetNumberOfInputConnections(0) == 0) { return nullptr; @@ -195,7 +202,6 @@ vtkSmartPointer FemPostFilter::getInputData() { vtkAlgorithmOutput* output = active.source->GetInputConnection(0,0); vtkAlgorithm* algo = output->GetProducer(); algo->Update(); - return vtkDataSet::SafeDownCast(algo->GetOutputDataObject(0)); } @@ -579,14 +585,20 @@ FemPostClipFilter::FemPostClipFilter() App::Prop_None, "Decides if cells are cut and interpolated or if the cells are kept as a whole"); + auto sphere = vtkSmartPointer::New(); + sphere->SetRadius(1e12); + m_defaultFunction = sphere; + FilterPipeline clip; m_clipper = vtkSmartPointer::New(); + m_clipper->SetClipFunction(m_defaultFunction); clip.source = m_clipper; clip.target = m_clipper; addFilterPipeline(clip, "clip"); FilterPipeline extr; m_extractor = vtkSmartPointer::New(); + m_extractor->SetImplicitFunction(m_defaultFunction); extr.source = m_extractor; extr.target = m_extractor; addFilterPipeline(extr, "extract"); @@ -604,6 +616,9 @@ void FemPostClipFilter::onChanged(const Property* prop) if (auto* value = Base::freecad_dynamic_cast(Function.getValue())) { m_clipper->SetClipFunction(value->getImplicitFunction()); m_extractor->SetImplicitFunction(value->getImplicitFunction()); + } else { + m_clipper->SetClipFunction(m_defaultFunction); + m_extractor->SetImplicitFunction(m_defaultFunction); } } else if (prop == &InsideOut) { @@ -783,6 +798,7 @@ FemPostContoursFilter::~FemPostContoursFilter() = default; DocumentObjectExecReturn* FemPostContoursFilter::execute() { + // update list of available fields and their vectors if (!m_blockPropertyChanges) { refreshFields(); @@ -790,20 +806,7 @@ DocumentObjectExecReturn* FemPostContoursFilter::execute() } // recalculate the filter - auto returnObject = Fem::FemPostFilter::execute(); - - // delete contour field - vtkDataSet* dset = getInputData(); - if (!dset) { - return returnObject; - } - dset->GetPointData()->RemoveArray(contourFieldName.c_str()); - // refresh fields to reflect the deletion - if (!m_blockPropertyChanges) { - refreshFields(); - } - - return returnObject; + return Fem::FemPostFilter::execute(); } void FemPostContoursFilter::onChanged(const Property* prop) @@ -812,7 +815,7 @@ void FemPostContoursFilter::onChanged(const Property* prop) return; } - if (prop == &Field && (Field.getValue() >= 0)) { + if (prop == &Field && (Field.isValid())) { refreshVectors(); } @@ -820,7 +823,7 @@ void FemPostContoursFilter::onChanged(const Property* prop) // otherwise the contours output would be empty and the ViewProviderFemPostObject // would not get any data if ((prop == &Field || prop == &VectorMode || prop == &NumberOfContours || prop == &Data) - && (Field.getValue() >= 0)) { + && (Field.isValid())) { double p[2]; // get the field and its data @@ -932,20 +935,25 @@ void FemPostContoursFilter::refreshFields() m_blockPropertyChanges = true; std::string fieldName; - if (Field.getValue() >= 0) { + if (Field.isValid()) { fieldName = Field.getValueAsString(); } std::vector FieldsArray; vtkDataSet* dset = getInputData(); - if (!dset) { + if (!dset || !dset->GetPointData()) { + // no valid data: no fields to choose! + App::Enumeration empty; + Field.setValue(empty); + m_fields.setEnums(FieldsArray); + Field.setValue(m_fields); m_blockPropertyChanges = false; return; } - vtkPointData* pd = dset->GetPointData(); // get all fields + auto pd = dset->GetPointData(); for (int i = 0; i < pd->GetNumberOfArrays(); ++i) { FieldsArray.emplace_back(pd->GetArrayName(i)); } @@ -956,6 +964,7 @@ void FemPostContoursFilter::refreshFields() Field.setValue(m_fields); // search if the current field is in the available ones and set it +<<<<<<< HEAD const auto it = std::ranges::find(FieldsArray, fieldName); if (!fieldName.empty() && it != FieldsArray.end()) { Field.setValue(fieldName.c_str()); @@ -965,6 +974,20 @@ void FemPostContoursFilter::refreshFields() // select the first field Field.setValue(long(0)); fieldName = Field.getValueAsString(); +======= + // Note: list could be empty and hence Field invalid + if (Field.isValid()) { + std::vector::iterator it = + std::find(FieldsArray.begin(), FieldsArray.end(), fieldName); + if (!fieldName.empty() && it != FieldsArray.end()) { + Field.setValue(fieldName.c_str()); + } + else { + m_blockPropertyChanges = false; + // select the first field + Field.setValue(long(0)); + } +>>>>>>> cf27d7e05b (FEM: Multistep test fix for vtk 7.x) } m_blockPropertyChanges = false; @@ -975,25 +998,36 @@ void FemPostContoursFilter::refreshVectors() { // refreshes the list of available vectors for the current Field m_blockPropertyChanges = true; + std::vector vectorArray; vtkDataSet* dset = getInputData(); - if (!dset) { + if (!dset || !dset->GetPointData() || !Field.isValid()) { + vectorArray.emplace_back("Not a vector"); + App::Enumeration empty; + VectorMode.setValue(empty); + m_vectors.setEnums(vectorArray); + VectorMode.setValue(m_vectors); m_blockPropertyChanges = false; return; } + vtkDataArray* fieldArray = dset->GetPointData()->GetArray(Field.getValueAsString()); if (!fieldArray) { + vectorArray.emplace_back("Not a vector"); + App::Enumeration empty; + VectorMode.setValue(empty); + m_vectors.setEnums(vectorArray); + VectorMode.setValue(m_vectors); m_blockPropertyChanges = false; return; } // store name if already set std::string vectorName; - if (VectorMode.hasEnums() && VectorMode.getValue() >= 0) { + if (VectorMode.isValid()) { vectorName = VectorMode.getValueAsString(); } - std::vector vectorArray; if (fieldArray->GetNumberOfComponents() == 1) { vectorArray.emplace_back("Not a vector"); } @@ -1035,8 +1069,14 @@ FemPostCutFilter::FemPostCutFilter() App::Prop_None, "The function object which defines the cut function"); + auto sphere = vtkSmartPointer::New(); + sphere->SetRadius(1e12); + m_defaultFunction = sphere; + + FilterPipeline cut; m_cutter = vtkSmartPointer::New(); + m_cutter->SetCutFunction(m_defaultFunction); cut.source = m_cutter; cut.target = m_cutter; addFilterPipeline(cut, "cut"); @@ -1050,6 +1090,8 @@ void FemPostCutFilter::onChanged(const Property* prop) if (prop == &Function) { if (auto* value = Base::freecad_dynamic_cast(Function.getValue())) { m_cutter->SetCutFunction(value->getImplicitFunction()); + } else { + m_cutter->SetCutFunction(m_defaultFunction); } } @@ -1106,8 +1148,8 @@ FemPostScalarClipFilter::~FemPostScalarClipFilter() = default; DocumentObjectExecReturn* FemPostScalarClipFilter::execute() { - std::string val; - if (Scalars.getValue() >= 0) { + std::string val = ""; + if (Scalars.isValid()) { val = Scalars.getValueAsString(); } @@ -1136,7 +1178,7 @@ void FemPostScalarClipFilter::onChanged(const Property* prop) else if (prop == &InsideOut) { m_clipper->SetInsideOut(InsideOut.getValue()); } - else if (prop == &Scalars && (Scalars.getValue() >= 0)) { + else if (prop == &Scalars && (Scalars.isValid())) { m_clipper->SetInputArrayToProcess(0, 0, 0, @@ -1161,7 +1203,10 @@ short int FemPostScalarClipFilter::mustExecute() const void FemPostScalarClipFilter::setConstraintForField() { vtkDataSet* dset = getInputData(); - if (!dset) { + if (!dset || !dset->GetPointData() || !Scalars.isValid()) { + m_constraints.LowerBound = 0; + m_constraints.UpperBound = 1; + m_constraints.StepSize = 1; return; } @@ -1169,6 +1214,9 @@ void FemPostScalarClipFilter::setConstraintForField() // VTK cannot deliver data when the filer relies e.g. on a cut clip filter // whose value is set so that all data are cut if (!pdata) { + m_constraints.LowerBound = 0; + m_constraints.UpperBound = 1; + m_constraints.StepSize = 1; return; } double p[2]; @@ -1211,7 +1259,7 @@ DocumentObjectExecReturn* FemPostWarpVectorFilter::execute() { std::string val; - if (Vector.getValue() >= 0) { + if (Vector.isValid()) { val = Vector.getValueAsString(); } @@ -1238,7 +1286,7 @@ void FemPostWarpVectorFilter::onChanged(const Property* prop) // since our mesh is in mm, we must scale the factor m_warp->SetScaleFactor(1000 * Factor.getValue()); } - else if (prop == &Vector && (Vector.getValue() >= 0)) { + else if (prop == &Vector && Vector.isValid()) { m_warp->SetInputArrayToProcess(0, 0, 0, diff --git a/src/Mod/Fem/App/FemPostFilter.h b/src/Mod/Fem/App/FemPostFilter.h index 03300c9d93..f7cdab208d 100644 --- a/src/Mod/Fem/App/FemPostFilter.h +++ b/src/Mod/Fem/App/FemPostFilter.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -232,6 +233,7 @@ protected: private: vtkSmartPointer m_clipper; vtkSmartPointer m_extractor; + vtkSmartPointer m_defaultFunction; }; @@ -304,6 +306,7 @@ protected: private: vtkSmartPointer m_cutter; + vtkSmartPointer m_defaultFunction; }; diff --git a/src/Mod/Fem/App/FemPostPipeline.cpp b/src/Mod/Fem/App/FemPostPipeline.cpp index 2d7d19426f..26410af542 100644 --- a/src/Mod/Fem/App/FemPostPipeline.cpp +++ b/src/Mod/Fem/App/FemPostPipeline.cpp @@ -125,13 +125,20 @@ int FemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo, 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 sfficient return 1; } @@ -155,10 +162,15 @@ int FemFrameSourceAlgorithm::RequestData(vtkInformation*, vtkUnstructuredGrid* output = vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); - if (!output || !m_data) { + 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); diff --git a/src/Mod/Fem/App/PropertyPostDataObject.cpp b/src/Mod/Fem/App/PropertyPostDataObject.cpp index 8f495a1faf..ab28d207c0 100644 --- a/src/Mod/Fem/App/PropertyPostDataObject.cpp +++ b/src/Mod/Fem/App/PropertyPostDataObject.cpp @@ -334,7 +334,6 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const // once the tmp. filename is known use always the same because otherwise // we may run into some problems on the Linux platform static Base::FileInfo fi = Base::FileInfo(App::Application::getTempFileName()); - bool success = false; Base::FileInfo datafolder; vtkSmartPointer xmlWriter; diff --git a/src/Mod/Fem/femtest/app/test_object.py b/src/Mod/Fem/femtest/app/test_object.py index 6ca7f073e5..f2b154b5be 100644 --- a/src/Mod/Fem/femtest/app/test_object.py +++ b/src/Mod/Fem/femtest/app/test_object.py @@ -1123,9 +1123,9 @@ def create_all_fem_objects_doc(doc): vres = analysis.addObject(ObjectsFem.makePostVtkResult(doc, [res]))[0] ObjectsFem.makePostVtkFilterClipRegion(doc, vres) ObjectsFem.makePostVtkFilterClipScalar(doc, vres) - ObjectsFem.makePostVtkFilterContours(doc, vres) ObjectsFem.makePostVtkFilterCutFunction(doc, vres) ObjectsFem.makePostVtkFilterWarp(doc, vres) + ObjectsFem.makePostVtkFilterContours(doc, vres) analysis.addObject(ObjectsFem.makeSolverCalculiXCcxTools(doc)) analysis.addObject(ObjectsFem.makeSolverCalculix(doc))