FEM: Make post object placement work
This commit is contained in:
committed by
Benjamin Nauck
parent
12a6ea5038
commit
1296bb825a
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
// ***************************************************************************
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user