Fem: Add calculator filter

This commit is contained in:
marioalexis
2025-03-19 00:59:59 -03:00
parent 9744dc5527
commit c8ccbeb421
16 changed files with 879 additions and 1 deletions

View File

@@ -189,6 +189,7 @@ PyMOD_INIT_FUNC(Fem)
Fem::FemPostPipeline ::init();
Fem::FemPostFilter ::init();
Fem::FemPostBranchFilter ::init();
Fem::FemPostCalculatorFilter ::init();
Fem::FemPostClipFilter ::init();
Fem::FemPostContoursFilter ::init();
Fem::FemPostCutFilter ::init();

View File

@@ -1297,3 +1297,132 @@ short int FemPostWarpVectorFilter::mustExecute() const
return App::DocumentObject::mustExecute();
}
}
// ***************************************************************************
// calculator filter
PROPERTY_SOURCE(Fem::FemPostCalculatorFilter, Fem::FemPostFilter)
FemPostCalculatorFilter::FemPostCalculatorFilter()
: FemPostFilter()
{
ADD_PROPERTY_TYPE(FieldName,
("Calculator"),
"Calculator",
App::Prop_None,
"Name of the calculated field");
ADD_PROPERTY_TYPE(Function,
(""),
"Calculator",
App::Prop_None,
"Expression of the unction to evaluate");
ADD_PROPERTY_TYPE(ReplacementValue,
(0.0f),
"Calculator",
App::Prop_None,
"Value used to replace invalid operations");
ADD_PROPERTY_TYPE(ReplaceInvalid,
(false),
"Calculator",
App::Prop_None,
"Replace invalid values");
FilterPipeline calculator;
m_calculator = vtkSmartPointer<vtkArrayCalculator>::New();
m_calculator->SetResultArrayName(FieldName.getValue());
calculator.source = m_calculator;
calculator.target = m_calculator;
addFilterPipeline(calculator, "calculator");
setActiveFilterPipeline("calculator");
}
FemPostCalculatorFilter::~FemPostCalculatorFilter() = default;
DocumentObjectExecReturn* FemPostCalculatorFilter::execute()
{
updateAvailableFields();
return FemPostFilter::execute();
}
void FemPostCalculatorFilter::onChanged(const Property* prop)
{
if (prop == &Function) {
m_calculator->SetFunction(Function.getValue());
}
else if (prop == &FieldName) {
m_calculator->SetResultArrayName(FieldName.getValue());
}
else if (prop == &ReplaceInvalid) {
m_calculator->SetReplaceInvalidValues(ReplaceInvalid.getValue());
}
else if (prop == &ReplacementValue) {
m_calculator->SetReplacementValue(ReplacementValue.getValue());
}
else if (prop == &Data) {
updateAvailableFields();
}
Fem::FemPostFilter::onChanged(prop);
}
short int FemPostCalculatorFilter::mustExecute() const
{
if (Function.isTouched() || FieldName.isTouched()) {
return 1;
}
else {
return FemPostFilter::mustExecute();
}
}
void FemPostCalculatorFilter::updateAvailableFields()
{
// clear all variables
m_calculator->RemoveAllVariables();
m_calculator->AddCoordinateScalarVariable("coordsX", 0);
m_calculator->AddCoordinateScalarVariable("coordsY", 1);
m_calculator->AddCoordinateScalarVariable("coordsZ", 2);
m_calculator->AddCoordinateVectorVariable("coords");
std::vector<std::string> scalars;
std::vector<std::string> vectors;
// std::vector<std::string> tensors;
vtkSmartPointer<vtkDataObject> data = getInputData();
vtkDataSet* dset = vtkDataSet::SafeDownCast(data);
if (!dset) {
return;
}
vtkPointData* pd = dset->GetPointData();
// get all vector fields
for (int i = 0; i < pd->GetNumberOfArrays(); ++i) {
std::string name1 = pd->GetArrayName(i);
std::string name2 = name1;
std::replace(name2.begin(), name2.end(), ' ', '_');
if (pd->GetArray(i)->GetNumberOfComponents() == 3) {
m_calculator->AddVectorVariable(name2.c_str(), name1.c_str());
// add components as scalar variable
m_calculator->AddScalarVariable((name2 + "_X").c_str(), name1.c_str(), 0);
m_calculator->AddScalarVariable((name2 + "_Y").c_str(), name1.c_str(), 1);
m_calculator->AddScalarVariable((name2 + "_Z").c_str(), name1.c_str(), 2);
}
else if (pd->GetArray(i)->GetNumberOfComponents() == 1) {
m_calculator->AddScalarVariable(name2.c_str(), name1.c_str());
}
}
}
const std::vector<std::string> FemPostCalculatorFilter::getScalarVariables()
{
std::vector<std::string> scalars = m_calculator->GetScalarVariableNames();
scalars.insert(scalars.begin(), {"coordsX", "coordsY", "coordsZ"});
return scalars;
}
const std::vector<std::string> FemPostCalculatorFilter::getVectorVariables()
{
std::vector<std::string> vectors = m_calculator->GetVectorVariableNames();
vectors.insert(vectors.begin(), "coords");
return vectors;
}

View File

@@ -23,6 +23,7 @@
#ifndef Fem_FemPostFilter_H
#define Fem_FemPostFilter_H
#include <vtkArrayCalculator.h>
#include <vtkContourFilter.h>
#include <vtkSmoothPolyDataFilter.h>
#include <vtkCutter.h>
@@ -372,6 +373,41 @@ private:
App::Enumeration m_vectorFields;
};
// ***************************************************************************
// calculator filter
class FemExport FemPostCalculatorFilter: public FemPostFilter
{
PROPERTY_HEADER_WITH_OVERRIDE(Fem::FemPostCalculatorFilter);
public:
FemPostCalculatorFilter();
~FemPostCalculatorFilter() override;
App::PropertyString FieldName;
App::PropertyString Function;
App::PropertyFloat ReplacementValue;
App::PropertyBool ReplaceInvalid;
const char* getViewProviderName() const override
{
return "FemGui::ViewProviderFemPostCalculator";
}
short int mustExecute() const override;
const std::vector<std::string> getScalarVariables();
const std::vector<std::string> getVectorVariables();
protected:
App::DocumentObjectExecReturn* execute() override;
void onChanged(const App::Property* prop) override;
void updateAvailableFields();
private:
vtkSmartPointer<vtkArrayCalculator> m_calculator;
};
} // namespace Fem

View File

@@ -155,6 +155,7 @@
// VTK
#include <vtkAlgorithmOutput.h>
#include <vtkAppendFilter.h>
#include <vtkArrayCalculator.h>
#include <vtkCellArray.h>
#include <vtkCompositeDataSet.h>
#include <vtkDataArray.h>

View File

@@ -162,6 +162,7 @@ PyMOD_INIT_FUNC(FemGui)
FemGui::ViewProviderFemPostObject ::init();
FemGui::ViewProviderFemPostPipeline ::init();
FemGui::ViewProviderFemPostBranchFilter ::init();
FemGui::ViewProviderFemPostCalculator ::init();
FemGui::ViewProviderFemPostClip ::init();
FemGui::ViewProviderFemPostContours ::init();
FemGui::ViewProviderFemPostCut ::init();

View File

@@ -87,6 +87,7 @@ if(BUILD_FEM_VTK)
CylinderWidget.ui
PlaneWidget.ui
SphereWidget.ui
TaskPostCalculator.ui
TaskPostClip.ui
TaskPostContours.ui
TaskPostCut.ui
@@ -281,6 +282,7 @@ if(BUILD_FEM_VTK)
SphereWidget.ui
TaskPostBoxes.h
TaskPostBoxes.cpp
TaskPostCalculator.ui
TaskPostClip.ui
TaskPostContours.ui
TaskPostCut.ui

View File

@@ -2338,6 +2338,42 @@ bool CmdFemPostContoursFilter::isActive()
}
//================================================================================================
DEF_STD_CMD_A(CmdFemPostCalculatorFilter)
CmdFemPostCalculatorFilter::CmdFemPostCalculatorFilter()
: Command("FEM_PostFilterCalculator")
{
sAppModule = "Fem";
sGroup = QT_TR_NOOP("Fem");
sMenuText = QT_TR_NOOP("Calculator filter");
sToolTipText = QT_TR_NOOP("Create new fields from current data");
sWhatsThis = "FEM_PostFilterCalculator";
sStatusTip = sToolTipText;
sPixmap = "FEM_PostFilterCalculator";
}
void CmdFemPostCalculatorFilter::activated(int)
{
setupFilter(this, "Calculator");
}
bool CmdFemPostCalculatorFilter::isActive()
{
// only allow one object
auto selection = getSelection().getSelection();
if (selection.size() > 1) {
return false;
}
for (auto obj : selection) {
if (obj.pObject->isDerivedFrom<Fem::FemPostObject>()) {
return true;
}
}
return false;
}
//================================================================================================
DEF_STD_CMD_ACL(CmdFemPostFunctions)
@@ -2783,6 +2819,7 @@ void CreateFemCommands()
// vtk post processing
#ifdef FC_USE_VTK
rcCmdMgr.addCommand(new CmdFemPostApllyChanges);
rcCmdMgr.addCommand(new CmdFemPostCalculatorFilter);
rcCmdMgr.addCommand(new CmdFemPostClipFilter);
rcCmdMgr.addCommand(new CmdFemPostContoursFilter);
rcCmdMgr.addCommand(new CmdFemPostCutFilter);

View File

@@ -81,6 +81,7 @@
#include <QSlider>
#include <QStackedWidget>
#include <QString>
#include <QStringList>
#include <QTextCharFormat>
#include <QTextStream>
#include <QThread>

View File

@@ -72,6 +72,7 @@
<file>icons/FEM_MaterialSolid.svg</file>
<!-- gui command icons: post processing -->
<file>icons/FEM_PostFilterCalculator.svg</file>
<file>icons/FEM_PostFilterClipRegion.svg</file>
<file>icons/FEM_PostFilterClipScalar.svg</file>
<file>icons/FEM_PostFilterContours.svg</file>

View File

@@ -0,0 +1,324 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="64"
height="64"
id="svg5816"
version="1.1"
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/">
<defs
id="defs3">
<linearGradient
id="linearGradient6">
<stop
style="stop-color:#729fcf;stop-opacity:1;"
offset="0"
id="stop6" />
<stop
style="stop-color:#204a87;stop-opacity:1;"
offset="1"
id="stop7" />
</linearGradient>
<linearGradient
id="linearGradient4">
<stop
style="stop-color:#eeeeec;stop-opacity:1;"
offset="0"
id="stop4" />
<stop
style="stop-color:#888a85;stop-opacity:1;"
offset="1"
id="stop5" />
</linearGradient>
<linearGradient
id="linearGradient13">
<stop
style="stop-color:#4e9a06;stop-opacity:1;"
offset="0"
id="stop13" />
<stop
style="stop-color:#172a04;stop-opacity:1;"
offset="1"
id="stop14" />
</linearGradient>
<linearGradient
id="linearGradient5048">
<stop
style="stop-color:black;stop-opacity:0;"
offset="0"
id="stop5050" />
<stop
id="stop5056"
offset="0.5"
style="stop-color:black;stop-opacity:1;" />
<stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5052" />
</linearGradient>
<linearGradient
id="linearGradient30695">
<stop
style="stop-color:#b8babc;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop30697" />
<stop
id="stop30703"
offset="0.37383178"
style="stop-color:#5d6062;stop-opacity:1.0000000;" />
<stop
style="stop-color:#585b5d;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop30699" />
</linearGradient>
<linearGradient
id="linearGradient30685">
<stop
style="stop-color:#8d8d8d;stop-opacity:1;"
offset="0"
id="stop30687" />
<stop
style="stop-color:#747474;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop30689" />
</linearGradient>
<linearGradient
id="linearGradient30649">
<stop
style="stop-color:#858585;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop30651" />
<stop
style="stop-color:#5f5f5f;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop30653" />
</linearGradient>
<linearGradient
id="linearGradient30518">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop30520" />
<stop
style="stop-color:#ffffff;stop-opacity:0.78698224;"
offset="1.0000000"
id="stop30522" />
</linearGradient>
<linearGradient
id="linearGradient30505">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop30507" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop30509" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient30505"
id="radialGradient30511"
cx="25.375"
cy="43.375"
fx="25.375"
fy="43.375"
r="17.125"
gradientTransform="matrix(1,0,0,0.343066,0,28.49453)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient13"
id="linearGradient14"
x1="14.194713"
y1="14.959513"
x2="50.307402"
y2="14.959513"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.94136107,0,0,0.80047803,1.6398337,2.9844794)" />
<linearGradient
xlink:href="#linearGradient4"
id="linearGradient5"
x1="13.925462"
y1="28.767968"
x2="23.993155"
y2="28.767968"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient6"
id="linearGradient7"
x1="14.345589"
y1="45.668344"
x2="23.573026"
y2="45.668344"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata4">
<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>Jakub Steiner</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:contributor>
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
<dc:subject>
<rdf:Bag>
<rdf:li>calc</rdf:li>
<rdf:li>calculator</rdf:li>
</rdf:Bag>
</dc:subject>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer2"
style="display:inline"
transform="matrix(0.99687962,0,0,0.99742739,0.09985202,0.07897114)">
<rect
style="display:inline;fill:#888a85;fill-opacity:1;stroke:#2e3436;stroke-width:2.00570932;stroke-dasharray:none;stroke-opacity:1"
id="rect1"
width="46.144329"
height="58.148834"
x="8.9278355"
y="2.9289429"
rx="2.0969498"
ry="2.0765483" />
<path
style="font-variation-settings:normal;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:#d3d7cf;stroke-width:2.00573;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1"
id="rect4"
width="46"
height="58"
x="9"
y="3"
rx="2.0903912"
ry="2.0712333"
d="M 10.934001,4.9340059 H 53.066 V 59.072716 H 10.934001 Z" />
<path
style="display:inline;fill:url(#linearGradient14);fill-opacity:1;stroke:#2e3436;stroke-width:2.00563;stroke-linejoin:round;stroke-dasharray:none"
id="rect3"
width="32"
height="8"
x="16"
y="11"
rx="1.9157546"
ry="1.6495458"
d="m 17.8516,10.949162 h 28.29625 c 1.053857,0 1.902269,0.715397 1.902269,1.60403 v 4.812098 c 0,0.888633 -0.848412,1.604033 -1.902269,1.604033 H 17.8516 c -1.053858,0 -1.902269,-0.7154 -1.902269,-1.604033 v -4.812098 c 0,-0.888633 0.848411,-1.60403 1.902269,-1.60403 z" />
<g
id="g18"
transform="translate(0.01819536,-1.7775041)">
<rect
style="font-variation-settings:normal;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:2.00589;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect14"
width="8.0254087"
height="8.0199003"
x="14.928866"
y="24.758476"
rx="1.0018098"
ry="1.0005703" />
<rect
style="font-variation-settings:normal;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:2.00589;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect16"
width="8.0254192"
height="8.019906"
x="14.92884"
y="35.786819"
rx="1.0018109"
ry="1.0005711" />
<rect
style="font-variation-settings:normal;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:2.00589;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect17"
width="8.0254192"
height="8.019906"
x="14.928837"
y="46.815189"
rx="1.001811"
ry="1.0005711" />
</g>
<g
id="g20"
transform="translate(13.058887,-1.7775041)">
<rect
style="font-variation-settings:normal;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:2.00589;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect18"
width="8.0254192"
height="8.019906"
x="14.92884"
y="24.75845"
rx="1.0018109"
ry="1.0005711" />
<rect
style="font-variation-settings:normal;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:2.00589;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect19"
width="8.0254087"
height="8.0199003"
x="14.928866"
y="35.78685"
rx="1.0063162"
ry="1.0050732" />
<rect
style="font-variation-settings:normal;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:2.00570932;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect20"
width="8.0246763"
height="8.019165"
x="14.928775"
y="46.815128"
rx="1.0062244"
ry="1.004981" />
</g>
<g
id="g24"
transform="translate(26.099579,-1.7775041)"
style="display:inline;stroke-width:2.00571;stroke-dasharray:none">
<rect
style="font-variation-settings:normal;display:inline;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:2.00658;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect22"
width="8.0247221"
height="8.0192175"
x="14.928753"
y="24.758362"
rx="1.0017239"
ry="1.0004852" />
<rect
style="font-variation-settings:normal;display:inline;opacity:1;fill:#729fcf;stroke:#2e3436;stroke-width:2.00557;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="rect23"
width="8.0257349"
height="18.046019"
x="14.927776"
y="36.788338"
rx="1.1226069"
ry="0.9708975" />
</g>
</g>
<text
xml:space="preserve"
style="font-style:oblique;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.66667px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Bold Oblique';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#babdb6;fill-opacity:1;stroke:#2e3436;stroke-width:0.3;stroke-dasharray:none;stroke-opacity:1"
x="27.29318"
y="16.778864"
id="text1"><tspan
id="tspan1"
x="27.29318"
y="16.778864">f(x)</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -52,6 +52,7 @@
#include <Mod/Fem/App/FemPostBranchFilter.h>
#include <Mod/Fem/App/FemPostPipeline.h>
#include "ui_TaskPostCalculator.h"
#include "ui_TaskPostClip.h"
#include "ui_TaskPostContours.h"
#include "ui_TaskPostCut.h"
@@ -323,6 +324,9 @@ void TaskDlgPost::open()
void TaskDlgPost::clicked(int button)
{
if (button == QDialogButtonBox::Apply) {
for (auto box : m_boxes) {
box->apply();
}
recompute();
}
}
@@ -2123,4 +2127,130 @@ void TaskPostWarpVector::onMinValueChanged(double)
}
// ***************************************************************************
// calculator filter
static const std::vector<std::string> calculatorOperators = {
"+", "-", "*", "/", "-", "^", "abs", "cos", "sin", "tan", "exp",
"log", "pow", "sqrt", "iHat", "jHat", "kHat", "cross", "dot", "mag", "norm"};
TaskPostCalculator::TaskPostCalculator(ViewProviderFemPostCalculator* view, QWidget* parent)
: TaskPostBox(view,
Gui::BitmapFactory().pixmap("FEM_PostFilterCalculator"),
tr("Calculator options"),
parent)
, ui(new Ui_TaskPostCalculator)
{
// we load the views widget
proxy = new QWidget(this);
ui->setupUi(proxy);
setupConnections();
this->groupLayout()->addWidget(proxy);
// load the default values
auto obj = getObject<Fem::FemPostCalculatorFilter>();
ui->let_field_name->blockSignals(true);
ui->let_field_name->setText(QString::fromUtf8(obj->FieldName.getValue()));
ui->let_field_name->blockSignals(false);
ui->let_function->blockSignals(true);
ui->let_function->setText(QString::fromUtf8(obj->Function.getValue()));
ui->let_function->blockSignals(false);
ui->ckb_replace_invalid->setChecked(obj->ReplaceInvalid.getValue());
ui->dsb_replacement_value->setEnabled(obj->ReplaceInvalid.getValue());
ui->dsb_replacement_value->setValue(obj->ReplacementValue.getValue());
ui->dsb_replacement_value->setMaximum(std::numeric_limits<double>::max());
ui->dsb_replacement_value->setMinimum(std::numeric_limits<double>::lowest());
// fill available fields
for (const auto& f : obj->getScalarVariables()) {
ui->cb_scalars->addItem(QString::fromStdString(f));
}
for (const auto& f : obj->getVectorVariables()) {
ui->cb_vectors->addItem(QString::fromStdString(f));
}
QStringList qOperators;
for (const auto& o : calculatorOperators) {
qOperators << QString::fromStdString(o);
}
ui->cb_operators->addItems(qOperators);
ui->cb_scalars->setCurrentIndex(-1);
ui->cb_vectors->setCurrentIndex(-1);
ui->cb_operators->setCurrentIndex(-1);
}
TaskPostCalculator::~TaskPostCalculator() = default;
void TaskPostCalculator::setupConnections()
{
connect(ui->dsb_replacement_value,
qOverload<double>(&QDoubleSpinBox::valueChanged),
this,
&TaskPostCalculator::onReplacementValueChanged);
connect(ui->ckb_replace_invalid,
&QCheckBox::toggled,
this,
&TaskPostCalculator::onReplaceInvalidChanged);
connect(ui->cb_scalars,
qOverload<int>(&QComboBox::activated),
this,
&TaskPostCalculator::onScalarsActivated);
connect(ui->cb_vectors,
qOverload<int>(&QComboBox::activated),
this,
&TaskPostCalculator::onVectorsActivated);
connect(ui->cb_operators,
qOverload<int>(&QComboBox::activated),
this,
&TaskPostCalculator::onOperatorsActivated);
}
void TaskPostCalculator::onReplaceInvalidChanged(bool state)
{
auto obj = static_cast<Fem::FemPostCalculatorFilter*>(getObject());
obj->ReplaceInvalid.setValue(state);
ui->dsb_replacement_value->setEnabled(state);
recompute();
}
void TaskPostCalculator::onReplacementValueChanged(double value)
{
auto obj = static_cast<Fem::FemPostCalculatorFilter*>(getObject());
obj->ReplacementValue.setValue(value);
recompute();
}
void TaskPostCalculator::onScalarsActivated(int index)
{
QString item = ui->cb_scalars->itemText(index);
ui->let_function->insert(item);
}
void TaskPostCalculator::onVectorsActivated(int index)
{
QString item = ui->cb_vectors->itemText(index);
ui->let_function->insert(item);
}
void TaskPostCalculator::onOperatorsActivated(int index)
{
QString item = ui->cb_operators->itemText(index);
ui->let_function->insert(item);
}
void TaskPostCalculator::apply()
{
auto obj = getObject<Fem::FemPostCalculatorFilter>();
std::string function = ui->let_function->text().toStdString();
std::string name = ui->let_field_name->text().toStdString();
obj->Function.setValue(function);
obj->FieldName.setValue(name);
recompute();
auto view = getTypedView<ViewProviderFemPostCalculator>();
view->Field.setValue(obj->FieldName.getValue());
}
#include "moc_TaskPostBoxes.cpp"

View File

@@ -32,6 +32,7 @@
class QComboBox;
class Ui_TaskPostDisplay;
class Ui_TaskPostCalculator;
class Ui_TaskPostClip;
class Ui_TaskPostContours;
class Ui_TaskPostDataAlongLine;
@@ -141,12 +142,15 @@ public:
QWidget* parent = nullptr);
~TaskPostBox() override;
virtual void applyPythonCode() = 0;
virtual void applyPythonCode() {};
virtual bool isGuiTaskOnly()
{
return false;
} // return true if only gui properties are manipulated
// executed when the apply button is pressed in the task dialog
virtual void apply() {};
protected:
App::DocumentObject* getObject() const
{
@@ -555,6 +559,35 @@ private:
std::unique_ptr<Ui_TaskPostWarpVector> ui;
};
// ***************************************************************************
// calculator filter
class ViewProviderFemPostCalculator;
class TaskPostCalculator: public TaskPostBox
{
Q_OBJECT
public:
explicit TaskPostCalculator(ViewProviderFemPostCalculator* view, QWidget* parent = nullptr);
~TaskPostCalculator() override;
protected:
void apply() override;
private:
void setupConnections();
void onReplaceInvalidChanged(bool state);
void onReplacementValueChanged(double value);
void onScalarsActivated(int index);
void onVectorsActivated(int index);
void onOperatorsActivated(int index);
private:
QWidget* proxy;
std::unique_ptr<Ui_TaskPostCalculator> ui;
};
} // namespace FemGui
#endif // GUI_TASKVIEW_TaskPostDisplay_H

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TaskPostCalculator</class>
<widget class="QWidget" name="TaskPostCalculator">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>250</width>
<height>115</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="vertica_layout">
<item>
<widget class="QGroupBox" name="gpb_function">
<property name="title">
<string></string>
</property>
<layout class="QVBoxLayout" name="vbl_function">
<item>
<layout class="QFormLayout" name="form_layout">
<item row="0" column="0">
<widget class="QLabel" name="lbl_field_name">
<property name="text">
<string>Field Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="let_field_name">
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLineEdit" name="let_function">
<property name="toolTip">
<string>Mathematical expression</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gpb_fields">
<property name="title">
<string></string>
</property>
<property name="toolTip">
<string>Available fields</string>
</property>
<layout class="QVBoxLayout" name="vbl_fields">
<item>
<layout class="QFormLayout" name="fl_fields">
<item row="0" column="0">
<widget class="QLabel" name="lbl_scalars">
<property name="text">
<string>Scalars:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cb_scalars"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbl_vectors">
<property name="text">
<string>Vectors:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cb_vectors"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_operators">
<property name="text">
<string>Operators:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cb_operators"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gpb_replacement">
<layout class="QVBoxLayout" name="vbl_replacement">
<item>
<layout class="QFormLayout" name="fl_replacement">
<item row="0" column="0">
<widget class="QCheckBox" name="ckb_replace_invalid">
<property name="text">
<string>Replace invalid data:</string>
</property>
<property name="toolTip">
<string>Replacement value for invalid operations</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::DoubleSpinBox" name="dsb_replacement_value">
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::DoubleSpinBox</class>
<extends>QWidget</extends>
<header>Gui/SpinBox.h</header>
</customwidget>
</customwidgets>
</ui>

View File

@@ -215,3 +215,38 @@ void ViewProviderFemPostWarpVector::setupTaskDialog(TaskDlgPost* dlg)
// add the display options
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
}
// ***************************************************************************
// calculator filter
PROPERTY_SOURCE(FemGui::ViewProviderFemPostCalculator, FemGui::ViewProviderFemPostObject)
ViewProviderFemPostCalculator::ViewProviderFemPostCalculator()
{
sPixmap = "FEM_PostFilterCalculator";
}
ViewProviderFemPostCalculator::~ViewProviderFemPostCalculator() = default;
void ViewProviderFemPostCalculator::updateData(const App::Property* prop)
{
auto obj = getObject<Fem::FemPostCalculatorFilter>();
if (prop == &obj->Data) {
// update color bar
ViewProviderFemPostObject::updateData(prop);
updateMaterial();
}
else {
return ViewProviderFemPostObject::updateData(prop);
}
}
void ViewProviderFemPostCalculator::setupTaskDialog(TaskDlgPost* dlg)
{
// add the function box
assert(dlg->getView() == this);
dlg->appendBox(new TaskPostCalculator(this));
// add the display options
FemGui::ViewProviderFemPostObject::setupTaskDialog(dlg);
}

View File

@@ -154,6 +154,24 @@ protected:
void setupTaskDialog(TaskDlgPost* dlg) override;
};
// ***************************************************************************
// calculator filter
class FemGuiExport ViewProviderFemPostCalculator: public ViewProviderFemPostObject
{
PROPERTY_HEADER_WITH_OVERRIDE(FemGui::ViewProviderFemPostCalculator);
public:
/// constructor.
ViewProviderFemPostCalculator();
~ViewProviderFemPostCalculator() override;
void updateData(const App::Property* prop) override;
protected:
void setupTaskDialog(TaskDlgPost* dlg) override;
};
} // namespace FemGui

View File

@@ -209,6 +209,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "FEM_PostFilterDataAlongLine"
<< "FEM_PostFilterLinearizedStresses"
<< "FEM_PostFilterDataAtPoint"
<< "FEM_PostFilterCalculator"
<< "Separator"
<< "FEM_PostCreateFunctions";
#endif
@@ -356,6 +357,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const
<< "FEM_PostFilterDataAlongLine"
<< "FEM_PostFilterLinearizedStresses"
<< "FEM_PostFilterDataAtPoint"
<< "FEM_PostFilterCalculator"
<< "Separator"
<< "FEM_PostCreateFunctions";
#endif