PartDesign: Extrude 2 sides (#21794)

* PartDesign: extrude 2 sides

* Part: OpCodes XOR

* PartDesign: Remove deprecated generatePrism functions

* PartDesign: Extrude : Update Sides combobox strings

* Change "Sides" to "Mode"

* Use OpCodes::Extrude instead of Prism.
This commit is contained in:
PaddleStroke
2025-08-25 19:17:23 +02:00
committed by GitHub
parent 3b3516de71
commit a346c266e7
22 changed files with 1656 additions and 1258 deletions
+29
View File
@@ -1422,6 +1422,35 @@ public:
return TopoShape(0, Hasher).makeElementCut({*this, source}, op, tol); return TopoShape(0, Hasher).makeElementCut({*this, source}, op, tol);
} }
/** Make a boolean xor of this shape with an input shape
*
* @param source: the source shape
* @param op: optional string to be encoded into topo naming for indicating
* the operation
* @param tol: tolerance for the fusion
*
* @return The original content of this TopoShape is discarded and replaced
* with the new shape. The function returns the TopoShape itself as
* a self reference so that multiple operations can be carried out
* for the same shape in the same line of code.
*/
TopoShape&
makeElementXor(const std::vector<TopoShape>& sources, const char* op = nullptr, double tol = -1.0);
/** Make a boolean xor of this shape with an input shape
*
* @param source: the source shape
* @param op: optional string to be encoded into topo naming for indicating
* the operation
* @param tol: tolerance for the fusion
*
* @return Return the new shape. The TopoShape itself is not modified.
*/
TopoShape
makeElementXor(const TopoShape& source, const char* op = nullptr, double tol = -1.0) const
{
return TopoShape(0, Hasher).makeElementXor({*this, source}, op, tol);
}
/** Try to simplify geometry of any linear/planar subshape to line/plane /** Try to simplify geometry of any linear/planar subshape to line/plane
* *
* @return Return true if the shape is modified * @return Return true if the shape is modified
+82
View File
@@ -4143,6 +4143,84 @@ TopoShape::makeElementCut(const std::vector<TopoShape>& shapes, const char* op,
return makeElementBoolean(Part::OpCodes::Cut, shapes, op, tol); return makeElementBoolean(Part::OpCodes::Cut, shapes, op, tol);
} }
TopoShape&
TopoShape::makeElementXor(const std::vector<TopoShape>& shapes, const char* op, double tol)
{
if (shapes.empty()) {
FC_THROWM(NullShapeException, "Null shape");
}
if (OCCTProgressIndicator::getAppIndicator().UserBreak()) {
FC_THROWM(Base::CADKernelError, "User aborted");
}
if (!op) {
op = Part::OpCodes::Xor;
}
std::vector<TopoShape> expandedShapes;
// Same compound expansion as Fuse
for (auto it = shapes.begin(); it != shapes.end(); ++it) {
auto& shape = *it;
if (shape.isNull()) {
FC_THROWM(NullShapeException, "Null input shape for XOR operation");
}
if (shape.shapeType() == TopAbs_COMPOUND) {
if (expandedShapes.empty()) {
expandedShapes.insert(expandedShapes.end(), shapes.begin(), it);
}
expandCompound(shape, expandedShapes);
}
else if (!expandedShapes.empty()) {
expandedShapes.push_back(shape);
}
}
const auto& inputs = expandedShapes.empty() ? shapes : expandedShapes;
// Note: The inputs.empty() check is now redundant because of the check at the top,
// but it's harmless to leave it.
if (inputs.empty()) {
FC_THROWM(NullShapeException, "Null shape");
}
if (inputs.size() == 1) {
*this = inputs[0];
if (shapes.size() == 1) {
FC_WARN("Boolean operation with only one shape input");
}
return *this;
}
TopoShape result = inputs[0];
for (size_t i = 1; i < inputs.size(); ++i) {
// The final op is only applied on the very last iteration.
const char* currentOp = (i == inputs.size() - 1) ? op : nullptr;
// Step 1: Union(A, B) - intermediate result, no op code.
TopoShape tempUnion(0, Hasher);
tempUnion.makeElementBoolean(Part::OpCodes::Fuse, {result, inputs[i]}, nullptr, tol);
// Step 2: Common(A, B) - intermediate result, no op code.
TopoShape tempCommon(0, Hasher);
tempCommon.makeElementBoolean(Part::OpCodes::Common, {result, inputs[i]}, nullptr, tol);
// Step 3: Compute the final result for this iteration
if (tempCommon.isNull() || tempCommon.getShape().IsNull()) {
// No intersection, XOR is the same as Union.
// We still call the boolean op to get the correct history.
result.makeElementBoolean(Part::OpCodes::Fuse, {result, inputs[i]}, currentOp, tol);
}
else {
// Final result is Cut(Union, Common).
result.makeElementBoolean(Part::OpCodes::Cut,
{tempUnion, tempCommon},
currentOp,
tol);
}
}
*this = result;
return *this;
}
TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape,
const TopoShape& source, const TopoShape& source,
@@ -5686,6 +5764,10 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker,
return *this; return *this;
} }
if (strcmp(maker, Part::OpCodes::Xor) == 0) {
return makeElementXor(shapes, op, tolerance);
}
bool buildShell = true; bool buildShell = true;
std::vector<TopoShape> _shapes; std::vector<TopoShape> _shapes;
+2 -1
View File
@@ -48,7 +48,8 @@ public:
static constexpr const char *Fuse = "FUS"; static constexpr const char *Fuse = "FUS";
static constexpr const char *Cut = "CUT"; static constexpr const char *Cut = "CUT";
static constexpr const char *Common = "CMN"; static constexpr const char *Common = "CMN";
static constexpr const char *Section = "SEC"; static constexpr const char* Section = "SEC";
static constexpr const char* Xor = "XOR";
static constexpr const char *Compound = "CMP"; static constexpr const char *Compound = "CMP";
static constexpr const char *Compsolid = "CSD"; static constexpr const char *Compsolid = "CSD";
static constexpr const char *Pipe = "PIP"; static constexpr const char *Pipe = "PIP";
+338 -444
View File
@@ -30,6 +30,7 @@
# include <BRepFeat_MakePrism.hxx> # include <BRepFeat_MakePrism.hxx>
# include <BRepPrimAPI_MakePrism.hxx> # include <BRepPrimAPI_MakePrism.hxx>
# include <gp_Dir.hxx> # include <gp_Dir.hxx>
# include <gp_Ax2.hxx>
# include <Precision.hxx> # include <Precision.hxx>
# include <TopExp_Explorer.hxx> # include <TopExp_Explorer.hxx>
# include <TopoDS_Compound.hxx> # include <TopoDS_Compound.hxx>
@@ -49,6 +50,8 @@ FC_LOG_LEVEL_INIT("PartDesign", true, true)
using namespace PartDesign; using namespace PartDesign;
const char* FeatureExtrude::SideTypesEnums[] = {"One side", "Two sides", "Symmetric", nullptr};
PROPERTY_SOURCE(PartDesign::FeatureExtrude, PartDesign::ProfileBased) PROPERTY_SOURCE(PartDesign::FeatureExtrude, PartDesign::ProfileBased)
App::PropertyQuantityConstraint::Constraints FeatureExtrude::signedLengthConstraint = { App::PropertyQuantityConstraint::Constraints FeatureExtrude::signedLengthConstraint = {
@@ -61,7 +64,9 @@ FeatureExtrude::FeatureExtrude() = default;
short FeatureExtrude::mustExecute() const short FeatureExtrude::mustExecute() const
{ {
if (Placement.isTouched() || if (Placement.isTouched() ||
SideType.isTouched() ||
Type.isTouched() || Type.isTouched() ||
Type2.isTouched() ||
Length.isTouched() || Length.isTouched() ||
Length2.isTouched() || Length2.isTouched() ||
TaperAngle.isTouched() || TaperAngle.isTouched() ||
@@ -71,7 +76,11 @@ short FeatureExtrude::mustExecute() const
ReferenceAxis.isTouched() || ReferenceAxis.isTouched() ||
AlongSketchNormal.isTouched() || AlongSketchNormal.isTouched() ||
Offset.isTouched() || Offset.isTouched() ||
UpToFace.isTouched()) Offset2.isTouched() ||
UpToFace.isTouched() ||
UpToFace2.isTouched() ||
UpToShape.isTouched() ||
UpToShape2.isTouched())
return 1; return 1;
return ProfileBased::mustExecute(); return ProfileBased::mustExecute();
} }
@@ -174,293 +183,115 @@ TopoShape FeatureExtrude::makeShellFromUpToShape(TopoShape shape, TopoShape sket
return shape; return shape;
} }
// TODO: Toponaming April 2024 Deprecated in favor of TopoShape method. Remove when possible. void FeatureExtrude::updateProperties()
void FeatureExtrude::generatePrism(TopoDS_Shape& prism,
const TopoDS_Shape& sketchshape,
const std::string& method,
const gp_Dir& direction,
const double L,
const double L2,
const bool midplane,
const bool reversed)
{ {
if (method == "Length" || method == "TwoLengths" || method == "ThroughAll") { std::string sideTypeVal = SideType.getValueAsString();
double Ltotal = L; std::string methodSide1 = Type.getValueAsString();
double Loffset = 0.; std::string methodSide2 = Type2.getValueAsString();
if (method == "ThroughAll")
Ltotal = getThroughAllLength();
bool isLength1Enabled = false;
bool isTaper1Visible = false;
bool isUpToFace1Enabled = false;
bool isUpToShape1Enabled = false;
bool isOffset1Enabled = false;
if (method == "TwoLengths") { bool isType2Enabled = false;
Ltotal += L2;
if (reversed)
Loffset = -L;
else
Loffset = -L2;
}
else if (midplane) {
Loffset = -Ltotal / 2;
}
TopoDS_Shape from = sketchshape;
if (method == "TwoLengths" || midplane) {
gp_Trsf mov;
mov.SetTranslation(Loffset * gp_Vec(direction));
TopLoc_Location loc(mov);
from = sketchshape.Moved(loc);
}
else if (reversed) {
Ltotal *= -1.0;
}
if (fabs(Ltotal) < Precision::Confusion()) {
if (addSubType == Type::Additive)
throw Base::ValueError("Cannot create a pad with a height of zero.");
else
throw Base::ValueError("Cannot create a pocket with a depth of zero.");
}
// Without taper angle we create a prism because its shells are in every case no B-splines and can therefore
// be use as support for further features like Pads, Lofts etc. B-spline shells can break certain features,
// see e.g. https://forum.freecad.org/viewtopic.php?p=560785#p560785
// It is better not to use BRepFeat_MakePrism here even if we have a support because the
// resulting shape creates problems with Pocket
BRepPrimAPI_MakePrism PrismMaker(from, Ltotal * gp_Vec(direction), Standard_False, Standard_True); // finite prism
if (!PrismMaker.IsDone())
throw Base::RuntimeError("ProfileBased: Length: Could not extrude the sketch!");
prism = PrismMaker.Shape();
}
else {
std::stringstream str;
str << "ProfileBased: Internal error: Unknown method '"
<< method << "' for generatePrism()";
throw Base::RuntimeError(str.str());
}
}
void FeatureExtrude::generatePrism(TopoDS_Shape& prism,
const std::string& method,
const TopoDS_Shape& baseshape,
const TopoDS_Shape& profileshape,
const TopoDS_Face& supportface,
const TopoDS_Shape& uptoface,
const gp_Dir& direction,
PrismMode Mode,
Standard_Boolean Modify)
{
if (method == "UpToFirst" || method == "UpToFace") {
BRepFeat_MakePrism PrismMaker;
TopoDS_Shape base = baseshape;
for (TopExp_Explorer xp(profileshape, TopAbs_FACE); xp.More(); xp.Next()) {
PrismMaker.Init(base, xp.Current(), supportface, direction, Mode, Modify);
PrismMaker.Perform(uptoface);
if (!PrismMaker.IsDone())
throw Base::RuntimeError("ProfileBased: Up to face: Could not extrude the sketch!");
base = PrismMaker.Shape();
if (Mode == PrismMode::None)
Mode = PrismMode::FuseWithBase;
}
prism = base;
}
else if (method == "UpToLast") {
BRepFeat_MakePrism PrismMaker;
prism = baseshape;
for (TopExp_Explorer xp(profileshape, TopAbs_FACE); xp.More(); xp.Next()) {
PrismMaker.Init(baseshape, xp.Current(), supportface, direction, PrismMode::None, Modify);
//Each face needs 2 prisms because if uptoFace is intersected twice the first one ends too soon
for (int i=0; i<2; i++){
if (i==0){
PrismMaker.Perform(uptoface);
}else{
PrismMaker.Perform(uptoface, uptoface);
}
if (!PrismMaker.IsDone())
throw Base::RuntimeError("ProfileBased: Up to face: Could not extrude the sketch!");
auto onePrism = PrismMaker.Shape();
FCBRepAlgoAPI_Fuse fuse(prism, onePrism);
prism = fuse.Shape();
}
}
}
else {
std::stringstream str;
str << "ProfileBased: Internal error: Unknown method '"
<< method << "' for generatePrism()";
throw Base::RuntimeError(str.str());
}
}
void FeatureExtrude::generatePrism(TopoShape& prism,
TopoShape sketchTopoShape,
const std::string& method,
const gp_Dir& dir,
const double L,
const double L2,
const bool midplane,
const bool reversed)
{
auto sketchShape = sketchTopoShape.getShape();
if (method == "Length" || method == "TwoLengths" || method == "ThroughAll") {
double Ltotal = L;
double Loffset = 0.;
if (method == "ThroughAll") {
Ltotal = getThroughAllLength();
}
if (method == "TwoLengths") {
Ltotal += L2;
if (reversed) {
Loffset = -L;
}
else {
Loffset = -L2;
}
}
else if (midplane) {
Loffset = -Ltotal / 2;
}
if (method == "TwoLengths" || midplane) {
gp_Trsf mov;
mov.SetTranslation(Loffset * gp_Vec(dir));
TopLoc_Location loc(mov);
sketchTopoShape.move(loc);
}
else if (reversed) {
Ltotal *= -1.0;
}
// Without taper angle we create a prism because its shells are in every case no B-splines
// and can therefore be use as support for further features like Pads, Lofts etc. B-spline
// shells can break certain features, see e.g.
// https://forum.freecad.org/viewtopic.php?p=560785#p560785 It is better not to use
// BRepFeat_MakePrism here even if we have a support because the resulting shape creates
// problems with Pocket
try {
prism.makeElementPrism(sketchTopoShape, Ltotal * gp_Vec(dir)); // finite prism
}
catch (Standard_Failure&) {
throw Base::RuntimeError("FeatureExtrusion: Length: Could not extrude the sketch!");
}
}
else {
std::stringstream str;
str << "FeatureExtrusion: Internal error: Unknown method '" << method
<< "' for generatePrism()";
throw Base::RuntimeError(str.str());
}
}
void FeatureExtrude::generateTaperedPrism(TopoDS_Shape& prism,
const TopoDS_Shape& sketchshape,
const std::string& method,
const gp_Dir& direction,
const double L,
const double L2,
const double angle,
const double angle2,
const bool midplane)
{
std::list<TopoDS_Shape> drafts;
bool isSolid = true; // in PD we only generate solids, while Part Extrude can also create only shells
bool isPartDesign = true; // there is an OCC bug with single-edge wires (circles) we need to treat differently for PD and Part
if (method == "ThroughAll") {
Part::ExtrusionHelper::makeDraft(sketchshape, direction, getThroughAllLength(),
0.0, Base::toRadians(angle), 0.0, isSolid, drafts, isPartDesign);
}
else if (method == "TwoLengths") {
Part::ExtrusionHelper::makeDraft(sketchshape, direction, L, L2,
Base::toRadians(angle), Base::toRadians(angle2), isSolid, drafts, isPartDesign);
}
else if (method == "Length") {
if (midplane) {
Part::ExtrusionHelper::makeDraft(sketchshape, direction, L / 2, L / 2,
Base::toRadians(angle), Base::toRadians(angle), isSolid, drafts, isPartDesign);
}
else
Part::ExtrusionHelper::makeDraft(sketchshape, direction, L, 0.0,
Base::toRadians(angle), 0.0, isSolid, drafts, isPartDesign);
}
if (drafts.empty()) {
throw Base::RuntimeError("Creation of tapered object failed");
}
else if (drafts.size() == 1) {
prism = drafts.front();
}
else {
TopoDS_Compound comp;
BRep_Builder builder;
builder.MakeCompound(comp);
for (const auto & draft : drafts)
builder.Add(comp, draft);
prism = comp;
}
}
void FeatureExtrude::updateProperties(const std::string &method)
{
// disable settings that are not valid on the current method
// disable everything unless we are sure we need it
bool isLengthEnabled = false;
bool isLength2Enabled = false; bool isLength2Enabled = false;
bool isOffsetEnabled = false;
bool isMidplaneEnabled = false;
bool isReversedEnabled = false;
bool isUpToFaceEnabled = false;
bool isUpToShapeEnabled = false;
bool isTaperVisible = false;
bool isTaper2Visible = false; bool isTaper2Visible = false;
if (method == "Length") { bool isUpToFace2Enabled = false;
isLengthEnabled = true; bool isUpToShape2Enabled = false;
isTaperVisible = true; bool isOffset2Enabled = false;
isMidplaneEnabled = true;
isReversedEnabled = !Midplane.getValue(); bool currentAlongSketchNormalEnabled = false;
auto configureSideProperties = [&](const std::string& method,
bool& lengthEnabled,
bool& taperVisible,
bool& upToFaceEnabled,
bool& upToShapeEnabled,
bool& localAlongSketchNormal,
bool& localOffset) {
if (method == "Length") {
lengthEnabled = true;
taperVisible = true;
localAlongSketchNormal = true;
}
else if (method == "UpToFace") {
upToFaceEnabled = true;
localOffset = true;
}
else if (method == "UpToShape") {
upToShapeEnabled = true;
localOffset = true;
}
else if (method == "UpToLast" || method == "UpToFirst") {
localOffset = true;
}
else if (method == "ThroughAll") {
// No specific length/taper/offset for ThroughAll type
}
};
if (sideTypeVal == "One side") {
bool side1ASN = false;
configureSideProperties(methodSide1,
isLength1Enabled,
isTaper1Visible,
isUpToFace1Enabled,
isUpToShape1Enabled,
side1ASN,
isOffset1Enabled);
currentAlongSketchNormalEnabled = side1ASN;
} }
else if (method == "UpToLast") { else if (sideTypeVal == "Two sides") {
isOffsetEnabled = true; isType2Enabled = true;
isReversedEnabled = true;
bool side1ASN = false;
configureSideProperties(methodSide1,
isLength1Enabled,
isTaper1Visible,
isUpToFace1Enabled,
isUpToShape1Enabled,
side1ASN,
isOffset1Enabled);
bool side2ASN = false;
configureSideProperties(methodSide2,
isLength2Enabled,
isTaper2Visible,
isUpToFace2Enabled,
isUpToShape2Enabled,
side2ASN,
isOffset2Enabled);
currentAlongSketchNormalEnabled = side1ASN || side2ASN; // Enable if either side needs it
} }
else if (method == "ThroughAll") { else if (sideTypeVal == "Symmetric") {
isMidplaneEnabled = true; bool symASN = false;
isReversedEnabled = !Midplane.getValue(); configureSideProperties(methodSide1,
} isLength1Enabled,
else if (method == "UpToFirst") { isTaper1Visible,
isOffsetEnabled = true; isUpToFace1Enabled,
isReversedEnabled = true; isUpToShape1Enabled,
} symASN,
else if (method == "UpToFace") { isOffset1Enabled);
isOffsetEnabled = true; currentAlongSketchNormalEnabled = symASN;
isReversedEnabled = true;
isUpToFaceEnabled = true;
}
else if (method == "TwoLengths") {
isLengthEnabled = true;
isLength2Enabled = true;
isTaperVisible = true;
isTaper2Visible = true;
isReversedEnabled = true;
}
else if (method == "UpToShape") {
isReversedEnabled = true;
isUpToShapeEnabled = true;
} }
Length.setReadOnly(!isLengthEnabled); Length.setReadOnly(!isLength1Enabled);
AlongSketchNormal.setReadOnly(!isLengthEnabled); TaperAngle.setReadOnly(!isTaper1Visible);
UpToFace.setReadOnly(!isUpToFace1Enabled);
UpToShape.setReadOnly(!isUpToShape1Enabled);
Offset.setReadOnly(!isOffset1Enabled);
Type2.setReadOnly(!isType2Enabled);
Length2.setReadOnly(!isLength2Enabled); Length2.setReadOnly(!isLength2Enabled);
Offset.setReadOnly(!isOffsetEnabled);
TaperAngle.setReadOnly(!isTaperVisible);
TaperAngle2.setReadOnly(!isTaper2Visible); TaperAngle2.setReadOnly(!isTaper2Visible);
Midplane.setReadOnly(!isMidplaneEnabled); UpToFace2.setReadOnly(!isUpToFace2Enabled);
Reversed.setReadOnly(!isReversedEnabled); UpToShape2.setReadOnly(!isUpToShape2Enabled);
UpToFace.setReadOnly(!isUpToFaceEnabled); Offset2.setReadOnly(!isOffset2Enabled);
UpToShape.setReadOnly(!isUpToShapeEnabled);
AlongSketchNormal.setReadOnly(!currentAlongSketchNormalEnabled);
} }
void FeatureExtrude::setupObject() void FeatureExtrude::setupObject()
@@ -475,23 +306,30 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
bool makeface = options.testFlag(ExtrudeOption::MakeFace); bool makeface = options.testFlag(ExtrudeOption::MakeFace);
bool fuse = options.testFlag(ExtrudeOption::MakeFuse); bool fuse = options.testFlag(ExtrudeOption::MakeFuse);
bool legacyPocket = options.testFlag(ExtrudeOption::LegacyPocket);
bool inverseDirection = options.testFlag(ExtrudeOption::InverseDirection); bool inverseDirection = options.testFlag(ExtrudeOption::InverseDirection);
std::string Sidemethod(SideType.getValueAsString());
std::string method(Type.getValueAsString()); std::string method(Type.getValueAsString());
std::string method2(Type2.getValueAsString());
// Validate parameters // Validate parameters
double L = Length.getValue(); double L = Length.getValue();
if ((method == "Length") && (L < Precision::Confusion())) { double L2 = (Sidemethod == "Two sides" && method2 == "Length") ? Length2.getValue() : 0;
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Length too small")); if ((Sidemethod == "One side" && method == "Length")
} || (Sidemethod == "Two sides" && method == "Length" && method2 == "Length")) {
double L2 = 0;
if ((method == "TwoLengths")) { if (std::abs(L + L2) < Precision::Confusion()) {
L2 = Length2.getValue(); if (addSubType == Type::Additive) {
if (std::abs(L2) < Precision::Confusion()) { return new App::DocumentObjectExecReturn(
return new App::DocumentObjectExecReturn( QT_TRANSLATE_NOOP("Exception",
QT_TRANSLATE_NOOP("Exception", "Second length too small")); "Cannot create a pad with a total length of zero."));
}
else {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception",
"Cannot create a pocket with a total length of zero."));
}
} }
} }
@@ -598,6 +436,9 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
Direction.setValue(paddingDirection); Direction.setValue(paddingDirection);
dir.Transform(invTrsf); dir.Transform(invTrsf);
if (Reversed.getValue()) {
dir.Reverse();
}
if (sketchshape.isNull()) { if (sketchshape.isNull()) {
return new App::DocumentObjectExecReturn( return new App::DocumentObjectExecReturn(
@@ -605,174 +446,96 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
} }
sketchshape.move(invObjLoc); sketchshape.move(invObjLoc);
TopoShape prism(0, getDocument()->getStringHasher()); std::vector<TopoShape> prisms; // Stores prisms, all in global CS
std::string sideTypeStr = SideType.getValueAsString();
std::string method1 = Type.getValueAsString();
double len1 = method1 == "ThroughAll" ? getThroughAllLength() : Length.getValue();
double taper1 = TaperAngle.getValue();
double offset1 = Offset.getValue();
if (method == "UpToFirst" || method == "UpToLast" || method == "UpToFace" || method == "UpToShape") { if (sideTypeStr == "One side") {
// Note: This will return an unlimited planar face if support is a datum plane TopoShape prism1 = generateSingleExtrusionSide(sketchshape,
TopoShape supportface = getTopoShapeSupportFace(); method1, len1, taper1, UpToFace, UpToShape,
supportface.move(invObjLoc); dir, offset1, makeface, base);
prisms.push_back(prism1);
}
else if (sideTypeStr == "Symmetric") {
TopoShape prism1 = generateSingleExtrusionSide(sketchshape,
method1, len1, taper1, UpToFace, UpToShape,
dir, offset1, makeface, base);
prisms.push_back(prism1);
if (Reversed.getValue()) { // Prism 2 : Make a symmetric of prism1
dir.Reverse(); Base::Vector3d base = sketchshape.getBoundBox().GetCenter();
gp_Ax2 axe(gp_Pnt(base.x, base.y, base.z), dir);
TopoShape prism2 = prism1.makeElementMirror(axe);
prisms.push_back(prism2);
}
else if (sideTypeStr == "Two sides") {
TopoShape prism1 = generateSingleExtrusionSide(sketchshape.makeElementCopy(),
method1,
len1,
taper1,
UpToFace,
UpToShape,
dir,
offset1,
makeface,
base);
if (!prism1.isNull() && !prism1.getShape().IsNull()) {
prisms.push_back(prism1);
} }
TopoShape upToShape; // Side 2
int faceCount = 1; std::string method2 = Type2.getValueAsString();
// Find a valid shape, face or datum plane to extrude up to double len2 = method2 == "ThroughAll" ? getThroughAllLength() : Length2.getValue();
if (method == "UpToFace") { double taper2 = TaperAngle2.getValue();
getUpToFaceFromLinkSub(upToShape, UpToFace); double offset2 = Offset2.getValue();
upToShape.move(invObjLoc); gp_Dir dir2 = dir;
faceCount = 1; dir2.Reverse();
}
else if (method == "UpToShape") {
faceCount = getUpToShapeFromLinkSubList(upToShape, UpToShape);
upToShape.move(invObjLoc);
if (faceCount == 0){
// No shape selected, use the base
upToShape = base;
faceCount = 0;
}
}
if (faceCount == 1) { TopoShape prism2 = generateSingleExtrusionSide(sketchshape.makeElementCopy(),
getUpToFace(upToShape, base, sketchshape, method, dir); method2,
addOffsetToFace(upToShape, dir, Offset.getValue()); len2,
} taper2,
else{ UpToFace2,
if (fabs(Offset.getValue()) > Precision::Confusion()){ UpToShape2,
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Extrude: Can only offset one face")); dir2,
} offset2,
// open the shell by removing the furthest face makeface,
upToShape = makeShellFromUpToShape(upToShape, sketchshape, dir); base);
} if (!prism2.isNull() && !prism2.getShape().IsNull()) {
prisms.push_back(prism2);
if (!supportface.hasSubShape(TopAbs_WIRE)) {
supportface = TopoShape();
}
if (legacyPocket) {
auto mode =
base.isNull() ? TopoShape::PrismMode::None : TopoShape::PrismMode::CutFromBase;
prism = base.makeElementPrismUntil(sketchshape,
supportface,
upToShape,
dir,
mode,
false /*CheckUpToFaceLimits.getValue()*/);
// DO NOT assign id to the generated prism, because this prism is
// actually the final result. We obtain the subtracted shape by cut
// this prism with the original base. Assigning a minus self id here
// will mess up with preselection highlight. It is enough to re-tag
// the profile shape above.
//
// prism.Tag = -this->getID();
// And the really expensive way to get the SubShape...
try {
TopoShape result(0, getDocument()->getStringHasher());
if (base.isNull()) {
result = prism;
}
else {
result.makeElementCut({base, prism});
}
// store shape before refinement
this->rawShape = result;
result = refineShapeIfActive(result);
this->AddSubShape.setValue(result);
}
catch (Standard_Failure&) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Up to face: Could not get SubShape!"));
}
if (getAddSubType() == Additive) {
prism = base.makeElementFuse(this->AddSubShape.getShape());
}
else {
// store shape before refinement
this->rawShape = prism;
prism = refineShapeIfActive(prism);
}
this->Shape.setValue(getSolid(prism));
return App::DocumentObject::StdReturn;
}
try {
TopoShape _base;
if (addSubType!=FeatureAddSub::Subtractive) {
_base=base; // avoid issue #16690
}
prism.makeElementPrismUntil(_base,
sketchshape,
supportface,
upToShape,
dir,
TopoShape::PrismMode::None,
true /*CheckUpToFaceLimits.getValue()*/);
}
catch (Base::Exception&) {
if (method == "UpToShape" && faceCount > 1){
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
"Exception",
"Unable to reach the selected shape, please select faces"));
}
} }
} }
else {
using std::numbers::pi;
Part::ExtrusionParameters params; // --- Combine generated prisms (all in global CS) ---
params.dir = dir; TopoShape prism(0, getDocument()->getStringHasher());
params.solid = makeface; if (prisms.empty()) {
params.taperAngleFwd = Base::toRadians(this->TaperAngle.getValue()); return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "No extrusion geometry was generated."));
params.taperAngleRev = Base::toRadians(this->TaperAngle2.getValue()); }
if (L2 == 0.0 && Midplane.getValue()) { else if (prisms.size() == 1) {
params.lengthFwd = L / 2; prism = prisms[0];
params.lengthRev = L / 2; }
if (params.taperAngleRev == 0.0) { else {
params.taperAngleRev = params.taperAngleFwd; try {
} prism.makeElementXor(prisms, Part::OpCodes::Extrude);
} }
else { catch (const Standard_Failure& e) {
params.lengthFwd = L; return new App::DocumentObjectExecReturn(
params.lengthRev = L2; std::string("Failed to xor extrusion sides (OCC): ") + e.GetMessageString());
} }
if (std::fabs(params.taperAngleFwd) >= Precision::Angular() catch (const Base::Exception& e) {
|| std::fabs(params.taperAngleRev) >= Precision::Angular()) { return new App::DocumentObjectExecReturn(
if (fabs(params.taperAngleFwd) > pi * 0.5 - Precision::Angular() std::string("Failed to xor extrusion sides: ") + e.what());
|| fabs(params.taperAngleRev) > pi * 0.5 - Precision::Angular()) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
"Exception",
"Magnitude of taper angle matches or exceeds 90 degrees"));
}
if (Reversed.getValue()) {
params.dir.Reverse();
}
std::vector<TopoShape> drafts;
Part::ExtrusionHelper::makeElementDraft(params, sketchshape, drafts, getDocument()->getStringHasher());
if (drafts.empty()) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Padding with draft angle failed"));
}
prism.makeElementCompound(
drafts,
nullptr,
TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
}
else {
generatePrism(prism,
sketchshape,
method,
dir,
L,
L2,
Midplane.getValue(),
Reversed.getValue());
} }
} }
if (prism.isNull()) {
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting fused extrusion is null."));
}
// store shape before refinement // store shape before refinement
this->rawShape = prism; this->rawShape = prism;
prism = refineShapeIfActive(prism); prism = refineShapeIfActive(prism);
@@ -841,7 +604,7 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
} }
// eventually disable some settings that are not valid for the current method // eventually disable some settings that are not valid for the current method
updateProperties(method); updateProperties();
return App::DocumentObject::StdReturn; return App::DocumentObject::StdReturn;
} }
@@ -860,3 +623,134 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
return new App::DocumentObjectExecReturn(e.what()); return new App::DocumentObjectExecReturn(e.what());
} }
} }
TopoShape FeatureExtrude::generateSingleExtrusionSide(const TopoShape& sketchshape,
const std::string& method,
double length,
double taperAngleDeg,
App::PropertyLinkSub& upToFacePropHandle,
App::PropertyLinkSubList& upToShapePropHandle,
gp_Dir dir,
double offsetVal,
bool makeFace,
const TopoShape& base)
{
TopoShape prism(0, getDocument()->getStringHasher());
if (method == "UpToFirst" || method == "UpToLast" || method == "UpToFace" || method == "UpToShape") {
// Note: This will return an unlimited planar face if support is a datum plane
TopoShape supportface = getTopoShapeSupportFace();
auto invObjLoc = getLocation().Inverted();
supportface.move(invObjLoc);
if (!supportface.hasSubShape(TopAbs_WIRE)) {
supportface = TopoShape();
}
TopoShape upToShape;
int faceCount = 1;
// Find a valid shape, face or datum plane to extrude up to
if (method == "UpToFace") {
getUpToFaceFromLinkSub(upToShape, upToFacePropHandle);
upToShape.move(invObjLoc);
}
else if (method == "UpToShape") {
faceCount = getUpToShapeFromLinkSubList(upToShape, upToShapePropHandle);
upToShape.move(invObjLoc);
if (faceCount == 0) {
// No shape selected, use the base
upToShape = base;
}
}
if (faceCount == 1) {
getUpToFace(upToShape, base, sketchshape, method, dir);
addOffsetToFace(upToShape, dir, offsetVal);
}
else {
if (fabs(offsetVal) > Precision::Confusion()) {
throw Base::RuntimeError("Extrude: Can only offset one face");
}
// open the shell by removing the furthest face
upToShape = makeShellFromUpToShape(upToShape, sketchshape, dir);
}
try {
TopoShape _base;
if (addSubType != FeatureAddSub::Subtractive) {
_base = base; // avoid issue #16690
}
prism.makeElementPrismUntil(_base,
sketchshape,
supportface,
upToShape,
dir,
TopoShape::PrismMode::None,
true /*CheckUpToFaceLimits.getValue()*/);
}
catch (Base::Exception&) {
if (method == "UpToShape" && faceCount > 1) {
throw Base::RuntimeError("Extrude: Unable to reach the selected shape, please select faces");
}
}
}
else if (method == "Length" || method == "ThroughAll") {
using std::numbers::pi;
Part::ExtrusionParameters params;
params.taperAngleFwd = Base::toRadians(taperAngleDeg);
if (std::fabs(params.taperAngleFwd) >= Precision::Angular() || std::fabs(params.taperAngleRev) >= Precision::Angular()) {
if (fabs(params.taperAngleFwd) > pi * 0.5 - Precision::Angular() || fabs(params.taperAngleRev) > pi * 0.5 - Precision::Angular()) {
return prism;
}
params.dir = dir;
params.solid = makeFace;
params.lengthFwd = length;
std::vector<TopoShape> drafts;
Part::ExtrusionHelper::makeElementDraft(params,
sketchshape,
drafts,
getDocument()->getStringHasher());
if (drafts.empty()) {
return prism;
}
prism.makeElementCompound(drafts,
nullptr,
TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
}
else {
// Without taper angle we create a prism because its shells are in every case no
// B-splines and can therefore be use as support for further features like Pads,
// Lofts etc. B-spline shells can break certain features, see e.g.
// https://forum.freecad.org/viewtopic.php?p=560785#p560785 It is better not to use
// BRepFeat_MakePrism here even if we have a support because the resulting shape
// creates problems with Pocket
try {
prism.makeElementPrism(sketchshape, length * gp_Vec(dir));
}
catch (Standard_Failure&) {
throw Base::RuntimeError("FeatureExtrusion: Length: Could not extrude the sketch!");
}
}
}
return prism;
}
void FeatureExtrude::handleChangedPropertyType(Base::XMLReader& reader,
const char* TypeName,
App::Property* prop)
{
// property Type no longer has TwoLengths.
if (prop == &Type && strcmp(Type.getValueAsString(), "TwoLengths") == 0) {
Type.setValue("Length");
Type2.setValue("Length");
SideType.setValue("Two sides");
}
else {
ProfileBased::handleChangedPropertyType(reader, TypeName, prop);
}
}
+20 -56
View File
@@ -42,7 +42,9 @@ class PartDesignExport FeatureExtrude : public ProfileBased
public: public:
FeatureExtrude(); FeatureExtrude();
App::PropertyEnumeration SideType;
App::PropertyEnumeration Type; App::PropertyEnumeration Type;
App::PropertyEnumeration Type2;
App::PropertyLength Length; App::PropertyLength Length;
App::PropertyLength Length2; App::PropertyLength Length2;
App::PropertyAngle TaperAngle; App::PropertyAngle TaperAngle;
@@ -51,6 +53,7 @@ public:
App::PropertyVector Direction; App::PropertyVector Direction;
App::PropertyBool AlongSketchNormal; App::PropertyBool AlongSketchNormal;
App::PropertyLength Offset; App::PropertyLength Offset;
App::PropertyLength Offset2;
App::PropertyLinkSub ReferenceAxis; App::PropertyLinkSub ReferenceAxis;
static App::PropertyQuantityConstraint::Constraints signedLengthConstraint; static App::PropertyQuantityConstraint::Constraints signedLengthConstraint;
@@ -67,7 +70,10 @@ public:
} }
//@} //@}
static const char* SideTypesEnums[];
protected: protected:
void handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) override;
Base::Vector3d computeDirection(const Base::Vector3d& sketchVector, bool inverse); Base::Vector3d computeDirection(const Base::Vector3d& sketchVector, bool inverse);
bool hasTaperedAngle() const; bool hasTaperedAngle() const;
@@ -92,65 +98,23 @@ protected:
*/ */
TopoShape makeShellFromUpToShape(TopoShape shape, TopoShape sketchshape, gp_Dir dir); TopoShape makeShellFromUpToShape(TopoShape shape, TopoShape sketchshape, gp_Dir dir);
/**
* Generates an extrusion of the input sketchshape and stores it in the given \a prism
*/
void generatePrism(TopoDS_Shape& prism,
const TopoDS_Shape& sketchshape,
const std::string& method,
const gp_Dir& direction,
const double L,
const double L2,
const bool midplane,
const bool reversed);
void generatePrism(TopoShape& prism,
TopoShape sketchshape,
const std::string& method,
const gp_Dir& direction,
const double L,
const double L2,
const bool midplane,
const bool reversed);
// See BRepFeat_MakePrism
enum PrismMode {
CutFromBase = 0,
FuseWithBase = 1,
None = 2
};
/**
* Generates an extrusion of the input profileshape
* It will be a stand-alone solid created with BRepFeat_MakePrism
*/
static void generatePrism(TopoDS_Shape& prism,
const std::string& method,
const TopoDS_Shape& baseshape,
const TopoDS_Shape& profileshape,
const TopoDS_Face& sketchface,
const TopoDS_Shape& uptoface,
const gp_Dir& direction,
PrismMode Mode,
Standard_Boolean Modify);
/**
* Generates a tapered prism of the input sketchshape and stores it in the given \a prism
*/
void generateTaperedPrism(TopoDS_Shape& prism,
const TopoDS_Shape& sketchshape,
const std::string& method,
const gp_Dir& direction,
const double L,
const double L2,
const double angle,
const double angle2,
const bool midplane);
/** /**
* Disables settings that are not valid for the current method * Disables settings that are not valid for the current method
*/ */
void updateProperties(const std::string &method); void updateProperties();
TopoShape generateSingleExtrusionSide(
const TopoShape& sketchShape, // The base sketch for this side (global CS)
const std::string& method,
double length,
double taperAngleDeg,
App::PropertyLinkSub& upToFacePropHandle, // e.g., &UpToFace or &UpToFace2
App::PropertyLinkSubList& upToShapePropHandle, // e.g., &UpToShape or &UpToShape2
gp_Dir dir,
double offsetVal,
bool makeFace,
const TopoShape& base // The base shape for context (global CS)
);
}; };
} //namespace PartDesign } //namespace PartDesign
+18 -9
View File
@@ -38,7 +38,7 @@
using namespace PartDesign; using namespace PartDesign;
const char* Pad::TypeEnums[]= {"Length", "UpToLast", "UpToFirst", "UpToFace", "TwoLengths", "UpToShape", nullptr}; const char* Pad::TypeEnums[]= {"Length", "UpToLast", "UpToFirst", "UpToFace", "UpToShape", nullptr};
PROPERTY_SOURCE(PartDesign::Pad, PartDesign::FeatureExtrude) PROPERTY_SOURCE(PartDesign::Pad, PartDesign::FeatureExtrude)
@@ -46,25 +46,34 @@ Pad::Pad()
{ {
addSubType = FeatureAddSub::Additive; addSubType = FeatureAddSub::Additive;
ADD_PROPERTY_TYPE(Type, (0L), "Pad", App::Prop_None, "Pad type"); ADD_PROPERTY_TYPE(SideType, (0L), "Pad", App::Prop_None, "Type of sides definition");
ADD_PROPERTY_TYPE(Type, (0L), "Side1", App::Prop_None, "Pad type for side 1");
ADD_PROPERTY_TYPE(Type2, (0L), "Side2", App::Prop_None, "Pad type for side 2");
SideType.setEnums(SideTypesEnums);
Type.setEnums(TypeEnums); Type.setEnums(TypeEnums);
ADD_PROPERTY_TYPE(Length, (10.0), "Pad", App::Prop_None, "Pad length"); Type2.setEnums(TypeEnums);
ADD_PROPERTY_TYPE(Length2, (10.0), "Pad", App::Prop_None, "Pad length in 2nd direction"); ADD_PROPERTY_TYPE(Length, (10.0), "Side1", App::Prop_None, "Pad length");
ADD_PROPERTY_TYPE(Length2, (10.0), "Side2", App::Prop_None, "Pad length in 2nd direction");
ADD_PROPERTY_TYPE(UseCustomVector, (false), "Pad", App::Prop_None, "Use custom vector for pad direction"); ADD_PROPERTY_TYPE(UseCustomVector, (false), "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(Direction, (Base::Vector3d(1.0, 1.0, 1.0)), "Pad", App::Prop_None, "Pad direction vector");
ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Pad", App::Prop_None, "Reference axis of direction"); ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Pad", App::Prop_None, "Reference axis of direction");
ADD_PROPERTY_TYPE(AlongSketchNormal, (true), "Pad", App::Prop_None, "Measure pad length along the sketch normal direction"); ADD_PROPERTY_TYPE(AlongSketchNormal, (true), "Pad", App::Prop_None, "Measure pad length along the sketch normal direction");
ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Pad", App::Prop_None, "Face where pad will end"); ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Side1", App::Prop_None, "Face where pad will end");
ADD_PROPERTY_TYPE(UpToShape, (nullptr), "Pad", App::Prop_None, "Faces or shape(s) where pad will end"); ADD_PROPERTY_TYPE(UpToShape, (nullptr), "Side1", App::Prop_None, "Faces or shape(s) where pad will end");
ADD_PROPERTY_TYPE(Offset, (0.0), "Pad", App::Prop_None, "Offset from face in which pad will end"); ADD_PROPERTY_TYPE(UpToFace2, (nullptr), "Side2", App::Prop_None, "Face where pad will end on side2");
ADD_PROPERTY_TYPE(UpToShape2, (nullptr), "Side2", App::Prop_None, "Faces or shape(s) where pad will end on side2");
ADD_PROPERTY_TYPE(Offset, (0.0), "Side1", App::Prop_None, "Offset from face in which pad will end");
ADD_PROPERTY_TYPE(Offset2, (0.0), "Side2", App::Prop_None, "Offset from face in which pad will end on side 2");
Offset.setConstraints(&signedLengthConstraint); Offset.setConstraints(&signedLengthConstraint);
ADD_PROPERTY_TYPE(TaperAngle, (0.0), "Pad", App::Prop_None, "Taper angle"); Offset2.setConstraints(&signedLengthConstraint);
ADD_PROPERTY_TYPE(TaperAngle, (0.0), "Side1", App::Prop_None, "Taper angle");
TaperAngle.setConstraints(&floatAngle); TaperAngle.setConstraints(&floatAngle);
ADD_PROPERTY_TYPE(TaperAngle2, (0.0), "Pad", App::Prop_None, "Taper angle for 2nd direction"); ADD_PROPERTY_TYPE(TaperAngle2, (0.0), "Side2", App::Prop_None, "Taper angle for 2nd direction");
TaperAngle2.setConstraints(&floatAngle); TaperAngle2.setConstraints(&floatAngle);
// Remove the constraints and keep the type to allow one to accept negative values // Remove the constraints and keep the type to allow one to accept negative values
// https://forum.freecad.org/viewtopic.php?f=3&t=52075&p=448410#p447636 // https://forum.freecad.org/viewtopic.php?f=3&t=52075&p=448410#p447636
Length.setConstraints(nullptr);
Length2.setConstraints(nullptr); Length2.setConstraints(nullptr);
} }
-2
View File
@@ -45,8 +45,6 @@ public:
* that is cut by a line through the centre of gravite of the sketch * that is cut by a line through the centre of gravite of the sketch
* If Type is "UpToFirst" then extrusion will stop at the first face of the support * If Type is "UpToFirst" then extrusion will stop at the first face of the support
* If Type is "UpToFace" then the extrusion will stop at FaceName in the support * If Type is "UpToFace" then the extrusion will stop at FaceName in the support
* If Type is "TwoLengths" then the extrusion will extend Length in the direction away from the support
* and Length2 in the opposite direction
* If Midplane is true, then the extrusion will extend for half of the length on both sides of the sketch plane * If Midplane is true, then the extrusion will extend for half of the length on both sides of the sketch plane
* If Reversed is true then the direction of revolution will be reversed. * If Reversed is true then the direction of revolution will be reversed.
* The created material will be fused with the sketch support (if there is one) * The created material will be fused with the sketch support (if there is one)
+18 -9
View File
@@ -39,7 +39,7 @@ using namespace PartDesign;
/* TRANSLATOR PartDesign::Pocket */ /* TRANSLATOR PartDesign::Pocket */
const char* Pocket::TypeEnums[]= {"Length", "ThroughAll", "UpToFirst", "UpToFace", "TwoLengths", "UpToShape", nullptr}; const char* Pocket::TypeEnums[]= {"Length", "ThroughAll", "UpToFirst", "UpToFace", "UpToShape", nullptr};
PROPERTY_SOURCE(PartDesign::Pocket, PartDesign::FeatureExtrude) PROPERTY_SOURCE(PartDesign::Pocket, PartDesign::FeatureExtrude)
@@ -47,25 +47,34 @@ Pocket::Pocket()
{ {
addSubType = FeatureAddSub::Subtractive; addSubType = FeatureAddSub::Subtractive;
ADD_PROPERTY_TYPE(Type, ((long)0), "Pocket", App::Prop_None, "Pocket type"); ADD_PROPERTY_TYPE(SideType, (0L), "Pocket", App::Prop_None, "Type of sides definition");
ADD_PROPERTY_TYPE(Type, ((long)0), "Side1", App::Prop_None, "Pocket type");
ADD_PROPERTY_TYPE(Type2, ((long)0), "Side2", App::Prop_None, "Pocket type");
SideType.setEnums(SideTypesEnums);
Type.setEnums(TypeEnums); Type.setEnums(TypeEnums);
ADD_PROPERTY_TYPE(Length, (5.0), "Pocket", App::Prop_None, "Pocket length"); Type2.setEnums(TypeEnums);
ADD_PROPERTY_TYPE(Length2, (5.0), "Pocket", App::Prop_None, "Pocket length in 2nd direction"); ADD_PROPERTY_TYPE(Length, (5.0), "Side1", App::Prop_None, "Pocket length");
ADD_PROPERTY_TYPE(Length2, (5.0), "Side2", App::Prop_None, "Pocket length in 2nd direction");
ADD_PROPERTY_TYPE(UseCustomVector, (false), "Pocket", App::Prop_None, "Use custom vector for pocket direction"); ADD_PROPERTY_TYPE(UseCustomVector, (false), "Pocket", App::Prop_None, "Use custom vector for pocket direction");
ADD_PROPERTY_TYPE(Direction, (Base::Vector3d(1.0, 1.0, 1.0)), "Pocket", App::Prop_None, "Pocket direction vector"); ADD_PROPERTY_TYPE(Direction, (Base::Vector3d(1.0, 1.0, 1.0)), "Pocket", App::Prop_None, "Pocket direction vector");
ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Pocket", App::Prop_None, "Reference axis of direction"); ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Pocket", App::Prop_None, "Reference axis of direction");
ADD_PROPERTY_TYPE(AlongSketchNormal, (true), "Pocket", App::Prop_None, "Measure pocket length along the sketch normal direction"); ADD_PROPERTY_TYPE(AlongSketchNormal, (true), "Pocket", App::Prop_None, "Measure pocket length along the sketch normal direction");
ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Pocket", App::Prop_None, "Face where pocket will end"); ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Side1", App::Prop_None, "Face where pocket will end");
ADD_PROPERTY_TYPE(UpToShape, (nullptr), "Pocket", App::Prop_None, "Face(s) or shape(s) where pocket will end"); ADD_PROPERTY_TYPE(UpToShape, (nullptr), "Side1", App::Prop_None, "Face(s) or shape(s) where pocket will end");
ADD_PROPERTY_TYPE(Offset, (0.0), "Pocket", App::Prop_None, "Offset from face in which pocket will end"); ADD_PROPERTY_TYPE(UpToFace2, (nullptr), "Side2", App::Prop_None, "Face where pocket will end");
ADD_PROPERTY_TYPE(UpToShape2, (nullptr), "Side2", App::Prop_None, "Face(s) or shape(s) where pocket will end");
ADD_PROPERTY_TYPE(Offset, (0.0), "Side1", App::Prop_None, "Offset from face in which pocket will end");
ADD_PROPERTY_TYPE(Offset2, (0.0), "Side2", App::Prop_None, "Offset from face in which pocket will end on side 2");
Offset.setConstraints(&signedLengthConstraint); Offset.setConstraints(&signedLengthConstraint);
ADD_PROPERTY_TYPE(TaperAngle, (0.0), "Pocket", App::Prop_None, "Taper angle"); Offset2.setConstraints(&signedLengthConstraint);
ADD_PROPERTY_TYPE(TaperAngle, (0.0), "Side1", App::Prop_None, "Taper angle");
TaperAngle.setConstraints(&floatAngle); TaperAngle.setConstraints(&floatAngle);
ADD_PROPERTY_TYPE(TaperAngle2, (0.0), "Pocket", App::Prop_None, "Taper angle for 2nd direction"); ADD_PROPERTY_TYPE(TaperAngle2, (0.0), "Side2", App::Prop_None, "Taper angle for 2nd direction");
TaperAngle2.setConstraints(&floatAngle); TaperAngle2.setConstraints(&floatAngle);
// Remove the constraints and keep the type to allow one to accept negative values // Remove the constraints and keep the type to allow one to accept negative values
// https://forum.freecad.org/viewtopic.php?f=3&t=52075&p=448410#p447636 // https://forum.freecad.org/viewtopic.php?f=3&t=52075&p=448410#p447636
Length.setConstraints(nullptr);
Length2.setConstraints(nullptr); Length2.setConstraints(nullptr);
} }
@@ -74,8 +74,10 @@ ProfileBased::ProfileBased()
ADD_PROPERTY_TYPE(Profile, (nullptr), "SketchBased", App::Prop_None, "Reference to sketch"); ADD_PROPERTY_TYPE(Profile, (nullptr), "SketchBased", App::Prop_None, "Reference to sketch");
ADD_PROPERTY_TYPE(Midplane, (0), "SketchBased", App::Prop_None, "Extrude symmetric to sketch face"); ADD_PROPERTY_TYPE(Midplane, (0), "SketchBased", App::Prop_None, "Extrude symmetric to sketch face");
ADD_PROPERTY_TYPE(Reversed, (0), "SketchBased", App::Prop_None, "Reverse extrusion direction"); ADD_PROPERTY_TYPE(Reversed, (0), "SketchBased", App::Prop_None, "Reverse extrusion direction");
ADD_PROPERTY_TYPE(UpToFace, (nullptr), "SketchBased", (App::PropertyType)(App::Prop_None), "Face where feature will end"); ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Side1", (App::PropertyType)(App::Prop_None), "Face where feature will end");
ADD_PROPERTY_TYPE(UpToShape, (nullptr), "SketchBased", (App::PropertyType)(App::Prop_None), "Shape where feature will end"); ADD_PROPERTY_TYPE(UpToShape, (nullptr), "Side1", (App::PropertyType)(App::Prop_None), "Shape where feature will end");
ADD_PROPERTY_TYPE(UpToFace2, (nullptr), "Side2", (App::PropertyType)(App::Prop_None), "Face where feature will end");
ADD_PROPERTY_TYPE(UpToShape2, (nullptr), "Side2", (App::PropertyType)(App::Prop_None), "Shape where feature will end");
ADD_PROPERTY_TYPE(AllowMultiFace, (false), "SketchBased", App::Prop_None, "Allow multiple faces in profile"); ADD_PROPERTY_TYPE(AllowMultiFace, (false), "SketchBased", App::Prop_None, "Allow multiple faces in profile");
} }
@@ -84,7 +86,10 @@ short ProfileBased::mustExecute() const
if (Profile.isTouched() || if (Profile.isTouched() ||
Midplane.isTouched() || Midplane.isTouched() ||
Reversed.isTouched() || Reversed.isTouched() ||
UpToFace.isTouched()) UpToFace.isTouched() ||
UpToFace2.isTouched() ||
UpToShape.isTouched() ||
UpToShape2.isTouched())
return 1; return 1;
return PartDesign::FeatureAddSub::mustExecute(); return PartDesign::FeatureAddSub::mustExecute();
} }
@@ -57,8 +57,10 @@ public:
App::PropertyBool Midplane; App::PropertyBool Midplane;
/// Face to extrude up to /// Face to extrude up to
App::PropertyLinkSub UpToFace; App::PropertyLinkSub UpToFace;
App::PropertyLinkSub UpToFace2;
/// Shape to extrude up to /// Shape to extrude up to
App::PropertyLinkSubList UpToShape; App::PropertyLinkSubList UpToShape;
App::PropertyLinkSubList UpToShape2;
App::PropertyBool AllowMultiFace; App::PropertyBool AllowMultiFace;
File diff suppressed because it is too large Load Diff
+110 -31
View File
@@ -26,11 +26,20 @@
#include "TaskSketchBasedParameters.h" #include "TaskSketchBasedParameters.h"
#include "ViewProviderExtrude.h" #include "ViewProviderExtrude.h"
class QCheckBox;
class QComboBox;
class QLineEdit;
class QListWidget;
class QToolButton;
class Ui_TaskPadPocketParameters; class Ui_TaskPadPocketParameters;
namespace App { namespace App {
class Property; class Property;
class PropertyLinkSubList;
}
namespace Gui {
class PrefQuantitySpinBox;
} }
namespace PartDesign { namespace PartDesign {
@@ -57,13 +66,23 @@ public:
Pocket Pocket
}; };
enum class SidesMode {
OneSide,
TwoSides,
Symmetric,
};
enum class Side {
First,
Second,
};
enum class Mode { enum class Mode {
Dimension, Dimension,
ThroughAll, ThroughAll,
ToLast = ThroughAll, ToLast = ThroughAll,
ToFirst, ToFirst,
ToFace, ToFace,
TwoDimensions,
ToShape, ToShape,
}; };
@@ -84,35 +103,84 @@ public:
void fillDirectionCombo(); void fillDirectionCombo();
void addAxisToCombo(App::DocumentObject* linkObj, std::string linkSubname, QString itemText, void addAxisToCombo(App::DocumentObject* linkObj, std::string linkSubname, QString itemText,
bool hasSketch = true); bool hasSketch = true);
void applyParameters(QString facename); void applyParameters();
void setSelectionMode(SelectionMode mode); void setSelectionMode(SelectionMode mode, Side side = Side::First);
protected:
// This struct holds all pointers for one side's UI and properties
struct SideController
{
// UI Widgets
QComboBox* changeMode = nullptr;
QLabel* labelLength = nullptr;
QLabel* labelOffset = nullptr;
QLabel* labelTaperAngle = nullptr;
Gui::PrefQuantitySpinBox* lengthEdit = nullptr;
Gui::PrefQuantitySpinBox* offsetEdit = nullptr;
Gui::PrefQuantitySpinBox* taperEdit = nullptr;
QLineEdit* lineFaceName = nullptr;
QToolButton* buttonFace = nullptr;
QLineEdit* lineShapeName = nullptr;
QToolButton* buttonShape = nullptr;
QListWidget* listWidgetReferences = nullptr;
QToolButton* buttonShapeFace = nullptr;
QCheckBox* checkBoxAllFaces = nullptr;
QWidget* upToShapeList = nullptr;
QWidget* upToShapeFaces = nullptr;
QAction* unselectShapeFaceAction = nullptr;
// Feature Properties
App::PropertyEnumeration* Type = nullptr;
App::PropertyLength* Length = nullptr;
App::PropertyLength* Offset = nullptr;
App::PropertyAngle* TaperAngle = nullptr;
App::PropertyLinkSub* UpToFace = nullptr;
App::PropertyLinkSubList* UpToShape = nullptr;
};
SideController m_side1;
SideController m_side2;
SideController& getSideController(Side side)
{
return (side == Side::First) ? m_side1 : m_side2;
}
protected Q_SLOTS: protected Q_SLOTS:
void onLengthChanged(double); void onSidesModeChanged(int);
void onLength2Changed(double); virtual void onModeChanged(int index, Side side) = 0;
void onOffsetChanged(double);
void onTaperChanged(double); private Q_SLOTS:
void onTaper2Changed(double);
void onDirectionCBChanged(int); void onDirectionCBChanged(int);
void onAlongSketchNormalChanged(bool); void onAlongSketchNormalChanged(bool);
void onDirectionToggled(bool); void onDirectionToggled(bool);
void onAllFacesToggled(bool);
void onXDirectionEditChanged(double); void onXDirectionEditChanged(double);
void onYDirectionEditChanged(double); void onYDirectionEditChanged(double);
void onZDirectionEditChanged(double); void onZDirectionEditChanged(double);
void onMidplaneChanged(bool);
void onReversedChanged(bool); void onReversedChanged(bool);
void onFaceName(const QString& text);
void onSelectFaceToggle(const bool checked = true);
void onSelectShapeToggle(const bool checked = true);
void onSelectShapeFacesToggle(const bool checked);
void onUnselectShapeFacesTrigger();
virtual void onModeChanged(int); private:
void onModeChanged_Side1(int index);
void onModeChanged_Side2(int index);
void onLengthChanged(double len, Side side);
void onOffsetChanged(double len, Side side);
void onTaperChanged(double angle, Side side);
void onSelectFaceToggle(bool checked, Side side);
void onFaceName(const QString& text, Side side);
void onAllFacesToggled(bool checked, Side side);
void onSelectShapeToggle(bool checked, Side side);
void onSelectShapeFacesToggle(bool checked, Side side);
void onUnselectShapeFacesTrigger(Side side);
protected: protected:
void setCheckboxes(Mode mode, Type type); void updateWholeUI(Type type, Side side);
void updateSideUI(const SideController& s,
Type featureType,
Mode sideMode,
bool isParentVisible,
bool setFocus);
void setupDialog(); void setupDialog();
void readValuesFromHistory(); void readValuesFromHistory();
void changeEvent(QEvent *e) override; void changeEvent(QEvent *e) override;
@@ -120,6 +188,7 @@ protected:
void getReferenceAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const; void getReferenceAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const;
double getOffset() const; double getOffset() const;
double getOffset2() const;
bool getAlongSketchNormal() const; bool getAlongSketchNormal() const;
bool getCustom() const; bool getCustom() const;
std::string getReferenceAxis() const; std::string getReferenceAxis() const;
@@ -127,42 +196,52 @@ protected:
double getYDirection() const; double getYDirection() const;
double getZDirection() const; double getZDirection() const;
bool getReversed() const; bool getReversed() const;
bool getMidplane() const;
int getMode() const; int getMode() const;
QString getFaceName() const; int getMode2() const;
int getSidesMode() const;
QString getFaceName(QLineEdit*) const;
void onSelectionChanged(const Gui::SelectionChanges& msg) override; void onSelectionChanged(const Gui::SelectionChanges& msg) override;
virtual void translateModeList(int index); void translateSidesList(int index);
virtual void updateUI(int index); virtual void translateModeList(QComboBox* box, int index);
virtual void updateUI(Side side);
void updateDirectionEdits(); void updateDirectionEdits();
void setDirectionMode(int index); void setDirectionMode(int index);
void handleLineFaceNameClick(); void handleLineFaceNameClick(QLineEdit*);
void handleLineFaceNameNo(); void handleLineFaceNameNo(QLineEdit*);
private: private:
void setupSideDialog(SideController& side);
void selectedReferenceAxis(const Gui::SelectionChanges& msg); void selectedReferenceAxis(const Gui::SelectionChanges& msg);
void selectedFace(const Gui::SelectionChanges& msg); void selectedFace(const Gui::SelectionChanges& msg, SideController& side);
void selectedShape(const Gui::SelectionChanges& msg); void selectedShape(const Gui::SelectionChanges& msg, SideController& side);
void selectedShapeFace(const Gui::SelectionChanges& msg); void selectedShapeFace(const Gui::SelectionChanges& msg, SideController& side);
void tryRecomputeFeature(); void tryRecomputeFeature();
void translateFaceName(); void translateFaceName(QLineEdit*);
void connectSlots(); void connectSlots();
bool hasProfileFace(PartDesign::ProfileBased*) const; bool hasProfileFace(PartDesign::ProfileBased*) const;
void clearFaceName(); void clearFaceName(QLineEdit*);
void updateShapeName(); void updateShapeName(QLineEdit*, App::PropertyLinkSubList&);
void updateShapeFaces(); void updateShapeFaces(QListWidget* list, App::PropertyLinkSubList& prop);
std::vector<std::string> getShapeFaces(); std::vector<std::string> getShapeFaces(App::PropertyLinkSubList& prop);
void changeFaceName(QLineEdit* lineEdit, const QString& text);
void createSideControllers();
protected: protected:
QWidget* proxy; QWidget* proxy;
QAction* unselectShapeFaceAction; QAction* unselectShapeFaceAction;
QAction* unselectShapeFaceAction2;
std::unique_ptr<Ui_TaskPadPocketParameters> ui; std::unique_ptr<Ui_TaskPadPocketParameters> ui;
std::vector<std::unique_ptr<App::PropertyLinkSub>> axesInList; std::vector<std::unique_ptr<App::PropertyLinkSub>> axesInList;
SelectionMode selectionMode = None; SelectionMode selectionMode = None;
Side activeSelectionSide = Side::First;
}; };
class TaskDlgExtrudeParameters : public TaskDlgSketchBasedParameters class TaskDlgExtrudeParameters : public TaskDlgSketchBasedParameters
+49 -47
View File
@@ -39,7 +39,8 @@ using namespace Gui;
TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent, bool newObj) TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent, bool newObj)
: TaskExtrudeParameters(PadView, parent, "PartDesign_Pad", tr("Pad Parameters")) : TaskExtrudeParameters(PadView, parent, "PartDesign_Pad", tr("Pad Parameters"))
{ {
ui->offsetEdit->setToolTip(tr("Offsets the pad from the face at which the pad will end")); ui->offsetEdit->setToolTip(tr("Offset the pad from the face at which the pad will end on side 1"));
ui->offsetEdit2->setToolTip(tr("Offset the pad from the face at which the pad will end on side 2"));
ui->checkBoxReversed->setToolTip(tr("Reverses pad direction")); ui->checkBoxReversed->setToolTip(tr("Reverses pad direction"));
// set the history path // set the history path
@@ -49,6 +50,8 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadLength2")); ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadLength2"));
ui->offsetEdit->setEntryName(QByteArray("Offset")); ui->offsetEdit->setEntryName(QByteArray("Offset"));
ui->offsetEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadOffset")); ui->offsetEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadOffset"));
ui->offsetEdit2->setEntryName(QByteArray("Offset2"));
ui->offsetEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadOffset2"));
ui->taperEdit->setEntryName(QByteArray("TaperAngle")); ui->taperEdit->setEntryName(QByteArray("TaperAngle"));
ui->taperEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadTaperAngle")); ui->taperEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadTaperAngle"));
ui->taperEdit2->setEntryName(QByteArray("TaperAngle2")); ui->taperEdit2->setEntryName(QByteArray("TaperAngle2"));
@@ -64,71 +67,70 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
TaskPadParameters::~TaskPadParameters() = default; TaskPadParameters::~TaskPadParameters() = default;
void TaskPadParameters::translateModeList(int index) void TaskPadParameters::translateModeList(QComboBox* box, int index)
{ {
ui->changeMode->clear(); box->clear();
ui->changeMode->addItem(tr("Dimension")); box->addItem(tr("Dimension"));
ui->changeMode->addItem(tr("To last")); box->addItem(tr("To last"));
ui->changeMode->addItem(tr("To first")); box->addItem(tr("To first"));
ui->changeMode->addItem(tr("Up to face")); box->addItem(tr("Up to face"));
ui->changeMode->addItem(tr("Two dimensions")); box->addItem(tr("Up to shape"));
ui->changeMode->addItem(tr("Up to shape")); box->setCurrentIndex(index);
ui->changeMode->setCurrentIndex(index);
} }
void TaskPadParameters::updateUI(int index) void TaskPadParameters::updateUI(Side side)
{ {
// update direction combobox // update direction combobox
fillDirectionCombo(); fillDirectionCombo();
// set and enable checkboxes // set and enable checkboxes
setCheckboxes(static_cast<Mode>(index), Type::Pad); updateWholeUI(Type::Pad, side);
} }
void TaskPadParameters::onModeChanged(int index) void TaskPadParameters::onModeChanged(int index, Side side)
{ {
auto pcPad = getObject<PartDesign::Pad>(); auto& sideCtrl = getSideController(side);
switch (static_cast<Mode>(index)) { switch (static_cast<Mode>(index)) {
case Mode::Dimension: case Mode::Dimension:
pcPad->Type.setValue("Length"); sideCtrl.Type->setValue("Length");
// Avoid error message if (side == Side::First) {
if (ui->lengthEdit->value() < Base::Quantity(Precision::Confusion(), Base::Unit::Length)) // Avoid error message
ui->lengthEdit->setValue(5.0); double L = sideCtrl.lengthEdit->value().getValue();
break; Side otherSide = side == Side::First ? Side::Second : Side::First;
case Mode::ToLast: auto& sideCtrl2 = getSideController(otherSide);
pcPad->Type.setValue("UpToLast"); double L2 = static_cast<SidesMode>(getSidesMode()) == SidesMode::TwoSides
break; ? sideCtrl2.lengthEdit->value().getValue()
case Mode::ToFirst: : 0;
pcPad->Type.setValue("UpToFirst"); if (std::abs(L + L2) < Precision::Confusion()) {
break; sideCtrl.lengthEdit->setValue(5.0);
case Mode::ToFace: }
// Note: ui->checkBoxReversed is purposely enabled because the selected face }
// could be a circular one around the sketch break;
pcPad->Type.setValue("UpToFace"); case Mode::ToLast:
if (ui->lineFaceName->text().isEmpty()) { sideCtrl.Type->setValue("UpToLast");
ui->buttonFace->setChecked(true); break;
handleLineFaceNameClick(); // sets placeholder text case Mode::ToFirst:
} sideCtrl.Type->setValue("UpToFirst");
break; break;
case Mode::TwoDimensions: case Mode::ToFace:
pcPad->Type.setValue("TwoLengths"); sideCtrl.Type->setValue("UpToFace");
break; if (sideCtrl.lineFaceName->text().isEmpty()) {
case Mode::ToShape: sideCtrl.buttonFace->setChecked(true);
pcPad->Type.setValue("UpToShape"); handleLineFaceNameClick(sideCtrl.lineFaceName); // sets placeholder text
break; }
break;
case Mode::ToShape:
sideCtrl.Type->setValue("UpToShape");
break;
} }
updateUI(index); updateUI(side);
recomputeFeature(); recomputeFeature();
} }
void TaskPadParameters::apply() void TaskPadParameters::apply()
{ {
QString facename = QStringLiteral("None"); applyParameters();
if (static_cast<Mode>(getMode()) == Mode::ToFace) {
facename = getFaceName();
}
applyParameters(facename);
} }
//************************************************************************** //**************************************************************************
+4 -3
View File
@@ -26,6 +26,7 @@
#include "TaskExtrudeParameters.h" #include "TaskExtrudeParameters.h"
#include "ViewProviderPad.h" #include "ViewProviderPad.h"
class QComboBox;
namespace App { namespace App {
class Property; class Property;
@@ -49,9 +50,9 @@ public:
void apply() override; void apply() override;
private: private:
void onModeChanged(int index) override; void onModeChanged(int index, Side side) override;
void translateModeList(int index) override; void translateModeList(QComboBox* box, int index) override;
void updateUI(int index) override; void updateUI(Side side) override;
}; };
/// simulation dialog for the TaskView /// simulation dialog for the TaskView
+414 -219
View File
@@ -17,13 +17,59 @@
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="sidesLabel">
<property name="text">
<string>Mode</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="sidesMode"/>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="verticalLayout_22">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="side1Label">
<property name="text">
<string>Side 1</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="textLabel1"> <widget class="QLabel" name="textLabel1">
<property name="text"> <property name="text">
<string>Type</string> <string>Type</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="2" column="1">
<widget class="QComboBox" name="changeMode"> <widget class="QComboBox" name="changeMode">
<item> <item>
<property name="text"> <property name="text">
@@ -32,14 +78,14 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="3" column="0">
<widget class="QLabel" name="labelLength"> <widget class="QLabel" name="labelLength">
<property name="text"> <property name="text">
<string>Length</string> <string>Length</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="3" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="lengthEdit" native="true"> <widget class="Gui::PrefQuantitySpinBox" name="lengthEdit" native="true">
<property name="keyboardTracking" stdset="0"> <property name="keyboardTracking" stdset="0">
<bool>false</bool> <bool>false</bool>
@@ -47,36 +93,16 @@
<property name="unit" stdset="0"> <property name="unit" stdset="0">
<string notr="true">mm</string> <string notr="true">mm</string>
</property> </property>
<property name="minimum" stdset="0">
<double>0.000000000000000</double>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="4" column="0">
<widget class="QLabel" name="labelLength2">
<property name="text">
<string>2nd length</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="lengthEdit2" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelOffset"> <widget class="QLabel" name="labelOffset">
<property name="text"> <property name="text">
<string>Offset to face</string> <string>Offset to face</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="offsetEdit" native="true"> <widget class="Gui::PrefQuantitySpinBox" name="offsetEdit" native="true">
<property name="keyboardTracking" stdset="0"> <property name="keyboardTracking" stdset="0">
<bool>false</bool> <bool>false</bool>
@@ -86,147 +112,390 @@
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="5" column="0">
</item> <widget class="QLabel" name="labelTaperAngle">
<item> <property name="toolTip">
<widget class="QFrame" name="upToShapeList"> <string>Angle to taper the extrusion</string>
<property name="frameShadow"> </property>
<enum>QFrame::Plain</enum> <property name="text">
</property> <string>Taper angle</string>
<property name="lineWidth"> </property>
<number>0</number> </widget>
</property> </item>
<layout class="QVBoxLayout"> <item row="5" column="1">
<property name="leftMargin"> <widget class="Gui::PrefQuantitySpinBox" name="taperEdit" native="true">
<number>0</number> <property name="keyboardTracking" stdset="0">
</property> <bool>false</bool>
<property name="topMargin"> </property>
<number>0</number> <property name="unit" stdset="0">
</property> <string notr="true">deg</string>
<property name="rightMargin"> </property>
<number>0</number> </widget>
</property> </item>
<property name="bottomMargin"> <item row="6" column="0" colspan="2">
<number>0</number> <widget class="QFrame" name="upToShapeList">
</property> <property name="frameShadow">
<item> <enum>QFrame::Plain</enum>
<layout class="QGridLayout" name="_2"> </property>
<item row="0" column="1"> <property name="lineWidth">
<widget class="QLineEdit" name="lineShapeName"> <number>0</number>
<property name="readOnly"> </property>
<layout class="QVBoxLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="_2">
<item row="0" column="1">
<widget class="QLineEdit" name="lineShapeName">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QToolButton" name="buttonShape">
<property name="minimumSize">
<size>
<width>0</width>
<height>22</height>
</size>
</property>
<property name="text">
<string>Select Shape</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxAllFaces">
<property name="enabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="toolTip">
<string>Selects all faces of the shape</string>
</property>
<property name="text">
<string>Select all faces</string>
</property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item>
<widget class="QToolButton" name="buttonShape"> <widget class="QWidget" name="upToShapeFaces" native="true">
<property name="minimumSize"> <layout class="QVBoxLayout" name="verticalLayout_2">
<size> <property name="leftMargin">
<width>0</width> <number>0</number>
<height>22</height> </property>
</size> <property name="topMargin">
</property> <number>0</number>
<property name="text"> </property>
<string>Select shape</string> <property name="rightMargin">
</property> <number>0</number>
<property name="checkable"> </property>
<bool>true</bool> <property name="bottomMargin">
</property> <number>0</number>
</property>
<item>
<widget class="QToolButton" name="buttonShapeFace">
<property name="toolTip">
<string>Toggles between selection and preview mode</string>
</property>
<property name="text">
<string>Select</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidgetReferences">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </widget>
<item> </item>
<widget class="QCheckBox" name="checkBoxAllFaces"> <item row="7" column="0" colspan="2">
<property name="enabled"> <layout class="QGridLayout" name="gridLayout_5">
<bool>true</bool> <item row="0" column="1">
</property> <widget class="QLineEdit" name="lineFaceName">
<property name="toolTip"> <property name="readOnly">
<string>Applies length symmetrically to sketch plane</string> <bool>true</bool>
</property>
<property name="text">
<string>Select all faces</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="upToShapeFaces" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property> </property>
<property name="topMargin"> </widget>
<number>0</number> </item>
<item row="0" column="0">
<widget class="QToolButton" name="buttonFace">
<property name="minimumSize">
<size>
<width>0</width>
<height>22</height>
</size>
</property> </property>
<property name="rightMargin"> <property name="text">
<number>0</number> <string>Select Face</string>
</property> </property>
<property name="bottomMargin"> <property name="checkable">
<number>0</number> <bool>true</bool>
</property> </property>
<item> </widget>
<widget class="QToolButton" name="buttonShapeFace"> </item>
<property name="toolTip"> </layout>
<string>Toggles between selection and preview mode</string> </item>
</property> <item row="8" column="0" colspan="2">
<property name="text"> <layout class="QHBoxLayout" name="verticalLayout_22">
<string>Select</string> <property name="leftMargin">
</property> <number>0</number>
<property name="checkable"> </property>
<bool>true</bool> <property name="topMargin">
</property> <number>0</number>
</widget> </property>
</item> <property name="rightMargin">
<item> <number>0</number>
<widget class="QListWidget" name="listWidgetReferences"> </property>
<property name="selectionMode"> <property name="bottomMargin">
<enum>QAbstractItemView::ExtendedSelection</enum> <number>0</number>
</property> </property>
</widget> <item>
</item> <widget class="QLabel" name="side2Label">
</layout> <property name="text">
</widget> <string>Side 2</string>
</item> </property>
</layout> </widget>
</widget> </item>
</item> <item>
<item> <widget class="Line" name="line2">
<layout class="QGridLayout" name="gridLayout_5"> <property name="orientation">
<item row="0" column="1"> <enum>Qt::Horizontal</enum>
<widget class="QLineEdit" name="lineFaceName"> </property>
<property name="readOnly"> <property name="sizePolicy">
<bool>true</bool> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QLabel" name="typeLabel2">
<property name="text">
<string>Type</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="9" column="1">
<widget class="QToolButton" name="buttonFace"> <widget class="QComboBox" name="changeMode2"/>
<property name="minimumSize"> </item>
<size> <item row="10" column="0">
<width>0</width> <widget class="QLabel" name="labelLength2">
<height>22</height>
</size>
</property>
<property name="text"> <property name="text">
<string>Select face</string> <string>Length</string>
</property>
<property name="checkable">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="lengthEdit2" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="labelOffset2">
<property name="text">
<string>Offset to face</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="offsetEdit2" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="labelTaperAngle2">
<property name="toolTip">
<string>Angle to taper the extrusion</string>
</property>
<property name="text">
<string>Taper angle</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="taperEdit2" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">deg</string>
</property>
</widget>
</item>
<item row="13" column="0" colspan="2">
<widget class="QFrame" name="upToShapeList2">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="_22">
<item row="0" column="1">
<widget class="QLineEdit" name="lineShapeName2">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QToolButton" name="buttonShape2">
<property name="minimumSize">
<size>
<width>0</width>
<height>22</height>
</size>
</property>
<property name="text">
<string>Select Shape</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxAllFaces2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Selects all faces of the shape</string>
</property>
<property name="text">
<string>Select all faces</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="upToShapeFaces2" native="true">
<layout class="QVBoxLayout" name="verticalLayout_22">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="buttonShapeFace2">
<property name="toolTip">
<string>Toggles between selection and preview mode</string>
</property>
<property name="text">
<string>Select</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidgetReferences2">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="14" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_52">
<item row="0" column="1">
<widget class="QLineEdit" name="lineFaceName2">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QToolButton" name="buttonFace2">
<property name="minimumSize">
<size>
<width>0</width>
<height>22</height>
</size>
</property>
<property name="text">
<string>Select Face</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="checkBoxMidplane"> <widget class="Line" name="line">
<property name="enabled"> <property name="orientation">
<bool>true</bool> <enum>Qt::Horizontal</enum>
</property>
<property name="toolTip">
<string>Applies length symmetrically to sketch plane</string>
</property>
<property name="text">
<string>Symmetric to plane</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -410,61 +679,6 @@ measured along the specified direction</string>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="labelTaperAngle">
<property name="toolTip">
<string>Angle to taper the extrusion</string>
</property>
<property name="text">
<string>Taper angle</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefQuantitySpinBox" name="taperEdit" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">deg</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="labelTaperAngle2">
<property name="toolTip">
<string>Angle to taper the extrusion</string>
</property>
<property name="text">
<string>2nd taper angle</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefQuantitySpinBox" name="taperEdit2" native="true">
<property name="keyboardTracking" stdset="0">
<bool>false</bool>
</property>
<property name="unit" stdset="0">
<string notr="true">deg</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="checkBoxUpdateView"> <widget class="QCheckBox" name="checkBoxUpdateView">
<property name="text"> <property name="text">
@@ -494,25 +708,6 @@ measured along the specified direction</string>
<header>Gui/PrefWidgets.h</header> <header>Gui/PrefWidgets.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<tabstops>
<tabstop>changeMode</tabstop>
<tabstop>lengthEdit</tabstop>
<tabstop>lengthEdit2</tabstop>
<tabstop>offsetEdit</tabstop>
<tabstop>buttonFace</tabstop>
<tabstop>lineFaceName</tabstop>
<tabstop>checkBoxMidplane</tabstop>
<tabstop>checkBoxReversed</tabstop>
<tabstop>directionCB</tabstop>
<tabstop>checkBoxDirection</tabstop>
<tabstop>XDirectionEdit</tabstop>
<tabstop>YDirectionEdit</tabstop>
<tabstop>ZDirectionEdit</tabstop>
<tabstop>checkBoxAlongDirection</tabstop>
<tabstop>taperEdit</tabstop>
<tabstop>taperEdit2</tabstop>
<tabstop>checkBoxUpdateView</tabstop>
</tabstops>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>
+38 -43
View File
@@ -38,9 +38,9 @@ using namespace Gui;
TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidget *parent, bool newObj) TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidget *parent, bool newObj)
: TaskExtrudeParameters(PocketView, parent, "PartDesign_Pocket", tr("Pocket Parameters")) : TaskExtrudeParameters(PocketView, parent, "PartDesign_Pocket", tr("Pocket Parameters"))
, oldLength(0)
{ {
ui->offsetEdit->setToolTip(tr("Offset from face at which pocket will end")); ui->offsetEdit->setToolTip(tr("Offset from the selected face at which the pocket will end on side 1"));
ui->offsetEdit2->setToolTip(tr("Offset from the selected face at which the pocket will end on side 2"));
ui->checkBoxReversed->setToolTip(tr("Reverses pocket direction")); ui->checkBoxReversed->setToolTip(tr("Reverses pocket direction"));
// set the history path // set the history path
@@ -50,6 +50,8 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketLength2")); ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketLength2"));
ui->offsetEdit->setEntryName(QByteArray("Offset")); ui->offsetEdit->setEntryName(QByteArray("Offset"));
ui->offsetEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketOffset")); ui->offsetEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketOffset"));
ui->offsetEdit2->setEntryName(QByteArray("Offset2"));
ui->offsetEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketOffset2"));
ui->taperEdit->setEntryName(QByteArray("TaperAngle")); ui->taperEdit->setEntryName(QByteArray("TaperAngle"));
ui->taperEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketTaperAngle")); ui->taperEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketTaperAngle"));
ui->taperEdit2->setEntryName(QByteArray("TaperAngle2")); ui->taperEdit2->setEntryName(QByteArray("TaperAngle2"));
@@ -65,81 +67,74 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
TaskPocketParameters::~TaskPocketParameters() = default; TaskPocketParameters::~TaskPocketParameters() = default;
void TaskPocketParameters::translateModeList(int index) void TaskPocketParameters::translateModeList(QComboBox* box, int index)
{ {
ui->changeMode->clear(); box->clear();
ui->changeMode->addItem(tr("Dimension")); box->addItem(tr("Dimension"));
ui->changeMode->addItem(tr("Through all")); box->addItem(tr("Through all"));
ui->changeMode->addItem(tr("To first")); box->addItem(tr("To first"));
ui->changeMode->addItem(tr("Up to face")); box->addItem(tr("Up to face"));
ui->changeMode->addItem(tr("Two dimensions")); box->addItem(tr("Up to shape"));
ui->changeMode->addItem(tr("Up to shape")); box->setCurrentIndex(index);
ui->changeMode->setCurrentIndex(index);
} }
void TaskPocketParameters::updateUI(int index) void TaskPocketParameters::updateUI(Side side)
{ {
// update direction combobox // update direction combobox
fillDirectionCombo(); fillDirectionCombo();
// set and enable checkboxes // set and enable checkboxes
setCheckboxes(static_cast<Mode>(index), Type::Pocket); updateWholeUI(Type::Pocket, side);
} }
void TaskPocketParameters::onModeChanged(int index) void TaskPocketParameters::onModeChanged(int index, Side side)
{ {
auto pcPocket = getObject<PartDesign::Pocket>(); auto& sideCtrl = getSideController(side);
switch (static_cast<Mode>(index)) { switch (static_cast<Mode>(index)) {
case Mode::Dimension: case Mode::Dimension:
// Why? See below for "UpToFace" sideCtrl.Type->setValue("Length");
if (oldLength < Precision::Confusion()) if (side == Side::First) {
oldLength = 5.0; // Avoid error message
pcPocket->Length.setValue(oldLength); double L = sideCtrl.lengthEdit->value().getValue();
ui->lengthEdit->setValue(oldLength); Side otherSide = side == Side::First ? Side::Second : Side::First;
pcPocket->Type.setValue("Length"); auto& sideCtrl2 = getSideController(otherSide);
double L2 = static_cast<SidesMode>(getSidesMode()) == SidesMode::TwoSides
? sideCtrl2.lengthEdit->value().getValue()
: 0;
if (std::abs(L + L2) < Precision::Confusion()) {
sideCtrl.lengthEdit->setValue(5.0);
}
}
break; break;
case Mode::ThroughAll: case Mode::ThroughAll:
oldLength = pcPocket->Length.getValue(); sideCtrl.Type->setValue("ThroughAll");
pcPocket->Type.setValue("ThroughAll");
break; break;
case Mode::ToFirst: case Mode::ToFirst:
oldLength = pcPocket->Length.getValue(); sideCtrl.Type->setValue("UpToFirst");
pcPocket->Type.setValue("UpToFirst");
break; break;
case Mode::ToFace: case Mode::ToFace:
// Note: ui->checkBoxReversed is purposely enabled because the selected face // Note: ui->checkBoxReversed is purposely enabled because the selected face
// could be a circular one around the sketch // could be a circular one around the sketch
// Also note: Because of the code at the beginning of Pocket::execute() which is used // Also note: Because of the code at the beginning of Pocket::execute() which is used
// to detect broken legacy parts, we must set the length to zero here! // to detect broken legacy parts, we must set the length to zero here!
oldLength = pcPocket->Length.getValue(); sideCtrl.Type->setValue("UpToFace");
pcPocket->Type.setValue("UpToFace"); if (sideCtrl.lineFaceName->text().isEmpty()) {
pcPocket->Length.setValue(0.0); sideCtrl.buttonFace->setChecked(true);
ui->lengthEdit->setValue(0.0); handleLineFaceNameClick(sideCtrl.lineFaceName); // sets placeholder text
if (ui->lineFaceName->text().isEmpty()) {
ui->buttonFace->setChecked(true);
handleLineFaceNameClick(); // sets placeholder text
} }
break; break;
case Mode::TwoDimensions:
oldLength = pcPocket->Length.getValue();
pcPocket->Type.setValue("TwoLengths");
break;
case Mode::ToShape: case Mode::ToShape:
pcPocket->Type.setValue("UpToShape"); sideCtrl.Type->setValue("UpToShape");
break; break;
} }
updateUI(index); updateUI(side);
recomputeFeature(); recomputeFeature();
} }
void TaskPocketParameters::apply() void TaskPocketParameters::apply()
{ {
QString facename = QStringLiteral("None"); applyParameters();
if (static_cast<Mode>(getMode()) == Mode::ToFace) {
facename = getFaceName();
}
applyParameters(facename);
} }
//************************************************************************** //**************************************************************************
@@ -26,6 +26,7 @@
#include "TaskExtrudeParameters.h" #include "TaskExtrudeParameters.h"
#include "ViewProviderPocket.h" #include "ViewProviderPocket.h"
class QComboBox;
namespace App { namespace App {
class Property; class Property;
@@ -49,12 +50,9 @@ public:
void apply() override; void apply() override;
private: private:
void onModeChanged(int index) override; void onModeChanged(int index, Side side) override;
void translateModeList(int index) override; void translateModeList(QComboBox* box, int index) override;
void updateUI(int index) override; void updateUI(Side side) override;
private:
double oldLength;
}; };
/// simulation dialog for the TaskView /// simulation dialog for the TaskView
@@ -385,7 +385,8 @@ void TaskRevolutionParameters::onSelectionChanged(const Gui::SelectionChanges& m
if (msg.Type == Gui::SelectionChanges::AddSelection) { if (msg.Type == Gui::SelectionChanges::AddSelection) {
int mode = ui->changeMode->currentIndex(); int mode = ui->changeMode->currentIndex();
if (selectionFace) { if (selectionFace) {
QString refText = onAddSelection(msg); auto rev = getObject<PartDesign::Revolution>();
QString refText = onAddSelection(msg, rev->UpToFace);
if (refText.length() > 0) { if (refText.length() > 0) {
QSignalBlocker block(ui->lineFaceName); QSignalBlocker block(ui->lineFaceName);
ui->lineFaceName->setText(refText); ui->lineFaceName->setText(refText);
@@ -58,7 +58,7 @@ TaskSketchBasedParameters::TaskSketchBasedParameters(PartDesignGui::ViewProvider
this->blockSelection(true); this->blockSelection(true);
} }
const QString TaskSketchBasedParameters::onAddSelection(const Gui::SelectionChanges& msg) const QString TaskSketchBasedParameters::onAddSelection(const Gui::SelectionChanges& msg, App::PropertyLinkSub& prop)
{ {
// Note: The validity checking has already been done in ReferenceSelection.cpp // Note: The validity checking has already been done in ReferenceSelection.cpp
auto sketchBased = getObject<PartDesign::ProfileBased>(); auto sketchBased = getObject<PartDesign::ProfileBased>();
@@ -81,7 +81,7 @@ const QString TaskSketchBasedParameters::onAddSelection(const Gui::SelectionChan
} }
std::vector<std::string> upToFaces(1,subname); std::vector<std::string> upToFaces(1,subname);
sketchBased->UpToFace.setValue(selObj, upToFaces); prop.setValue(selObj, upToFaces);
recomputeFeature(); recomputeFeature();
return refStr; return refStr;
@@ -33,6 +33,7 @@
namespace App { namespace App {
class Property; class Property;
class PropertyLinkSubList;
} }
namespace PartDesignGui { namespace PartDesignGui {
@@ -51,7 +52,7 @@ public:
protected: protected:
void onSelectionChanged(const Gui::SelectionChanges& msg) override =0; void onSelectionChanged(const Gui::SelectionChanges& msg) override =0;
const QString onAddSelection(const Gui::SelectionChanges& msg); const QString onAddSelection(const Gui::SelectionChanges& msg, App::PropertyLinkSub& prop);
virtual void startReferenceSelection(App::DocumentObject* profile, App::DocumentObject* base); virtual void startReferenceSelection(App::DocumentObject* profile, App::DocumentObject* base);
virtual void finishReferenceSelection(App::DocumentObject* profile, App::DocumentObject* base); virtual void finishReferenceSelection(App::DocumentObject* profile, App::DocumentObject* base);
/*! /*!
@@ -178,7 +178,7 @@ class TestPad(unittest.TestCase):
self.Pad1 = self.Doc.addObject("PartDesign::Pad", "Pad1") self.Pad1 = self.Doc.addObject("PartDesign::Pad", "Pad1")
self.Body.addObject(self.Pad1) self.Body.addObject(self.Pad1)
self.Pad1.Profile = self.PadSketch1 self.Pad1.Profile = self.PadSketch1
self.Pad1.Type = 4 self.Pad1.SideType = 1
self.Pad1.Length = 1.0 self.Pad1.Length = 1.0
self.Pad1.Length2 = 2.0 self.Pad1.Length2 = 2.0
self.Pad1.Reversed = 1 self.Pad1.Reversed = 1
@@ -238,7 +238,7 @@ class TestPad(unittest.TestCase):
self.Pad1 = self.Doc.addObject("PartDesign::Pad", "Pad1") self.Pad1 = self.Doc.addObject("PartDesign::Pad", "Pad1")
self.Body.addObject(self.Pad1) self.Body.addObject(self.Pad1)
self.Pad1.Profile = self.PadSketch1 self.Pad1.Profile = self.PadSketch1
self.Pad1.Type = 5 self.Pad1.Type = 4
self.Doc.recompute() self.Doc.recompute()
self.assertAlmostEqual(self.Pad1.Shape.Volume, 2.58787, places=4) self.assertAlmostEqual(self.Pad1.Shape.Volume, 2.58787, places=4)
+1 -1
View File
@@ -76,7 +76,7 @@ TEST_F(PadTest, TestMidPlaneTwoLength)
pad->Length.setValue(10.0); pad->Length.setValue(10.0);
pad->Length2.setValue(20.0); pad->Length2.setValue(20.0);
pad->Type.setValue("TwoLengths"); pad->SideType.setValue("Two sides");
doc->recompute(); doc->recompute();