[PD] add along length option for pad

- as discussed here: https://forum.freecadweb.org/viewtopic.php?f=17&t=50466&start=10#p433327
this PR is the first step for new Pad features.
It implements 3 features requested by users:
1. an option to measure the pad length along its direction if there is a custom direction
2. when there are two lengths, the reverse feature should be active
3. the pad dialog shows now always the used pad direction, no matter if it is a custom one or the sketch's normal

- Furthermore there are some code improvements (avoid rounding and blocking signals)

- Also adapt TestPad.py because its test used the case two lengths + reversed but this was not possible before this PR - the reverse option was not take into account for two lengths in FeatureSketchBased.cpp)
Now it is and therefore the test must be updated:
In the test the second sketch has the normal vector 0, -1, 0. As the reverse option is set the pad direction is 0, 1, 0. So in y direction is the length (1mm) and in -y direction is length2 (2mm). This gives together with the other pad of volume 1, a total volume of 4.

- fix UI issue: either reversed or midplane
As noticed by @chennes, when the pad uses symmetric, reversed is not sensible and vice versa.
This commit fixes the missing Gui side for the case midplane is checked and the App side.

- the PR also fix two typos
This commit is contained in:
donovaly
2020-09-24 01:56:24 +02:00
committed by wmayer
parent 5e90c07375
commit cf11f38875
7 changed files with 215 additions and 137 deletions

View File

@@ -56,7 +56,7 @@
using namespace PartDesign;
const char* Pad::TypeEnums[]= {"Length","UpToLast","UpToFirst","UpToFace","TwoLengths",NULL};
const char* Pad::TypeEnums[]= {"Length", "UpToLast", "UpToFirst", "UpToFace", "TwoLengths", NULL};
PROPERTY_SOURCE(PartDesign::Pad, PartDesign::ProfileBased)
@@ -70,6 +70,7 @@ Pad::Pad()
ADD_PROPERTY_TYPE(Length2, (100.0), "Pad", App::Prop_None,"Second Pad length");
ADD_PROPERTY_TYPE(UseCustomVector, (0), "Pad", App::Prop_None, "Use custom vector for pad direction");
ADD_PROPERTY_TYPE(Direction, (Base::Vector3d(1.0, 1.0, 1.0)), "Pad", App::Prop_None, "Pad direction vector");
ADD_PROPERTY_TYPE(AlongCustomVector, (1), "Pad", App::Prop_None, "Measure length along custom direction vector");
ADD_PROPERTY_TYPE(UpToFace, (0), "Pad", App::Prop_None, "Face where pad will end");
ADD_PROPERTY_TYPE(Offset, (0.0), "Pad", App::Prop_None, "Offset from face in which pad will end");
static const App::PropertyQuantityConstraint::Constraints signedLengthConstraint = {-DBL_MAX, DBL_MAX, 1.0};
@@ -88,6 +89,7 @@ short Pad::mustExecute() const
Length2.isTouched() ||
UseCustomVector.isTouched() ||
Direction.isTouched() ||
AlongCustomVector.isTouched() ||
Offset.isTouched() ||
UpToFace.isTouched())
return 1;
@@ -104,6 +106,12 @@ App::DocumentObjectExecReturn *Pad::execute(void)
if ((std::string(Type.getValueAsString()) == "TwoLengths") && (L < Precision::Confusion()))
return new App::DocumentObjectExecReturn("Second length of pad too small");
// if midplane is true, disable reversed and vice versa
bool hasMidplane = Midplane.getValue();
bool hasReversed = Reversed.getValue();
Midplane.setReadOnly(hasReversed);
Reversed.setReadOnly(hasMidplane);
Part::Feature* obj = 0;
TopoDS_Shape sketchshape;
try {
@@ -133,12 +141,13 @@ App::DocumentObjectExecReturn *Pad::execute(void)
base.Move(invObjLoc);
Base::Vector3d paddingDirection;
// use the given vector if necessary
if (!UseCustomVector.getValue()) {
// use sketch's normal vector for direction
paddingDirection = SketchVector;
}
else {
// use the given vector
// if null vector, use SketchVector
if ( (fabs(Direction.getValue().x) < Precision::Confusion())
&& (fabs(Direction.getValue().y) < Precision::Confusion())
@@ -168,9 +177,15 @@ App::DocumentObjectExecReturn *Pad::execute(void)
if (factor < Precision::Confusion())
return new App::DocumentObjectExecReturn("Pad: Creation failed because direction is orthogonal to sketch's normal vector");
// perform the length correction
L = L / factor;
L2 = L2 / factor;
// perform the length correction if not along custom vector
if (AlongCustomVector.getValue()) {
L = L / factor;
L2 = L2 / factor;
}
// explicitly set the Direction so that the dialog shows also the used direction
// if the sketch's normal vector was used
Direction.setValue(paddingDirection);
dir.Transform(invObjLoc.Transformation());
@@ -306,7 +321,7 @@ App::DocumentObjectExecReturn *Pad::execute(void)
}
} else {
generatePrism(prism, sketchshape, method, dir, L, L2,
Midplane.getValue(), Reversed.getValue());
hasMidplane, hasReversed);
}
if (prism.IsNull())

View File

@@ -45,6 +45,7 @@ public:
App::PropertyLength Length2;
App::PropertyBool UseCustomVector;
App::PropertyVector Direction;
App::PropertyBool AlongCustomVector;
App::PropertyLength Offset;
/** @name methods override feature */

View File

@@ -587,8 +587,11 @@ void ProfileBased::generatePrism(TopoDS_Shape& prism,
if (method == "TwoLengths") {
// midplane makes no sense here
Loffset = -L2;
Ltotal += L2;
if (reversed)
Loffset = -L;
else
Loffset = -L2;
} else if (midplane)
Loffset = -Ltotal/2;

View File

@@ -34,15 +34,15 @@
#include "TaskPadParameters.h"
#include <App/Application.h>
#include <App/Document.h>
#include <Base/Console.h>
#include <Base/UnitsApi.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/Selection.h>
#include <Gui/ViewProvider.h>
#include <Gui/WaitCursor.h>
#include <Base/Console.h>
#include <Gui/Selection.h>
#include <Gui/Command.h>
#include <Mod/PartDesign/App/FeaturePad.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include "TaskSketchBasedParameters.h"
@@ -75,6 +75,7 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
Base::Quantity l = pcPad->Length.getQuantityValue();
Base::Quantity l2 = pcPad->Length2.getQuantityValue();
bool alongCustom = pcPad->AlongCustomVector.getValue();
bool useCustom = pcPad->UseCustomVector.getValue();
double xs = pcPad->Direction.getValue().x;
double ys = pcPad->Direction.getValue().y;
@@ -93,10 +94,18 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
faceId = std::atoi(&upToFace[4]);
}
// set decimals for the direction edits
// do this here before the edits are filed to avoid rounding mistakes
int UserDecimals = Base::UnitsApi::getDecimals();
ui->XDirectionEdit->setDecimals(UserDecimals);
ui->YDirectionEdit->setDecimals(UserDecimals);
ui->ZDirectionEdit->setDecimals(UserDecimals);
// Fill data into dialog elements
ui->lengthEdit->setValue(l);
ui->lengthEdit2->setValue(l2);
ui->groupBoxDirection->setChecked(useCustom);
ui->checkBoxAlongDirection->setChecked(alongCustom);
ui->XDirectionEdit->setValue(xs);
ui->YDirectionEdit->setValue(ys);
ui->ZDirectionEdit->setValue(zs);
@@ -105,23 +114,16 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
// Bind input fields to properties
ui->lengthEdit->bind(pcPad->Length);
ui->lengthEdit2->bind(pcPad->Length2);
ui->XDirectionEdit->bind(App::ObjectIdentifier::parse(pcPad, std::string("Direction.x")));
ui->YDirectionEdit->bind(App::ObjectIdentifier::parse(pcPad, std::string("Direction.y")));
ui->ZDirectionEdit->bind(App::ObjectIdentifier::parse(pcPad, std::string("Direction.z")));
ui->offsetEdit->bind(pcPad->Offset);
ui->checkBoxMidplane->setChecked(midplane);
// According to bug #0000521 the reversed option
// shouldn't be de-activated if the pad has a support face
ui->checkBoxReversed->setChecked(reversed);
// set decimals for the direction edits
int UserDecimals = Base::UnitsApi::getDecimals();
ui->XDirectionEdit->setDecimals(UserDecimals);
ui->YDirectionEdit->setDecimals(UserDecimals);
ui->ZDirectionEdit->setDecimals(UserDecimals);
// Set object labels
if (obj && PartDesign::Feature::isDatum(obj)) {
ui->lineFaceName->setText(QString::fromUtf8(obj->Label.getValue()));
@@ -138,7 +140,6 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
ui->lineFaceName->clear();
ui->lineFaceName->setProperty("FeatureName", QVariant());
}
ui->lineFaceName->setProperty("FaceName", QByteArray(upToFace.c_str()));
ui->changeMode->clear();
@@ -155,6 +156,8 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
this, SLOT(onLengthChanged(double)));
connect(ui->lengthEdit2, SIGNAL(valueChanged(double)),
this, SLOT(onLength2Changed(double)));
connect(ui->checkBoxAlongDirection, SIGNAL(toggled(bool)),
this, SLOT(onCBAlongDirectionChanged(bool)));
connect(ui->groupBoxDirection, SIGNAL(toggled(bool)),
this, SLOT(onGBDirectionChanged(bool)));
connect(ui->XDirectionEdit, SIGNAL(valueChanged(double)),
@@ -197,16 +200,17 @@ void TaskPadParameters::updateUI(int index)
{
// disable/hide everything unless we are sure we don't need it
// exception: the direction parameters are in any case visible
bool isLengthEditVisable = false;
bool isLengthEdit2Visable = false;
bool isOffsetEditVisable = false;
bool isLengthEditVisible = false;
bool isLengthEdit2Visible = false;
bool isOffsetEditVisible = false;
bool isMidplateEnabled = false;
bool isReversedEnabled = false;
bool isReversedEnabled = true;
bool isReversedVisible = true;
bool isFaceEditEnabled = false;
// dimension
if (index == 0) {
isLengthEditVisable = true;
isLengthEditVisible = true;
ui->lengthEdit->selectNumber();
// Make sure that the spin box has the focus to get key events
// Calling setFocus() directly doesn't work because the spin box is not
@@ -218,13 +222,16 @@ void TaskPadParameters::updateUI(int index)
}
// up to first/last
else if (index == 1 || index == 2) {
isOffsetEditVisable = true;
isReversedEnabled = true;
isOffsetEditVisible = true;
isReversedEnabled = false;
isReversedVisible = false;
}
// up to face
else if (index == 3) {
isOffsetEditVisable = true;
isFaceEditEnabled = true;
isOffsetEditVisible = true;
isFaceEditEnabled = true;
isReversedEnabled = false;
isReversedVisible = false;
QMetaObject::invokeMethod(ui->lineFaceName, "setFocus", Qt::QueuedConnection);
// Go into reference selection mode if no face has been selected yet
if (ui->lineFaceName->property("FeatureName").isNull())
@@ -232,25 +239,27 @@ void TaskPadParameters::updateUI(int index)
}
// two dimensions
else {
isLengthEditVisable = true;
isLengthEdit2Visable = true;
isLengthEditVisible = true;
isLengthEdit2Visible = true;
}
ui->lengthEdit->setVisible( isLengthEditVisable );
ui->lengthEdit->setEnabled( isLengthEditVisable );
ui->labelLength->setVisible( isLengthEditVisable );
ui->lengthEdit->setVisible( isLengthEditVisible );
ui->lengthEdit->setEnabled( isLengthEditVisible );
ui->labelLength->setVisible( isLengthEditVisible );
ui->checkBoxAlongDirection->setVisible( isLengthEditVisible );
ui->offsetEdit->setVisible( isOffsetEditVisable );
ui->offsetEdit->setEnabled( isOffsetEditVisable );
ui->labelOffset->setVisible( isOffsetEditVisable );
ui->offsetEdit->setVisible( isOffsetEditVisible );
ui->offsetEdit->setEnabled( isOffsetEditVisible );
ui->labelOffset->setVisible( isOffsetEditVisible );
ui->checkBoxMidplane->setEnabled( isMidplateEnabled );
ui->checkBoxReversed->setEnabled( isReversedEnabled );
ui->checkBoxReversed->setVisible( isReversedVisible );
ui->lengthEdit2->setVisible( isLengthEdit2Visable );
ui->lengthEdit2->setEnabled( isLengthEdit2Visable );
ui->labelLength2->setVisible( isLengthEdit2Visable );
ui->lengthEdit2->setVisible( isLengthEdit2Visible );
ui->lengthEdit2->setEnabled( isLengthEdit2Visible );
ui->labelLength2->setVisible( isLengthEdit2Visible );
ui->buttonFace->setEnabled( isFaceEditEnabled );
ui->lineFaceName->setEnabled( isFaceEditEnabled );
@@ -301,11 +310,26 @@ void TaskPadParameters::onLength2Changed(double len)
recomputeFeature();
}
void TaskPadParameters::onCBAlongDirectionChanged(bool on)
{
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
pcPad->AlongCustomVector.setValue(on);
recomputeFeature();
}
void TaskPadParameters::onGBDirectionChanged(bool on)
{
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
pcPad->UseCustomVector.setValue(on);
// dis/enable length direction
ui->checkBoxAlongDirection->setEnabled(on);
if (!on)
ui->checkBoxAlongDirection->setChecked(!on);
recomputeFeature();
// the calculation of the sketch's normal vector is done in FeaturePad.cpp
// if this vector was used for the recomputation we must fill the direction
// vector edit fields. Therefore update
updateDirectionEdits();
}
void TaskPadParameters::onXDirectionEditChanged(double len)
@@ -319,7 +343,6 @@ void TaskPadParameters::onXDirectionEditChanged(double len)
updateDirectionEdits();
}
void TaskPadParameters::onYDirectionEditChanged(double len)
{
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
@@ -339,9 +362,16 @@ void TaskPadParameters::onZDirectionEditChanged(double len)
void TaskPadParameters::updateDirectionEdits(void)
{
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
// we don't want to execute the onChanged edits, but just update their contents
ui->XDirectionEdit->blockSignals(true);
ui->YDirectionEdit->blockSignals(true);
ui->ZDirectionEdit->blockSignals(true);
ui->XDirectionEdit->setValue(pcPad->Direction.getValue().x);
ui->YDirectionEdit->setValue(pcPad->Direction.getValue().y);
ui->ZDirectionEdit->setValue(pcPad->Direction.getValue().z);
ui->XDirectionEdit->blockSignals(false);
ui->YDirectionEdit->blockSignals(false);
ui->ZDirectionEdit->blockSignals(false);
}
void TaskPadParameters::onOffsetChanged(double len)
@@ -355,6 +385,7 @@ void TaskPadParameters::onMidplaneChanged(bool on)
{
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
pcPad->Midplane.setValue(on);
// reversed is not sensible when midplane
ui->checkBoxReversed->setEnabled(!on);
recomputeFeature();
}
@@ -363,6 +394,8 @@ void TaskPadParameters::onReversedChanged(bool on)
{
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
pcPad->Reversed.setValue(on);
// midplane is not sensible when reversed
ui->checkBoxMidplane->setEnabled(!on);
recomputeFeature();
}
@@ -391,6 +424,7 @@ void TaskPadParameters::onButtonFace(const bool pressed)
{
this->blockConnection(!pressed);
// only faces are allowed
TaskSketchBasedParameters::onSelectReference(pressed, false, true, false);
// Update button if onButtonFace() is called explicitly
@@ -432,6 +466,11 @@ double TaskPadParameters::getLength2(void) const
return ui->lengthEdit2->value().getValue();
}
bool TaskPadParameters::getAlongCustom(void) const
{
return ui->checkBoxAlongDirection->isChecked();
}
bool TaskPadParameters::getCustom(void) const
{
return ui->groupBoxDirection->isChecked();
@@ -563,6 +602,7 @@ void TaskPadParameters::apply()
FCMD_OBJ_CMD(obj, "UseCustomVector = " << (getCustom() ? 1 : 0));
FCMD_OBJ_CMD(obj, "Direction = ("
<< getXDirection() << ", " << getYDirection() << ", " << getZDirection() << ")");
FCMD_OBJ_CMD(obj, "AlongCustomVector = " << (getAlongCustom() ? 1 : 0));
FCMD_OBJ_CMD(obj,"Type = " << getMode());
QString facename = getFaceName();
FCMD_OBJ_CMD(obj,"UpToFace = " << facename.toLatin1().data());

View File

@@ -58,6 +58,7 @@ public:
private Q_SLOTS:
void onLengthChanged(double);
void onLength2Changed(double);
void onCBAlongDirectionChanged(bool);
void onGBDirectionChanged(bool);
void onXDirectionEditChanged(double);
void onYDirectionEditChanged(double);
@@ -75,6 +76,7 @@ protected:
private:
double getLength(void) const;
double getLength2(void) const;
bool getAlongCustom(void) const;
bool getCustom(void) const;
double getXDirection(void) const;
double getYDirection(void) const;

View File

@@ -7,13 +7,13 @@
<x>0</x>
<y>0</y>
<width>280</width>
<height>350</height>
<height>373</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
@@ -66,97 +66,114 @@ the sketch plane's normal vector will be used</string>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout1">
<item row="0" column="0">
<widget class="QLabel" name="labelXSkew">
<property name="text">
<string>x</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::DoubleSpinBox" name="XDirectionEdit">
<property name="toolTip">
<string>x-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelYSkew">
<property name="text">
<string>y</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::DoubleSpinBox" name="YDirectionEdit">
<property name="toolTip">
<string>y-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelZSkew">
<property name="text">
<string>z</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::DoubleSpinBox" name="ZDirectionEdit">
<property name="toolTip">
<string>z-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="labelXSkew">
<property name="text">
<string>x</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::DoubleSpinBox" name="XDirectionEdit">
<property name="toolTip">
<string>x-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelYSkew">
<property name="text">
<string>y</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::DoubleSpinBox" name="YDirectionEdit">
<property name="toolTip">
<string>y-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelZSkew">
<property name="text">
<string>z</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::DoubleSpinBox" name="ZDirectionEdit">
<property name="toolTip">
<string>z-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxAlongDirection">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>If unchecked, the length will be
measured along the specified direction</string>
</property>
<property name="text">
<string>Length along sketch normal</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -159,8 +159,8 @@ class TestPad(unittest.TestCase):
self.Body.addObject(self.Pad1)
self.Pad1.Profile = self.PadSketch1
self.Pad1.Type = 4
self.Pad1.Length = 2.0
self.Pad1.Length2 = 1.0
self.Pad1.Length = 1.0
self.Pad1.Length2 = 2.0
self.Pad1.Reversed = 1
self.Doc.recompute()
self.assertAlmostEqual(self.Pad1.Shape.Volume, 4.0)