FEM: Make post object placement work

This commit is contained in:
Stefan Tröger
2024-12-26 10:07:36 +01:00
committed by Benjamin Nauck
parent 12a6ea5038
commit 1296bb825a
14 changed files with 302 additions and 162 deletions

View File

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

View File

@@ -79,6 +79,7 @@ FemPostBranchFilter::FemPostBranchFilter() : Fem::FemPostFilter(), Fem::FemPostG
m_append = vtkSmartPointer<vtkAppendFilter>::New();
m_passthrough = vtkSmartPointer<vtkPassThrough>::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<App::DocumentObject*> objs = Group.getValues();
if (objs.empty()) {
return;
}
// prepare output filter: we make all connections new!
m_append->RemoveAllInputConnections(0);
FemPostFilter* filter = NULL;
std::vector<App::DocumentObject*>::iterator it = objs.begin();
for (; it != objs.end(); ++it) {
// prepare the filter: make all connections new
FemPostFilter* nextFilter = static_cast<FemPostFilter*>(*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<App::DocumentObject*> objs = Group.getValues();
if (objs.empty()) {
return;
}
// prepare output filter: we make all connections new!
m_append->RemoveAllInputConnections(0);
FemPostFilter* filter = NULL;
std::vector<App::DocumentObject*>::iterator it = objs.begin();
for (; it != objs.end(); ++it) {
// prepare the filter: make all connections new
FemPostFilter* nextFilter = static_cast<FemPostFilter*>(*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) {

View File

@@ -65,6 +65,9 @@ protected:
private:
static const char* OutputEnums[];
bool m_transform_used = false;
void setupPipeline();
vtkSmartPointer<vtkAppendFilter> m_append;
vtkSmartPointer<vtkPassThrough> m_passthrough;
};

View File

@@ -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<FemPostGroupExtension>();
postgroup->filterPipelineChanged(this);
}
pipelineChanged();
}
}
FemPostFilter::FilterPipeline& FemPostFilter::getActiveFilterPipeline()
vtkSmartPointer<vtkAlgorithm> 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<vtkAlgorithm> 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<FemPostGroupExtension>();
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<FemPostGroupExtension>();
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<vtkDataSet> 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<std::string> FemPostFilter::getInputScalarFields()
return ScalarArray;
}
void FemPostFilter::setTransformLocation(TransformLocation loc)
{
m_transform_location = loc;
}
// ***************************************************************************
// in the following, the different filters sorted alphabetically
// ***************************************************************************

View File

@@ -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<vtkAlgorithm> getFilterInput();
vtkSmartPointer<vtkAlgorithm> getFilterOutput();
private:
// handling of multiple pipelines which can be the filter
std::map<std::string, FilterPipeline> 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

View File

@@ -123,6 +123,10 @@ void FemPostGroupExtension::recomputeChildren()
{
for (const auto& obj : Group.getValues()) {
obj->touch();
if (obj->hasExtension(Fem::FemPostGroupExtension::getExtensionClassTypeId())) {
obj->getExtension<Fem::FemPostGroupExtension>()->recomputeChildren();
}
}
}

View File

@@ -26,6 +26,7 @@
#include <vtkDataSet.h>
#include <vtkXMLDataSetWriter.h>
#include <vtkXMLMultiBlockDataWriter.h>
#include <vtkTransform.h>
#endif
#include <Base/Exception.h>
@@ -43,6 +44,16 @@ PROPERTY_SOURCE(Fem::FemPostObject, App::GeoFeature)
FemPostObject::FemPostObject()
{
ADD_PROPERTY(Data, (nullptr));
m_transform_filter = vtkSmartPointer<vtkTransformFilter>::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
{

View File

@@ -26,8 +26,11 @@
#include "PropertyPostDataObject.h"
#include <App/GeoFeature.h>
#include <App/PropertyStandard.h>
#include <vtkBoundingBox.h>
#include <vtkDataSet.h>
#include <vtkTransformFilter.h>
#include <vtkSmartPointer.h>
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<vtkTransformFilter> m_transform_filter;
void onChanged(const App::Property* prop) override;
};
} // namespace Fem

View File

@@ -79,6 +79,9 @@ void FemFrameSourceAlgorithm::setDataObject(vtkSmartPointer<vtkDataObject> data)
Update();
}
bool FemFrameSourceAlgorithm::isValid() {
return m_data.GetPointer() != nullptr;
}
std::vector<double> FemFrameSourceAlgorithm::getFrameValues()
{
@@ -128,10 +131,6 @@ int FemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo,
return 0;
}
std::stringstream strm;
outVector->Print(strm);
std::vector<double> 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<FemFrameSourceAlgorithm>::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<std::string>::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<Fem::FemPostFilter*>(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<FemPostGroupExtension>()->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<Fem::FemPostFilter*>(obj)->Frame.setValue(frame);
}
}
}
bool FemPostPipeline::hasFrames()
{
// lazy implementation

View File

@@ -48,6 +48,7 @@ public:
static FemFrameSourceAlgorithm* New();
vtkTypeMacro(FemFrameSourceAlgorithm, vtkUnstructuredGridAlgorithm);
bool isValid();
void setDataObject(vtkSmartPointer<vtkDataObject> data);
std::vector<double> 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<FemFrameSourceAlgorithm> m_source_algorithm;
bool m_block_property = false;
bool m_data_updated = false;
bool m_use_transform = false;
void updateData();
template<class TReader>
void readXMLFile(std::string file)
{

View File

@@ -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<vtkXMLWriter> 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<vtkXMLMultiBlockDataWriter>::New();
xmlWriter = vtkSmartPointer<vtkXMLMultiBlockDataWriter>::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<vtkXMLDataSetWriter>::New();
xmlWriter = vtkSmartPointer<vtkXMLDataSetWriter>::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) {

View File

@@ -58,13 +58,10 @@ void ViewProviderFemPostPipeline::updateData(const App::Property* prop)
FemGui::ViewProviderFemPostObject::updateData(prop);
Fem::FemPostPipeline* pipeline = getObject<Fem::FemPostPipeline>();
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();
}
}

View File

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

View File

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