FEM: Multiframe adoptions
- To support timedata and the relevant filters the pipeline needs to be fully setup, hence not only working on data - Multiblock source algorithm is needed to supply the time data for the algorithms
This commit is contained in:
committed by
Benjamin Nauck
parent
2119f9dfb4
commit
1cff507a7f
@@ -67,9 +67,12 @@ if(BUILD_FEM_VTK)
|
||||
FemPostObjectPyImp.cpp
|
||||
FemPostPipelinePy.xml
|
||||
FemPostPipelinePyImp.cpp
|
||||
FemPostBranchPy.xml
|
||||
FemPostBranchPyImp.cpp
|
||||
)
|
||||
generate_from_xml(FemPostObjectPy)
|
||||
generate_from_xml(FemPostPipelinePy)
|
||||
generate_from_xml(FemPostBranchPy)
|
||||
endif(BUILD_FEM_VTK)
|
||||
SOURCE_GROUP("Python" FILES ${Python_SRCS})
|
||||
|
||||
@@ -82,6 +85,8 @@ if(BUILD_FEM_VTK)
|
||||
FemPostObject.cpp
|
||||
FemPostPipeline.h
|
||||
FemPostPipeline.cpp
|
||||
FemPostBranch.h
|
||||
FemPostBranch.cpp
|
||||
FemPostFilter.h
|
||||
FemPostFilter.cpp
|
||||
FemPostFunction.h
|
||||
|
||||
274
src/Mod/Fem/App/FemPostBranch.cpp
Normal file
274
src/Mod/Fem/App/FemPostBranch.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
#include <Python.h>
|
||||
#include <vtkDataSetReader.h>
|
||||
#include <vtkImageData.h>
|
||||
#include <vtkRectilinearGrid.h>
|
||||
#include <vtkStructuredGrid.h>
|
||||
#include <vtkUnstructuredGrid.h>
|
||||
#include <vtkXMLImageDataReader.h>
|
||||
#include <vtkXMLPUnstructuredGridReader.h>
|
||||
#include <vtkXMLPolyDataReader.h>
|
||||
#include <vtkXMLRectilinearGridReader.h>
|
||||
#include <vtkXMLStructuredGridReader.h>
|
||||
#include <vtkXMLUnstructuredGridReader.h>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include "FemMesh.h"
|
||||
#include "FemMeshObject.h"
|
||||
#include "FemPostPipeline.h"
|
||||
#include "FemPostBranch.h"
|
||||
#include "FemPostBranchPy.h"
|
||||
#include "FemVTKTools.h"
|
||||
|
||||
|
||||
using namespace Fem;
|
||||
using namespace App;
|
||||
|
||||
PROPERTY_SOURCE(Fem::FemPostBranch, Fem::FemPostFilter)
|
||||
const char* FemPostBranch::ModeEnums[] = {"Serial", "Parallel", nullptr};
|
||||
const char* FemPostBranch::OutputEnums[] = {"Passthrough", "Append", nullptr};
|
||||
|
||||
FemPostBranch::FemPostBranch() : Fem::FemPostFilter(), App::GroupExtension()
|
||||
{
|
||||
GroupExtension::initExtension(this);
|
||||
|
||||
ADD_PROPERTY_TYPE(Mode,
|
||||
(long(0)),
|
||||
"Branch",
|
||||
App::Prop_None,
|
||||
"Selects which input the child filters of the branch receive\n"
|
||||
"In serial the first filter receives the branch input, and the concecitive ones get the prior filter output.\n"
|
||||
"In parallel, every filter receives the branch input.");
|
||||
ADD_PROPERTY_TYPE(Output,
|
||||
(long(0)),
|
||||
"Branch",
|
||||
App::Prop_None,
|
||||
"Selects what the output of the branch itself is\n"
|
||||
"In passthrough the branchs output is equal its imput.\n"
|
||||
"In append, all filters outputs gets appended as the branches output");
|
||||
|
||||
Mode.setEnums(ModeEnums);
|
||||
Output.setEnums(OutputEnums);
|
||||
|
||||
/* We always have a passthrough filter. This allows to connect our children
|
||||
* dependend on the Mode setting, without worrying about the connection to our input
|
||||
* filter. We do not care if the input filter changes, as this is affecting only the passthrough
|
||||
* input and does not affect our child connections.
|
||||
* Dependent on our output mode, the passthrough is also used as output, but potentially
|
||||
* the append filter is used. in this case our children need to be connected into the append filter.
|
||||
* Here the same holds as before: Append filter output can be connected to arbitrary other filters
|
||||
* in the pipeline, not affecting our internal connections to our children.
|
||||
*/
|
||||
|
||||
m_append = vtkSmartPointer<vtkAppendFilter>::New();
|
||||
m_passthrough = vtkSmartPointer<vtkPassThrough>::New();
|
||||
|
||||
FilterPipeline passthrough;
|
||||
passthrough.source = m_passthrough;
|
||||
passthrough.target = m_passthrough;
|
||||
addFilterPipeline(passthrough, "passthrough");
|
||||
|
||||
FilterPipeline append;
|
||||
append.source = m_passthrough;
|
||||
append.target = m_append;
|
||||
addFilterPipeline(append, "append");
|
||||
|
||||
setActiveFilterPipeline("passthrough");
|
||||
}
|
||||
|
||||
FemPostBranch::~FemPostBranch() = default;
|
||||
|
||||
short FemPostBranch::mustExecute() const
|
||||
{
|
||||
if (Mode.isTouched()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return FemPostFilter::mustExecute();
|
||||
}
|
||||
|
||||
|
||||
void FemPostBranch::onChanged(const Property* prop)
|
||||
{
|
||||
/* onChanged handles the Pipeline setup: we connect the inputs and outputs
|
||||
* of our child filters correctly according to the new settings
|
||||
*/
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
if (prop == &Frame) {
|
||||
//Update all children with the new step
|
||||
for (const auto& obj : Group.getValues()) {
|
||||
if (obj->isDerivedFrom(FemPostFilter::getClassTypeId())) {
|
||||
static_cast<Fem::FemPostFilter*>(obj)->Frame.setValue(Frame.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prop == &Output) {
|
||||
if (Output.getValue() == 0) {
|
||||
setActiveFilterPipeline("passthrough");
|
||||
}
|
||||
else {
|
||||
setActiveFilterPipeline("append");
|
||||
}
|
||||
}
|
||||
|
||||
FemPostFilter::onChanged(prop);
|
||||
}
|
||||
|
||||
void FemPostBranch::filterChanged(FemPostFilter* filter)
|
||||
{
|
||||
//we only need to update the following children if we are in serial mode
|
||||
if (Mode.getValue() == 0) {
|
||||
|
||||
std::vector<App::DocumentObject*> objs = Group.getValues();
|
||||
|
||||
if (objs.empty()) {
|
||||
return;
|
||||
}
|
||||
bool started = false;
|
||||
std::vector<App::DocumentObject*>::iterator it = objs.begin();
|
||||
for (; it != objs.end(); ++it) {
|
||||
|
||||
if (started) {
|
||||
(*it)->touch();
|
||||
}
|
||||
|
||||
if (*it == filter) {
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we append as output, we need to inform the parent object that we are isTouched
|
||||
if (Output.getValue() == 1) {
|
||||
//make sure we inform our parent object that we changed, it then can inform others if needed
|
||||
App::DocumentObject* group = App::GroupExtension::getGroupOfObject(this);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (group->isDerivedFrom(Fem::FemPostPipeline::getClassTypeId())) {
|
||||
auto pipe = dynamic_cast<Fem::FemPostPipeline*>(group);
|
||||
pipe->filterChanged(this);
|
||||
}
|
||||
else if (group->isDerivedFrom(Fem::FemPostBranch::getClassTypeId())) {
|
||||
auto branch = dynamic_cast<Fem::FemPostBranch*>(group);
|
||||
branch->filterChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FemPostBranch::pipelineChanged(FemPostFilter* filter) {
|
||||
// one of our filters has changed its active pipeline. We need to reconnect it properly.
|
||||
// As we are cheap we just reconnect everything
|
||||
// TODO: Do more efficiently
|
||||
onChanged(&Group);
|
||||
}
|
||||
|
||||
void FemPostBranch::recomputeChildren()
|
||||
{
|
||||
for (const auto& obj : Group.getValues()) {
|
||||
obj->touch();
|
||||
}
|
||||
}
|
||||
|
||||
FemPostObject* FemPostBranch::getLastPostObject()
|
||||
{
|
||||
|
||||
if (Group.getValues().empty()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return static_cast<FemPostObject*>(Group.getValues().back());
|
||||
}
|
||||
|
||||
bool FemPostBranch::holdsPostObject(FemPostObject* obj)
|
||||
{
|
||||
|
||||
std::vector<App::DocumentObject*>::const_iterator it = Group.getValues().begin();
|
||||
for (; it != Group.getValues().end(); ++it) {
|
||||
|
||||
if (*it == obj) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PyObject* FemPostBranch::getPyObject()
|
||||
{
|
||||
if (PythonObject.is(Py::_None())) {
|
||||
// ref counter is set to 1
|
||||
PythonObject = Py::Object(new FemPostBranchPy(this), true);
|
||||
}
|
||||
return Py::new_reference_to(PythonObject);
|
||||
}
|
||||
101
src/Mod/Fem/App/FemPostBranch.h
Normal file
101
src/Mod/Fem/App/FemPostBranch.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2024 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef Fem_FemPostBranch_H
|
||||
#define Fem_FemPostBranch_H
|
||||
|
||||
#include "App/GroupExtension.h"
|
||||
|
||||
#include "FemPostFilter.h"
|
||||
#include "FemPostFunction.h"
|
||||
#include "FemPostObject.h"
|
||||
#include "FemResultObject.h"
|
||||
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkAppendFilter.h>
|
||||
#include <vtkPassThrough.h>
|
||||
|
||||
|
||||
namespace Fem
|
||||
{
|
||||
|
||||
class FemExport FemPostBranch: public Fem::FemPostFilter, public App::GroupExtension
|
||||
{
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(Fem::FemPostBranch);
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
FemPostBranch();
|
||||
~FemPostBranch() override;
|
||||
|
||||
App::PropertyEnumeration Mode;
|
||||
App::PropertyEnumeration Output;
|
||||
|
||||
|
||||
short mustExecute() const override;
|
||||
PyObject* getPyObject() override;
|
||||
|
||||
const char* getViewProviderName() const override
|
||||
{
|
||||
return "FemGui::ViewProviderFemPostBranch";
|
||||
}
|
||||
|
||||
// load data from files
|
||||
static bool canRead(Base::FileInfo file);
|
||||
void read(Base::FileInfo file);
|
||||
void scale(double s);
|
||||
|
||||
// load from results
|
||||
void load(FemResultObject* res);
|
||||
|
||||
// Branch handling
|
||||
void filterChanged(FemPostFilter* filter);
|
||||
void pipelineChanged(FemPostFilter* filter);
|
||||
void recomputeChildren();
|
||||
FemPostObject* getLastPostObject();
|
||||
bool holdsPostObject(FemPostObject* obj);
|
||||
|
||||
protected:
|
||||
void onChanged(const App::Property* prop) override;
|
||||
|
||||
private:
|
||||
static const char* ModeEnums[];
|
||||
static const char* OutputEnums[];
|
||||
|
||||
vtkSmartPointer<vtkAppendFilter> m_append;
|
||||
vtkSmartPointer<vtkPassThrough> m_passthrough;
|
||||
|
||||
template<class TReader>
|
||||
void readXMLFile(std::string file)
|
||||
{
|
||||
|
||||
vtkSmartPointer<TReader> reader = vtkSmartPointer<TReader>::New();
|
||||
reader->SetFileName(file.c_str());
|
||||
reader->Update();
|
||||
Data.setValue(reader->GetOutput());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Fem
|
||||
|
||||
|
||||
#endif // Fem_FemPostBranch_H
|
||||
32
src/Mod/Fem/App/FemPostBranchPy.xml
Normal file
32
src/Mod/Fem/App/FemPostBranchPy.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="FemPostObjectPy"
|
||||
Name="FemPostBranchPy"
|
||||
Twin="FemPostBranch"
|
||||
TwinPointer="FemPostBranch"
|
||||
Include="Mod/Fem/App/FemPostBranch.h"
|
||||
Namespace="Fem"
|
||||
FatherInclude="Mod/Fem/App/FemPostObjectPy.h"
|
||||
FatherNamespace="Fem">
|
||||
<Documentation>
|
||||
<Author Licence="LGPL" Name="Werner Mayer" EMail="wmayer@users.sourceforge.net" />
|
||||
<UserDocu>The FemPostBranch class.</UserDocu>
|
||||
</Documentation>
|
||||
<Methode Name="recomputeChildren">
|
||||
<Documentation>
|
||||
<UserDocu>Recomputes all children of the pipeline</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getLastPostObject">
|
||||
<Documentation>
|
||||
<UserDocu>Get the last post-processing object</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="holdsPostObject">
|
||||
<Documentation>
|
||||
<UserDocu>Check if this pipeline holds a given post-processing object</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
94
src/Mod/Fem/App/FemPostBranchPyImp.cpp
Normal file
94
src/Mod/Fem/App/FemPostBranchPyImp.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2017 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#include <Python.h>
|
||||
#endif
|
||||
|
||||
#include <Base/FileInfo.h>
|
||||
|
||||
// clang-format off
|
||||
#include "FemPostBranch.h"
|
||||
#include "FemPostBranchPy.h"
|
||||
#include "FemPostBranchPy.cpp"
|
||||
// clang-format on
|
||||
|
||||
|
||||
using namespace Fem;
|
||||
|
||||
// returns a string which represents the object e.g. when printed in python
|
||||
std::string FemPostBranchPy::representation() const
|
||||
{
|
||||
return {"<FemPostBranch object>"};
|
||||
}
|
||||
|
||||
|
||||
PyObject* FemPostBranchPy::recomputeChildren(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
getFemPostBranchPtr()->recomputeChildren();
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* FemPostBranchPy::getLastPostObject(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App::DocumentObject* obj = getFemPostBranchPtr()->getLastPostObject();
|
||||
if (obj) {
|
||||
return obj->getPyObject();
|
||||
}
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* FemPostBranchPy::holdsPostObject(PyObject* args)
|
||||
{
|
||||
PyObject* py;
|
||||
if (!PyArg_ParseTuple(args, "O!", &(App::DocumentObjectPy::Type), &py)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(py)->getDocumentObjectPtr();
|
||||
if (!obj->isDerivedFrom<FemPostObject>()) {
|
||||
PyErr_SetString(PyExc_TypeError, "object is not a post-processing object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ok = getFemPostBranchPtr()->holdsPostObject(static_cast<FemPostObject*>(obj));
|
||||
return Py_BuildValue("O", (ok ? Py_True : Py_False));
|
||||
}
|
||||
|
||||
PyObject* FemPostBranchPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int FemPostBranchPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -26,12 +26,16 @@
|
||||
#include <Python.h>
|
||||
#include <vtkDoubleArray.h>
|
||||
#include <vtkPointData.h>
|
||||
#include <vtkAlgorithm.h>
|
||||
#include <vtkAlgorithmOutput.h>
|
||||
#endif
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include "FemPostFilter.h"
|
||||
#include "FemPostPipeline.h"
|
||||
#include "FemPostBranch.h"
|
||||
|
||||
|
||||
using namespace Fem;
|
||||
@@ -42,7 +46,12 @@ PROPERTY_SOURCE(Fem::FemPostFilter, Fem::FemPostObject)
|
||||
|
||||
FemPostFilter::FemPostFilter()
|
||||
{
|
||||
ADD_PROPERTY(Input, (nullptr));
|
||||
ADD_PROPERTY_TYPE(Frame,
|
||||
((long)0),
|
||||
"Data",
|
||||
App::Prop_ReadOnly,
|
||||
"The step used to calculate the data");
|
||||
|
||||
}
|
||||
|
||||
FemPostFilter::~FemPostFilter() = default;
|
||||
@@ -60,57 +69,130 @@ FemPostFilter::FilterPipeline& FemPostFilter::getFilterPipeline(std::string name
|
||||
void FemPostFilter::setActiveFilterPipeline(std::string name)
|
||||
{
|
||||
if (m_activePipeline != name && isValid()) {
|
||||
|
||||
// disable all inputs of current pipeline
|
||||
if (m_activePipeline != "" and m_pipelines.find( m_activePipeline ) != m_pipelines.end()) {
|
||||
m_pipelines[m_activePipeline].source->RemoveAllInputConnections(0);
|
||||
}
|
||||
|
||||
//set the new pipeline active
|
||||
m_activePipeline = name;
|
||||
|
||||
//inform our parent, that we need to be connected a new
|
||||
App::DocumentObject* group = App::GroupExtension::getGroupOfObject(this);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (group->isDerivedFrom(Fem::FemPostPipeline::getClassTypeId())) {
|
||||
auto pipe = dynamic_cast<Fem::FemPostPipeline*>(group);
|
||||
pipe->pipelineChanged(this);
|
||||
}
|
||||
else if (group->isDerivedFrom(Fem::FemPostBranch::getClassTypeId())) {
|
||||
auto branch = dynamic_cast<Fem::FemPostBranch*>(group);
|
||||
branch->pipelineChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FemPostFilter::FilterPipeline& FemPostFilter::getActiveFilterPipeline()
|
||||
{
|
||||
return m_pipelines[m_activePipeline];
|
||||
}
|
||||
|
||||
void FemPostFilter::onChanged(const App::Property* prop)
|
||||
{
|
||||
//make sure we inform our parent object that we changed, it then can inform others if needed
|
||||
App::DocumentObject* group = App::GroupExtension::getGroupOfObject(this);
|
||||
if (!group) {
|
||||
return FemPostObject::onChanged(prop);
|
||||
}
|
||||
|
||||
if (group->isDerivedFrom(Fem::FemPostPipeline::getClassTypeId())) {
|
||||
auto pipe = dynamic_cast<Fem::FemPostPipeline*>(group);
|
||||
pipe->filterChanged(this);
|
||||
}
|
||||
else if (group->isDerivedFrom(Fem::FemPostBranch::getClassTypeId())) {
|
||||
auto branch = dynamic_cast<Fem::FemPostBranch*>(group);
|
||||
branch->filterChanged(this);
|
||||
}
|
||||
|
||||
return FemPostObject::onChanged(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()) {
|
||||
FemPostFilter::FilterPipeline& pipe = m_pipelines[m_activePipeline];
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
if (!data || !data->IsA("vtkDataSet")) {
|
||||
|
||||
if (pipe.source->GetNumberOfInputConnections(0) == 0) {
|
||||
return StdReturn;
|
||||
}
|
||||
|
||||
if ((m_activePipeline == "DataAlongLine") || (m_activePipeline == "DataAtPoint")) {
|
||||
pipe.filterSource->SetSourceData(getInputData());
|
||||
pipe.filterTarget->Update();
|
||||
Data.setValue(pipe.filterTarget->GetOutputDataObject(0));
|
||||
if (Frame.getValue()>0) {
|
||||
pipe.target->UpdateTimeStep(Frame.getValue());
|
||||
}
|
||||
else {
|
||||
pipe.source->SetInputDataObject(data);
|
||||
pipe.target->Update();
|
||||
Data.setValue(pipe.target->GetOutputDataObject(0));
|
||||
}
|
||||
Data.setValue(pipe.target->GetOutputDataObject(0));
|
||||
}
|
||||
|
||||
return StdReturn;
|
||||
}
|
||||
|
||||
vtkDataObject* FemPostFilter::getInputData()
|
||||
{
|
||||
if (Input.getValue()) {
|
||||
if (Input.getValue()->isDerivedFrom<Fem::FemPostObject>()) {
|
||||
return Input.getValue<FemPostObject*>()->Data.getValue();
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error(
|
||||
"The filter's Input object is not a 'Fem::FemPostObject' object!");
|
||||
}
|
||||
vtkSmartPointer<vtkDataSet> FemPostFilter::getInputData() {
|
||||
|
||||
if (getActiveFilterPipeline().source->GetNumberOfInputConnections(0) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
else {
|
||||
// get the pipeline and use the pipelinedata
|
||||
std::vector<App::DocumentObject*> objs =
|
||||
getDocument()->getObjectsOfType(FemPostPipeline::getClassTypeId());
|
||||
for (auto it : objs) {
|
||||
if (static_cast<FemPostPipeline*>(it)->holdsPostObject(this)) {
|
||||
return static_cast<FemPostObject*>(it)->Data.getValue();
|
||||
}
|
||||
|
||||
vtkAlgorithmOutput* output = getActiveFilterPipeline().source->GetInputConnection(0,0);
|
||||
vtkAlgorithm* algo = output->GetProducer();
|
||||
algo->Update();
|
||||
|
||||
return vtkDataSet::SafeDownCast(algo->GetOutputDataObject(0));
|
||||
}
|
||||
|
||||
std::vector<std::string> FemPostFilter::getInputVectorFields()
|
||||
{
|
||||
vtkDataSet* dset = getInputData();
|
||||
if (!dset) {
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
vtkPointData* pd = dset->GetPointData();
|
||||
|
||||
// get all vector fields
|
||||
std::vector<std::string> VectorArray;
|
||||
for (int i = 0; i < pd->GetNumberOfArrays(); ++i) {
|
||||
if (pd->GetArray(i)->GetNumberOfComponents() == 3) {
|
||||
VectorArray.emplace_back(pd->GetArrayName(i));
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return VectorArray;
|
||||
}
|
||||
|
||||
std::vector<std::string> FemPostFilter::getInputScalarFields()
|
||||
{
|
||||
vtkDataSet* dset = getInputData();
|
||||
if (!dset) {
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
vtkPointData* pd = dset->GetPointData();
|
||||
|
||||
// get all scalar fields
|
||||
std::vector<std::string> ScalarArray;
|
||||
for (int i = 0; i < pd->GetNumberOfArrays(); ++i) {
|
||||
if (pd->GetArray(i)->GetNumberOfComponents() == 1) {
|
||||
ScalarArray.emplace_back(pd->GetArrayName(i));
|
||||
}
|
||||
}
|
||||
|
||||
return ScalarArray;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
@@ -172,7 +254,9 @@ FemPostDataAlongLineFilter::FemPostDataAlongLineFilter()
|
||||
m_line->SetResolution(Resolution.getValue());
|
||||
|
||||
|
||||
auto passthrough = vtkSmartPointer<vtkPassThrough>::New();
|
||||
m_probe = vtkSmartPointer<vtkProbeFilter>::New();
|
||||
m_probe->SetSourceConnection(passthrough->GetOutputPort(0));
|
||||
m_probe->SetInputConnection(m_line->GetOutputPort());
|
||||
m_probe->SetValidPointMaskArrayName("ValidPointArray");
|
||||
m_probe->SetPassPointArrays(1);
|
||||
@@ -183,8 +267,8 @@ FemPostDataAlongLineFilter::FemPostDataAlongLineFilter()
|
||||
m_probe->SetTolerance(0.01);
|
||||
#endif
|
||||
|
||||
clip.filterSource = m_probe;
|
||||
clip.filterTarget = m_probe;
|
||||
clip.source = passthrough;
|
||||
clip.target = m_probe;
|
||||
|
||||
addFilterPipeline(clip, "DataAlongLine");
|
||||
setActiveFilterPipeline("DataAlongLine");
|
||||
@@ -343,7 +427,9 @@ FemPostDataAtPointFilter::FemPostDataAtPointFilter()
|
||||
m_point->SetCenter(vec.x, vec.y, vec.z);
|
||||
m_point->SetRadius(0);
|
||||
|
||||
auto passthrough = vtkSmartPointer<vtkPassThrough>::New();
|
||||
m_probe = vtkSmartPointer<vtkProbeFilter>::New();
|
||||
m_probe->SetSourceConnection(passthrough->GetOutputPort(0));
|
||||
m_probe->SetInputConnection(m_point->GetOutputPort());
|
||||
m_probe->SetValidPointMaskArrayName("ValidPointArray");
|
||||
m_probe->SetPassPointArrays(1);
|
||||
@@ -354,8 +440,8 @@ FemPostDataAtPointFilter::FemPostDataAtPointFilter()
|
||||
m_probe->SetTolerance(0.01);
|
||||
#endif
|
||||
|
||||
clip.filterSource = m_probe;
|
||||
clip.filterTarget = m_probe;
|
||||
clip.source = passthrough;
|
||||
clip.target = m_probe;
|
||||
|
||||
addFilterPipeline(clip, "DataAtPoint");
|
||||
setActiveFilterPipeline("DataAtPoint");
|
||||
@@ -660,8 +746,7 @@ DocumentObjectExecReturn* FemPostContoursFilter::execute()
|
||||
auto returnObject = Fem::FemPostFilter::execute();
|
||||
|
||||
// delete contour field
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
vtkDataSet* dset = getInputData();
|
||||
if (!dset) {
|
||||
return returnObject;
|
||||
}
|
||||
@@ -692,8 +777,7 @@ void FemPostContoursFilter::onChanged(const Property* prop)
|
||||
double p[2];
|
||||
|
||||
// get the field and its data
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
vtkDataSet* dset = getInputData();
|
||||
if (!dset) {
|
||||
return;
|
||||
}
|
||||
@@ -807,8 +891,7 @@ void FemPostContoursFilter::refreshFields()
|
||||
|
||||
std::vector<std::string> FieldsArray;
|
||||
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
vtkDataSet* dset = getInputData();
|
||||
if (!dset) {
|
||||
m_blockPropertyChanges = false;
|
||||
return;
|
||||
@@ -838,6 +921,7 @@ void FemPostContoursFilter::refreshFields()
|
||||
}
|
||||
|
||||
m_blockPropertyChanges = false;
|
||||
|
||||
}
|
||||
|
||||
void FemPostContoursFilter::refreshVectors()
|
||||
@@ -845,8 +929,7 @@ void FemPostContoursFilter::refreshVectors()
|
||||
// refreshes the list of available vectors for the current Field
|
||||
m_blockPropertyChanges = true;
|
||||
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
vtkDataSet* dset = getInputData();
|
||||
if (!dset) {
|
||||
m_blockPropertyChanges = false;
|
||||
return;
|
||||
@@ -981,21 +1064,7 @@ DocumentObjectExecReturn* FemPostScalarClipFilter::execute()
|
||||
val = Scalars.getValueAsString();
|
||||
}
|
||||
|
||||
std::vector<std::string> ScalarsArray;
|
||||
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
if (!dset) {
|
||||
return StdReturn;
|
||||
}
|
||||
vtkPointData* pd = dset->GetPointData();
|
||||
|
||||
// get all scalar fields
|
||||
for (int i = 0; i < pd->GetNumberOfArrays(); ++i) {
|
||||
if (pd->GetArray(i)->GetNumberOfComponents() == 1) {
|
||||
ScalarsArray.emplace_back(pd->GetArrayName(i));
|
||||
}
|
||||
}
|
||||
std::vector<std::string> ScalarsArray = getInputScalarFields();
|
||||
|
||||
App::Enumeration empty;
|
||||
Scalars.setValue(empty);
|
||||
@@ -1044,8 +1113,7 @@ short int FemPostScalarClipFilter::mustExecute() const
|
||||
|
||||
void FemPostScalarClipFilter::setConstraintForField()
|
||||
{
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
vtkDataSet* dset = getInputData();
|
||||
if (!dset) {
|
||||
return;
|
||||
}
|
||||
@@ -1094,26 +1162,14 @@ FemPostWarpVectorFilter::~FemPostWarpVectorFilter() = default;
|
||||
|
||||
DocumentObjectExecReturn* FemPostWarpVectorFilter::execute()
|
||||
{
|
||||
Base::Console().Message("Warp Execute\n");
|
||||
|
||||
std::string val;
|
||||
if (Vector.getValue() >= 0) {
|
||||
val = Vector.getValueAsString();
|
||||
}
|
||||
|
||||
std::vector<std::string> VectorArray;
|
||||
|
||||
vtkSmartPointer<vtkDataObject> data = getInputData();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
if (!dset) {
|
||||
return StdReturn;
|
||||
}
|
||||
vtkPointData* pd = dset->GetPointData();
|
||||
|
||||
// get all vector fields
|
||||
for (int i = 0; i < pd->GetNumberOfArrays(); ++i) {
|
||||
if (pd->GetArray(i)->GetNumberOfComponents() == 3) {
|
||||
VectorArray.emplace_back(pd->GetArrayName(i));
|
||||
}
|
||||
}
|
||||
std::vector<std::string> VectorArray = getInputVectorFields();
|
||||
|
||||
App::Enumeration empty;
|
||||
Vector.setValue(empty);
|
||||
|
||||
@@ -49,23 +49,15 @@ class FemExport FemPostFilter: public Fem::FemPostObject
|
||||
{
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostFilter);
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
FemPostFilter();
|
||||
~FemPostFilter() override;
|
||||
|
||||
App::PropertyLink Input;
|
||||
|
||||
App::DocumentObjectExecReturn* execute() override;
|
||||
|
||||
protected:
|
||||
vtkDataObject* getInputData();
|
||||
vtkSmartPointer<vtkDataSet> getInputData();
|
||||
std::vector<std::string> getInputVectorFields();
|
||||
std::vector<std::string> getInputScalarFields();
|
||||
|
||||
// pipeline handling for derived filter
|
||||
struct FilterPipeline
|
||||
{
|
||||
vtkSmartPointer<vtkAlgorithm> source, target;
|
||||
vtkSmartPointer<vtkProbeFilter> filterSource, filterTarget;
|
||||
std::vector<vtkSmartPointer<vtkAlgorithm>> algorithmStorage;
|
||||
};
|
||||
|
||||
@@ -73,6 +65,18 @@ protected:
|
||||
void setActiveFilterPipeline(std::string name);
|
||||
FilterPipeline& getFilterPipeline(std::string name);
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
FemPostFilter();
|
||||
~FemPostFilter() override;
|
||||
|
||||
App::PropertyFloat Frame;
|
||||
|
||||
void onChanged(const App::Property* prop) override;
|
||||
App::DocumentObjectExecReturn* execute() override;
|
||||
|
||||
FilterPipeline& getActiveFilterPipeline();
|
||||
|
||||
private:
|
||||
// handling of multiple pipelines which can be the filter
|
||||
std::map<std::string, FilterPipeline> m_pipelines;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef _PreComp_
|
||||
#include <vtkDataSet.h>
|
||||
#include <vtkXMLDataSetWriter.h>
|
||||
#include <vtkXMLMultiBlockDataWriter.h>
|
||||
#endif
|
||||
|
||||
#include <Base/Exception.h>
|
||||
@@ -46,12 +47,26 @@ FemPostObject::FemPostObject()
|
||||
|
||||
FemPostObject::~FemPostObject() = default;
|
||||
|
||||
vtkDataSet* FemPostObject::getDataSet() {
|
||||
|
||||
if (!Data.getValue()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Data.getValue()->IsA("vtkDataSet")) {
|
||||
return vtkDataSet::SafeDownCast(Data.getValue());
|
||||
}
|
||||
|
||||
// we could be a composite dataset... hope that our subclasses handle this,
|
||||
// as this should only be possible for input data (So FemPostPipeline)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vtkBoundingBox FemPostObject::getBoundingBox()
|
||||
{
|
||||
|
||||
vtkBoundingBox box;
|
||||
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(Data.getValue());
|
||||
vtkDataSet* dset = getDataSet();
|
||||
if (dset) {
|
||||
box.AddBounds(dset->GetBounds());
|
||||
}
|
||||
@@ -77,7 +92,7 @@ namespace
|
||||
template<typename T>
|
||||
void femVTKWriter(const char* filename, const vtkSmartPointer<vtkDataObject>& dataObject)
|
||||
{
|
||||
if (vtkDataSet::SafeDownCast(dataObject)->GetNumberOfPoints() <= 0) {
|
||||
if (dataObject->IsA("vtkDataSet") && vtkDataSet::SafeDownCast(dataObject)->GetNumberOfPoints() <= 0) {
|
||||
throw Base::ValueError("Empty data object");
|
||||
}
|
||||
|
||||
@@ -107,6 +122,9 @@ std::string vtkWriterExtension(const vtkSmartPointer<vtkDataObject>& dataObject)
|
||||
case VTK_UNIFORM_GRID:
|
||||
extension = "vti";
|
||||
break;
|
||||
case VTK_MULTIBLOCK_DATA_SET:
|
||||
extension = "vtm";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -135,5 +153,9 @@ void FemPostObject::writeVTK(const char* filename) const
|
||||
name = name.append(".").append(extension);
|
||||
}
|
||||
|
||||
femVTKWriter<vtkXMLDataSetWriter>(name.c_str(), data);
|
||||
if (extension == "vtm") {
|
||||
femVTKWriter<vtkXMLMultiBlockDataWriter>(name.c_str(), data);
|
||||
} else {
|
||||
femVTKWriter<vtkXMLDataSetWriter>(name.c_str(), data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "PropertyPostDataObject.h"
|
||||
#include <App/GeoFeature.h>
|
||||
#include <vtkBoundingBox.h>
|
||||
#include <vtkDataSet.h>
|
||||
|
||||
|
||||
namespace Fem
|
||||
@@ -45,6 +46,12 @@ public:
|
||||
|
||||
Fem::PropertyPostDataObject Data;
|
||||
|
||||
// returns the DataSet from the data property. Better use this
|
||||
// instead of casting Data.getValue(), as data does not need to be a dataset,
|
||||
// but could for example also be a composite data structure.
|
||||
// Could return NULL if no dataset is available
|
||||
virtual vtkDataSet* getDataSet();
|
||||
|
||||
PyObject* getPyObject() override;
|
||||
|
||||
vtkBoundingBox getBoundingBox();
|
||||
|
||||
@@ -36,9 +36,17 @@
|
||||
#include <vtkXMLRectilinearGridReader.h>
|
||||
#include <vtkXMLStructuredGridReader.h>
|
||||
#include <vtkXMLUnstructuredGridReader.h>
|
||||
#include <vtkXMLMultiBlockDataReader.h>
|
||||
#include <vtkMultiBlockDataSet.h>
|
||||
#include <vtkStreamingDemandDrivenPipeline.h>
|
||||
#include <vtkPointData.h>
|
||||
#include <vtkFloatArray.h>
|
||||
#include <vtkStringArray.h>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <cmath>
|
||||
#include <QString>
|
||||
|
||||
#include "FemMesh.h"
|
||||
#include "FemMeshObject.h"
|
||||
@@ -51,54 +59,201 @@
|
||||
using namespace Fem;
|
||||
using namespace App;
|
||||
|
||||
PROPERTY_SOURCE(Fem::FemPostPipeline, Fem::FemPostObject)
|
||||
const char* FemPostPipeline::ModeEnums[] = {"Serial", "Parallel", "Custom", nullptr};
|
||||
|
||||
FemPostPipeline::FemPostPipeline()
|
||||
vtkStandardNewMacro(FemFrameSourceAlgorithm);
|
||||
|
||||
FemFrameSourceAlgorithm::FemFrameSourceAlgorithm::FemFrameSourceAlgorithm()
|
||||
{
|
||||
ADD_PROPERTY_TYPE(Filter,
|
||||
(nullptr),
|
||||
"Pipeline",
|
||||
App::Prop_None,
|
||||
"The filter used in this pipeline");
|
||||
// we are a source
|
||||
SetNumberOfInputPorts(0);
|
||||
SetNumberOfOutputPorts(1);
|
||||
}
|
||||
|
||||
|
||||
FemFrameSourceAlgorithm::FemFrameSourceAlgorithm::~FemFrameSourceAlgorithm()
|
||||
{
|
||||
}
|
||||
|
||||
void FemFrameSourceAlgorithm::setDataObject(vtkSmartPointer<vtkDataObject> data ) {
|
||||
m_data = data;
|
||||
Update();
|
||||
}
|
||||
|
||||
|
||||
std::vector<double> FemFrameSourceAlgorithm::getFrameValues()
|
||||
{
|
||||
|
||||
// check if we have frame data
|
||||
if (!m_data || !m_data->IsA("vtkMultiBlockDataSet")) {
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
// we have multiple frames! let's check the amount and times
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
|
||||
|
||||
unsigned long nblocks = multiblock->GetNumberOfBlocks();
|
||||
std::vector<double> tFrames(nblocks);
|
||||
|
||||
for (unsigned long i=0; i<nblocks; i++) {
|
||||
|
||||
vtkDataObject* block = multiblock->GetBlock(i);
|
||||
// check if the TimeValue field is available
|
||||
if (!block->GetFieldData()->HasArray("TimeValue")) {
|
||||
break;
|
||||
}
|
||||
|
||||
//store the time value!
|
||||
vtkDataArray* TimeValue = block->GetFieldData()->GetArray("TimeValue");
|
||||
if (!TimeValue->IsA("vtkFloatArray") ||
|
||||
TimeValue->GetNumberOfTuples() < 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
tFrames[i] = vtkFloatArray::SafeDownCast(TimeValue)->GetValue(0);
|
||||
}
|
||||
|
||||
if (tFrames.size() != nblocks) {
|
||||
// not every block had time data
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
return tFrames;
|
||||
}
|
||||
|
||||
int FemFrameSourceAlgorithm::RequestInformation(vtkInformation*reqInfo, vtkInformationVector **inVector, vtkInformationVector* outVector)
|
||||
{
|
||||
|
||||
if (!this->Superclass::RequestInformation(reqInfo, inVector, outVector))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::stringstream strm;
|
||||
outVector->Print(strm);
|
||||
|
||||
std::vector<double> frames = getFrameValues();
|
||||
|
||||
if (frames.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
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(CAN_HANDLE_PIECE_REQUEST(), 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FemFrameSourceAlgorithm::RequestData(vtkInformation* reqInfo, vtkInformationVector** inVector, vtkInformationVector* outVector)
|
||||
{
|
||||
|
||||
std::stringstream strstm;
|
||||
outVector->Print(strstm);
|
||||
|
||||
vtkInformation* outInfo = outVector->GetInformationObject(0);
|
||||
vtkUnstructuredGrid* output = vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
|
||||
|
||||
if (!output || !m_data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_data->IsA("vtkMultiBlockDataSet")) {
|
||||
// no multi frame data, return directly
|
||||
outInfo->Set(vtkDataObject::DATA_OBJECT(), m_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
|
||||
// find the block asked for (lazy implementation)
|
||||
unsigned long idx = 0;
|
||||
if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()))
|
||||
{
|
||||
auto time = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
|
||||
auto frames = getFrameValues();
|
||||
|
||||
// we have float values, so be aware of roundign erros. lets subtract the searched time and then use the smalles value
|
||||
for(auto& frame : frames)
|
||||
frame = std::abs(frame-time);
|
||||
|
||||
auto it = std::min_element(std::begin(frames), std::end(frames));
|
||||
idx = std::distance(std::begin(frames), it);
|
||||
}
|
||||
|
||||
auto block = multiblock->GetBlock(idx);
|
||||
output->ShallowCopy(block);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PROPERTY_SOURCE(Fem::FemPostPipeline, Fem::FemPostObject)
|
||||
const char* FemPostPipeline::ModeEnums[] = {"Serial", "Parallel", nullptr};
|
||||
|
||||
FemPostPipeline::FemPostPipeline() : Fem::FemPostObject(), App::GroupExtension()
|
||||
{
|
||||
GroupExtension::initExtension(this);
|
||||
|
||||
ADD_PROPERTY_TYPE(Functions,
|
||||
(nullptr),
|
||||
"Pipeline",
|
||||
App::Prop_Hidden,
|
||||
"The function provider which groups all pipeline functions");
|
||||
ADD_PROPERTY_TYPE(Mode,
|
||||
(long(2)),
|
||||
(long(0)),
|
||||
"Pipeline",
|
||||
App::Prop_None,
|
||||
"Selects the pipeline data transition mode.\n"
|
||||
"In serial, every filter gets the output of the previous one as input.\n"
|
||||
"In parallel, every filter gets the pipeline source as input.\n"
|
||||
"In custom, every filter keeps its input set by the user.");
|
||||
"In parallel, every filter gets the pipeline source as input.\n");
|
||||
ADD_PROPERTY_TYPE(Frame,
|
||||
(long(0)),
|
||||
"Pipeline",
|
||||
App::Prop_None,
|
||||
"The frame used to calculate the data in the pipeline processing (read only, set via pipeline object).");
|
||||
|
||||
Mode.setEnums(ModeEnums);
|
||||
|
||||
// create our source algorithm
|
||||
m_source_algorithm = vtkSmartPointer<FemFrameSourceAlgorithm>::New();
|
||||
}
|
||||
|
||||
FemPostPipeline::~FemPostPipeline() = default;
|
||||
|
||||
short FemPostPipeline::mustExecute() const
|
||||
{
|
||||
if (Mode.isTouched()) {
|
||||
if (Mode.isTouched() ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return FemPostObject::mustExecute();
|
||||
}
|
||||
|
||||
DocumentObjectExecReturn* FemPostPipeline::execute()
|
||||
{
|
||||
return Fem::FemPostObject::execute();
|
||||
}
|
||||
vtkDataSet* FemPostPipeline::getDataSet() {
|
||||
|
||||
vtkDataObject* data = m_source_algorithm->GetOutputDataObject(0);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (data->IsA("vtkDataSet")) {
|
||||
return vtkDataSet::SafeDownCast(data);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FemPostPipeline::canRead(Base::FileInfo File)
|
||||
{
|
||||
|
||||
// from FemResult only unstructural mesh is supported in femvtktoools.cpp
|
||||
return File.hasExtension({"vtk", "vtp", "vts", "vtr", "vti", "vtu", "pvtu"});
|
||||
return File.hasExtension({"vtk", "vtp", "vts", "vtr", "vti", "vtu", "pvtu", "vtm"});
|
||||
}
|
||||
|
||||
void FemPostPipeline::read(Base::FileInfo File)
|
||||
@@ -130,6 +285,9 @@ void FemPostPipeline::read(Base::FileInfo File)
|
||||
else if (File.hasExtension("vtk")) {
|
||||
readXMLFile<vtkDataSetReader>(File.filePath());
|
||||
}
|
||||
else if (File.hasExtension("vtm")) {
|
||||
readXMLFile<vtkXMLMultiBlockDataReader>(File.filePath());
|
||||
}
|
||||
else {
|
||||
throw Base::FileException("Unknown extension");
|
||||
}
|
||||
@@ -138,79 +296,180 @@ void FemPostPipeline::read(Base::FileInfo File)
|
||||
void FemPostPipeline::scale(double s)
|
||||
{
|
||||
Data.scale(s);
|
||||
onChanged(&Data);
|
||||
}
|
||||
|
||||
void FemPostPipeline::onChanged(const Property* prop)
|
||||
{
|
||||
if (prop == &Filter || prop == &Mode) {
|
||||
/* onChanged handles the Pipeline setup: we connect the inputs and outputs
|
||||
* of our child filters correctly according to the new settings
|
||||
*/
|
||||
|
||||
// if we are in custom mode the user is free to set the input
|
||||
// thus nothing needs to be done here
|
||||
if (Mode.getValue() == 2) { // custom
|
||||
return;
|
||||
|
||||
// use the correct data as source
|
||||
if (prop == &Data) {
|
||||
m_source_algorithm->setDataObject(Data.getValue());
|
||||
|
||||
// change the frame enum to correct values
|
||||
std::string val;
|
||||
if (Frame.hasEnums() && Frame.getValue() >= 0) {
|
||||
val = Frame.getValueAsString();
|
||||
}
|
||||
|
||||
std::vector<double> frames = m_source_algorithm->getFrameValues();
|
||||
std::vector<std::string> frame_values;
|
||||
if (frames.empty()) {
|
||||
frame_values.push_back("No frames available");
|
||||
}
|
||||
else {
|
||||
auto unit = getFrameUnit();
|
||||
for (const double& frame : frames) {
|
||||
auto quantity = Base::Quantity(frame, unit);
|
||||
frame_values.push_back(quantity.getUserString().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
App::Enumeration empty;
|
||||
Frame.setValue(empty);
|
||||
m_frameEnum.setEnums(frame_values);
|
||||
Frame.setValue(m_frameEnum);
|
||||
|
||||
std::vector<std::string>::iterator it = std::find(frame_values.begin(), frame_values.end(), val);
|
||||
if (!val.empty() && it != frame_values.end()) {
|
||||
Frame.setValue(val.c_str());
|
||||
}
|
||||
|
||||
Frame.purgeTouched();
|
||||
recomputeChildren();
|
||||
}
|
||||
|
||||
if (prop == &Frame) {
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// inform the downstream pipeline
|
||||
recomputeChildren();
|
||||
}
|
||||
|
||||
|
||||
// connect all filters correctly to the source
|
||||
if (prop == &Group || prop == &Mode) {
|
||||
|
||||
// we check if all connections are right and add new ones if needed
|
||||
std::vector<App::DocumentObject*> objs = Filter.getValues();
|
||||
std::vector<App::DocumentObject*> objs = Group.getValues();
|
||||
|
||||
if (objs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FemPostFilter* filter = NULL;
|
||||
std::vector<App::DocumentObject*>::iterator it = objs.begin();
|
||||
FemPostFilter* filter = static_cast<FemPostFilter*>(*it);
|
||||
|
||||
// the first filter must always grab the data
|
||||
if (filter->Input.getValue()) {
|
||||
filter->Input.setValue(nullptr);
|
||||
}
|
||||
|
||||
// all the others need to be connected to the previous filter or grab the data,
|
||||
// dependent on mode
|
||||
++it;
|
||||
for (; it != objs.end(); ++it) {
|
||||
auto* nextFilter = static_cast<FemPostFilter*>(*it);
|
||||
|
||||
if (Mode.getValue() == 0) { // serial mode
|
||||
if (nextFilter->Input.getValue() != filter) {
|
||||
nextFilter->Input.setValue(filter);
|
||||
// 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_source_algorithm->GetOutputPort(0));
|
||||
} else {
|
||||
nextFilter->getActiveFilterPipeline().source->SetInputConnection(filter->getActiveFilterPipeline().target->GetOutputPort());
|
||||
}
|
||||
|
||||
}
|
||||
else { // Parallel mode
|
||||
if (nextFilter->Input.getValue()) {
|
||||
nextFilter->Input.setValue(nullptr);
|
||||
}
|
||||
else if (Mode.getValue() == 1) {
|
||||
// parallel: all filters get out input
|
||||
nextFilter->getActiveFilterPipeline().source->SetInputConnection(m_source_algorithm->GetOutputPort(0));
|
||||
}
|
||||
else {
|
||||
throw Base::ValueError("Unknown Mode set for Pipeline");
|
||||
}
|
||||
|
||||
filter = nextFilter;
|
||||
};
|
||||
}
|
||||
|
||||
App::GeoFeature::onChanged(prop);
|
||||
FemPostObject::onChanged(prop);
|
||||
|
||||
}
|
||||
|
||||
void FemPostPipeline::filterChanged(FemPostFilter* filter)
|
||||
{
|
||||
//we only need to update the following children if we are in serial mode
|
||||
if (Mode.getValue() == 0) {
|
||||
|
||||
std::vector<App::DocumentObject*> objs = Group.getValues();
|
||||
|
||||
if (objs.empty()) {
|
||||
return;
|
||||
}
|
||||
bool started = false;
|
||||
std::vector<App::DocumentObject*>::iterator it = objs.begin();
|
||||
for (; it != objs.end(); ++it) {
|
||||
|
||||
if (started) {
|
||||
(*it)->touch();
|
||||
}
|
||||
|
||||
if (*it == filter) {
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FemPostPipeline::pipelineChanged(FemPostFilter* filter) {
|
||||
// one of our filters has changed its active pipeline. We need to reconnect it properly.
|
||||
// As we are cheap we just reconnect everything
|
||||
// TODO: Do more efficiently
|
||||
onChanged(&Group);
|
||||
}
|
||||
|
||||
void FemPostPipeline::recomputeChildren()
|
||||
{
|
||||
for (const auto& obj : Filter.getValues()) {
|
||||
// get the frame we use
|
||||
double frame = 0;
|
||||
auto frames = getFrameValues();
|
||||
if (!frames.empty() &&
|
||||
Frame.getValue() < 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FemPostObject* FemPostPipeline::getLastPostObject()
|
||||
{
|
||||
|
||||
if (Filter.getValues().empty()) {
|
||||
if (Group.getValues().empty()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return static_cast<FemPostObject*>(Filter.getValues().back());
|
||||
return static_cast<FemPostObject*>(Group.getValues().back());
|
||||
}
|
||||
|
||||
bool FemPostPipeline::holdsPostObject(FemPostObject* obj)
|
||||
{
|
||||
|
||||
std::vector<App::DocumentObject*>::const_iterator it = Filter.getValues().begin();
|
||||
for (; it != Filter.getValues().end(); ++it) {
|
||||
std::vector<App::DocumentObject*>::const_iterator it = Group.getValues().begin();
|
||||
for (; it != Group.getValues().end(); ++it) {
|
||||
|
||||
if (*it == obj) {
|
||||
return true;
|
||||
@@ -219,6 +478,79 @@ bool FemPostPipeline::holdsPostObject(FemPostObject* obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool FemPostPipeline::hasFrames()
|
||||
{
|
||||
// lazy implementation
|
||||
return !m_source_algorithm->getFrameValues().empty();
|
||||
}
|
||||
|
||||
std::string FemPostPipeline::getFrameType()
|
||||
{
|
||||
vtkSmartPointer<vtkDataObject> data = Data.getValue();
|
||||
|
||||
// check if we have frame data
|
||||
if (!data || !data->IsA("vtkMultiBlockDataSet")) {
|
||||
return std::string("no frames");
|
||||
}
|
||||
|
||||
// we have multiple frames! let's check the amount and times
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(data);
|
||||
if (!multiblock->GetFieldData()->HasArray("TimeInfo")) {
|
||||
return std::string("unknown");
|
||||
}
|
||||
|
||||
vtkAbstractArray* TimeInfo = multiblock->GetFieldData()->GetAbstractArray("TimeInfo");
|
||||
if (!TimeInfo ||
|
||||
!TimeInfo->IsA("vtkStringArray") ||
|
||||
TimeInfo->GetNumberOfTuples() < 2) {
|
||||
|
||||
return std::string("unknown");
|
||||
}
|
||||
|
||||
return vtkStringArray::SafeDownCast(TimeInfo)->GetValue(0);
|
||||
}
|
||||
|
||||
Base::Unit FemPostPipeline::getFrameUnit()
|
||||
{
|
||||
vtkSmartPointer<vtkDataObject> data = Data.getValue();
|
||||
|
||||
// check if we have frame data
|
||||
if (!data || !data->IsA("vtkMultiBlockDataSet")) {
|
||||
// units cannot be undefined, so use time
|
||||
return Base::Unit::TimeSpan;
|
||||
}
|
||||
|
||||
// we have multiple frames! let's check the amount and times
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(data);
|
||||
if (!multiblock->GetFieldData()->HasArray("TimeInfo")) {
|
||||
// units cannot be undefined, so use time
|
||||
return Base::Unit::TimeSpan;
|
||||
}
|
||||
|
||||
vtkAbstractArray* TimeInfo = multiblock->GetFieldData()->GetAbstractArray("TimeInfo");
|
||||
if (!TimeInfo->IsA("vtkStringArray") ||
|
||||
TimeInfo->GetNumberOfTuples() < 2) {
|
||||
|
||||
// units cannot be undefined, so use time
|
||||
return Base::Unit::TimeSpan;
|
||||
}
|
||||
|
||||
return Base::Unit(QString::fromStdString(vtkStringArray::SafeDownCast(TimeInfo)->GetValue(1)));
|
||||
}
|
||||
|
||||
std::vector<double> FemPostPipeline::getFrameValues()
|
||||
{
|
||||
return m_source_algorithm->getFrameValues();
|
||||
}
|
||||
|
||||
unsigned int FemPostPipeline::getFrameNumber()
|
||||
{
|
||||
// lazy implementation
|
||||
return getFrameValues().size();
|
||||
}
|
||||
|
||||
void FemPostPipeline::load(FemResultObject* res)
|
||||
{
|
||||
if (!res->Mesh.getValue()) {
|
||||
@@ -243,6 +575,53 @@ void FemPostPipeline::load(FemResultObject* res)
|
||||
Data.setValue(grid);
|
||||
}
|
||||
|
||||
// set multiple result objects as frames for one pipeline
|
||||
// Notes:
|
||||
// 1. values vector must contain growing value, smallest first
|
||||
void FemPostPipeline::load(std::vector<FemResultObject*> res, std::vector<double> values, Base::Unit unit, std::string frame_type) {
|
||||
|
||||
if (res.size() != values.size() ) {
|
||||
Base::Console().Error("Result values and frame values have different length.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// setup the time information for the multiblock
|
||||
vtkStringArray* TimeInfo = vtkStringArray::New();
|
||||
TimeInfo->SetName("TimeInfo");
|
||||
TimeInfo->InsertNextValue(frame_type);
|
||||
TimeInfo->InsertNextValue(unit.getString().toStdString());
|
||||
|
||||
auto multiblock = vtkSmartPointer<vtkMultiBlockDataSet>::New();
|
||||
for (ulong i=0; i<res.size(); i++) {
|
||||
|
||||
if (!res[i]->Mesh.getValue()->isDerivedFrom(Fem::FemMeshObject::getClassTypeId())) {
|
||||
Base::Console().Error("Result mesh object is not derived from Fem::FemMeshObject.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// first copy the mesh over
|
||||
const FemMesh& mesh = static_cast<FemMeshObject*>(res[i]->Mesh.getValue())->FemMesh.getValue();
|
||||
vtkSmartPointer<vtkUnstructuredGrid> grid = vtkSmartPointer<vtkUnstructuredGrid>::New();
|
||||
FemVTKTools::exportVTKMesh(&mesh, grid);
|
||||
|
||||
// Now copy the point data over
|
||||
FemVTKTools::exportFreeCADResult(res[i], grid);
|
||||
|
||||
// add time information
|
||||
vtkFloatArray* TimeValue = vtkFloatArray::New();
|
||||
TimeValue->SetNumberOfComponents(1);
|
||||
TimeValue->SetName("TimeValue");
|
||||
TimeValue->InsertNextValue(values[i]);
|
||||
grid->GetFieldData()->AddArray(TimeValue);
|
||||
grid->GetFieldData()->AddArray(TimeInfo);
|
||||
|
||||
multiblock->SetBlock(i, grid);
|
||||
}
|
||||
|
||||
multiblock->GetFieldData()->AddArray(TimeInfo);
|
||||
Data.setValue(multiblock);
|
||||
}
|
||||
|
||||
PyObject* FemPostPipeline::getPyObject()
|
||||
{
|
||||
if (PythonObject.is(Py::_None())) {
|
||||
|
||||
@@ -23,31 +23,66 @@
|
||||
#ifndef Fem_FemPostPipeline_H
|
||||
#define Fem_FemPostPipeline_H
|
||||
|
||||
<<<<<<< HEAD
|
||||
|
||||
=======
|
||||
#include "Base/Unit.h"
|
||||
>>>>>>> 782848c556 (FEM: Make multistep work for eigenmodes)
|
||||
#include "App/GroupExtension.h"
|
||||
|
||||
#include "FemPostFilter.h"
|
||||
#include "FemPostFunction.h"
|
||||
#include "FemPostObject.h"
|
||||
#include "FemResultObject.h"
|
||||
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkUnstructuredGridAlgorithm.h>
|
||||
#include <vtkInformation.h>
|
||||
#include <vtkInformationVector.h>
|
||||
|
||||
|
||||
namespace Fem
|
||||
{
|
||||
|
||||
class FemExport FemPostPipeline: public Fem::FemPostObject
|
||||
// algorithm that allows multi frame handling: if data is stored in MultiBlock dataset
|
||||
// this source enables the downstream filters to query the blocks as different time frames
|
||||
class FemFrameSourceAlgorithm : public vtkUnstructuredGridAlgorithm
|
||||
{
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostPipeline);
|
||||
public:
|
||||
static FemFrameSourceAlgorithm* New();
|
||||
vtkTypeMacro(FemFrameSourceAlgorithm, vtkUnstructuredGridAlgorithm);
|
||||
|
||||
void setDataObject(vtkSmartPointer<vtkDataObject> data);
|
||||
std::vector<double> getFrameValues();
|
||||
|
||||
protected:
|
||||
FemFrameSourceAlgorithm();
|
||||
~FemFrameSourceAlgorithm() override;
|
||||
|
||||
vtkSmartPointer<vtkDataObject> m_data;
|
||||
|
||||
int RequestInformation(vtkInformation* reqInfo, vtkInformationVector** inVector, vtkInformationVector* outVector) override;
|
||||
int RequestData(vtkInformation* reqInfo, vtkInformationVector** inVector, vtkInformationVector* outVector) override;
|
||||
};
|
||||
|
||||
|
||||
class FemExport FemPostPipeline: public Fem::FemPostObject, public App::GroupExtension
|
||||
{
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(Fem::FemPostPipeline);
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
FemPostPipeline();
|
||||
~FemPostPipeline() override;
|
||||
|
||||
App::PropertyLinkList Filter;
|
||||
App::PropertyLink Functions;
|
||||
App::PropertyEnumeration Mode;
|
||||
App::PropertyEnumeration Frame;
|
||||
|
||||
|
||||
virtual vtkDataSet* getDataSet() override;
|
||||
|
||||
short mustExecute() const override;
|
||||
App::DocumentObjectExecReturn* execute() override;
|
||||
PyObject* getPyObject() override;
|
||||
|
||||
const char* getViewProviderName() const override
|
||||
@@ -62,17 +97,29 @@ public:
|
||||
|
||||
// load from results
|
||||
void load(FemResultObject* res);
|
||||
void load(std::vector<FemResultObject*> res, std::vector<double> values, Base::Unit unit, std::string frame_type);
|
||||
|
||||
// Pipeline handling
|
||||
void filterChanged(FemPostFilter* filter);
|
||||
void pipelineChanged(FemPostFilter* filter);
|
||||
void recomputeChildren();
|
||||
FemPostObject* getLastPostObject();
|
||||
bool holdsPostObject(FemPostObject* obj);
|
||||
|
||||
// frame handling
|
||||
bool hasFrames();
|
||||
std::string getFrameType();
|
||||
Base::Unit getFrameUnit();
|
||||
unsigned int getFrameNumber();
|
||||
std::vector<double> getFrameValues();
|
||||
|
||||
protected:
|
||||
void onChanged(const App::Property* prop) override;
|
||||
|
||||
private:
|
||||
static const char* ModeEnums[];
|
||||
App::Enumeration m_frameEnum;
|
||||
vtkSmartPointer<FemFrameSourceAlgorithm> m_source_algorithm;
|
||||
|
||||
template<class TReader>
|
||||
void readXMLFile(std::string file)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</Methode>
|
||||
<Methode Name="load">
|
||||
<Documentation>
|
||||
<UserDocu>Load a result object</UserDocu>
|
||||
<UserDocu>Load a single result object or create a multiframe result by loading multiple result frames. If multistep is wanted, 4 argumenhts are needed: 1. List of result object each being one frame, 2. List of values valid for each frame (e.g. [s] if time data), 3. the unit of the value, 4. the Description of the frames</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="recomputeChildren">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#endif
|
||||
|
||||
#include <Base/FileInfo.h>
|
||||
#include <Base/UnitPy.h>
|
||||
|
||||
// clang-format off
|
||||
#include "FemPostPipeline.h"
|
||||
@@ -65,18 +66,87 @@ PyObject* FemPostPipelinePy::scale(PyObject* args)
|
||||
|
||||
PyObject* FemPostPipelinePy::load(PyObject* args)
|
||||
{
|
||||
PyObject* py;
|
||||
if (!PyArg_ParseTuple(args, "O!", &(App::DocumentObjectPy::Type), &py)) {
|
||||
return nullptr;
|
||||
PyObject *py;
|
||||
PyObject *list = nullptr;
|
||||
PyObject *unitobj = nullptr;
|
||||
const char* value_type;
|
||||
|
||||
if (PyArg_ParseTuple(args, "O|OO!s", &py, &list, &(Base::UnitPy::Type), &unitobj, &value_type)) {
|
||||
|
||||
if (list == nullptr) {
|
||||
|
||||
// single argument version!
|
||||
|
||||
if (!PyObject_TypeCheck(py, &(App::DocumentObjectPy::Type))) {
|
||||
PyErr_SetString(PyExc_TypeError, "object is not a result object");
|
||||
return nullptr;
|
||||
}
|
||||
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(py)->getDocumentObjectPtr();
|
||||
if (!obj->isDerivedFrom<FemResultObject>()) {
|
||||
PyErr_SetString(PyExc_TypeError, "object is not a result object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
getFemPostPipelinePtr()->load(static_cast<FemResultObject*>(obj));
|
||||
Py_Return;
|
||||
}
|
||||
else if (list != nullptr && unitobj != nullptr) {
|
||||
|
||||
//multistep version!
|
||||
|
||||
if ( !(PyTuple_Check(py) || PyList_Check(py)) ||
|
||||
!(PyTuple_Check(list) || PyList_Check(list)) ) {
|
||||
|
||||
std::string error = std::string("Result and value must be list of ResultObjet and number respectively.");
|
||||
throw Base::TypeError(error);
|
||||
}
|
||||
|
||||
// extract the result objects
|
||||
Py::Sequence result_list(py);
|
||||
Py::Sequence::size_type size = result_list.size();
|
||||
std::vector<FemResultObject*> results;
|
||||
results.resize(size);
|
||||
|
||||
for (Py::Sequence::size_type i = 0; i < size; i++) {
|
||||
Py::Object item = result_list[i];
|
||||
if (!PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) {
|
||||
std::string error = std::string("type in result list must be 'ResultObject', not ");
|
||||
throw Base::TypeError(error);
|
||||
}
|
||||
auto obj = static_cast<DocumentObjectPy*>(*item)->getDocumentObjectPtr();
|
||||
if (!obj->isDerivedFrom<FemResultObject>()) {
|
||||
throw Base::TypeError("object is not a result object");
|
||||
}
|
||||
results[i] = static_cast<FemResultObject*>(obj);
|
||||
}
|
||||
|
||||
//extract the values
|
||||
Py::Sequence values_list(list);
|
||||
size = values_list.size();
|
||||
std::vector<double> values;
|
||||
values.resize(size);
|
||||
|
||||
for (Py::Sequence::size_type i = 0; i < size; i++) {
|
||||
Py::Object item = values_list[i];
|
||||
if (!PyFloat_Check(*item)) {
|
||||
std::string error = std::string("Values must be float");
|
||||
throw Base::TypeError(error);
|
||||
}
|
||||
values[i] = PyFloat_AsDouble(*item);
|
||||
}
|
||||
|
||||
// extract the unit
|
||||
Base::Unit unit = *(static_cast<Base::UnitPy*>(unitobj)->getUnitPtr());
|
||||
|
||||
// extract the value type
|
||||
std::string step_type = std::string(value_type);
|
||||
|
||||
// Finally call the c++ function!
|
||||
getFemPostPipelinePtr()->load(results, values, unit, step_type);
|
||||
Py_Return;
|
||||
}
|
||||
}
|
||||
|
||||
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(py)->getDocumentObjectPtr();
|
||||
if (!obj->isDerivedFrom<FemResultObject>()) {
|
||||
PyErr_SetString(PyExc_TypeError, "object is not a result object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
getFemPostPipelinePtr()->load(static_cast<FemResultObject*>(obj));
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <vtkUniformGrid.h>
|
||||
#include <vtkUnstructuredGrid.h>
|
||||
#include <vtkXMLDataSetWriter.h>
|
||||
#include <vtkXMLMultiBlockDataWriter.h>
|
||||
#include <vtkXMLMultiBlockDataReader.h>
|
||||
#include <vtkXMLImageDataReader.h>
|
||||
#include <vtkXMLPolyDataReader.h>
|
||||
#include <vtkXMLRectilinearGridReader.h>
|
||||
@@ -49,6 +51,12 @@
|
||||
#include <Base/Writer.h>
|
||||
#include <CXX/Objects.hxx>
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <zipios++/zipios-config.h>
|
||||
#endif
|
||||
#include <zipios++/zipoutputstream.h>
|
||||
|
||||
#include "PropertyPostDataObject.h"
|
||||
|
||||
|
||||
@@ -246,11 +254,11 @@ void PropertyPostDataObject::getPaths(std::vector<App::ObjectIdentifier>& /*path
|
||||
|
||||
void PropertyPostDataObject::Save(Base::Writer& writer) const
|
||||
{
|
||||
std::string extension;
|
||||
if (!m_dataObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string extension;
|
||||
switch (m_dataObject->GetDataObjectType()) {
|
||||
|
||||
case VTK_POLY_DATA:
|
||||
@@ -268,16 +276,9 @@ void PropertyPostDataObject::Save(Base::Writer& writer) const
|
||||
case VTK_UNIFORM_GRID:
|
||||
extension = "vti"; // image data
|
||||
break;
|
||||
// TODO:multi-datasets use multiple files, this needs to be implemented specially
|
||||
// case VTK_COMPOSITE_DATA_SET:
|
||||
// prop->m_dataObject = vtkCompositeDataSet::New();
|
||||
// break;
|
||||
// case VTK_MULTIBLOCK_DATA_SET:
|
||||
// prop->m_dataObject = vtkMultiBlockDataSet::New();
|
||||
// break;
|
||||
// case VTK_MULTIPIECE_DATA_SET:
|
||||
// prop->m_dataObject = vtkMultiPieceDataSet::New();
|
||||
// break;
|
||||
case VTK_MULTIBLOCK_DATA_SET:
|
||||
extension = "zip";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
@@ -297,13 +298,29 @@ void PropertyPostDataObject::Restore(Base::XMLReader& reader)
|
||||
}
|
||||
|
||||
std::string file(reader.getAttribute("file"));
|
||||
|
||||
if (!file.empty()) {
|
||||
// initiate a file read
|
||||
reader.addFile(file.c_str(), this);
|
||||
}
|
||||
}
|
||||
|
||||
void add_to_zip(Base::FileInfo path, int zip_path_idx, zipios::ZipOutputStream& ZipWriter) {
|
||||
|
||||
if (path.isDir()) {
|
||||
for(auto file : path.getDirectoryContent()) {
|
||||
add_to_zip(file, zip_path_idx, ZipWriter);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ZipWriter.putNextEntry(path.filePath().substr(zip_path_idx));
|
||||
Base::ifstream file(path, std::ios::in | std::ios::binary);
|
||||
if (file) {
|
||||
std::streambuf* buf = file.rdbuf();
|
||||
ZipWriter << buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const
|
||||
{
|
||||
// If the shape is empty we simply store nothing. The file size will be 0 which
|
||||
@@ -315,12 +332,41 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const
|
||||
// create a temporary file and copy the content to the zip stream
|
||||
// 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(App::Application::getTempFileName());
|
||||
static Base::FileInfo fi = Base::FileInfo(App::Application::getTempFileName());
|
||||
bool success = false;
|
||||
|
||||
vtkSmartPointer<vtkXMLDataSetWriter> xmlWriter = vtkSmartPointer<vtkXMLDataSetWriter>::New();
|
||||
xmlWriter->SetInputDataObject(m_dataObject);
|
||||
xmlWriter->SetFileName(fi.filePath().c_str());
|
||||
xmlWriter->SetDataModeToBinary();
|
||||
|
||||
if (m_dataObject->IsA("vtkMultiBlockDataSet")) {
|
||||
|
||||
// create a tmp directory to write in
|
||||
auto 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->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->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
|
||||
@@ -331,7 +377,7 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const
|
||||
}
|
||||
#endif
|
||||
|
||||
if (xmlWriter->Write() != 1) {
|
||||
if (!success) {
|
||||
// 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
|
||||
@@ -352,6 +398,7 @@ void PropertyPostDataObject::SaveDocFile(Base::Writer& writer) const
|
||||
writer.addError(ss.str());
|
||||
}
|
||||
|
||||
|
||||
Base::ifstream file(fi, std::ios::in | std::ios::binary);
|
||||
if (file) {
|
||||
std::streambuf* buf = file.rdbuf();
|
||||
@@ -368,6 +415,7 @@ void PropertyPostDataObject::RestoreDocFile(Base::Reader& reader)
|
||||
Base::FileInfo xml(reader.getFileName());
|
||||
// create a temporary file and copy the content from the zip stream
|
||||
Base::FileInfo fi(App::Application::getTempFileName());
|
||||
Base::FileInfo fo;
|
||||
|
||||
// read in the ASCII file and write back to the file stream
|
||||
Base::ofstream file(fi, std::ios::out | std::ios::binary);
|
||||
@@ -402,11 +450,50 @@ void PropertyPostDataObject::RestoreDocFile(Base::Reader& reader)
|
||||
else if (extension == "vti") {
|
||||
xmlReader = vtkSmartPointer<vtkXMLImageDataReader>::New();
|
||||
}
|
||||
else if (extension == "zip") {
|
||||
|
||||
// first unzip the file into a datafolder
|
||||
zipios::ZipInputStream ZipReader(fi.filePath());
|
||||
fo = Base::FileInfo(App::Application::getTempPath() + "vtk_extract_datadir");
|
||||
fo.createDirectories();
|
||||
|
||||
try {
|
||||
zipios::ConstEntryPointer entry = ZipReader.getNextEntry();
|
||||
while(entry->isValid()) {
|
||||
Base::FileInfo entry_path(fo.filePath() + entry->getName());
|
||||
if (entry->isDirectory()) {
|
||||
// seems not to be called
|
||||
entry_path.createDirectories();
|
||||
}
|
||||
else {
|
||||
auto entry_dir = Base::FileInfo(entry_path.dirPath());
|
||||
if(!entry_dir.exists()) {
|
||||
entry_dir.createDirectories();
|
||||
}
|
||||
|
||||
Base::ofstream file(entry_path, std::ios::out | std::ios::binary);
|
||||
std::streambuf* buf = file.rdbuf();
|
||||
ZipReader >> buf;
|
||||
file.flush();
|
||||
file.close();
|
||||
}
|
||||
entry = ZipReader.getNextEntry();
|
||||
}
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
// there is no further entry
|
||||
}
|
||||
|
||||
// create the reader, and change the file for it to read. Also delete zip file, not needed anymore
|
||||
fi.deleteFile();
|
||||
fi = Base::FileInfo(fo.filePath() + "/datafile.vtm");
|
||||
xmlReader = vtkSmartPointer<vtkXMLMultiBlockDataReader>::New();
|
||||
}
|
||||
|
||||
xmlReader->SetFileName(fi.filePath().c_str());
|
||||
xmlReader->Update();
|
||||
|
||||
if (!xmlReader->GetOutputAsDataSet()) {
|
||||
if (!xmlReader->GetOutputDataObject(0)) {
|
||||
// Note: Do NOT throw an exception here because if the tmp. created file could
|
||||
// not be read it's NOT an indication for an invalid input stream 'reader'.
|
||||
// We only print an error message but continue reading the next files from the
|
||||
@@ -425,12 +512,15 @@ void PropertyPostDataObject::RestoreDocFile(Base::Reader& reader)
|
||||
}
|
||||
else {
|
||||
aboutToSetValue();
|
||||
createDataObjectByExternalType(xmlReader->GetOutputAsDataSet());
|
||||
m_dataObject->DeepCopy(xmlReader->GetOutputAsDataSet());
|
||||
createDataObjectByExternalType(xmlReader->GetOutputDataObject(0));
|
||||
m_dataObject->DeepCopy(xmlReader->GetOutputDataObject(0));
|
||||
hasSetValue();
|
||||
}
|
||||
}
|
||||
|
||||
// delete the temp file
|
||||
fi.deleteFile();
|
||||
if (xml.extension() == "zip") {
|
||||
fo.deleteDirectoryRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ if(BUILD_FEM_VTK)
|
||||
TaskPostDisplay.ui
|
||||
TaskPostScalarClip.ui
|
||||
TaskPostWarpVector.ui
|
||||
TaskPostFrames.ui
|
||||
)
|
||||
endif(BUILD_FEM_VTK)
|
||||
|
||||
|
||||
@@ -1871,13 +1871,8 @@ void setupFilter(Gui::Command* cmd, std::string Name)
|
||||
FeatName.c_str());
|
||||
// add it as subobject to the pipeline
|
||||
cmd->doCommand(Gui::Command::Doc,
|
||||
"__list__ = App.ActiveDocument.%s.Filter",
|
||||
pipeline->getNameInDocument());
|
||||
cmd->doCommand(Gui::Command::Doc, "__list__.append(App.ActiveDocument.%s)", FeatName.c_str());
|
||||
cmd->doCommand(Gui::Command::Doc,
|
||||
"App.ActiveDocument.%s.Filter = __list__",
|
||||
pipeline->getNameInDocument());
|
||||
cmd->doCommand(Gui::Command::Doc, "del __list__");
|
||||
"App.ActiveDocument.%s.addObject(App.ActiveDocument.%s)",
|
||||
pipeline->getNameInDocument(), FeatName.c_str());
|
||||
|
||||
// set display to assure the user sees the new object
|
||||
cmd->doCommand(Gui::Command::Doc,
|
||||
@@ -1888,23 +1883,20 @@ void setupFilter(Gui::Command* cmd, std::string Name)
|
||||
cmd->doCommand(Gui::Command::Doc,
|
||||
"App.activeDocument().ActiveObject.ViewObject.SelectionStyle = \"BoundBox\"");
|
||||
|
||||
// in case selObject is no pipeline we must set it as input object
|
||||
auto objFilter = App::GetApplication().getActiveDocument()->getActiveObject();
|
||||
auto femFilter = static_cast<Fem::FemPostFilter*>(objFilter);
|
||||
if (!selectionIsPipeline) {
|
||||
femFilter->Input.setValue(selObject);
|
||||
}
|
||||
|
||||
femFilter->Data.setValue(static_cast<Fem::FemPostObject*>(selObject)->Data.getValue());
|
||||
auto selObjectView = static_cast<FemGui::ViewProviderFemPostObject*>(
|
||||
Gui::Application::Instance->getViewProvider(selObject));
|
||||
|
||||
cmd->doCommand(Gui::Command::Doc,
|
||||
//TODO: FIX
|
||||
/*cmd->doCommand(Gui::Command::Doc,
|
||||
"App.activeDocument().ActiveObject.ViewObject.Field = \"%s\"",
|
||||
selObjectView->Field.getValueAsString());
|
||||
cmd->doCommand(Gui::Command::Doc,
|
||||
"App.activeDocument().ActiveObject.ViewObject.VectorMode = \"%s\"",
|
||||
selObjectView->VectorMode.getValueAsString());
|
||||
*/
|
||||
|
||||
// hide selected filter
|
||||
if (!femFilter->isDerivedFrom<Fem::FemPostDataAlongLineFilter>()
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
<file>icons/FEM_PostFilterDataAtPoint.svg</file>
|
||||
<file>icons/FEM_PostFilterLinearizedStresses.svg</file>
|
||||
<file>icons/FEM_PostFilterWarp.svg</file>
|
||||
<file>icons/FEM_PostFrames.svg</file>
|
||||
<file>icons/FEM_PostPipelineFromResult.svg</file>
|
||||
<file>icons/FEM_ResultShow.svg</file>
|
||||
<file>icons/FEM_ResultsPurge.svg</file>
|
||||
|
||||
108
src/Mod/Fem/Gui/Resources/icons/FEM_PostFrames.svg
Normal file
108
src/Mod/Fem/Gui/Resources/icons/FEM_PostFrames.svg
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
sodipodi:docname="FEM_PostSteps.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="10.650796"
|
||||
inkscape:cx="26.24217"
|
||||
inkscape:cy="37.743658"
|
||||
inkscape:window-width="3772"
|
||||
inkscape:window-height="2132"
|
||||
inkscape:window-x="68"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient3802">
|
||||
<stop
|
||||
style="stop-color:#4e9a06;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3804" />
|
||||
<stop
|
||||
style="stop-color:#73d216;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3806" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3802"
|
||||
id="linearGradient3808"
|
||||
x1="49"
|
||||
y1="58"
|
||||
x2="47"
|
||||
y2="42"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[Alexander Gryson]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:date>2017-03-11</dc:date>
|
||||
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="translate(0,-988.36218)">
|
||||
<path
|
||||
style="fill:none;stroke:#172a04;stroke-width:7.88137;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4.3166604,1048.6979 c 10.0528756,-6.3495 11.0960996,-30.5752 16.6915666,-33.7987 5.595471,-3.2236 6.828368,10.3544 14.225768,14.3596 3.888376,1.9536 12.80319,-6.5449 15.838022,-11.8199 3.03483,-5.275 9.389006,-25.69077 9.389006,-25.69077 v 0"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#8ae234;stroke-width:5.1317;stroke-opacity:1"
|
||||
d="m 4.3878505,1048.9004 c 10.0583295,-6.3607 11.1021185,-30.6296 16.7006225,-33.859 5.598506,-3.2291 6.832073,10.3729 14.233486,14.3854 3.890486,1.9571 12.810136,-6.5567 15.846614,-11.841 3.036476,-5.2844 9.394101,-25.73679 9.394101,-25.73679 v 0"
|
||||
id="path1-6"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -59,6 +59,8 @@
|
||||
#include "ui_TaskPostDisplay.h"
|
||||
#include "ui_TaskPostScalarClip.h"
|
||||
#include "ui_TaskPostWarpVector.h"
|
||||
#include "ui_TaskPostFrames.h"
|
||||
|
||||
|
||||
#include "FemSettings.h"
|
||||
#include "TaskPostBoxes.h"
|
||||
@@ -473,6 +475,66 @@ void TaskPostFunction::applyPythonCode()
|
||||
}
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// Frames
|
||||
TaskPostFrames::TaskPostFrames(ViewProviderFemPostObject* view, QWidget* parent)
|
||||
: TaskPostBox(view,
|
||||
Gui::BitmapFactory().pixmap("FEM_PostFrames"),
|
||||
tr("Result Frames"),
|
||||
parent), ui(new Ui_TaskPostFrames)
|
||||
{
|
||||
// we load the views widget
|
||||
proxy = new QWidget(this);
|
||||
ui->setupUi(proxy);
|
||||
this->groupLayout()->addWidget(proxy);
|
||||
setupConnections();
|
||||
|
||||
// populate the data
|
||||
auto pipeline = static_cast<Fem::FemPostPipeline*>(getObject());
|
||||
ui->Type->setText(QString::fromStdString(pipeline->getFrameType()));
|
||||
|
||||
auto unit = pipeline->getFrameUnit();
|
||||
auto steps = pipeline->getFrameValues();
|
||||
for (unsigned long i=0; i<steps.size(); i++) {
|
||||
QTableWidgetItem *idx = new QTableWidgetItem(QString::number(i));
|
||||
QTableWidgetItem *value = new QTableWidgetItem(Base::Quantity(steps[i], unit).getUserString());
|
||||
|
||||
int rowIdx = ui->FrameTable->rowCount();
|
||||
ui->FrameTable->insertRow (rowIdx);
|
||||
ui->FrameTable->setItem(rowIdx, 0, idx);
|
||||
ui->FrameTable->setItem(rowIdx, 1, value);
|
||||
}
|
||||
ui->FrameTable->selectRow(pipeline->Frame.getValue());
|
||||
}
|
||||
|
||||
TaskPostFrames::~TaskPostFrames() = default;
|
||||
|
||||
void TaskPostFrames::setupConnections()
|
||||
{
|
||||
connect(ui->FrameTable,
|
||||
qOverload<>(&QTableWidget::itemSelectionChanged),
|
||||
this,
|
||||
&TaskPostFrames::onSelectionChanged);
|
||||
}
|
||||
|
||||
void TaskPostFrames::onSelectionChanged()
|
||||
{
|
||||
auto selection = ui->FrameTable->selectedItems();
|
||||
if (selection.count() > 0) {
|
||||
static_cast<Fem::FemPostPipeline*>(getObject())->Frame.setValue(selection.front()->row());
|
||||
recompute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TaskPostFrames::applyPythonCode()
|
||||
{
|
||||
// we apply the views widgets python code
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// in the following, the different filters sorted alphabetically
|
||||
// ***************************************************************************
|
||||
|
||||
@@ -40,6 +40,7 @@ class Ui_TaskPostDataAtPoint;
|
||||
class Ui_TaskPostScalarClip;
|
||||
class Ui_TaskPostWarpVector;
|
||||
class Ui_TaskPostCut;
|
||||
class Ui_TaskPostFrames;
|
||||
|
||||
class SoFontStyle;
|
||||
class SoText2;
|
||||
@@ -276,6 +277,26 @@ public:
|
||||
void applyPythonCode() override;
|
||||
};
|
||||
|
||||
// ***************************************************************************
|
||||
// steps
|
||||
class TaskPostFrames: public TaskPostBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TaskPostFrames(ViewProviderFemPostObject* view, QWidget* parent = nullptr);
|
||||
~TaskPostFrames() override;
|
||||
|
||||
void applyPythonCode() override;
|
||||
|
||||
private:
|
||||
void setupConnections();
|
||||
void onSelectionChanged();
|
||||
|
||||
QWidget* proxy;
|
||||
std::unique_ptr<Ui_TaskPostFrames> ui;
|
||||
};
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// in the following, the different filters sorted alphabetically
|
||||
|
||||
98
src/Mod/Fem/Gui/TaskPostFrames.ui
Normal file
98
src/Mod/Fem/Gui/TaskPostFrames.ui
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TaskPostFrames</class>
|
||||
<widget class="QWidget" name="TaskPostFrames">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>232</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Type of frames:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="Type">
|
||||
<property name="text">
|
||||
<string>Ressonance Frequencies</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="FrameTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Frame</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -850,15 +850,9 @@ bool ViewProviderFemPostObject::setupPipeline()
|
||||
|
||||
auto postObject = getObject<Fem::FemPostObject>();
|
||||
|
||||
vtkDataObject* data = postObject->Data.getValue();
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check all fields if there is a real/imaginary one and if so
|
||||
// add a field with an absolute value
|
||||
vtkSmartPointer<vtkDataObject> SPdata = data;
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(SPdata);
|
||||
vtkDataSet* dset = postObject->getDataSet();
|
||||
if (!dset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "ViewProviderFemPostFunction.h"
|
||||
#include "ViewProviderFemPostPipeline.h"
|
||||
#include "ViewProviderFemPostPipelinePy.h"
|
||||
#include "TaskPostBoxes.h"
|
||||
|
||||
|
||||
using namespace FemGui;
|
||||
@@ -45,6 +46,7 @@ PROPERTY_SOURCE(FemGui::ViewProviderFemPostPipeline, FemGui::ViewProviderFemPost
|
||||
|
||||
ViewProviderFemPostPipeline::ViewProviderFemPostPipeline()
|
||||
{
|
||||
ViewProviderGroupExtension::initExtension(this);
|
||||
sPixmap = "FEM_PostPipelineFromResult";
|
||||
}
|
||||
|
||||
@@ -54,31 +56,37 @@ std::vector<App::DocumentObject*> ViewProviderFemPostPipeline::claimChildren() c
|
||||
{
|
||||
|
||||
Fem::FemPostPipeline* pipeline = getObject<Fem::FemPostPipeline>();
|
||||
std::vector<App::DocumentObject*> children;
|
||||
std::vector<App::DocumentObject*> children = FemGui::ViewProviderFemPostObject::claimChildren();
|
||||
|
||||
if (pipeline->Functions.getValue()) {
|
||||
children.push_back(pipeline->Functions.getValue());
|
||||
children.insert(children.begin(), pipeline->Functions.getValue());
|
||||
}
|
||||
|
||||
children.insert(children.end(),
|
||||
pipeline->Filter.getValues().begin(),
|
||||
pipeline->Filter.getValues().end());
|
||||
return children;
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> ViewProviderFemPostPipeline::claimChildren3D() const
|
||||
{
|
||||
|
||||
return claimChildren();
|
||||
}
|
||||
|
||||
void ViewProviderFemPostPipeline::updateData(const App::Property* prop)
|
||||
{
|
||||
FemGui::ViewProviderFemPostObject::updateData(prop);
|
||||
<<<<<<< HEAD
|
||||
Fem::FemPostPipeline* pipeline = getObject<Fem::FemPostPipeline>();
|
||||
=======
|
||||
Fem::FemPostPipeline* pipeline = static_cast<Fem::FemPostPipeline*>(getObject());
|
||||
|
||||
>>>>>>> 782848c556 (FEM: Make multistep work for eigenmodes)
|
||||
if (prop == &pipeline->Functions) {
|
||||
updateFunctionSize();
|
||||
}
|
||||
|
||||
if (prop == &pipeline->Frame) {
|
||||
// Frame is pipeline property, not post object, parent updateData does not catch it for update
|
||||
updateVtk();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProviderFemPostPipeline::updateFunctionSize()
|
||||
@@ -180,8 +188,7 @@ void ViewProviderFemPostPipeline::transformField(char* FieldName, double FieldFa
|
||||
{
|
||||
Fem::FemPostPipeline* obj = getObject<Fem::FemPostPipeline>();
|
||||
|
||||
vtkSmartPointer<vtkDataObject> data = obj->Data.getValue();
|
||||
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
|
||||
vtkDataSet* dset = obj->getDataSet();
|
||||
if (!dset) {
|
||||
return;
|
||||
}
|
||||
@@ -238,6 +245,15 @@ void ViewProviderFemPostPipeline::scaleField(vtkDataSet* dset,
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProviderFemPostPipeline::setupTaskDialog(TaskDlgPost* dlg)
|
||||
{
|
||||
// add the function box
|
||||
assert(dlg->getView() == this);
|
||||
ViewProviderFemPostObject::setupTaskDialog(dlg);
|
||||
dlg->appendBox(new TaskPostFrames(this));
|
||||
}
|
||||
|
||||
|
||||
PyObject* ViewProviderFemPostPipeline::getPyObject()
|
||||
{
|
||||
if (!pyViewObject) {
|
||||
|
||||
@@ -26,16 +26,17 @@
|
||||
#include <Gui/ViewProviderFeaturePython.h>
|
||||
#include <Mod/Fem/FemGlobal.h>
|
||||
|
||||
#include "Gui/ViewProviderGroupExtension.h"
|
||||
#include "ViewProviderFemPostObject.h"
|
||||
|
||||
|
||||
namespace FemGui
|
||||
{
|
||||
|
||||
class FemGuiExport ViewProviderFemPostPipeline: public ViewProviderFemPostObject
|
||||
class FemGuiExport ViewProviderFemPostPipeline: public ViewProviderFemPostObject, public Gui::ViewProviderGroupExtension
|
||||
{
|
||||
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(FemGui::ViewProviderFemPostPipeline);
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(FemGui::ViewProviderFemPostPipeline);
|
||||
|
||||
public:
|
||||
/// constructor.
|
||||
@@ -52,8 +53,13 @@ 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;
|
||||
};
|
||||
|
||||
} // namespace FemGui
|
||||
|
||||
@@ -93,9 +93,9 @@ FreeCAD.addImportType(
|
||||
|
||||
if "BUILD_FEM_VTK" in FreeCAD.__cmake__:
|
||||
FreeCAD.addImportType(
|
||||
"FEM result VTK (*.vtk *.VTK *.vtu *.VTU *.pvtu *.PVTU)",
|
||||
"FEM result VTK (*.vtk *.VTK *.vtu *.VTU *.pvtu *.PVTU *.vtm .VTM)",
|
||||
"feminout.importVTKResults",
|
||||
)
|
||||
FreeCAD.addExportType(
|
||||
"FEM result VTK (*.vtu *.vtp *.vts *.vtr *.vti)", "feminout.importVTKResults"
|
||||
"FEM result VTK (*.vtu *.vtp *.vts *.vtr *.vti *.vtm)", "feminout.importVTKResults"
|
||||
)
|
||||
|
||||
@@ -603,10 +603,7 @@ def makePostVtkFilterClipRegion(doc, base_vtk_result, name="VtkFilterClipRegion"
|
||||
"""makePostVtkFilterClipRegion(document, base_vtk_result, [name]):
|
||||
creates a FEM post processing region clip filter object (vtk based)"""
|
||||
obj = doc.addObject("Fem::FemPostClipFilter", name)
|
||||
tmp_filter_list = base_vtk_result.Filter
|
||||
tmp_filter_list.append(obj)
|
||||
base_vtk_result.Filter = tmp_filter_list
|
||||
del tmp_filter_list
|
||||
base_vtk_result.addObject(obj)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -614,10 +611,7 @@ def makePostVtkFilterClipScalar(doc, base_vtk_result, name="VtkFilterClipScalar"
|
||||
"""makePostVtkFilterClipScalar(document, base_vtk_result, [name]):
|
||||
creates a FEM post processing scalar clip filter object (vtk based)"""
|
||||
obj = doc.addObject("Fem::FemPostScalarClipFilter", name)
|
||||
tmp_filter_list = base_vtk_result.Filter
|
||||
tmp_filter_list.append(obj)
|
||||
base_vtk_result.Filter = tmp_filter_list
|
||||
del tmp_filter_list
|
||||
base_vtk_result.addObject(obj)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -625,10 +619,7 @@ def makePostVtkFilterCutFunction(doc, base_vtk_result, name="VtkFilterCutFunctio
|
||||
"""makePostVtkFilterCutFunction(document, base_vtk_result, [name]):
|
||||
creates a FEM post processing cut function filter object (vtk based)"""
|
||||
obj = doc.addObject("Fem::FemPostClipFilter", name)
|
||||
tmp_filter_list = base_vtk_result.Filter
|
||||
tmp_filter_list.append(obj)
|
||||
base_vtk_result.Filter = tmp_filter_list
|
||||
del tmp_filter_list
|
||||
base_vtk_result.addObject(obj)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -636,10 +627,7 @@ def makePostVtkFilterWarp(doc, base_vtk_result, name="VtkFilterWarp"):
|
||||
"""makePostVtkFilterWarp(document, base_vtk_result, [name]):
|
||||
creates a FEM post processing warp filter object (vtk based)"""
|
||||
obj = doc.addObject("Fem::FemPostWarpVectorFilter", name)
|
||||
tmp_filter_list = base_vtk_result.Filter
|
||||
tmp_filter_list.append(obj)
|
||||
base_vtk_result.Filter = tmp_filter_list
|
||||
del tmp_filter_list
|
||||
base_vtk_result.addObject(obj)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -647,19 +635,24 @@ def makePostVtkFilterContours(doc, base_vtk_result, name="VtkFilterContours"):
|
||||
"""makePostVtkFilterContours(document, base_vtk_result, [name]):
|
||||
creates a FEM post processing contours filter object (vtk based)"""
|
||||
obj = doc.addObject("Fem::FemPostContoursFilter", name)
|
||||
tmp_filter_list = base_vtk_result.Filter
|
||||
tmp_filter_list.append(obj)
|
||||
base_vtk_result.Filter = tmp_filter_list
|
||||
del tmp_filter_list
|
||||
base_vtk_result.addObject(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def makePostVtkResult(doc, base_result, name="VtkResult"):
|
||||
def makePostVtkResult(doc, result_data, name="VtkResult"):
|
||||
"""makePostVtkResult(document, base_result, [name]):
|
||||
creates a FEM post processing result object (vtk based) to hold FEM results"""
|
||||
creates a FEM post processing result data (vtk based) to hold FEM results
|
||||
Note: Result data get expanded, it can either be single result [result] or everything
|
||||
needed for a multistep result: [results_list, value_list, unit, description]
|
||||
"""
|
||||
|
||||
print(result_data)
|
||||
|
||||
Pipeline_Name = "Pipeline_" + name
|
||||
obj = doc.addObject("Fem::FemPostPipeline", Pipeline_Name)
|
||||
obj.load(base_result)
|
||||
print("load")
|
||||
obj.load(*result_data)
|
||||
print("load done")
|
||||
if FreeCAD.GuiUp:
|
||||
obj.ViewObject.SelectionStyle = "BoundBox"
|
||||
# to assure the user sees something, set the default to Surface
|
||||
|
||||
@@ -58,6 +58,44 @@ def insert(filename, docname):
|
||||
|
||||
|
||||
# ********* module specific methods *********
|
||||
def setupPipeline(doc, analysis, results_name, result_data):
|
||||
import ObjectsFem
|
||||
from . import importToolsFem
|
||||
|
||||
|
||||
# create a results pipeline if not already existing
|
||||
pipeline_name = "Pipeline_" + results_name
|
||||
pipeline_obj = doc.getObject(pipeline_name)
|
||||
if pipeline_obj is None:
|
||||
|
||||
pipeline_obj = ObjectsFem.makePostVtkResult(doc, result_data, results_name)
|
||||
pipeline_visibility = True
|
||||
if analysis:
|
||||
analysis.addObject(pipeline_obj)
|
||||
else:
|
||||
if FreeCAD.GuiUp:
|
||||
# store pipeline visibility because pipeline_obj.load makes the
|
||||
# pipeline always visible
|
||||
pipeline_visibility = pipeline_obj.ViewObject.Visibility
|
||||
|
||||
pipeline_obj.load(*result_data)
|
||||
|
||||
# update the pipeline
|
||||
pipeline_obj.recomputeChildren()
|
||||
pipeline_obj.recompute()
|
||||
if FreeCAD.GuiUp:
|
||||
pipeline_obj.ViewObject.updateColorBars()
|
||||
# make results mesh invisible, will be made visible
|
||||
# later in task_solver_ccxtools.py
|
||||
if len(result_data) == 1:
|
||||
result_data[0].Mesh.ViewObject.Visibility = False
|
||||
else:
|
||||
for res in result_data[0]:
|
||||
res.Mesh.ViewObject.Visibility = False
|
||||
# restore pipeline visibility
|
||||
pipeline_obj.ViewObject.Visibility = pipeline_visibility
|
||||
|
||||
|
||||
def importFrd(filename, analysis=None, result_name_prefix="", result_analysis_type=""):
|
||||
import ObjectsFem
|
||||
from . import importToolsFem
|
||||
@@ -87,6 +125,8 @@ def importFrd(filename, analysis=None, result_name_prefix="", result_analysis_ty
|
||||
res_obj.Mesh = result_mesh_object
|
||||
return res_obj
|
||||
|
||||
multistep_result = []
|
||||
multistep_value = []
|
||||
if len(m["Results"]) > 0:
|
||||
for result_set in m["Results"]:
|
||||
if "number" in result_set:
|
||||
@@ -109,6 +149,7 @@ def importFrd(filename, analysis=None, result_name_prefix="", result_analysis_ty
|
||||
else:
|
||||
results_name = f"{result_name_prefix}Results"
|
||||
|
||||
multistep_value.append(step_time)
|
||||
res_obj = make_result_mesh(results_name)
|
||||
res_obj = importToolsFem.fill_femresult_mechanical(res_obj, result_set)
|
||||
if analysis:
|
||||
@@ -176,30 +217,19 @@ def importFrd(filename, analysis=None, result_name_prefix="", result_analysis_ty
|
||||
# fill Stats
|
||||
res_obj = resulttools.fill_femresult_stats(res_obj)
|
||||
|
||||
# create a results pipeline if not already existing
|
||||
pipeline_name = "Pipeline_" + results_name
|
||||
pipeline_obj = doc.getObject(pipeline_name)
|
||||
if pipeline_obj is None:
|
||||
pipeline_obj = ObjectsFem.makePostVtkResult(doc, res_obj, results_name)
|
||||
pipeline_visibility = True
|
||||
if analysis:
|
||||
analysis.addObject(pipeline_obj)
|
||||
# if we have multiple results we delay the pipeline creation
|
||||
if len(m["Results"]) == 1:
|
||||
setupPipeline(doc, analysis, results_name, [res_obj])
|
||||
else:
|
||||
if FreeCAD.GuiUp:
|
||||
# store pipeline visibility because pipeline_obj.load makes the
|
||||
# pipeline always visible
|
||||
pipeline_visibility = pipeline_obj.ViewObject.Visibility
|
||||
pipeline_obj.load(res_obj)
|
||||
# update the pipeline
|
||||
pipeline_obj.recomputeChildren()
|
||||
pipeline_obj.recompute()
|
||||
if FreeCAD.GuiUp:
|
||||
pipeline_obj.ViewObject.updateColorBars()
|
||||
# make results mesh invisible, will be made visible
|
||||
# later in task_solver_ccxtools.py
|
||||
res_obj.Mesh.ViewObject.Visibility = False
|
||||
# restore pipeline visibility
|
||||
pipeline_obj.ViewObject.Visibility = pipeline_visibility
|
||||
multistep_result.append(res_obj)
|
||||
|
||||
|
||||
# we have collected all result objects, lets create the multistep result pipeline
|
||||
if len(m["Results"]) > 1:
|
||||
unit = FreeCAD.Units.Frequency
|
||||
description = "Eigenmodes"
|
||||
setupPipeline(doc, analysis, results_name, [multistep_result, multistep_value, unit, description])
|
||||
|
||||
|
||||
elif result_analysis_type == "check":
|
||||
results_name = f"{result_name_prefix}Check"
|
||||
|
||||
@@ -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