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:
@@ -1422,6 +1422,35 @@ public:
|
||||
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
|
||||
*
|
||||
* @return Return true if the shape is modified
|
||||
|
||||
@@ -4143,6 +4143,84 @@ TopoShape::makeElementCut(const std::vector<TopoShape>& shapes, const char* op,
|
||||
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,
|
||||
const TopoShape& source,
|
||||
@@ -5686,6 +5764,10 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker,
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (strcmp(maker, Part::OpCodes::Xor) == 0) {
|
||||
return makeElementXor(shapes, op, tolerance);
|
||||
}
|
||||
|
||||
bool buildShell = true;
|
||||
|
||||
std::vector<TopoShape> _shapes;
|
||||
|
||||
@@ -48,7 +48,8 @@ public:
|
||||
static constexpr const char *Fuse = "FUS";
|
||||
static constexpr const char *Cut = "CUT";
|
||||
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 *Compsolid = "CSD";
|
||||
static constexpr const char *Pipe = "PIP";
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
# include <BRepFeat_MakePrism.hxx>
|
||||
# include <BRepPrimAPI_MakePrism.hxx>
|
||||
# include <gp_Dir.hxx>
|
||||
# include <gp_Ax2.hxx>
|
||||
# include <Precision.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
# include <TopoDS_Compound.hxx>
|
||||
@@ -49,6 +50,8 @@ FC_LOG_LEVEL_INIT("PartDesign", true, true)
|
||||
|
||||
using namespace PartDesign;
|
||||
|
||||
const char* FeatureExtrude::SideTypesEnums[] = {"One side", "Two sides", "Symmetric", nullptr};
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::FeatureExtrude, PartDesign::ProfileBased)
|
||||
|
||||
App::PropertyQuantityConstraint::Constraints FeatureExtrude::signedLengthConstraint = {
|
||||
@@ -61,7 +64,9 @@ FeatureExtrude::FeatureExtrude() = default;
|
||||
short FeatureExtrude::mustExecute() const
|
||||
{
|
||||
if (Placement.isTouched() ||
|
||||
SideType.isTouched() ||
|
||||
Type.isTouched() ||
|
||||
Type2.isTouched() ||
|
||||
Length.isTouched() ||
|
||||
Length2.isTouched() ||
|
||||
TaperAngle.isTouched() ||
|
||||
@@ -71,7 +76,11 @@ short FeatureExtrude::mustExecute() const
|
||||
ReferenceAxis.isTouched() ||
|
||||
AlongSketchNormal.isTouched() ||
|
||||
Offset.isTouched() ||
|
||||
UpToFace.isTouched())
|
||||
Offset2.isTouched() ||
|
||||
UpToFace.isTouched() ||
|
||||
UpToFace2.isTouched() ||
|
||||
UpToShape.isTouched() ||
|
||||
UpToShape2.isTouched())
|
||||
return 1;
|
||||
return ProfileBased::mustExecute();
|
||||
}
|
||||
@@ -174,293 +183,115 @@ TopoShape FeatureExtrude::makeShellFromUpToShape(TopoShape shape, TopoShape sket
|
||||
return shape;
|
||||
}
|
||||
|
||||
// TODO: Toponaming April 2024 Deprecated in favor of TopoShape method. Remove when possible.
|
||||
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)
|
||||
void FeatureExtrude::updateProperties()
|
||||
{
|
||||
if (method == "Length" || method == "TwoLengths" || method == "ThroughAll") {
|
||||
double Ltotal = L;
|
||||
double Loffset = 0.;
|
||||
if (method == "ThroughAll")
|
||||
Ltotal = getThroughAllLength();
|
||||
std::string sideTypeVal = SideType.getValueAsString();
|
||||
std::string methodSide1 = Type.getValueAsString();
|
||||
std::string methodSide2 = Type2.getValueAsString();
|
||||
|
||||
bool isLength1Enabled = false;
|
||||
bool isTaper1Visible = false;
|
||||
bool isUpToFace1Enabled = false;
|
||||
bool isUpToShape1Enabled = false;
|
||||
bool isOffset1Enabled = false;
|
||||
|
||||
if (method == "TwoLengths") {
|
||||
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 isType2Enabled = 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;
|
||||
if (method == "Length") {
|
||||
isLengthEnabled = true;
|
||||
isTaperVisible = true;
|
||||
isMidplaneEnabled = true;
|
||||
isReversedEnabled = !Midplane.getValue();
|
||||
bool isUpToFace2Enabled = false;
|
||||
bool isUpToShape2Enabled = false;
|
||||
bool isOffset2Enabled = false;
|
||||
|
||||
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") {
|
||||
isOffsetEnabled = true;
|
||||
isReversedEnabled = true;
|
||||
else if (sideTypeVal == "Two sides") {
|
||||
isType2Enabled = 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") {
|
||||
isMidplaneEnabled = true;
|
||||
isReversedEnabled = !Midplane.getValue();
|
||||
}
|
||||
else if (method == "UpToFirst") {
|
||||
isOffsetEnabled = true;
|
||||
isReversedEnabled = true;
|
||||
}
|
||||
else if (method == "UpToFace") {
|
||||
isOffsetEnabled = true;
|
||||
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;
|
||||
else if (sideTypeVal == "Symmetric") {
|
||||
bool symASN = false;
|
||||
configureSideProperties(methodSide1,
|
||||
isLength1Enabled,
|
||||
isTaper1Visible,
|
||||
isUpToFace1Enabled,
|
||||
isUpToShape1Enabled,
|
||||
symASN,
|
||||
isOffset1Enabled);
|
||||
currentAlongSketchNormalEnabled = symASN;
|
||||
}
|
||||
|
||||
Length.setReadOnly(!isLengthEnabled);
|
||||
AlongSketchNormal.setReadOnly(!isLengthEnabled);
|
||||
Length.setReadOnly(!isLength1Enabled);
|
||||
TaperAngle.setReadOnly(!isTaper1Visible);
|
||||
UpToFace.setReadOnly(!isUpToFace1Enabled);
|
||||
UpToShape.setReadOnly(!isUpToShape1Enabled);
|
||||
Offset.setReadOnly(!isOffset1Enabled);
|
||||
|
||||
Type2.setReadOnly(!isType2Enabled);
|
||||
Length2.setReadOnly(!isLength2Enabled);
|
||||
Offset.setReadOnly(!isOffsetEnabled);
|
||||
TaperAngle.setReadOnly(!isTaperVisible);
|
||||
TaperAngle2.setReadOnly(!isTaper2Visible);
|
||||
Midplane.setReadOnly(!isMidplaneEnabled);
|
||||
Reversed.setReadOnly(!isReversedEnabled);
|
||||
UpToFace.setReadOnly(!isUpToFaceEnabled);
|
||||
UpToShape.setReadOnly(!isUpToShapeEnabled);
|
||||
UpToFace2.setReadOnly(!isUpToFace2Enabled);
|
||||
UpToShape2.setReadOnly(!isUpToShape2Enabled);
|
||||
Offset2.setReadOnly(!isOffset2Enabled);
|
||||
|
||||
AlongSketchNormal.setReadOnly(!currentAlongSketchNormalEnabled);
|
||||
}
|
||||
|
||||
void FeatureExtrude::setupObject()
|
||||
@@ -475,23 +306,30 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
|
||||
|
||||
bool makeface = options.testFlag(ExtrudeOption::MakeFace);
|
||||
bool fuse = options.testFlag(ExtrudeOption::MakeFuse);
|
||||
bool legacyPocket = options.testFlag(ExtrudeOption::LegacyPocket);
|
||||
bool inverseDirection = options.testFlag(ExtrudeOption::InverseDirection);
|
||||
|
||||
std::string Sidemethod(SideType.getValueAsString());
|
||||
std::string method(Type.getValueAsString());
|
||||
std::string method2(Type2.getValueAsString());
|
||||
|
||||
// Validate parameters
|
||||
double L = Length.getValue();
|
||||
if ((method == "Length") && (L < Precision::Confusion())) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Length too small"));
|
||||
}
|
||||
double L2 = 0;
|
||||
if ((method == "TwoLengths")) {
|
||||
L2 = Length2.getValue();
|
||||
if (std::abs(L2) < Precision::Confusion()) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Second length too small"));
|
||||
double L2 = (Sidemethod == "Two sides" && method2 == "Length") ? Length2.getValue() : 0;
|
||||
|
||||
if ((Sidemethod == "One side" && method == "Length")
|
||||
|| (Sidemethod == "Two sides" && method == "Length" && method2 == "Length")) {
|
||||
|
||||
if (std::abs(L + L2) < Precision::Confusion()) {
|
||||
if (addSubType == Type::Additive) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception",
|
||||
"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);
|
||||
|
||||
dir.Transform(invTrsf);
|
||||
if (Reversed.getValue()) {
|
||||
dir.Reverse();
|
||||
}
|
||||
|
||||
if (sketchshape.isNull()) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
@@ -605,174 +446,96 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
|
||||
}
|
||||
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") {
|
||||
// Note: This will return an unlimited planar face if support is a datum plane
|
||||
TopoShape supportface = getTopoShapeSupportFace();
|
||||
supportface.move(invObjLoc);
|
||||
if (sideTypeStr == "One side") {
|
||||
TopoShape prism1 = generateSingleExtrusionSide(sketchshape,
|
||||
method1, len1, taper1, UpToFace, UpToShape,
|
||||
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()) {
|
||||
dir.Reverse();
|
||||
// Prism 2 : Make a symmetric of prism1
|
||||
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;
|
||||
int faceCount = 1;
|
||||
// Find a valid shape, face or datum plane to extrude up to
|
||||
if (method == "UpToFace") {
|
||||
getUpToFaceFromLinkSub(upToShape, UpToFace);
|
||||
upToShape.move(invObjLoc);
|
||||
faceCount = 1;
|
||||
}
|
||||
else if (method == "UpToShape") {
|
||||
faceCount = getUpToShapeFromLinkSubList(upToShape, UpToShape);
|
||||
upToShape.move(invObjLoc);
|
||||
if (faceCount == 0){
|
||||
// No shape selected, use the base
|
||||
upToShape = base;
|
||||
faceCount = 0;
|
||||
}
|
||||
}
|
||||
// Side 2
|
||||
std::string method2 = Type2.getValueAsString();
|
||||
double len2 = method2 == "ThroughAll" ? getThroughAllLength() : Length2.getValue();
|
||||
double taper2 = TaperAngle2.getValue();
|
||||
double offset2 = Offset2.getValue();
|
||||
gp_Dir dir2 = dir;
|
||||
dir2.Reverse();
|
||||
|
||||
if (faceCount == 1) {
|
||||
getUpToFace(upToShape, base, sketchshape, method, dir);
|
||||
addOffsetToFace(upToShape, dir, Offset.getValue());
|
||||
}
|
||||
else{
|
||||
if (fabs(Offset.getValue()) > Precision::Confusion()){
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Extrude: Can only offset one face"));
|
||||
}
|
||||
// open the shell by removing the furthest face
|
||||
upToShape = makeShellFromUpToShape(upToShape, sketchshape, dir);
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
TopoShape prism2 = generateSingleExtrusionSide(sketchshape.makeElementCopy(),
|
||||
method2,
|
||||
len2,
|
||||
taper2,
|
||||
UpToFace2,
|
||||
UpToShape2,
|
||||
dir2,
|
||||
offset2,
|
||||
makeface,
|
||||
base);
|
||||
if (!prism2.isNull() && !prism2.getShape().IsNull()) {
|
||||
prisms.push_back(prism2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
using std::numbers::pi;
|
||||
|
||||
Part::ExtrusionParameters params;
|
||||
params.dir = dir;
|
||||
params.solid = makeface;
|
||||
params.taperAngleFwd = Base::toRadians(this->TaperAngle.getValue());
|
||||
params.taperAngleRev = Base::toRadians(this->TaperAngle2.getValue());
|
||||
if (L2 == 0.0 && Midplane.getValue()) {
|
||||
params.lengthFwd = L / 2;
|
||||
params.lengthRev = L / 2;
|
||||
if (params.taperAngleRev == 0.0) {
|
||||
params.taperAngleRev = params.taperAngleFwd;
|
||||
}
|
||||
// --- Combine generated prisms (all in global CS) ---
|
||||
TopoShape prism(0, getDocument()->getStringHasher());
|
||||
if (prisms.empty()) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "No extrusion geometry was generated."));
|
||||
}
|
||||
else if (prisms.size() == 1) {
|
||||
prism = prisms[0];
|
||||
}
|
||||
else {
|
||||
try {
|
||||
prism.makeElementXor(prisms, Part::OpCodes::Extrude);
|
||||
}
|
||||
else {
|
||||
params.lengthFwd = L;
|
||||
params.lengthRev = L2;
|
||||
catch (const Standard_Failure& e) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
std::string("Failed to xor extrusion sides (OCC): ") + e.GetMessageString());
|
||||
}
|
||||
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 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());
|
||||
catch (const Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
std::string("Failed to xor extrusion sides: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
if (prism.isNull()) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting fused extrusion is null."));
|
||||
}
|
||||
|
||||
// store shape before refinement
|
||||
this->rawShape = 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
|
||||
updateProperties(method);
|
||||
updateProperties();
|
||||
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
@@ -860,3 +623,134 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ class PartDesignExport FeatureExtrude : public ProfileBased
|
||||
public:
|
||||
FeatureExtrude();
|
||||
|
||||
App::PropertyEnumeration SideType;
|
||||
App::PropertyEnumeration Type;
|
||||
App::PropertyEnumeration Type2;
|
||||
App::PropertyLength Length;
|
||||
App::PropertyLength Length2;
|
||||
App::PropertyAngle TaperAngle;
|
||||
@@ -51,6 +53,7 @@ public:
|
||||
App::PropertyVector Direction;
|
||||
App::PropertyBool AlongSketchNormal;
|
||||
App::PropertyLength Offset;
|
||||
App::PropertyLength Offset2;
|
||||
App::PropertyLinkSub ReferenceAxis;
|
||||
|
||||
static App::PropertyQuantityConstraint::Constraints signedLengthConstraint;
|
||||
@@ -67,7 +70,10 @@ public:
|
||||
}
|
||||
//@}
|
||||
|
||||
static const char* SideTypesEnums[];
|
||||
|
||||
protected:
|
||||
void handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) override;
|
||||
Base::Vector3d computeDirection(const Base::Vector3d& sketchVector, bool inverse);
|
||||
bool hasTaperedAngle() const;
|
||||
|
||||
@@ -92,65 +98,23 @@ protected:
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
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)
|
||||
|
||||
@@ -46,25 +46,34 @@ Pad::Pad()
|
||||
{
|
||||
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);
|
||||
ADD_PROPERTY_TYPE(Length, (10.0), "Pad", App::Prop_None, "Pad length");
|
||||
ADD_PROPERTY_TYPE(Length2, (10.0), "Pad", App::Prop_None, "Pad length in 2nd direction");
|
||||
Type2.setEnums(TypeEnums);
|
||||
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(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(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(UpToShape, (nullptr), "Pad", 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(UpToFace, (nullptr), "Side1", App::Prop_None, "Face where pad will end");
|
||||
ADD_PROPERTY_TYPE(UpToShape, (nullptr), "Side1", App::Prop_None, "Faces or shape(s) where 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);
|
||||
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);
|
||||
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);
|
||||
|
||||
// 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
|
||||
Length.setConstraints(nullptr);
|
||||
Length2.setConstraints(nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,6 @@ public:
|
||||
* 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 "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 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)
|
||||
|
||||
@@ -39,7 +39,7 @@ using namespace PartDesign;
|
||||
|
||||
/* 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)
|
||||
|
||||
@@ -47,25 +47,34 @@ Pocket::Pocket()
|
||||
{
|
||||
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);
|
||||
ADD_PROPERTY_TYPE(Length, (5.0), "Pocket", App::Prop_None, "Pocket length");
|
||||
ADD_PROPERTY_TYPE(Length2, (5.0), "Pocket", App::Prop_None, "Pocket length in 2nd direction");
|
||||
Type2.setEnums(TypeEnums);
|
||||
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(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(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(UpToShape, (nullptr), "Pocket", 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(UpToFace, (nullptr), "Side1", App::Prop_None, "Face 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(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);
|
||||
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);
|
||||
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);
|
||||
|
||||
// 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
|
||||
Length.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(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(UpToFace, (nullptr), "SketchBased", (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(UpToFace, (nullptr), "Side1", (App::PropertyType)(App::Prop_None), "Face 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");
|
||||
}
|
||||
|
||||
@@ -84,7 +86,10 @@ short ProfileBased::mustExecute() const
|
||||
if (Profile.isTouched() ||
|
||||
Midplane.isTouched() ||
|
||||
Reversed.isTouched() ||
|
||||
UpToFace.isTouched())
|
||||
UpToFace.isTouched() ||
|
||||
UpToFace2.isTouched() ||
|
||||
UpToShape.isTouched() ||
|
||||
UpToShape2.isTouched())
|
||||
return 1;
|
||||
return PartDesign::FeatureAddSub::mustExecute();
|
||||
}
|
||||
|
||||
@@ -57,8 +57,10 @@ public:
|
||||
App::PropertyBool Midplane;
|
||||
/// Face to extrude up to
|
||||
App::PropertyLinkSub UpToFace;
|
||||
App::PropertyLinkSub UpToFace2;
|
||||
/// Shape to extrude up to
|
||||
App::PropertyLinkSubList UpToShape;
|
||||
App::PropertyLinkSubList UpToShape2;
|
||||
|
||||
App::PropertyBool AllowMultiFace;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,11 +26,20 @@
|
||||
#include "TaskSketchBasedParameters.h"
|
||||
#include "ViewProviderExtrude.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLineEdit;
|
||||
class QListWidget;
|
||||
class QToolButton;
|
||||
|
||||
class Ui_TaskPadPocketParameters;
|
||||
|
||||
namespace App {
|
||||
class Property;
|
||||
class PropertyLinkSubList;
|
||||
}
|
||||
namespace Gui {
|
||||
class PrefQuantitySpinBox;
|
||||
}
|
||||
|
||||
namespace PartDesign {
|
||||
@@ -57,13 +66,23 @@ public:
|
||||
Pocket
|
||||
};
|
||||
|
||||
enum class SidesMode {
|
||||
OneSide,
|
||||
TwoSides,
|
||||
Symmetric,
|
||||
};
|
||||
|
||||
enum class Side {
|
||||
First,
|
||||
Second,
|
||||
};
|
||||
|
||||
enum class Mode {
|
||||
Dimension,
|
||||
ThroughAll,
|
||||
ToLast = ThroughAll,
|
||||
ToFirst,
|
||||
ToFace,
|
||||
TwoDimensions,
|
||||
ToShape,
|
||||
};
|
||||
|
||||
@@ -84,35 +103,84 @@ public:
|
||||
void fillDirectionCombo();
|
||||
void addAxisToCombo(App::DocumentObject* linkObj, std::string linkSubname, QString itemText,
|
||||
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:
|
||||
void onLengthChanged(double);
|
||||
void onLength2Changed(double);
|
||||
void onOffsetChanged(double);
|
||||
void onTaperChanged(double);
|
||||
void onTaper2Changed(double);
|
||||
void onSidesModeChanged(int);
|
||||
virtual void onModeChanged(int index, Side side) = 0;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onDirectionCBChanged(int);
|
||||
void onAlongSketchNormalChanged(bool);
|
||||
void onDirectionToggled(bool);
|
||||
void onAllFacesToggled(bool);
|
||||
void onXDirectionEditChanged(double);
|
||||
void onYDirectionEditChanged(double);
|
||||
void onZDirectionEditChanged(double);
|
||||
void onMidplaneChanged(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:
|
||||
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 readValuesFromHistory();
|
||||
void changeEvent(QEvent *e) override;
|
||||
@@ -120,6 +188,7 @@ protected:
|
||||
void getReferenceAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const;
|
||||
|
||||
double getOffset() const;
|
||||
double getOffset2() const;
|
||||
bool getAlongSketchNormal() const;
|
||||
bool getCustom() const;
|
||||
std::string getReferenceAxis() const;
|
||||
@@ -127,42 +196,52 @@ protected:
|
||||
double getYDirection() const;
|
||||
double getZDirection() const;
|
||||
bool getReversed() const;
|
||||
bool getMidplane() const;
|
||||
int getMode() const;
|
||||
QString getFaceName() const;
|
||||
int getMode2() const;
|
||||
int getSidesMode() const;
|
||||
QString getFaceName(QLineEdit*) const;
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
virtual void translateModeList(int index);
|
||||
virtual void updateUI(int index);
|
||||
void translateSidesList(int index);
|
||||
virtual void translateModeList(QComboBox* box, int index);
|
||||
virtual void updateUI(Side side);
|
||||
void updateDirectionEdits();
|
||||
void setDirectionMode(int index);
|
||||
void handleLineFaceNameClick();
|
||||
void handleLineFaceNameNo();
|
||||
void handleLineFaceNameClick(QLineEdit*);
|
||||
void handleLineFaceNameNo(QLineEdit*);
|
||||
|
||||
private:
|
||||
void setupSideDialog(SideController& side);
|
||||
|
||||
void selectedReferenceAxis(const Gui::SelectionChanges& msg);
|
||||
void selectedFace(const Gui::SelectionChanges& msg);
|
||||
void selectedShape(const Gui::SelectionChanges& msg);
|
||||
void selectedShapeFace(const Gui::SelectionChanges& msg);
|
||||
void selectedFace(const Gui::SelectionChanges& msg, SideController& side);
|
||||
void selectedShape(const Gui::SelectionChanges& msg, SideController& side);
|
||||
void selectedShapeFace(const Gui::SelectionChanges& msg, SideController& side);
|
||||
|
||||
void tryRecomputeFeature();
|
||||
void translateFaceName();
|
||||
void translateFaceName(QLineEdit*);
|
||||
void connectSlots();
|
||||
bool hasProfileFace(PartDesign::ProfileBased*) const;
|
||||
void clearFaceName();
|
||||
void clearFaceName(QLineEdit*);
|
||||
|
||||
void updateShapeName();
|
||||
void updateShapeFaces();
|
||||
void updateShapeName(QLineEdit*, App::PropertyLinkSubList&);
|
||||
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:
|
||||
QWidget* proxy;
|
||||
QAction* unselectShapeFaceAction;
|
||||
QAction* unselectShapeFaceAction2;
|
||||
|
||||
std::unique_ptr<Ui_TaskPadPocketParameters> ui;
|
||||
std::vector<std::unique_ptr<App::PropertyLinkSub>> axesInList;
|
||||
|
||||
SelectionMode selectionMode = None;
|
||||
Side activeSelectionSide = Side::First;
|
||||
};
|
||||
|
||||
class TaskDlgExtrudeParameters : public TaskDlgSketchBasedParameters
|
||||
|
||||
@@ -39,7 +39,8 @@ using namespace Gui;
|
||||
TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent, bool newObj)
|
||||
: 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"));
|
||||
|
||||
// set the history path
|
||||
@@ -49,6 +50,8 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
|
||||
ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadLength2"));
|
||||
ui->offsetEdit->setEntryName(QByteArray("Offset"));
|
||||
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->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadTaperAngle"));
|
||||
ui->taperEdit2->setEntryName(QByteArray("TaperAngle2"));
|
||||
@@ -64,71 +67,70 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
|
||||
|
||||
TaskPadParameters::~TaskPadParameters() = default;
|
||||
|
||||
void TaskPadParameters::translateModeList(int index)
|
||||
void TaskPadParameters::translateModeList(QComboBox* box, int index)
|
||||
{
|
||||
ui->changeMode->clear();
|
||||
ui->changeMode->addItem(tr("Dimension"));
|
||||
ui->changeMode->addItem(tr("To last"));
|
||||
ui->changeMode->addItem(tr("To first"));
|
||||
ui->changeMode->addItem(tr("Up to face"));
|
||||
ui->changeMode->addItem(tr("Two dimensions"));
|
||||
ui->changeMode->addItem(tr("Up to shape"));
|
||||
ui->changeMode->setCurrentIndex(index);
|
||||
box->clear();
|
||||
box->addItem(tr("Dimension"));
|
||||
box->addItem(tr("To last"));
|
||||
box->addItem(tr("To first"));
|
||||
box->addItem(tr("Up to face"));
|
||||
box->addItem(tr("Up to shape"));
|
||||
box->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void TaskPadParameters::updateUI(int index)
|
||||
void TaskPadParameters::updateUI(Side side)
|
||||
{
|
||||
// update direction combobox
|
||||
fillDirectionCombo();
|
||||
// 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)) {
|
||||
case Mode::Dimension:
|
||||
pcPad->Type.setValue("Length");
|
||||
// Avoid error message
|
||||
if (ui->lengthEdit->value() < Base::Quantity(Precision::Confusion(), Base::Unit::Length))
|
||||
ui->lengthEdit->setValue(5.0);
|
||||
break;
|
||||
case Mode::ToLast:
|
||||
pcPad->Type.setValue("UpToLast");
|
||||
break;
|
||||
case Mode::ToFirst:
|
||||
pcPad->Type.setValue("UpToFirst");
|
||||
break;
|
||||
case Mode::ToFace:
|
||||
// Note: ui->checkBoxReversed is purposely enabled because the selected face
|
||||
// could be a circular one around the sketch
|
||||
pcPad->Type.setValue("UpToFace");
|
||||
if (ui->lineFaceName->text().isEmpty()) {
|
||||
ui->buttonFace->setChecked(true);
|
||||
handleLineFaceNameClick(); // sets placeholder text
|
||||
}
|
||||
break;
|
||||
case Mode::TwoDimensions:
|
||||
pcPad->Type.setValue("TwoLengths");
|
||||
break;
|
||||
case Mode::ToShape:
|
||||
pcPad->Type.setValue("UpToShape");
|
||||
break;
|
||||
case Mode::Dimension:
|
||||
sideCtrl.Type->setValue("Length");
|
||||
if (side == Side::First) {
|
||||
// Avoid error message
|
||||
double L = sideCtrl.lengthEdit->value().getValue();
|
||||
Side otherSide = side == Side::First ? Side::Second : Side::First;
|
||||
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;
|
||||
case Mode::ToLast:
|
||||
sideCtrl.Type->setValue("UpToLast");
|
||||
break;
|
||||
case Mode::ToFirst:
|
||||
sideCtrl.Type->setValue("UpToFirst");
|
||||
break;
|
||||
case Mode::ToFace:
|
||||
sideCtrl.Type->setValue("UpToFace");
|
||||
if (sideCtrl.lineFaceName->text().isEmpty()) {
|
||||
sideCtrl.buttonFace->setChecked(true);
|
||||
handleLineFaceNameClick(sideCtrl.lineFaceName); // sets placeholder text
|
||||
}
|
||||
break;
|
||||
case Mode::ToShape:
|
||||
sideCtrl.Type->setValue("UpToShape");
|
||||
break;
|
||||
}
|
||||
|
||||
updateUI(index);
|
||||
updateUI(side);
|
||||
recomputeFeature();
|
||||
}
|
||||
|
||||
void TaskPadParameters::apply()
|
||||
{
|
||||
QString facename = QStringLiteral("None");
|
||||
if (static_cast<Mode>(getMode()) == Mode::ToFace) {
|
||||
facename = getFaceName();
|
||||
}
|
||||
applyParameters(facename);
|
||||
applyParameters();
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "TaskExtrudeParameters.h"
|
||||
#include "ViewProviderPad.h"
|
||||
|
||||
class QComboBox;
|
||||
|
||||
namespace App {
|
||||
class Property;
|
||||
@@ -49,9 +50,9 @@ public:
|
||||
void apply() override;
|
||||
|
||||
private:
|
||||
void onModeChanged(int index) override;
|
||||
void translateModeList(int index) override;
|
||||
void updateUI(int index) override;
|
||||
void onModeChanged(int index, Side side) override;
|
||||
void translateModeList(QComboBox* box, int index) override;
|
||||
void updateUI(Side side) override;
|
||||
};
|
||||
|
||||
/// simulation dialog for the TaskView
|
||||
|
||||
@@ -17,13 +17,59 @@
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<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">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="changeMode">
|
||||
<item>
|
||||
<property name="text">
|
||||
@@ -32,14 +78,14 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="labelLength">
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::PrefQuantitySpinBox" name="lengthEdit" native="true">
|
||||
<property name="keyboardTracking" stdset="0">
|
||||
<bool>false</bool>
|
||||
@@ -47,36 +93,16 @@
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="minimum" stdset="0">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" 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">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="labelOffset">
|
||||
<property name="text">
|
||||
<string>Offset to face</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="Gui::PrefQuantitySpinBox" name="offsetEdit" native="true">
|
||||
<property name="keyboardTracking" stdset="0">
|
||||
<bool>false</bool>
|
||||
@@ -86,147 +112,390 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="upToShapeList">
|
||||
<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="_2">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineShapeName">
|
||||
<property name="readOnly">
|
||||
<item row="5" column="0">
|
||||
<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 row="5" column="1">
|
||||
<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>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QFrame" name="upToShapeList">
|
||||
<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="_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>
|
||||
</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 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>
|
||||
<item>
|
||||
<widget class="QWidget" name="upToShapeFaces" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<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="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>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxAllFaces">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Applies length symmetrically to sketch plane</string>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineFaceName">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QToolButton" name="buttonFace">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
<property name="text">
|
||||
<string>Select Face</string>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</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>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineFaceName">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" 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="side2Label">
|
||||
<property name="text">
|
||||
<string>Side 2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line2">
|
||||
<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="9" column="0">
|
||||
<widget class="QLabel" name="typeLabel2">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QToolButton" name="buttonFace">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<item row="9" column="1">
|
||||
<widget class="QComboBox" name="changeMode2"/>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="labelLength2">
|
||||
<property name="text">
|
||||
<string>Select face</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxMidplane">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Applies length symmetrically to sketch plane</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Symmetric to plane</string>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -410,61 +679,6 @@ measured along the specified direction</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
<widget class="QCheckBox" name="checkBoxUpdateView">
|
||||
<property name="text">
|
||||
@@ -494,25 +708,6 @@ measured along the specified direction</string>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
</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/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -38,9 +38,9 @@ using namespace Gui;
|
||||
|
||||
TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidget *parent, bool newObj)
|
||||
: 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"));
|
||||
|
||||
// set the history path
|
||||
@@ -50,6 +50,8 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
|
||||
ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketLength2"));
|
||||
ui->offsetEdit->setEntryName(QByteArray("Offset"));
|
||||
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->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketTaperAngle"));
|
||||
ui->taperEdit2->setEntryName(QByteArray("TaperAngle2"));
|
||||
@@ -65,81 +67,74 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
|
||||
|
||||
TaskPocketParameters::~TaskPocketParameters() = default;
|
||||
|
||||
void TaskPocketParameters::translateModeList(int index)
|
||||
void TaskPocketParameters::translateModeList(QComboBox* box, int index)
|
||||
{
|
||||
ui->changeMode->clear();
|
||||
ui->changeMode->addItem(tr("Dimension"));
|
||||
ui->changeMode->addItem(tr("Through all"));
|
||||
ui->changeMode->addItem(tr("To first"));
|
||||
ui->changeMode->addItem(tr("Up to face"));
|
||||
ui->changeMode->addItem(tr("Two dimensions"));
|
||||
ui->changeMode->addItem(tr("Up to shape"));
|
||||
ui->changeMode->setCurrentIndex(index);
|
||||
box->clear();
|
||||
box->addItem(tr("Dimension"));
|
||||
box->addItem(tr("Through all"));
|
||||
box->addItem(tr("To first"));
|
||||
box->addItem(tr("Up to face"));
|
||||
box->addItem(tr("Up to shape"));
|
||||
box->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void TaskPocketParameters::updateUI(int index)
|
||||
void TaskPocketParameters::updateUI(Side side)
|
||||
{
|
||||
// update direction combobox
|
||||
fillDirectionCombo();
|
||||
// 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)) {
|
||||
case Mode::Dimension:
|
||||
// Why? See below for "UpToFace"
|
||||
if (oldLength < Precision::Confusion())
|
||||
oldLength = 5.0;
|
||||
pcPocket->Length.setValue(oldLength);
|
||||
ui->lengthEdit->setValue(oldLength);
|
||||
pcPocket->Type.setValue("Length");
|
||||
sideCtrl.Type->setValue("Length");
|
||||
if (side == Side::First) {
|
||||
// Avoid error message
|
||||
double L = sideCtrl.lengthEdit->value().getValue();
|
||||
Side otherSide = side == Side::First ? Side::Second : Side::First;
|
||||
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;
|
||||
case Mode::ThroughAll:
|
||||
oldLength = pcPocket->Length.getValue();
|
||||
pcPocket->Type.setValue("ThroughAll");
|
||||
sideCtrl.Type->setValue("ThroughAll");
|
||||
break;
|
||||
case Mode::ToFirst:
|
||||
oldLength = pcPocket->Length.getValue();
|
||||
pcPocket->Type.setValue("UpToFirst");
|
||||
sideCtrl.Type->setValue("UpToFirst");
|
||||
break;
|
||||
case Mode::ToFace:
|
||||
// Note: ui->checkBoxReversed is purposely enabled because the selected face
|
||||
// could be a circular one around the sketch
|
||||
// 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!
|
||||
oldLength = pcPocket->Length.getValue();
|
||||
pcPocket->Type.setValue("UpToFace");
|
||||
pcPocket->Length.setValue(0.0);
|
||||
ui->lengthEdit->setValue(0.0);
|
||||
if (ui->lineFaceName->text().isEmpty()) {
|
||||
ui->buttonFace->setChecked(true);
|
||||
handleLineFaceNameClick(); // sets placeholder text
|
||||
sideCtrl.Type->setValue("UpToFace");
|
||||
if (sideCtrl.lineFaceName->text().isEmpty()) {
|
||||
sideCtrl.buttonFace->setChecked(true);
|
||||
handleLineFaceNameClick(sideCtrl.lineFaceName); // sets placeholder text
|
||||
}
|
||||
break;
|
||||
case Mode::TwoDimensions:
|
||||
oldLength = pcPocket->Length.getValue();
|
||||
pcPocket->Type.setValue("TwoLengths");
|
||||
break;
|
||||
case Mode::ToShape:
|
||||
pcPocket->Type.setValue("UpToShape");
|
||||
sideCtrl.Type->setValue("UpToShape");
|
||||
break;
|
||||
}
|
||||
|
||||
updateUI(index);
|
||||
updateUI(side);
|
||||
recomputeFeature();
|
||||
}
|
||||
|
||||
void TaskPocketParameters::apply()
|
||||
{
|
||||
QString facename = QStringLiteral("None");
|
||||
if (static_cast<Mode>(getMode()) == Mode::ToFace) {
|
||||
facename = getFaceName();
|
||||
}
|
||||
applyParameters(facename);
|
||||
applyParameters();
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "TaskExtrudeParameters.h"
|
||||
#include "ViewProviderPocket.h"
|
||||
|
||||
class QComboBox;
|
||||
|
||||
namespace App {
|
||||
class Property;
|
||||
@@ -49,12 +50,9 @@ public:
|
||||
void apply() override;
|
||||
|
||||
private:
|
||||
void onModeChanged(int index) override;
|
||||
void translateModeList(int index) override;
|
||||
void updateUI(int index) override;
|
||||
|
||||
private:
|
||||
double oldLength;
|
||||
void onModeChanged(int index, Side side) override;
|
||||
void translateModeList(QComboBox* box, int index) override;
|
||||
void updateUI(Side side) override;
|
||||
};
|
||||
|
||||
/// simulation dialog for the TaskView
|
||||
|
||||
@@ -385,7 +385,8 @@ void TaskRevolutionParameters::onSelectionChanged(const Gui::SelectionChanges& m
|
||||
if (msg.Type == Gui::SelectionChanges::AddSelection) {
|
||||
int mode = ui->changeMode->currentIndex();
|
||||
if (selectionFace) {
|
||||
QString refText = onAddSelection(msg);
|
||||
auto rev = getObject<PartDesign::Revolution>();
|
||||
QString refText = onAddSelection(msg, rev->UpToFace);
|
||||
if (refText.length() > 0) {
|
||||
QSignalBlocker block(ui->lineFaceName);
|
||||
ui->lineFaceName->setText(refText);
|
||||
|
||||
@@ -58,7 +58,7 @@ TaskSketchBasedParameters::TaskSketchBasedParameters(PartDesignGui::ViewProvider
|
||||
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
|
||||
auto sketchBased = getObject<PartDesign::ProfileBased>();
|
||||
@@ -81,7 +81,7 @@ const QString TaskSketchBasedParameters::onAddSelection(const Gui::SelectionChan
|
||||
}
|
||||
|
||||
std::vector<std::string> upToFaces(1,subname);
|
||||
sketchBased->UpToFace.setValue(selObj, upToFaces);
|
||||
prop.setValue(selObj, upToFaces);
|
||||
recomputeFeature();
|
||||
|
||||
return refStr;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
namespace App {
|
||||
class Property;
|
||||
class PropertyLinkSubList;
|
||||
}
|
||||
|
||||
namespace PartDesignGui {
|
||||
@@ -51,7 +52,7 @@ public:
|
||||
|
||||
protected:
|
||||
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 finishReferenceSelection(App::DocumentObject* profile, App::DocumentObject* base);
|
||||
/*!
|
||||
|
||||
@@ -178,7 +178,7 @@ class TestPad(unittest.TestCase):
|
||||
self.Pad1 = self.Doc.addObject("PartDesign::Pad", "Pad1")
|
||||
self.Body.addObject(self.Pad1)
|
||||
self.Pad1.Profile = self.PadSketch1
|
||||
self.Pad1.Type = 4
|
||||
self.Pad1.SideType = 1
|
||||
self.Pad1.Length = 1.0
|
||||
self.Pad1.Length2 = 2.0
|
||||
self.Pad1.Reversed = 1
|
||||
@@ -238,7 +238,7 @@ class TestPad(unittest.TestCase):
|
||||
self.Pad1 = self.Doc.addObject("PartDesign::Pad", "Pad1")
|
||||
self.Body.addObject(self.Pad1)
|
||||
self.Pad1.Profile = self.PadSketch1
|
||||
self.Pad1.Type = 5
|
||||
self.Pad1.Type = 4
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(self.Pad1.Shape.Volume, 2.58787, places=4)
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ TEST_F(PadTest, TestMidPlaneTwoLength)
|
||||
pad->Length.setValue(10.0);
|
||||
pad->Length2.setValue(20.0);
|
||||
|
||||
pad->Type.setValue("TwoLengths");
|
||||
pad->SideType.setValue("Two sides");
|
||||
|
||||
doc->recompute();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user