diff --git a/src/App/OriginGroupExtension.cpp b/src/App/OriginGroupExtension.cpp index a657b9d7a0..5b8a9a0163 100644 --- a/src/App/OriginGroupExtension.cpp +++ b/src/App/OriginGroupExtension.cpp @@ -40,7 +40,7 @@ EXTENSION_PROPERTY_SOURCE(App::OriginGroupExtension, App::GeoFeatureGroupExtensi OriginGroupExtension::OriginGroupExtension() { - Base::Console().Message("Extension type: %s\n", OriginGroupExtension::getExtensionClassTypeId().getName()); + initExtensionType(OriginGroupExtension::getExtensionClassTypeId()); EXTENSION_ADD_PROPERTY_TYPE(Origin, diff --git a/src/Mod/Fem/App/FemPostBranchFilter.cpp b/src/Mod/Fem/App/FemPostBranchFilter.cpp index e9b2d76d2f..36eef44d85 100644 --- a/src/Mod/Fem/App/FemPostBranchFilter.cpp +++ b/src/Mod/Fem/App/FemPostBranchFilter.cpp @@ -79,6 +79,7 @@ FemPostBranchFilter::FemPostBranchFilter() : Fem::FemPostFilter(), Fem::FemPostG m_append = vtkSmartPointer::New(); m_passthrough = vtkSmartPointer::New(); + m_transform_filter->SetInputConnection(m_passthrough->GetOutputPort(0)); FilterPipeline passthrough; passthrough.source = m_passthrough; @@ -90,6 +91,7 @@ FemPostBranchFilter::FemPostBranchFilter() : Fem::FemPostFilter(), Fem::FemPostG append.target = m_append; addFilterPipeline(append, "append"); + setTransformLocation(TransformLocation::input); setActiveFilterPipeline("passthrough"); } @@ -104,6 +106,48 @@ short FemPostBranchFilter::mustExecute() const return FemPostFilter::mustExecute(); } +void FemPostBranchFilter::setupPipeline() +{ + // we check if all connections are right and add new ones if needed + std::vector objs = Group.getValues(); + + if (objs.empty()) { + return; + } + + + // prepare output filter: we make all connections new! + m_append->RemoveAllInputConnections(0); + + FemPostFilter* filter = NULL; + std::vector::iterator it = objs.begin(); + for (; it != objs.end(); ++it) { + + // prepare the filter: make all connections new + FemPostFilter* nextFilter = static_cast(*it); + nextFilter->getFilterInput()->RemoveAllInputConnections(0); + + // handle input modes + if (Mode.getValue() == 0) { + // serial: the next filter gets the previous output, the first one gets our input + if (filter == NULL) { + nextFilter->getFilterInput()->SetInputConnection(m_passthrough->GetOutputPort()); + } else { + nextFilter->getFilterInput()->SetInputConnection(filter->getFilterOutput()->GetOutputPort()); + } + + } + else if (Mode.getValue() == 1) { + // parallel: all filters get out input + nextFilter->getFilterInput()->SetInputConnection(m_passthrough->GetOutputPort()); + } + + // handle append filter + m_append->AddInputConnection(0, nextFilter->getFilterOutput()->GetOutputPort()); + + filter = nextFilter; + }; +} void FemPostBranchFilter::onChanged(const Property* prop) { @@ -112,46 +156,7 @@ void FemPostBranchFilter::onChanged(const Property* prop) */ if (prop == &Group || prop == &Mode) { - - - // we check if all connections are right and add new ones if needed - std::vector objs = Group.getValues(); - - if (objs.empty()) { - return; - } - - // prepare output filter: we make all connections new! - m_append->RemoveAllInputConnections(0); - - FemPostFilter* filter = NULL; - std::vector::iterator it = objs.begin(); - for (; it != objs.end(); ++it) { - - // prepare the filter: make all connections new - FemPostFilter* nextFilter = static_cast(*it); - nextFilter->getActiveFilterPipeline().source->RemoveAllInputConnections(0); - - // handle input modes - if (Mode.getValue() == 0) { - // serial: the next filter gets the previous output, the first one gets our input - if (filter == NULL) { - nextFilter->getActiveFilterPipeline().source->SetInputConnection(m_passthrough->GetOutputPort()); - } else { - nextFilter->getActiveFilterPipeline().source->SetInputConnection(filter->getActiveFilterPipeline().target->GetOutputPort()); - } - - } - else if (Mode.getValue() == 1) { - // parallel: all filters get out input - nextFilter->getActiveFilterPipeline().source->SetInputConnection(m_passthrough->GetOutputPort()); - } - - // handle append filter - m_append->AddInputConnection(0, nextFilter->getActiveFilterPipeline().target->GetOutputPort()); - - filter = nextFilter; - }; + setupPipeline(); } if (prop == &Frame) { diff --git a/src/Mod/Fem/App/FemPostBranchFilter.h b/src/Mod/Fem/App/FemPostBranchFilter.h index fd04b51e4c..60ccf05836 100644 --- a/src/Mod/Fem/App/FemPostBranchFilter.h +++ b/src/Mod/Fem/App/FemPostBranchFilter.h @@ -65,6 +65,9 @@ protected: private: static const char* OutputEnums[]; + bool m_transform_used = false; + void setupPipeline(); + vtkSmartPointer m_append; vtkSmartPointer m_passthrough; }; diff --git a/src/Mod/Fem/App/FemPostFilter.cpp b/src/Mod/Fem/App/FemPostFilter.cpp index 6fd7616e58..f4d2dd00db 100644 --- a/src/Mod/Fem/App/FemPostFilter.cpp +++ b/src/Mod/Fem/App/FemPostFilter.cpp @@ -75,34 +75,85 @@ void FemPostFilter::setActiveFilterPipeline(std::string name) m_pipelines[m_activePipeline].source->RemoveAllInputConnections(0); } + // handle the transform + if (m_use_transform) { + m_transform_filter->RemoveAllInputConnections(0); + if(m_transform_location == TransformLocation::output) { + m_transform_filter->SetInputConnection(m_pipelines[name].target->GetOutputPort(0)); + } else { + m_pipelines[name].source->SetInputConnection(m_transform_filter->GetOutputPort(0)); + } + } + //set the new pipeline active m_activePipeline = name; - - //inform our parent, that we need to be reconnected - App::DocumentObject* group = FemPostGroupExtension::getGroupOfObject(this); - if (!group) { - return; - } - if (group->hasExtension(FemPostGroupExtension::getExtensionClassTypeId())) { - auto postgroup = group->getExtensionByType(); - postgroup->filterPipelineChanged(this); - } + pipelineChanged(); } } -FemPostFilter::FilterPipeline& FemPostFilter::getActiveFilterPipeline() +vtkSmartPointer FemPostFilter::getFilterInput() { - return m_pipelines[m_activePipeline]; + if (m_use_transform && + m_transform_location == TransformLocation::input) { + + return m_transform_filter; + } + + return m_pipelines[m_activePipeline].source; } -void FemPostFilter::onChanged(const App::Property* prop) +vtkSmartPointer FemPostFilter::getFilterOutput() { - //make sure we inform our parent object that we changed, it then can inform others if needed + if (m_use_transform && + m_transform_location == TransformLocation::output) { + + return m_transform_filter; + } + + return m_pipelines[m_activePipeline].target; +} + +void FemPostFilter::pipelineChanged() { + //inform our parent, that we need to be reconnected App::DocumentObject* group = FemPostGroupExtension::getGroupOfObject(this); if (!group) { return; } if (group->hasExtension(FemPostGroupExtension::getExtensionClassTypeId())) { + auto postgroup = group->getExtensionByType(); + postgroup->filterPipelineChanged(this); + } +} + +void FemPostFilter::onChanged(const App::Property* prop) +{ + + if (prop == &Placement) { + if (Placement.getValue().isIdentity() && m_use_transform) { + // remove transform from pipeline + if (m_transform_location == TransformLocation::output) { + m_transform_filter->RemoveAllInputConnections(0); + } else { + m_pipelines[m_activePipeline].source->RemoveAllInputConnections(0); + } + m_use_transform = false; + pipelineChanged(); + } + if(!Placement.getValue().isIdentity() && !m_use_transform) { + // add transform to pipeline + if (m_transform_location == TransformLocation::output) { + m_transform_filter->SetInputConnection(m_pipelines[m_activePipeline].target->GetOutputPort(0)); + } else { + m_pipelines[m_activePipeline].source->SetInputConnection(m_transform_filter->GetOutputPort(0)); + } + m_use_transform = true; + pipelineChanged(); + } + } + + //make sure we inform our parent object that we changed, it then can inform others if needed + App::DocumentObject* group = FemPostGroupExtension::getGroupOfObject(this); + if (group && group->hasExtension(FemPostGroupExtension::getExtensionClassTypeId())) { auto postgroup = group->getExtensionByType(); postgroup->filterChanged(this); } @@ -116,19 +167,19 @@ 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()) { - FemPostFilter::FilterPipeline& pipe = m_pipelines[m_activePipeline]; + auto output = getFilterOutput(); - if (pipe.source->GetNumberOfInputConnections(0) == 0) { + if (output->GetNumberOfInputConnections(0) == 0) { return StdReturn; } if (Frame.getValue()>0) { - pipe.target->UpdateTimeStep(Frame.getValue()); + output->UpdateTimeStep(Frame.getValue()); } else { - pipe.target->Update(); + output->Update(); } - Data.setValue(pipe.target->GetOutputDataObject(0)); + Data.setValue(output->GetOutputDataObject(0)); } return StdReturn; @@ -136,11 +187,12 @@ DocumentObjectExecReturn* FemPostFilter::execute() vtkSmartPointer FemPostFilter::getInputData() { - if (getActiveFilterPipeline().source->GetNumberOfInputConnections(0) == 0) { + auto active = m_pipelines[m_activePipeline]; + if (active.source->GetNumberOfInputConnections(0) == 0) { return nullptr; } - vtkAlgorithmOutput* output = getActiveFilterPipeline().source->GetInputConnection(0,0); + vtkAlgorithmOutput* output = active.source->GetInputConnection(0,0); vtkAlgorithm* algo = output->GetProducer(); algo->Update(); @@ -185,6 +237,11 @@ std::vector FemPostFilter::getInputScalarFields() return ScalarArray; } +void FemPostFilter::setTransformLocation(TransformLocation loc) +{ + m_transform_location = loc; +} + // *************************************************************************** // in the following, the different filters sorted alphabetically // *************************************************************************** diff --git a/src/Mod/Fem/App/FemPostFilter.h b/src/Mod/Fem/App/FemPostFilter.h index 73d9b5e5bf..b0db05d949 100644 --- a/src/Mod/Fem/App/FemPostFilter.h +++ b/src/Mod/Fem/App/FemPostFilter.h @@ -45,6 +45,8 @@ namespace Fem { +enum TransformLocation { input, output }; + class FemExport FemPostFilter: public Fem::FemPostObject { PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostFilter); @@ -65,6 +67,8 @@ protected: void setActiveFilterPipeline(std::string name); FilterPipeline& getFilterPipeline(std::string name); + void setTransformLocation(TransformLocation loc); + public: /// Constructor FemPostFilter(); @@ -75,12 +79,17 @@ public: void onChanged(const App::Property* prop) override; App::DocumentObjectExecReturn* execute() override; - FilterPipeline& getActiveFilterPipeline(); + vtkSmartPointer getFilterInput(); + vtkSmartPointer getFilterOutput(); private: // handling of multiple pipelines which can be the filter std::map m_pipelines; std::string m_activePipeline; + bool m_use_transform = false; + TransformLocation m_transform_location = TransformLocation::output; + + void pipelineChanged(); // inform parents that the pipeline changed }; class FemExport FemPostSmoothFilterExtension: public App::DocumentObjectExtension diff --git a/src/Mod/Fem/App/FemPostGroupExtension.cpp b/src/Mod/Fem/App/FemPostGroupExtension.cpp index 040df298a3..97fec2cb0f 100644 --- a/src/Mod/Fem/App/FemPostGroupExtension.cpp +++ b/src/Mod/Fem/App/FemPostGroupExtension.cpp @@ -123,6 +123,10 @@ void FemPostGroupExtension::recomputeChildren() { for (const auto& obj : Group.getValues()) { obj->touch(); + + if (obj->hasExtension(Fem::FemPostGroupExtension::getExtensionClassTypeId())) { + obj->getExtension()->recomputeChildren(); + } } } diff --git a/src/Mod/Fem/App/FemPostObject.cpp b/src/Mod/Fem/App/FemPostObject.cpp index 6f4c717361..872520bb7e 100644 --- a/src/Mod/Fem/App/FemPostObject.cpp +++ b/src/Mod/Fem/App/FemPostObject.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #endif #include @@ -43,6 +44,16 @@ PROPERTY_SOURCE(Fem::FemPostObject, App::GeoFeature) FemPostObject::FemPostObject() { ADD_PROPERTY(Data, (nullptr)); + + m_transform_filter = vtkSmartPointer::New(); + + // define default transform + double data[16]; + auto matrix = Placement.getValue().toMatrix(); + matrix.getMatrix(data); + vtkTransform* transform = vtkTransform::New(); + transform->SetMatrix(data); + m_transform_filter->SetTransform(transform); } FemPostObject::~FemPostObject() = default; @@ -86,6 +97,26 @@ PyObject* FemPostObject::getPyObject() return Py::new_reference_to(PythonObject); } +void FemPostObject::onChanged(const App::Property* prop) +{ + if(prop == &Placement) { + // we update the transform filter to match the placement! + double data[16]; + auto matrix = Placement.getValue().toMatrix(); + matrix.getMatrix(data); + vtkTransform* transform = vtkTransform::New(); + transform->SetMatrix(data); + m_transform_filter->SetTransform(transform); + //note: no call to Update(), as we do not know the frame to use. has to happen + //in derived class + + // placement would not recompute, as it is a "not touch" prop. + this->touch(); + } + + App::GeoFeature::onChanged(prop); +} + namespace { diff --git a/src/Mod/Fem/App/FemPostObject.h b/src/Mod/Fem/App/FemPostObject.h index 25c9fbfe6c..d9fa1f3c4a 100644 --- a/src/Mod/Fem/App/FemPostObject.h +++ b/src/Mod/Fem/App/FemPostObject.h @@ -26,8 +26,11 @@ #include "PropertyPostDataObject.h" #include +#include #include #include +#include +#include namespace Fem @@ -56,6 +59,14 @@ public: vtkBoundingBox getBoundingBox(); void writeVTK(const char* filename) const; + +protected: + // placement is applied via transform filter. However, we do not know + // how this filter should be used to create data. This is to be implemented + // by the derived classes. + vtkSmartPointer m_transform_filter; + + void onChanged(const App::Property* prop) override; }; } // namespace Fem diff --git a/src/Mod/Fem/App/FemPostPipeline.cpp b/src/Mod/Fem/App/FemPostPipeline.cpp index 19c4fc8d25..002a5b0e0d 100644 --- a/src/Mod/Fem/App/FemPostPipeline.cpp +++ b/src/Mod/Fem/App/FemPostPipeline.cpp @@ -79,6 +79,9 @@ void FemFrameSourceAlgorithm::setDataObject(vtkSmartPointer data) Update(); } +bool FemFrameSourceAlgorithm::isValid() { + return m_data.GetPointer() != nullptr; +} std::vector FemFrameSourceAlgorithm::getFrameValues() { @@ -128,10 +131,6 @@ int FemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo, return 0; } - - std::stringstream strm; - outVector->Print(strm); - std::vector frames = getFrameValues(); if (frames.empty()) { @@ -139,13 +138,11 @@ int FemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo, } double tRange[2] = {frames.front(), frames.back()}; - double tFrames[frames.size()]; - std::copy(frames.begin(), frames.end(), tFrames); // finally set the time info! vtkInformation* info = outVector->GetInformationObject(0); info->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), tRange, 2); - info->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), tFrames, frames.size()); + info->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), &frames[0], frames.size()); info->Set(CAN_HANDLE_PIECE_REQUEST(), 1); return 1; @@ -156,9 +153,6 @@ int FemFrameSourceAlgorithm::RequestData(vtkInformation*, vtkInformationVector* outVector) { - std::stringstream strstm; - outVector->Print(strstm); - vtkInformation* outInfo = outVector->GetInformationObject(0); vtkUnstructuredGrid* output = vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); @@ -214,23 +208,19 @@ FemPostPipeline::FemPostPipeline() // create our source algorithm m_source_algorithm = vtkSmartPointer::New(); + m_transform_filter->SetInputConnection(m_source_algorithm->GetOutputPort(0)); } FemPostPipeline::~FemPostPipeline() = default; -short FemPostPipeline::mustExecute() const -{ - if (Mode.isTouched()) { - return 1; - } - - return FemPostObject::mustExecute(); -} - vtkDataSet* FemPostPipeline::getDataSet() { - vtkDataObject* data = m_source_algorithm->GetOutputDataObject(0); + if (!m_source_algorithm->isValid()) { + return nullptr; + } + + vtkDataObject* data = m_transform_filter->GetOutputDataObject(0); if (!data) { return nullptr; } @@ -316,6 +306,35 @@ void FemPostPipeline::scale(double s) onChanged(&Data); } +App::DocumentObjectExecReturn* FemPostPipeline::execute() +{ + // we fake a recalculated data oject, so that the viewprovider updates + // the visualization. We do not want to do this in onChange, as it + // could theoretically be long running + if (m_data_updated) { + + auto frames = getFrameValues(); + if (!frames.empty() && Frame.getValue() < long(frames.size())) { + + double time = frames[Frame.getValue()]; + m_transform_filter->UpdateTimeStep(time); + } else { + m_transform_filter->Update(); + } + + m_block_property = true; + FemPostObject::onChanged(&Data); + m_block_property = false; + m_data_updated = false; + } + return FemPostObject::execute(); +} + +void FemPostPipeline::updateData() +{ + m_data_updated = true; +} + void FemPostPipeline::onChanged(const Property* prop) { /* onChanged handles the Pipeline setup: we connect the inputs and outputs @@ -324,9 +343,17 @@ void FemPostPipeline::onChanged(const Property* prop) FemPostObject::onChanged(prop); + // update placement + if (prop == &Placement) { + // pipeline data updated! + updateData(); + recomputeChildren(); + } + // use the correct data as source - if (prop == &Data) { + if (prop == &Data && !m_block_property) { m_source_algorithm->setDataObject(Data.getValue()); + m_transform_filter->Update(); // change the frame enum to correct values std::string val; @@ -348,31 +375,43 @@ void FemPostPipeline::onChanged(const Property* prop) } App::Enumeration empty; + m_block_property = true; Frame.setValue(empty); m_frameEnum.setEnums(frame_values); Frame.setValue(m_frameEnum); + Frame.purgeTouched(); + m_block_property = false; std::vector::iterator it = std::find(frame_values.begin(), frame_values.end(), val); if (!val.empty() && it != frame_values.end()) { + // frame stays the same + m_block_property = true; Frame.setValue(val.c_str()); + m_block_property = false; + } else { + // frame gets updated + Frame.setValue(long(0)); } - Frame.purgeTouched(); recomputeChildren(); } - if (prop == &Frame) { + if (prop == &Frame && !m_block_property) { - // update the algorithm for the visulization - auto frames = getFrameValues(); - if (!frames.empty() && Frame.getValue() < long(frames.size())) { - - double time = frames[Frame.getValue()]; - m_source_algorithm->UpdateTimeStep(time); + //Update all children with the new frame + double value = 0; + auto frames = m_source_algorithm->getFrameValues(); + if (!frames.empty() && frames.size() > ulong(Frame.getValue())) { + value = frames[Frame.getValue()]; } - - // inform the downstream pipeline + for (const auto& obj : Group.getValues()) { + if (obj->isDerivedFrom(FemPostFilter::getClassTypeId())) { + static_cast(obj)->Frame.setValue(value); + } + } + // pipeline data updated! + updateData(); recomputeChildren(); } @@ -393,29 +432,32 @@ void FemPostPipeline::onChanged(const Property* prop) // prepare the filter: make all connections new FemPostFilter* nextFilter = *it; - nextFilter->getActiveFilterPipeline().source->RemoveAllInputConnections(0); + nextFilter->getFilterInput()->RemoveAllInputConnections(0); // handle input modes (Parallel is seperated, alll other settings are serial, just in // case an old document is loaded with "custom" mode, idx 2) if (Mode.getValue() == 1) { // parallel: all filters get out input - nextFilter->getActiveFilterPipeline().source->SetInputConnection( - m_source_algorithm->GetOutputPort(0)); + nextFilter->getFilterInput()->SetInputConnection( + m_transform_filter->GetOutputPort(0)); } else { // serial: the next filter gets the previous output, the first one gets our input if (filter == NULL) { - nextFilter->getActiveFilterPipeline().source->SetInputConnection( - m_source_algorithm->GetOutputPort(0)); + nextFilter->getFilterInput()->SetInputConnection( + m_transform_filter->GetOutputPort(0)); } else { - nextFilter->getActiveFilterPipeline().source->SetInputConnection( - filter->getActiveFilterPipeline().target->GetOutputPort()); + nextFilter->getFilterInput()->SetInputConnection( + filter->getFilterOutput()->GetOutputPort()); } } filter = nextFilter; }; + + // inform the downstream pipeline + recomputeChildren(); } } @@ -435,6 +477,9 @@ void FemPostPipeline::filterChanged(FemPostFilter* filter) if (started) { (*it)->touch(); + if((*it)->hasExtension(Fem::FemPostGroupExtension::getExtensionClassTypeId())) { + (*it)->getExtension()->recomputeChildren(); + } } if (*it == filter) { @@ -452,26 +497,6 @@ void FemPostPipeline::filterPipelineChanged(FemPostFilter*) onChanged(&Group); } -void FemPostPipeline::recomputeChildren() -{ - // get the frame we use - double frame = 0; - auto frames = getFrameValues(); - if (!frames.empty() && Frame.getValue() < long(frames.size())) { - - frame = frames[Frame.getValue()]; - } - - for (const auto& obj : Group.getValues()) { - obj->touch(); - - if (obj->isDerivedFrom(FemPostFilter::getClassTypeId())) { - static_cast(obj)->Frame.setValue(frame); - } - } -} - - bool FemPostPipeline::hasFrames() { // lazy implementation diff --git a/src/Mod/Fem/App/FemPostPipeline.h b/src/Mod/Fem/App/FemPostPipeline.h index 0d147c1f4f..e0d7b7a781 100644 --- a/src/Mod/Fem/App/FemPostPipeline.h +++ b/src/Mod/Fem/App/FemPostPipeline.h @@ -48,6 +48,7 @@ public: static FemFrameSourceAlgorithm* New(); vtkTypeMacro(FemFrameSourceAlgorithm, vtkUnstructuredGridAlgorithm); + bool isValid(); void setDataObject(vtkSmartPointer data); std::vector getFrameValues(); @@ -77,7 +78,7 @@ public: virtual vtkDataSet* getDataSet() override; Fem::FemPostFunctionProvider* getFunctionProvider(); - short mustExecute() const override; + App::DocumentObjectExecReturn* execute() override; PyObject* getPyObject() override; const char* getViewProviderName() const override @@ -97,7 +98,6 @@ public: // Group pipeline handling void filterChanged(FemPostFilter* filter) override; void filterPipelineChanged(FemPostFilter* filter) override; - void recomputeChildren() override; // frame handling bool hasFrames(); @@ -118,6 +118,12 @@ private: App::Enumeration m_frameEnum; vtkSmartPointer m_source_algorithm; + bool m_block_property = false; + bool m_data_updated = false; + bool m_use_transform = false; + void updateData(); + + template void readXMLFile(std::string file) { diff --git a/src/Mod/Fem/App/PropertyPostDataObject.cpp b/src/Mod/Fem/App/PropertyPostDataObject.cpp index 4fbe6e1c18..cdd8faa7a4 100644 --- a/src/Mod/Fem/App/PropertyPostDataObject.cpp +++ b/src/Mod/Fem/App/PropertyPostDataObject.cpp @@ -335,49 +335,38 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const static Base::FileInfo fi = Base::FileInfo(App::Application::getTempFileName()); bool success = false; - + Base::FileInfo datafolder; + vtkSmartPointer xmlWriter; if (m_dataObject->IsA("vtkMultiBlockDataSet")) { // create a tmp directory to write in - auto datafolder = Base::FileInfo(App::Application::getTempPath() + "vtk_datadir"); + datafolder = Base::FileInfo(App::Application::getTempPath() + "vtk_datadir"); datafolder.createDirectories(); auto datafile = Base::FileInfo(datafolder.filePath() + "/datafile.vtm"); //create the data: vtm file and subfolder with the subsequent data files - auto xmlWriter = vtkSmartPointer::New(); + xmlWriter = vtkSmartPointer::New(); xmlWriter->SetInputDataObject(m_dataObject); xmlWriter->SetFileName(datafile.filePath().c_str()); xmlWriter->SetDataModeToBinary(); - success = xmlWriter->Write() == 1; - - if (success) { - // ZIP file we store all data in - zipios::ZipOutputStream ZipWriter(fi.filePath()); - ZipWriter.putNextEntry("dummy"); //need to add a dummy first, as the read stream always omits the first entry for unknown reasons - add_to_zip(datafolder, datafolder.filePath().length(), ZipWriter); - ZipWriter.close(); - datafolder.deleteDirectoryRecursive(); - } - } else { - auto xmlWriter = vtkSmartPointer::New(); + xmlWriter = vtkSmartPointer::New(); xmlWriter->SetInputDataObject(m_dataObject); xmlWriter->SetFileName(fi.filePath().c_str()); xmlWriter->SetDataModeToBinary(); - success = xmlWriter->Write() == 1; + + #ifdef VTK_CELL_ARRAY_V2 + // Looks like an invalid data object that causes a crash with vtk9 + vtkUnstructuredGrid* dataGrid = vtkUnstructuredGrid::SafeDownCast(m_dataObject); + if (dataGrid && (dataGrid->GetPiece() < 0 || dataGrid->GetNumberOfPoints() <= 0)) { + std::cerr << "PropertyPostDataObject::SaveDocFile: ignore empty vtkUnstructuredGrid\n"; + return; + } + #endif } -#ifdef VTK_CELL_ARRAY_V2 - // Looks like an invalid data object that causes a crash with vtk9 - vtkUnstructuredGrid* dataGrid = vtkUnstructuredGrid::SafeDownCast(m_dataObject); - if (dataGrid && (dataGrid->GetPiece() < 0 || dataGrid->GetNumberOfPoints() <= 0)) { - std::cerr << "PropertyPostDataObject::SaveDocFile: ignore empty vtkUnstructuredGrid\n"; - return; - } -#endif - - if (!success) { + if (xmlWriter->Write() != 1) { // Note: Do NOT throw an exception here because if the tmp. file could // not be created we should not abort. // We only print an error message but continue writing the next files to the @@ -397,7 +386,14 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const ss << "Cannot save vtk file '" << fi.filePath() << "'"; writer.addError(ss.str()); } - + else if (m_dataObject->IsA("vtkMultiBlockDataSet")) { + // ZIP file we store all data in + zipios::ZipOutputStream ZipWriter(fi.filePath()); + ZipWriter.putNextEntry("dummy"); //need to add a dummy first, as the read stream preloads the first entry, and we cannot get the file name... + add_to_zip(datafolder, datafolder.filePath().length(), ZipWriter); + ZipWriter.close(); + datafolder.deleteDirectoryRecursive(); + } Base::ifstream file(fi, std::ios::in | std::ios::binary); if (file) { diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp b/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp index 3d82cf233a..daa8bb4bc2 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.cpp @@ -58,13 +58,10 @@ void ViewProviderFemPostPipeline::updateData(const App::Property* prop) FemGui::ViewProviderFemPostObject::updateData(prop); Fem::FemPostPipeline* pipeline = getObject(); - if (prop == &pipeline->Frame) { - // Frame is pipeline property, not post object, parent updateData does not catch it for update - updateVtk(); - } - else if ((prop == &pipeline->Data) || - (prop == &pipeline->Group)) { - updateFunctionSize(); + if ((prop == &pipeline->Data) || + (prop == &pipeline->Group)) { + + updateFunctionSize(); } } diff --git a/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.h b/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.h index f83f2a7e18..f126df276c 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.h +++ b/src/Mod/Fem/Gui/ViewProviderFemPostPipeline.h @@ -51,10 +51,6 @@ public: void scaleField(vtkDataSet* dset, vtkDataArray* pdata, double FieldFactor); PyObject* getPyObject() override; - // override, to not show/hide children as the parent is shown/hidden like normal groups - void extensionHide() override {}; - void extensionShow() override {}; - protected: void updateFunctionSize(); virtual void setupTaskDialog(TaskDlgPost* dlg) override; diff --git a/src/Mod/Fem/femtest/app/test_object.py b/src/Mod/Fem/femtest/app/test_object.py index 4b8ff71452..6ca7f073e5 100644 --- a/src/Mod/Fem/femtest/app/test_object.py +++ b/src/Mod/Fem/femtest/app/test_object.py @@ -1120,7 +1120,7 @@ def create_all_fem_objects_doc(doc): res = analysis.addObject(ObjectsFem.makeResultMechanical(doc))[0] res.Mesh = rm if "BUILD_FEM_VTK" in FreeCAD.__cmake__: - vres = analysis.addObject(ObjectsFem.makePostVtkResult(doc, [res])[0] + vres = analysis.addObject(ObjectsFem.makePostVtkResult(doc, [res]))[0] ObjectsFem.makePostVtkFilterClipRegion(doc, vres) ObjectsFem.makePostVtkFilterClipScalar(doc, vres) ObjectsFem.makePostVtkFilterContours(doc, vres)