[FEM] add new filter to display iso contours

- very valuable to display for example electromagnetic field lines
This commit is contained in:
Uwe
2023-02-11 06:47:48 +01:00
parent 3937a746b8
commit fa780e8b5b
18 changed files with 788 additions and 68 deletions

View File

@@ -178,6 +178,7 @@ PyMOD_INIT_FUNC(Fem)
Fem::FemPostPipeline ::init();
Fem::FemPostFilter ::init();
Fem::FemPostClipFilter ::init();
Fem::FemPostContoursFilter ::init();
Fem::FemPostCutFilter ::init();
Fem::FemPostDataAlongLineFilter ::init();
Fem::FemPostDataAtPointFilter ::init();

View File

@@ -24,6 +24,7 @@
#ifndef _PreComp_
# include <Python.h>
# include <vtkDoubleArray.h>
# include <vtkPointData.h>
#endif
@@ -68,23 +69,22 @@ DocumentObjectExecReturn* FemPostFilter::execute() {
if (!m_pipelines.empty() && !m_activePipeline.empty()) {
FemPostFilter::FilterPipeline& pipe = m_pipelines[m_activePipeline];
if (m_activePipeline.length() >= 11) {
std::string LineClip = m_activePipeline.substr(0, 13);
std::string PointClip = m_activePipeline.substr(0, 11);
if ((LineClip == "DataAlongLine") || (PointClip == "DataAtPoint")) {
vtkSmartPointer<vtkDataObject> data = getInputData();
if (!data || !data->IsA("vtkDataSet"))
return StdReturn;
if ((m_activePipeline == "DataAlongLine") || (m_activePipeline == "DataAtPoint")) {
pipe.filterSource->SetSourceData(getInputData());
pipe.filterTarget->Update();
Data.setValue(pipe.filterTarget->GetOutputDataObject(0));
}
}
else {
pipe.source->SetInputDataObject(getInputData());
pipe.source->SetInputDataObject(data);
pipe.target->Update();
Data.setValue(pipe.target->GetOutputDataObject(0));
}
}
return StdReturn;
}
@@ -129,7 +129,7 @@ FemPostClipFilter::FemPostClipFilter() : FemPostFilter() {
(false),
"Clip",
App::Prop_None,
"Decides if cells are cuttet and interpolated or if the cells are kept as a whole");
"Decides if cells are cut and interpolated or if the cells are kept as a whole");
FilterPipeline clip;
m_clipper = vtkSmartPointer<vtkTableBasedClipDataSet>::New();
@@ -187,7 +187,8 @@ short int FemPostClipFilter::mustExecute() const {
return 1;
}
else return App::DocumentObject::mustExecute();
else
return App::DocumentObject::mustExecute();
}
DocumentObjectExecReturn* FemPostClipFilter::execute() {
@@ -711,3 +712,260 @@ DocumentObjectExecReturn* FemPostCutFilter::execute()
return Fem::FemPostFilter::execute();
}
// ***************************************************************************
// contours filter
PROPERTY_SOURCE(Fem::FemPostContoursFilter, Fem::FemPostFilter)
FemPostContoursFilter::FemPostContoursFilter()
: FemPostFilter()
{
ADD_PROPERTY_TYPE(NumberOfContours, (10), "Contours",
App::Prop_None, "The number of contours");
ADD_PROPERTY_TYPE(Field, (long(0)), "Clip",
App::Prop_None, "The field used to clip");
ADD_PROPERTY_TYPE(VectorMode, ((long)0), "Contours",
App::Prop_None, "Select what vector field");
ADD_PROPERTY_TYPE(NoColor, (false), "Contours",
App::Prop_None, "Don't color the contours");
m_contourConstraints.LowerBound = 1;
m_contourConstraints.UpperBound = 1000;
m_contourConstraints.StepSize = 1;
NumberOfContours.setConstraints(&m_contourConstraints);
FilterPipeline contours;
m_contours = vtkSmartPointer<vtkContourFilter>::New();
m_contours->ComputeScalarsOn();
contours.source = m_contours;
contours.target = m_contours;
addFilterPipeline(contours, "contours");
setActiveFilterPipeline("contours");
}
FemPostContoursFilter::~FemPostContoursFilter()
{
}
DocumentObjectExecReturn* FemPostContoursFilter::execute()
{
// update list of available fields and their vectors
if (!m_blockPropertyChanges) {
refreshFields();
refreshVectors();
}
// recalculate the filter
auto returnObject = Fem::FemPostFilter::execute();
// delete contour field
vtkSmartPointer<vtkDataObject> data = getInputData();
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
vtkPointData* pd = dset->GetPointData();
dset->GetPointData()->RemoveArray(contourFieldName.c_str());
// refresh fields to reflect the deletion
if (!m_blockPropertyChanges)
refreshFields();
return returnObject;
}
void FemPostContoursFilter::onChanged(const Property* prop)
{
if (m_blockPropertyChanges)
return;
if (prop == &Field && (Field.getValue() >= 0))
refreshVectors();
// note that we need to calculate also in case of a Data change
// otherwise the contours output would be empty and the ViewProviderFemPostObject
// would not get any data
if ((prop == &Field || prop == &VectorMode || prop == &NumberOfContours || prop == &Data)
&& (Field.getValue() >= 0)) {
double p[2];
// get the field and its data
vtkSmartPointer<vtkDataObject> data = getInputData();
if (!data || !data->IsA("vtkDataSet"))
return;
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
vtkDataArray* pdata = dset->GetPointData()->GetArray(Field.getValueAsString());
if (!pdata)
return;
if (pdata->GetNumberOfComponents() == 1) {
// if we have a scalar, we can directly use the array
m_contours->SetInputArrayToProcess(
0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, Field.getValueAsString());
pdata->GetRange(p);
recalculateContours(p[0], p[1]);
}
else {
// The contour filter handles vectors by taking always its first component.
// There is no other solution than to make the desired vectorn component a
// scalar array and append this temporarily to the data. (vtkExtractVectorComponents
// does not work because our data is an unstructured data set.)
int component = -1;
if (VectorMode.getValue() == 1)
component = 0;
else if (VectorMode.getValue() == 2)
component = 1;
else if (VectorMode.getValue() == 3)
component = 2;
// extract the component to a new array
vtkSmartPointer<vtkDoubleArray> componentArray = vtkSmartPointer<vtkDoubleArray>::New();
componentArray->SetNumberOfComponents(1);
vtkIdType numTuples = pdata->GetNumberOfTuples();
componentArray->SetNumberOfTuples(numTuples);
if (component >= 0) {
for (vtkIdType tupleIdx = 0; tupleIdx < numTuples; ++tupleIdx) {
componentArray->SetComponent(
tupleIdx, 0, pdata->GetComponent(tupleIdx, component));
}
}
else {
for (vtkIdType tupleIdx = 0; tupleIdx < numTuples; ++tupleIdx) {
componentArray->SetComponent(tupleIdx, 0,
std::sqrt(pdata->GetComponent(tupleIdx, 0) *
pdata->GetComponent(tupleIdx, 0) +
pdata->GetComponent(tupleIdx, 1) *
pdata->GetComponent(tupleIdx, 1) +
pdata->GetComponent(tupleIdx, 2) *
pdata->GetComponent(tupleIdx, 2) ) );
}
}
// name the array
contourFieldName =
std::string(Field.getValueAsString()) + "_contour";
componentArray->SetName(contourFieldName.c_str());
// add the array as new field and use it for the contour filter
dset->GetPointData()->AddArray(componentArray);
m_contours->SetInputArrayToProcess(0, 0, 0,
vtkDataObject::FIELD_ASSOCIATION_POINTS, contourFieldName.c_str());
componentArray->GetRange(p);
recalculateContours(p[0], p[1]);
if (prop == &Data) {
// we must recalculate to pass the new created contours field
// to ViewProviderFemPostObject
m_blockPropertyChanges = true;
execute();
m_blockPropertyChanges = false;
}
}
}
Fem::FemPostFilter::onChanged(prop);
}
short int FemPostContoursFilter::mustExecute() const
{
if (Field.isTouched() || VectorMode.isTouched() || NumberOfContours.isTouched()
|| Data.isTouched())
return 1;
else
return App::DocumentObject::mustExecute();
}
void FemPostContoursFilter::recalculateContours(double min, double max)
{
// As the min and max contours are not visible, an input of "3" leads
// to 1 visible contour. To not confuse the user, take the visible contours
// for NumberOfContours
int visibleNum = NumberOfContours.getValue() + 2;
m_contours->GenerateValues(visibleNum, min, max);
}
void FemPostContoursFilter::refreshFields()
{
m_blockPropertyChanges = true;
std::string fieldName;
if (Field.getValue() >= 0)
fieldName = Field.getValueAsString();
std::vector<std::string> FieldsArray;
vtkSmartPointer<vtkDataObject> data = getInputData();
if (!data || !data->IsA("vtkDataSet")) {
m_blockPropertyChanges = false;
return;
}
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
vtkPointData* pd = dset->GetPointData();
// get all fields
for (int i = 0; i < pd->GetNumberOfArrays(); ++i) {
FieldsArray.emplace_back(pd->GetArrayName(i));
}
App::Enumeration empty;
Field.setValue(empty);
m_fields.setEnums(FieldsArray);
Field.setValue(m_fields);
// search if the current field is in the available ones and set it
std::vector<std::string>::iterator it =
std::find(FieldsArray.begin(), FieldsArray.end(), fieldName);
if (!fieldName.empty() && it != FieldsArray.end()) {
Field.setValue(fieldName.c_str());
}
else {
m_blockPropertyChanges = false;
// select the first field
Field.setValue(long(0));
fieldName = Field.getValueAsString();
}
m_blockPropertyChanges = false;
}
void FemPostContoursFilter::refreshVectors()
{
// refreshes the list of available vectors for the current Field
m_blockPropertyChanges = true;
vtkSmartPointer<vtkDataObject> data = getInputData();
if (!data || !data->IsA("vtkDataSet")) {
m_blockPropertyChanges = false;
return;
}
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
vtkDataArray* fieldArray = dset->GetPointData()->GetArray(Field.getValueAsString());
if (!fieldArray) {
m_blockPropertyChanges = false;
return;
}
// store name if already set
std::string vectorName;
if (VectorMode.hasEnums() && VectorMode.getValue() >= 0)
vectorName = VectorMode.getValueAsString();
std::vector<std::string> vectorArray;
if (fieldArray->GetNumberOfComponents() == 1)
vectorArray.emplace_back("Not a vector");
else {
vectorArray.emplace_back("Magnitude");
if (fieldArray->GetNumberOfComponents() >= 2) {
vectorArray.emplace_back("X");
vectorArray.emplace_back("Y");
}
if (fieldArray->GetNumberOfComponents() >= 3) {
vectorArray.emplace_back("Z");
}
}
App::Enumeration empty;
VectorMode.setValue(empty);
m_vectors.setEnums(vectorArray);
VectorMode.setValue(m_vectors);
// apply stored name
auto it = std::find(vectorArray.begin(), vectorArray.end(), vectorName);
if (!vectorName.empty() && it != vectorArray.end())
VectorMode.setValue(vectorName.c_str());
m_blockPropertyChanges = false;
}

View File

@@ -23,9 +23,12 @@
#ifndef Fem_FemPostFilter_H
#define Fem_FemPostFilter_H
#include <vtkContourFilter.h>
#include <vtkCutter.h>
#include <vtkExtractGeometry.h>
#include <vtkExtractVectorComponents.h>
#include <vtkLineSource.h>
#include <vtkVectorNorm.h>
#include <vtkPointSource.h>
#include <vtkProbeFilter.h>
#include <vtkSmartPointer.h>
@@ -239,6 +242,44 @@ private:
vtkSmartPointer<vtkCutter> m_cutter;
};
class FemExport FemPostContoursFilter: public FemPostFilter
{
PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostContoursFilter);
public:
FemPostContoursFilter();
~FemPostContoursFilter() override;
App::PropertyEnumeration Field;
App::PropertyIntegerConstraint NumberOfContours;
App::PropertyEnumeration VectorMode;
App::PropertyBool NoColor;
const char* getViewProviderName() const override
{
return "FemGui::ViewProviderFemPostContours";
}
short int mustExecute() const override;
protected:
App::DocumentObjectExecReturn* execute() override;
void onChanged(const App::Property* prop) override;
void recalculateContours(double min, double max);
void refreshFields();
void refreshVectors();
bool m_blockPropertyChanges = false;
std::string contourFieldName;
private:
vtkSmartPointer<vtkContourFilter> m_contours;
vtkSmartPointer<vtkExtractVectorComponents> m_extractor;
vtkSmartPointer<vtkVectorNorm> m_norm;
App::Enumeration m_fields;
App::Enumeration m_vectors;
App::PropertyIntegerConstraint::Constraints m_contourConstraints;
};
} //namespace Fem

View File

@@ -152,6 +152,7 @@ PyMOD_INIT_FUNC(FemGui)
FemGui::ViewProviderFemPostObject ::init();
FemGui::ViewProviderFemPostPipeline ::init();
FemGui::ViewProviderFemPostClip ::init();
FemGui::ViewProviderFemPostContours ::init();
FemGui::ViewProviderFemPostCut ::init();
FemGui::ViewProviderFemPostDataAlongLine ::init();
FemGui::ViewProviderFemPostDataAtPoint ::init();

View File

@@ -93,6 +93,7 @@ if(BUILD_FEM_VTK)
${FemGui_UIC_SRCS}
TaskPostDisplay.ui
TaskPostClip.ui
TaskPostContours.ui
TaskPostDataAlongLine.ui
TaskPostDataAtPoint.ui
TaskPostScalarClip.ui
@@ -271,6 +272,7 @@ if(BUILD_FEM_VTK)
PlaneWidget.ui
SphereWidget.ui
TaskPostClip.ui
TaskPostContours.ui
TaskPostDataAlongLine.ui
TaskPostDataAtPoint.ui
TaskPostScalarClip.ui

View File

@@ -1534,13 +1534,14 @@ void setupFilter(Gui::Command* cmd, std::string Name) {
// issue error if no post object
if (!((selObject->getTypeId() == Base::Type::fromName("Fem::FemPostPipeline"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostWarpVectorFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostScalarClipFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostCutFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostClipFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostDataAlongLineFilter")) )
) {
QMessageBox::warning(Gui::getMainWindow(),
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostClipFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostContoursFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostCutFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostDataAlongLineFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostScalarClipFilter"))
|| (selObject->getTypeId() == Base::Type::fromName("Fem::FemPostWarpVectorFilter")))) {
QMessageBox::warning(
Gui::getMainWindow(),
qApp->translate("setupFilter", "Error: no post processing object selected."),
qApp->translate("setupFilter", "The filter could not be set up."));
return;
@@ -1721,20 +1722,21 @@ bool CmdFemPostClipFilter::isActive()
// only allow one object
if (getSelection().getSelection().size() > 1)
return false;
// only activate if a result is either a post pipeline, scalar, cut or warp filter,
// itself or along line filter
// only activate if a result is either a post pipeline or a possible filter
if (getSelection().getObjectsOfType<Fem::FemPostPipeline>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostDataAlongLineFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostContoursFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
else
return false;
}
@@ -1765,20 +1767,21 @@ bool CmdFemPostCutFilter::isActive()
// only allow one object
if (getSelection().getSelection().size() > 1)
return false;
// only activate if a result is either a post pipeline, scalar, clip or warp filter,
// itself, or along line filter
// only activate if a result is either a post pipeline or a possible filter
if (getSelection().getObjectsOfType<Fem::FemPostPipeline>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
else if (getSelection().getObjectsOfType<Fem::FemPostContoursFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostDataAlongLineFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
else
return false;
}
@@ -1809,14 +1812,16 @@ bool CmdFemPostDataAlongLineFilter::isActive()
// only allow one object
if (getSelection().getSelection().size() > 1)
return false;
// only activate if a result is either a post pipeline, scalar, cut, clip or warp filter
// only activate if a result is either a post pipeline or a possible filter
if (getSelection().getObjectsOfType<Fem::FemPostPipeline>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostContoursFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
@@ -1852,20 +1857,19 @@ bool CmdFemPostDataAtPointFilter::isActive()
// only allow one object
if (getSelection().getSelection().size() > 1)
return false;
// only activate if a result is either a post pipeline, scalar, cut, clip,
// warp or along line filter
// only activate if a result is either a post pipeline or a possible filter
if (getSelection().getObjectsOfType<Fem::FemPostPipeline>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostDataAlongLineFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
else
return false;
}
@@ -1970,17 +1974,19 @@ bool CmdFemPostScalarClipFilter::isActive()
// only allow one object
if (getSelection().getSelection().size() > 1)
return false;
// only activate if a result is either a post pipeline, clip, cut, warp or along line filter
// only activate if a result is either a post pipeline or a possible other filter
if (getSelection().getObjectsOfType<Fem::FemPostPipeline>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostContoursFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostDataAlongLineFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
else
return false;
}
@@ -2011,17 +2017,63 @@ bool CmdFemPostWarpVectorFilter::isActive()
// only allow one object
if (getSelection().getSelection().size() > 1)
return false;
// only activate if a result is either a post pipeline, scalar, clip, cut or along line filter
// only activate if a result is either a post pipeline or a possible other filter
if (getSelection().getObjectsOfType<Fem::FemPostPipeline>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
else if (getSelection().getObjectsOfType<Fem::FemPostContoursFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostDataAlongLineFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else
return false;
}
//================================================================================================
DEF_STD_CMD_A(CmdFemPostContoursFilter)
CmdFemPostContoursFilter::CmdFemPostContoursFilter()
: Command("FEM_PostFilterContours")
{
sAppModule = "Fem";
sGroup = QT_TR_NOOP("Fem");
sMenuText = QT_TR_NOOP("Contours filter");
sToolTipText =
QT_TR_NOOP("Define/create a contours filter which displays iso contours");
sWhatsThis = "FEM_PostFilterContours";
sStatusTip = sToolTipText;
sPixmap = "FEM_PostFilterContours";
}
void CmdFemPostContoursFilter::activated(int)
{
setupFilter(this, "Contours");
}
bool CmdFemPostContoursFilter::isActive()
{
// only allow one object
if (getSelection().getSelection().size() > 1)
return false;
// only activate if a result is either a post pipeline or a possible other filter
if (getSelection().getObjectsOfType<Fem::FemPostPipeline>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostCutFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostDataAlongLineFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostScalarClipFilter>().size() == 1)
return true;
else if (getSelection().getObjectsOfType<Fem::FemPostWarpVectorFilter>().size() == 1)
return true;
else
return false;
}
@@ -2370,15 +2422,16 @@ void CreateFemCommands()
// vtk post processing
#ifdef FC_USE_VTK
rcCmdMgr.addCommand(new CmdFemPostApllyChanges);
rcCmdMgr.addCommand(new CmdFemPostClipFilter);
rcCmdMgr.addCommand(new CmdFemPostContoursFilter);
rcCmdMgr.addCommand(new CmdFemPostCutFilter);
rcCmdMgr.addCommand(new CmdFemPostDataAlongLineFilter);
rcCmdMgr.addCommand(new CmdFemPostDataAtPointFilter);
rcCmdMgr.addCommand(new CmdFemPostLinearizedStressesFilter);
rcCmdMgr.addCommand(new CmdFemPostScalarClipFilter);
rcCmdMgr.addCommand(new CmdFemPostWarpVectorFilter);
rcCmdMgr.addCommand(new CmdFemPostFunctions);
rcCmdMgr.addCommand(new CmdFemPostApllyChanges);
rcCmdMgr.addCommand(new CmdFemPostPipelineFromResult);
rcCmdMgr.addCommand(new CmdFemPostScalarClipFilter);
rcCmdMgr.addCommand(new CmdFemPostWarpVectorFilter);
#endif
}

View File

@@ -70,6 +70,7 @@
<!-- gui command icons: post processing -->
<file>icons/FEM_PostFilterClipRegion.svg</file>
<file>icons/FEM_PostFilterClipScalar.svg</file>
<file>icons/FEM_PostFilterContours.svg</file>
<file>icons/FEM_PostFilterCutFunction.svg</file>
<file>icons/FEM_PostFilterDataAlongLine.svg</file>
<file>icons/FEM_PostFilterDataAtPoint.svg</file>

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg2" version="1.1" width="64" height="64" 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/">
<metadata id="metadata8">
<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>http://www.freecadweb.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>
<defs id="defs6">
<linearGradient id="linearGradient4937">
<stop style="stop-color:#4e9a06;stop-opacity:1" offset="0" id="stop4933" />
<stop style="stop-color:#73d216;stop-opacity:1" offset="1" id="stop4935" />
</linearGradient>
<linearGradient id="linearGradient4929">
<stop style="stop-color:#4e9a06;stop-opacity:1" offset="0" id="stop4925" />
<stop style="stop-color:#73d216;stop-opacity:1" offset="1" id="stop4927" />
</linearGradient>
<linearGradient id="linearGradient4921">
<stop style="stop-color:#4e9a06;stop-opacity:1" offset="0" id="stop4917" />
<stop style="stop-color:#73d216;stop-opacity:1" offset="1" id="stop4919" />
</linearGradient>
<linearGradient id="linearGradient3826">
<stop style="stop-color:#4e9a06;stop-opacity:1" offset="0" id="stop3828" />
<stop style="stop-color:#73d216;stop-opacity:1" offset="1" id="stop3830" />
</linearGradient>
<linearGradient id="linearGradient3816">
<stop style="stop-color:#73d216;stop-opacity:1" offset="0" id="stop3818" />
<stop style="stop-color:#8ae234;stop-opacity:1" offset="1" id="stop3820" />
</linearGradient>
<linearGradient xlink:href="#linearGradient3816" id="linearGradient3841" gradientUnits="userSpaceOnUse" x1="95" y1="47" x2="91" y2="15" />
<linearGradient xlink:href="#linearGradient3826" id="linearGradient3843" gradientUnits="userSpaceOnUse" x1="120" y1="46" x2="109" y2="15" />
<linearGradient xlink:href="#linearGradient4929" id="linearGradient4931" x1="117.5" y1="50.5" x2="117.5" y2="50.5" gradientUnits="userSpaceOnUse" />
<linearGradient xlink:href="#linearGradient4937" id="linearGradient4939" x1="100.60143" y1="9.4661894" x2="117.5" y2="50.5" gradientUnits="userSpaceOnUse" />
<linearGradient xlink:href="#linearGradient4921" id="linearGradient4949" gradientUnits="userSpaceOnUse" x1="100.60143" y1="9.4661894" x2="117.5" y2="50.5" />
</defs>
<g id="g3834" transform="matrix(1.5077443,0,0,1.2007124,-134.53727,-4.0213696)" style="stroke-width:0.743219">
<path id="path3812" d="M 117,4 L 131,45 L 104,56 L 90,13 Z" style="fill:none;fill-opacity:1;stroke:#172a04;stroke-width:1.48643;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path style="fill:none;stroke:url(#linearGradient4939);stroke-width:1.11483;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" d="M 115.09277,8.1017279 C 115.09277,8.1017279 111.03315,16.682926 114.5164,25.024389 C 117.99965,33.365852 121.44094,39.334366 123.38943,40.657696 L 128,43.789004" id="path1048" />
<path style="fill:none;stroke:url(#linearGradient4931);stroke-width:1.11483;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" d="M 107.86441,9.8386331 C 107.86441,9.8386331 102.54878,16.007596 105.02439,25.503784 C 107.5,34.999971 110.94492,40.843691 116.171,43.789004 L 121.3238,46.693017" id="path1050" />
<path style="fill:none;stroke:url(#linearGradient4949);stroke-width:1.11483;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" d="M 99.999951,11.793103 C 99.999951,11.793103 95.928545,17.853922 99.053812,26.962416 C 102.17908,36.07091 106.80942,47.780649 110.5,50.196728" id="path1052" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -56,6 +56,7 @@
#include "ui_TaskPostDataAtPoint.h"
#include "ui_TaskPostDisplay.h"
#include "ui_TaskPostScalarClip.h"
#include "ui_TaskPostContours.h"
#include "ui_TaskPostWarpVector.h"
#include "TaskPostBoxes.h"
@@ -1701,4 +1702,137 @@ void TaskPostCut::on_FunctionBox_currentIndexChanged(int idx) {
recompute();
}
// ***************************************************************************
// contours filter
TaskPostContours::TaskPostContours(ViewProviderDocumentObject* view, QWidget* parent)
: TaskPostBox(view, Gui::BitmapFactory().pixmap("FEM_PostFilterContours"),
tr("Contours filter options"), parent),
ui(new Ui_TaskPostContours)
{
assert(view->isDerivedFrom(ViewProviderFemPostContours::getClassTypeId()));
// load the views widget
proxy = new QWidget(this);
ui->setupUi(proxy);
QMetaObject::connectSlotsByName(this);
this->groupLayout()->addWidget(proxy);
// load filter settings
updateEnumerationList(getTypedObject<Fem::FemPostContoursFilter>()->Field, ui->fieldsCB);
updateEnumerationList(getTypedObject<Fem::FemPostContoursFilter>()->VectorMode, ui->vectorsCB);
// for a new filter, initialize the coloring
auto colorState = static_cast<Fem::FemPostContoursFilter*>(getObject())->NoColor.getValue();
if (!colorState && getTypedView<ViewProviderFemPostObject>()->Field.getValue() == 0) {
getTypedView<ViewProviderFemPostObject>()->Field.setValue(1);
}
ui->numberContoursSB->setValue(
static_cast<Fem::FemPostContoursFilter*>(getObject())->NumberOfContours.getValue());
ui->noColorCB->setChecked(colorState);
// connect
connect(ui->fieldsCB, qOverload<int>(&QComboBox::currentIndexChanged),
this, &TaskPostContours::onFieldsChanged);
connect(ui->vectorsCB, qOverload<int>(&QComboBox::currentIndexChanged),
this, &TaskPostContours::onVectorModeChanged);
connect(ui->numberContoursSB, qOverload<int>(&QSpinBox::valueChanged),
this, &TaskPostContours::onNumberOfContoursChanged);
connect(ui->noColorCB, &QCheckBox::toggled,
this, &TaskPostContours::onNoColorChanged);
}
TaskPostContours::~TaskPostContours()
{}
void TaskPostContours::applyPythonCode()
{}
void TaskPostContours::updateFields(int idx)
{
std::vector<std::string> fieldArray;
std::vector<std::string> vec =
getTypedObject<Fem::FemPostContoursFilter>()->Field.getEnumVector();
for (std::vector<std::string>::iterator it = vec.begin(); it != vec.end(); ++it) {
fieldArray.emplace_back(*it);
}
// update the ViewProvider Field enums
App::Enumeration anEnum;
getTypedView<ViewProviderFemPostObject>()->Field.setValue(anEnum);
anEnum.setEnums(fieldArray);
getTypedView<ViewProviderFemPostObject>()->Field.setValue(anEnum);
// set new Field index to ViewProvider Field
// the ViewProvider field starts with an additional entry "None",
// therefore the desired new setting is idx + 1
if (!static_cast<Fem::FemPostContoursFilter*>(getObject())->NoColor.getValue())
getTypedView<ViewProviderFemPostObject>()->Field.setValue(idx + 1);
else
getTypedView<ViewProviderFemPostObject>()->Field.setValue(long(0));
}
void TaskPostContours::onFieldsChanged(int idx)
{
static_cast<Fem::FemPostContoursFilter*>(getObject())->Field.setValue(idx);
blockVectorUpdate = true;
updateEnumerationList(getTypedObject<Fem::FemPostContoursFilter>()->VectorMode, ui->vectorsCB);
blockVectorUpdate = false;
// In > 99 % of the cases the coloring should be equal to the field,
// thus change the coloring field too. Users can override this be resetting only the coloring
// field afterwards in the properties if really necessary.
updateFields(idx);
// since a new field can be e.g. no vector while the previous one was,
// we must also update the VectorMode
if (!static_cast<Fem::FemPostContoursFilter*>(getObject())->NoColor.getValue()) {
auto newMode = getTypedObject<Fem::FemPostContoursFilter>()->VectorMode.getValue();
getTypedView<ViewProviderFemPostObject>()->VectorMode.setValue(newMode);
}
}
void TaskPostContours::onVectorModeChanged(int idx)
{
static_cast<Fem::FemPostContoursFilter*>(getObject())->VectorMode.setValue(idx);
recompute();
if (!blockVectorUpdate) {
// we can have the case that the previous field had VectorMode "Z" but
// since it is a 2D field, Z is eompty thus no field is available to color
// when the user noch goes back to e.g. "Y" we must set the Field
// first to get the possible VectorModes of that field
auto currentField = getTypedObject<Fem::FemPostContoursFilter>()->Field.getValue();
updateFields(currentField);
// now we can set the VectorMode
if (!static_cast<Fem::FemPostContoursFilter*>(getObject())->NoColor.getValue())
getTypedView<ViewProviderFemPostObject>()->VectorMode.setValue(idx);
}
}
void TaskPostContours::onNumberOfContoursChanged(int number)
{
static_cast<Fem::FemPostContoursFilter*>(getObject())->NumberOfContours.setValue(number);
recompute();
}
void TaskPostContours::onNoColorChanged(bool state)
{
static_cast<Fem::FemPostContoursFilter*>(getObject())->NoColor.setValue(state);
if (state) {
// no color
getTypedView<ViewProviderFemPostObject>()->Field.setValue(long(0));
}
else {
// set same field
auto currentField = getTypedObject<Fem::FemPostContoursFilter>()->Field.getValue();
// the ViewProvider field starts with an additional entry "None",
// therefore the desired new setting is idx + 1
getTypedView<ViewProviderFemPostObject>()->Field.setValue(currentField + 1);
// set the VectorMode too
auto currentMode = getTypedObject<Fem::FemPostContoursFilter>()->VectorMode.getValue();
getTypedView<ViewProviderFemPostObject>()->VectorMode.setValue(currentMode);
}
recompute();
}
#include "moc_TaskPostBoxes.cpp"

View File

@@ -34,6 +34,7 @@
class QComboBox;
class Ui_TaskPostDisplay;
class Ui_TaskPostClip;
class Ui_TaskPostContours;
class Ui_TaskPostDataAlongLine;
class Ui_TaskPostDataAtPoint;
class Ui_TaskPostScalarClip;
@@ -415,6 +416,30 @@ private:
FunctionWidget* fwidget;
};
class TaskPostContours: public TaskPostBox
{
Q_OBJECT
public:
explicit TaskPostContours(Gui::ViewProviderDocumentObject* view, QWidget* parent = nullptr);
~TaskPostContours() override;
void applyPythonCode() override;
private Q_SLOTS:
void onFieldsChanged(int idx);
void onVectorModeChanged(int idx);
void onNumberOfContoursChanged(int number);
void onNoColorChanged(bool state);
private:
QWidget* proxy;
std::unique_ptr<Ui_TaskPostContours> ui;
bool blockVectorUpdate = false;
void updateFields(int idx);
};
} //namespace FemGui
#endif // GUI_TASKVIEW_TaskPostDisplay_H

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TaskPostContours</class>
<widget class="QWidget" name="TaskPostContours">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>250</width>
<height>115</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QComboBox" name="fieldsCB"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="vectorL">
<property name="text">
<string>Vector:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="vectorsCB"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="fieldL">
<property name="text">
<string>Field:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="numberContoursL">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Number of contours:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="numberContoursSB">
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="noColorCB">
<property name="toolTip">
<string>Contour lines will not be colored</string>
</property>
<property name="text">
<string>No color</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -99,7 +99,6 @@ void ViewProviderFemPostDataAtPoint::setupTaskDialog(TaskDlgPost* dlg) {
}
PROPERTY_SOURCE(FemGui::ViewProviderFemPostScalarClip, FemGui::ViewProviderFemPostObject)
ViewProviderFemPostScalarClip::ViewProviderFemPostScalarClip() {
@@ -140,7 +139,6 @@ void ViewProviderFemPostWarpVector::setupTaskDialog(TaskDlgPost* dlg) {
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
}
PROPERTY_SOURCE(FemGui::ViewProviderFemPostCut, FemGui::ViewProviderFemPostObject)
ViewProviderFemPostCut::ViewProviderFemPostCut() {
@@ -161,3 +159,19 @@ void ViewProviderFemPostCut::setupTaskDialog(TaskDlgPost* dlg) {
//add the display options
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
}
PROPERTY_SOURCE(FemGui::ViewProviderFemPostContours, FemGui::ViewProviderFemPostObject)
ViewProviderFemPostContours::ViewProviderFemPostContours()
{
sPixmap = "FEM_PostFilterContours";
}
ViewProviderFemPostContours::~ViewProviderFemPostContours()
{}
void ViewProviderFemPostContours::setupTaskDialog(TaskDlgPost* dlg)
{
// the filter-specific task panel
dlg->appendBox(new TaskPostContours(dlg->getView()));
}

View File

@@ -108,6 +108,20 @@ protected:
void setupTaskDialog(TaskDlgPost* dlg) override;
};
class FemGuiExport ViewProviderFemPostContours: public ViewProviderFemPostObject
{
PROPERTY_HEADER_WITH_OVERRIDE(FemGui::ViewProviderFemPostContours);
public:
/// constructor.
ViewProviderFemPostContours();
~ViewProviderFemPostContours() override;
protected:
void setupTaskDialog(TaskDlgPost* dlg) override;
};
} //namespace FemGui

View File

@@ -203,6 +203,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "FEM_PostFilterClipScalar"
<< "FEM_PostFilterCutFunction"
<< "FEM_PostFilterClipRegion"
<< "FEM_PostFilterContours"
<< "FEM_PostFilterDataAlongLine"
<< "FEM_PostFilterLinearizedStresses"
<< "FEM_PostFilterDataAtPoint"
@@ -371,6 +372,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const
<< "FEM_PostFilterClipScalar"
<< "FEM_PostFilterCutFunction"
<< "FEM_PostFilterClipRegion"
<< "FEM_PostFilterContours"
<< "FEM_PostFilterDataAlongLine"
<< "FEM_PostFilterLinearizedStresses"
<< "FEM_PostFilterDataAtPoint"

View File

@@ -720,6 +720,21 @@ def makePostVtkFilterWarp(
return obj
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
return obj
def makePostVtkResult(
doc,
base_result,

View File

@@ -154,6 +154,7 @@ class TestObjectExistance(unittest.TestCase):
expected_vtk_obj_types = [
"Fem::FemPostClipFilter",
"Fem::FemPostContoursFilter",
"Fem::FemPostCutFilter",
"Fem::FemPostDataAlongLineFilter",
"Fem::FemPostDataAtPointFilter",

View File

@@ -87,11 +87,11 @@ class TestObjectCreate(unittest.TestCase):
# solver children: equations --> 8
# gmsh mesh children: group, region, boundary layer --> 3
# resule children: mesh result --> 1
# post pipeline children: region, scalar, cut, wrap --> 4
# post pipeline children: region, scalar, cut, wrap --> 5
# analysis itself is not in analysis group --> 1
# thus: -17
# thus: -18
self.assertEqual(len(doc.Analysis.Group), count_defmake - 17)
self.assertEqual(len(doc.Analysis.Group), count_defmake - 18)
self.assertEqual(len(doc.Objects), count_defmake)
fcc_print("doc objects count: {}, method: {}".format(
@@ -1858,6 +1858,7 @@ def create_all_fem_objects_doc(
vres = analysis.addObject(ObjectsFem.makePostVtkResult(doc, res))[0]
ObjectsFem.makePostVtkFilterClipRegion(doc, vres)
ObjectsFem.makePostVtkFilterClipScalar(doc, vres)
ObjectsFem.makePostVtkFilterContours(doc, vres)
ObjectsFem.makePostVtkFilterCutFunction(doc, vres)
ObjectsFem.makePostVtkFilterWarp(doc, vres)

View File

@@ -64,21 +64,21 @@ class VPMeshGmsh:
def setEdit(self, vobj, mode):
# hide all FEM meshes and VTK FemPost* objects
for o in vobj.Object.Document.Objects:
for obj in vobj.Object.Document.Objects:
if (
o.isDerivedFrom("Fem::FemMeshObject")
or o.isDerivedFrom("Fem::FemPostPipeline")
or o.isDerivedFrom("Fem::FemPostClipFilter")
or o.isDerivedFrom("Fem::FemPostScalarClipFilter")
or o.isDerivedFrom("Fem::FemPostWarpVectorFilter")
or o.isDerivedFrom("Fem::FemPostDataAlongLineFilter")
or o.isDerivedFrom("Fem::FemPostDataAtPointFilter")
or o.isDerivedFrom("Fem::FemPostCutFilter")
or o.isDerivedFrom("Fem::FemPostDataAlongLineFilter")
or o.isDerivedFrom("Fem::FemPostPlaneFunction")
or o.isDerivedFrom("Fem::FemPostSphereFunction")
obj.isDerivedFrom("Fem::FemMeshObject")
or obj.isDerivedFrom("Fem::FemPostClipFilter")
or obj.isDerivedFrom("Fem::FemPostContoursFilter")
or obj.isDerivedFrom("Fem::FemPostCutFilter")
or obj.isDerivedFrom("Fem::FemPostDataAlongLineFilter")
or obj.isDerivedFrom("Fem::FemPostDataAtPointFilter")
or obj.isDerivedFrom("Fem::FemPostPipeline")
or obj.isDerivedFrom("Fem::FemPostPlaneFunction")
or obj.isDerivedFrom("Fem::FemPostScalarClipFilter")
or obj.isDerivedFrom("Fem::FemPostSphereFunction")
or obj.isDerivedFrom("Fem::FemPostWarpVectorFilter")
):
o.ViewObject.hide()
obj.ViewObject.hide()
# show the mesh we like to edit
self.ViewObject.show()
# show task panel