From 796f5b1cfd079863a2bd327a5a405bf486f59325 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 22 Jan 2025 20:24:14 +0100 Subject: [PATCH 1/9] PD: Positive offset on a binder shouldn't turn holes into squares Fixes issue 18776 --- src/Mod/Part/App/TopoShapeExpansion.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 0b38d2be82..d4ad8cfe9f 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2697,8 +2697,15 @@ TopoShape& TopoShape::makeElementOffset2D(const TopoShape& shape, if (shape.getShape().ShapeType() == TopAbs_COMPOUND) { if (!intersection) { // simply recursively process the children, independently - expandCompound(shape, shapesToProcess); - outputPolicy = SingleShapeCompoundCreationPolicy::forceCompound; + for(TopoDS_Iterator it(shape.getShape()); it.More() ; it.Next()) { + shapesToReturn.push_back(TopoShape(it.Value()).makeElementOffset2D(offset, + joinType, + fill, + allowOpenResult, + intersection, + op)); + outputPolicy = SingleShapeCompoundCreationPolicy::forceCompound; + } } else { // collect non-compounds from this compound for collective offset. Process other shapes From a2c5788a98be64dd8c64acbea0b3ad435166e44c Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 31 Jan 2025 15:06:59 +0100 Subject: [PATCH 2/9] PD: SubtractivePipe Fails Fixes issue 18003 --- src/Mod/PartDesign/App/FeaturePipe.cpp | 137 +++++++++++++------------ 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/src/Mod/PartDesign/App/FeaturePipe.cpp b/src/Mod/PartDesign/App/FeaturePipe.cpp index b31b8fac14..12bcdf6815 100644 --- a/src/Mod/PartDesign/App/FeaturePipe.cpp +++ b/src/Mod/PartDesign/App/FeaturePipe.cpp @@ -40,6 +40,7 @@ # include +#include #include #include #include @@ -151,7 +152,6 @@ App::DocumentObjectExecReturn *Pipe::execute() // As the shell begins always at the spine and not the profile, the sketchshape // cannot be used directly as front face. We would need a method to translate // the front shape to match the shell starting position somehow... - std::vector wires; TopoDS_Shape profilePoint; // if the Base property has a valid shape, fuse the pipe into it @@ -162,6 +162,8 @@ App::DocumentObjectExecReturn *Pipe::execute() base = TopoShape(); } + auto hasher = getDocument()->getStringHasher(); + try { // setup the location this->positionByPrevious(); @@ -287,7 +289,7 @@ App::DocumentObjectExecReturn *Pipe::execute() "Exception", "Path must not be a null shape")); // build all shells - std::vector shells; + std::vector shells; TopoDS_Shape copyProfilePoint(profilePoint); if (!profilePoint.IsNull()) @@ -320,7 +322,7 @@ App::DocumentObjectExecReturn *Pipe::execute() if (!mkPS.IsReady()) return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Pipe could not be built")); - shells.push_back(mkPS.Shape()); + shells.emplace_back(mkPS.Shape()); if (!mkPS.Shape().Closed()) { // shell is not closed - use simulate to get the end wires @@ -336,8 +338,7 @@ App::DocumentObjectExecReturn *Pipe::execute() } } - BRepBuilderAPI_MakeSolid mkSolid; - + TopoShape result(0, hasher); if (!frontwires.empty() || !backwires.empty()) { BRepBuilderAPI_Sewing sewer; sewer.SetTolerance(Precision::Confusion()); @@ -346,94 +347,103 @@ App::DocumentObjectExecReturn *Pipe::execute() if (!frontwires.empty()) { TopoDS_Shape front = Part::FaceMakerCheese::makeFace(frontwires); sewer.Add(front); + shells.emplace_back(front); } if (!backwires.empty()) { TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires); sewer.Add(back); + shells.emplace_back(back); } - for (TopoDS_Shape& s : shells) - sewer.Add(s); + for (TopoShape& s : shells) + sewer.Add(s.getShape()); sewer.Perform(); - mkSolid.Add(TopoDS::Shell(sewer.SewedShape())); } else { + result = result.makeShapeWithElementMap(sewer.SewedShape(), Part::MapperSewing(sewer), shells, Part::OpCodes::Sewing); + } else { // shells are already closed - add them directly - for (TopoDS_Shape& s : shells) { - mkSolid.Add(TopoDS::Shell(s)); + BRepBuilderAPI_MakeSolid mkSolid; + for (TopoShape& s : shells) { + mkSolid.Add(TopoDS::Shell(s.getShape())); } + + if (!mkSolid.IsDone()) + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result is not a solid")); + result.setShape(mkSolid.Shape()); } - if (!mkSolid.IsDone()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result is not a solid")); + if(!result.countSubShapes(TopAbs_SHELL)) + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Loft: Failed to create shell")); - TopoDS_Shape result = mkSolid.Shape(); - BRepClass3d_SolidClassifier SC(result); - SC.PerformInfinitePoint(Precision::Confusion()); - if (SC.State() == TopAbs_IN) { - result.Reverse(); + auto shapes = result.getSubTopoShapes(TopAbs_SHELL); + for (auto &s : shapes) { + // build the solid + s = s.makeElementSolid(); + BRepClass3d_SolidClassifier SC(s.getShape()); + SC.PerformInfinitePoint(Precision::Confusion()); + if ( SC.State() == TopAbs_IN) + s.setShape(s.getShape().Reversed(),false); } - //result.Move(invObjLoc); - AddSubShape.setValue(result); // Converts result to a TopoShape, but no tag. + AddSubShape.setValue(result.makeElementCompound(shapes, nullptr, Part::TopoShape::SingleShapeCompoundCreationPolicy::returnShape)); - if (base.isNull()) { + if (shapes.size() > 1) + result.makeElementFuse(shapes); + else + result = shapes.front(); + + if(base.isNull()) { if (getAddSubType() == FeatureAddSub::Subtractive) return new App::DocumentObjectExecReturn( QT_TRANSLATE_NOOP("Exception", "Pipe: There is nothing to subtract from")); + if (!isSingleSolidRuleSatisfied(result.getShape())) { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: enable 'Allow Compound' in the active body.")); + } + // store shape before refinement this->rawShape = result; - auto ts_result = refineShapeIfActive(result); - Shape.setValue(getSolid(ts_result)); + + result = refineShapeIfActive(result); + Shape.setValue(getSolid(result)); return App::DocumentObject::StdReturn; } - if (getAddSubType() == FeatureAddSub::Additive) { - - FCBRepAlgoAPI_Fuse mkFuse(base.getShape(), result); - if (!mkFuse.IsDone()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Adding the pipe failed")); - // we have to get the solids (fuse sometimes creates compounds) - TopoShape boolOp = this->getSolid(mkFuse.Shape()); - // lets check if the result is a solid - if (boolOp.isNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid")); - - if (!isSingleSolidRuleSatisfied(boolOp.getShape())) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", - "Result has multiple solids: enable 'Allow Compound' in the active body.")); - } - - // store shape before refinement - this->rawShape = boolOp; - boolOp = refineShapeIfActive(boolOp); - Shape.setValue(getSolid(boolOp)); + TopoShape boolOp(0, getDocument()->getStringHasher()); + const char *maker; + switch (getAddSubType()) { + case Additive: + maker = Part::OpCodes::Fuse; + break; + case Subtractive: + maker = Part::OpCodes::Cut; + break; + default: + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Unknown operation type")); } - else if (getAddSubType() == FeatureAddSub::Subtractive) { - - FCBRepAlgoAPI_Cut mkCut(base.getShape(), result); - if (!mkCut.IsDone()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Subtracting the pipe failed")); - // we have to get the solids (fuse sometimes creates compounds) - TopoShape boolOp = this->getSolid(mkCut.Shape()); - // lets check if the result is a solid - if (boolOp.isNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid")); - - if (!isSingleSolidRuleSatisfied(boolOp.getShape())) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", - "Result has multiple solids: enable 'Allow Compound' in the active body.")); - } - - // store shape before refinement - this->rawShape = boolOp; - boolOp = refineShapeIfActive(boolOp); - Shape.setValue(getSolid(boolOp)); + try { + boolOp.makeElementBoolean(maker, {base,result}); + } + catch(Standard_Failure&) { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Failed to perform boolean operation")); } + TopoShape solid = getSolid(boolOp); + // lets check if the result is a solid + if (solid.isNull()) + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid")); + + // store shape before refinement + this->rawShape = boolOp; + boolOp = refineShapeIfActive(boolOp); + if (!isSingleSolidRuleSatisfied(boolOp.getShape())) { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", + "Result has multiple solids: enable 'Allow Compound' in the active body.")); + } + boolOp = getSolid(boolOp); + Shape.setValue(boolOp); return App::DocumentObject::StdReturn; } catch (Standard_Failure& e) { - return new App::DocumentObjectExecReturn(e.GetMessageString()); } catch (...) { @@ -636,4 +646,3 @@ void Pipe::handleChangedPropertyName(Base::XMLReader& reader, } } - From a539e5e460810dbfc50a9411bddada644de314ac Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 5 Feb 2025 00:29:40 +0100 Subject: [PATCH 3/9] PD: Fix support of datum lines in draft --- src/Mod/PartDesign/App/FeatureDraft.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureDraft.cpp b/src/Mod/PartDesign/App/FeatureDraft.cpp index b94a17b78f..24ce5544f9 100644 --- a/src/Mod/PartDesign/App/FeatureDraft.cpp +++ b/src/Mod/PartDesign/App/FeatureDraft.cpp @@ -126,9 +126,13 @@ App::DocumentObjectExecReturn *Draft::execute() App::DocumentObject* refDirection = PullDirection.getValue(); if (refDirection) { if (refDirection->isDerivedFrom()) { - PartDesign::Line* line = static_cast(refDirection); - Base::Vector3d d = line->getDirection(); - pullDirection = gp_Dir(d.x, d.y, d.z); + PartDesign::Line* line = static_cast(refDirection); + Base::Vector3d d = line->getDirection(); + pullDirection = gp_Dir(d.x, d.y, d.z); + } else if (refDirection->isDerivedFrom()) { + App::Line* line = static_cast(refDirection); + Base::Vector3d d = line->getDirection(); + pullDirection = gp_Dir(d.x, d.y, d.z); } else if (refDirection->isDerivedFrom()) { std::vector subStrings = PullDirection.getSubValues(); if (subStrings.empty() || subStrings[0].empty()) From cf0412b7e2954ef548d800c71ff1c816a5635233 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 5 Feb 2025 13:10:00 +0100 Subject: [PATCH 4/9] PD: Support of plane in linear pattern feature --- src/Mod/PartDesign/App/FeatureLinearPattern.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp index 7a0aaaf318..5e69fb4744 100644 --- a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp +++ b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp @@ -393,6 +393,10 @@ gp_Dir LinearPattern::getDirectionFromProperty(const App::PropertyLinkSub& dirPr Base::Vector3d d = line->getDirection(); dir = gp_Dir(d.x, d.y, d.z); } + else if (auto* plane = freecad_cast(refObject)) { + Base::Vector3d d = plane->getDirection(); + dir = gp_Dir(d.x, d.y, d.z); + } else if (auto* line = freecad_cast(refObject)) { Base::Vector3d d = line->getDirection(); dir = gp_Dir(d.x, d.y, d.z); From 5a3fafcc55ce7597100d23575484a2b124900ae1 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 18 May 2025 23:06:10 +0200 Subject: [PATCH 5/9] PD: Remove unneeded class member 'isApplying' from 'TaskHoleParameters' --- src/Mod/PartDesign/Gui/TaskHoleParameters.cpp | 5 ----- src/Mod/PartDesign/Gui/TaskHoleParameters.h | 1 - 2 files changed, 6 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp b/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp index 4dd128adb8..bdb1b32026 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.cpp @@ -57,7 +57,6 @@ namespace sp = std::placeholders; TaskHoleParameters::TaskHoleParameters(ViewProviderHole* HoleView, QWidget* parent) : TaskSketchBasedParameters(HoleView, parent, "PartDesign_Hole", tr("Hole Parameters")) , observer(new Observer(this, getObject())) - , isApplying(false) , ui(new Ui_TaskHoleParameters) { // we need a separate container widget to add all controls to @@ -1087,8 +1086,6 @@ void TaskHoleParameters::apply() { auto hole = getObject(); - isApplying = true; - ui->Diameter->apply(); ui->HoleCutDiameter->apply(); ui->HoleCutDepth->apply(); @@ -1152,8 +1149,6 @@ void TaskHoleParameters::apply() if (!hole->BaseProfileType.isReadOnly()) { FCMD_OBJ_CMD(hole, "BaseProfileType = " << getBaseProfileType()); } - - isApplying = false; } void TaskHoleParameters::updateHoleCutLimits(PartDesign::Hole* hole) diff --git a/src/Mod/PartDesign/Gui/TaskHoleParameters.h b/src/Mod/PartDesign/Gui/TaskHoleParameters.h index e0f22aa527..2e54089110 100644 --- a/src/Mod/PartDesign/Gui/TaskHoleParameters.h +++ b/src/Mod/PartDesign/Gui/TaskHoleParameters.h @@ -143,7 +143,6 @@ private: Connection connectPropChanged; std::unique_ptr observer; - bool isApplying; QWidget* proxy; std::unique_ptr ui; From 4d80f3ec28ef1ae209d1c1821a205f3da47ce43c Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 17 Feb 2025 12:15:31 +0100 Subject: [PATCH 6/9] PD: Improve revolution feature * Allow a minimum angle of 0.0 as this is needed in 'Two Angles' mode * Set the default value of Angle2 to 0.0 * Check for valid input in 'Angle' and 'Two Angles' mode * Replace the confusing enum labels 'Dimension' and 'TwoDimensions' --- src/Mod/PartDesign/App/FeatureRevolution.cpp | 58 +++++++++++-------- src/Mod/PartDesign/App/FeatureRevolution.h | 4 +- .../Gui/TaskRevolutionParameters.cpp | 16 ++--- .../Gui/TaskRevolutionParameters.ui | 4 +- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureRevolution.cpp b/src/Mod/PartDesign/App/FeatureRevolution.cpp index 62e516fb4c..9133bdda23 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.cpp +++ b/src/Mod/PartDesign/App/FeatureRevolution.cpp @@ -30,9 +30,7 @@ # include -#include #include -#include #include #include "FeatureRevolution.h" @@ -42,11 +40,13 @@ using namespace PartDesign; namespace PartDesign { +/* TRANSLATOR PartDesign::Revolution */ + const char* Revolution::TypeEnums[]= {"Angle", "UpToLast", "UpToFirst", "UpToFace", "TwoAngles", nullptr}; PROPERTY_SOURCE(PartDesign::Revolution, PartDesign::ProfileBased) -const App::PropertyAngle::Constraints Revolution::floatAngle = { Base::toDegrees(Precision::Angular()), 360.0, 1.0 }; +const App::PropertyAngle::Constraints Revolution::floatAngle = { 0.0, 360.0, 1.0 }; Revolution::Revolution() { @@ -54,14 +54,14 @@ Revolution::Revolution() ADD_PROPERTY_TYPE(Type, (0L), "Revolution", App::Prop_None, "Revolution type"); Type.setEnums(TypeEnums); - ADD_PROPERTY_TYPE(Base,(Base::Vector3d(0.0,0.0,0.0)),"Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Base"); - ADD_PROPERTY_TYPE(Axis,(Base::Vector3d(0.0,1.0,0.0)),"Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Axis"); - ADD_PROPERTY_TYPE(Angle,(360.0),"Revolution", App::Prop_None, "Angle"); - ADD_PROPERTY_TYPE(Angle2, (60.0), "Revolution", App::Prop_None, "Revolution length in 2nd direction"); + ADD_PROPERTY_TYPE(Base, (Base::Vector3d(0.0,0.0,0.0)), "Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Base"); + ADD_PROPERTY_TYPE(Axis, (Base::Vector3d(0.0,1.0,0.0)), "Revolution", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Axis"); + ADD_PROPERTY_TYPE(Angle, (360.0), "Revolution", App::Prop_None, "Angle"); + ADD_PROPERTY_TYPE(Angle2, (0.0), "Revolution", App::Prop_None, "Revolution length in 2nd direction"); ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Revolution", App::Prop_None, "Face where revolution will end"); Angle.setConstraints(&floatAngle); Angle2.setConstraints(&floatAngle); - ADD_PROPERTY_TYPE(ReferenceAxis,(nullptr),"Revolution",(App::Prop_None),"Reference axis of revolution"); + ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Revolution", (App::Prop_None), "Reference axis of revolution"); } short Revolution::mustExecute() const @@ -82,21 +82,27 @@ App::DocumentObjectExecReturn* Revolution::execute() if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } + constexpr double maxDegree = 360.0; + auto method = methodFromString(Type.getValueAsString()); + // Validate parameters - // All angles are in radians unless explicitly stated double angleDeg = Angle.getValue(); - if (angleDeg > 360.0) { + if (angleDeg > maxDegree) { return new App::DocumentObjectExecReturn( QT_TRANSLATE_NOOP("Exception", "Angle of revolution too large")); } double angle = Base::toRadians(angleDeg); - if (angle < Precision::Angular()) { + if (angle < Precision::Angular() && method == RevolMethod::Angle) { return new App::DocumentObjectExecReturn( QT_TRANSLATE_NOOP("Exception", "Angle of revolution too small")); } double angle2 = Base::toRadians(Angle2.getValue()); + if (std::fabs(angle + angle2) < Precision::Angular() && method == RevolMethod::TwoAngles) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Angles of revolution nullify each other")); + } TopoShape sketchshape = getTopoShapeVerifiedFace(); @@ -135,10 +141,8 @@ App::DocumentObjectExecReturn* Revolution::execute() QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed")); } - RevolMethod method = methodFromString(Type.getValueAsString()); - this->positionByPrevious(); - TopLoc_Location invObjLoc = this->getLocation().Inverted(); + auto invObjLoc = getLocation().Inverted(); pnt.Transform(invObjLoc.Transformation()); dir.Transform(invObjLoc.Transformation()); base.move(invObjLoc); @@ -166,8 +170,7 @@ App::DocumentObjectExecReturn* Revolution::execute() supportface.move(invObjLoc); - if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst - || method == RevolMethod::ToLast) { + if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst) { TopoShape upToFace; if (method == RevolMethod::ToFace) { getUpToFaceFromLinkSub(upToFace, UpToFace); @@ -194,7 +197,7 @@ App::DocumentObjectExecReturn* Revolution::execute() TopoDS::Face(supportface.getShape()), TopoDS::Face(upToFace.getShape()), nullptr, - Part::RevolMode::None, + Part::RevolMode::FuseWithBase, Standard_True); } catch (Standard_Failure&) { @@ -286,7 +289,7 @@ void Revolution::updateAxis() Revolution::RevolMethod Revolution::methodFromString(const std::string& methodStr) { if (methodStr == "Angle") - return RevolMethod::Dimension; + return RevolMethod::Angle; if (methodStr == "UpToLast") return RevolMethod::ToLast; if (methodStr == "ThroughAll") @@ -296,10 +299,9 @@ Revolution::RevolMethod Revolution::methodFromString(const std::string& methodSt if (methodStr == "UpToFace") return RevolMethod::ToFace; if (methodStr == "TwoAngles") - return RevolMethod::TwoDimensions; + return RevolMethod::TwoAngles; throw Base::ValueError("Revolution:: No such method"); - return RevolMethod::Dimension; } void Revolution::generateRevolution(TopoShape& revol, @@ -311,15 +313,18 @@ void Revolution::generateRevolution(TopoShape& revol, const bool reversed, RevolMethod method) { - if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) { + if (method == RevolMethod::Angle || method == RevolMethod::TwoAngles || method == RevolMethod::ThroughAll) { double angleTotal = angle; double angleOffset = 0.; - if (method == RevolMethod::TwoDimensions) { + if (method == RevolMethod::TwoAngles) { // Rotate the face by `angle2`/`angle` to get "second" angle angleTotal += angle2; angleOffset = angle2 * -1.0; } + else if (method == RevolMethod::ThroughAll) { + angleTotal = 2 * M_PI; + } else if (midplane) { // Rotate the face by half the angle to get Revolution symmetric to sketch plane angleOffset = -angle / 2; @@ -334,13 +339,16 @@ void Revolution::generateRevolution(TopoShape& revol, } TopoShape from = sketchshape; - if (method == RevolMethod::TwoDimensions || midplane) { + if (method == RevolMethod::TwoAngles || midplane) { gp_Trsf mov; mov.SetRotation(revolAx, angleOffset); TopLoc_Location loc(mov); from.move(loc); } + // revolve the face to a solid + // BRepPrimAPI is the only option that allows use of this shape for patterns. + // See https://forum.freecadweb.org/viewtopic.php?f=8&t=70185&p=611673#p611673. revol = from; revol = revol.makeElementRevolve(revolAx,angleTotal); revol.Tag = -getID(); @@ -381,7 +389,7 @@ void Revolution::updateProperties(RevolMethod method) bool isMidplaneEnabled = false; bool isReversedEnabled = false; bool isUpToFaceEnabled = false; - if (method == RevolMethod::Dimension) { + if (method == RevolMethod::Angle) { isAngleEnabled = true; isMidplaneEnabled = true; isReversedEnabled = !Midplane.getValue(); @@ -400,7 +408,7 @@ void Revolution::updateProperties(RevolMethod method) isReversedEnabled = true; isUpToFaceEnabled = true; } - else if (method == RevolMethod::TwoDimensions) { + else if (method == RevolMethod::TwoAngles) { isAngleEnabled = true; isAngle2Enabled = true; isReversedEnabled = true; diff --git a/src/Mod/PartDesign/App/FeatureRevolution.h b/src/Mod/PartDesign/App/FeatureRevolution.h index c6ef8336b8..95c3d64edd 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.h +++ b/src/Mod/PartDesign/App/FeatureRevolution.h @@ -69,12 +69,12 @@ public: bool suggestReversed(); enum class RevolMethod { - Dimension, + Angle, ThroughAll, ToLast = ThroughAll, ToFirst, ToFace, - TwoDimensions + TwoAngles }; protected: diff --git a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp index fdfa4f0557..1eabe7811c 100644 --- a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp @@ -188,7 +188,7 @@ void TaskRevolutionParameters::setupDialog() void TaskRevolutionParameters::translateModeList(int index) { ui->changeMode->clear(); - ui->changeMode->addItem(tr("Dimension")); + ui->changeMode->addItem(tr("Angle")); if (!isGroove) { ui->changeMode->addItem(tr("To last")); } @@ -197,7 +197,7 @@ void TaskRevolutionParameters::translateModeList(int index) } ui->changeMode->addItem(tr("To first")); ui->changeMode->addItem(tr("Up to face")); - ui->changeMode->addItem(tr("Two dimensions")); + ui->changeMode->addItem(tr("Two angles")); ui->changeMode->setCurrentIndex(index); } @@ -294,7 +294,7 @@ void TaskRevolutionParameters::setCheckboxes(PartDesign::Revolution::RevolMethod bool isReversedEnabled = false; bool isFaceEditEnabled = false; - if (mode == PartDesign::Revolution::RevolMethod::Dimension) { + if (mode == PartDesign::Revolution::RevolMethod::Angle) { isRevolveAngleVisible = true; ui->revolveAngle->selectNumber(); QMetaObject::invokeMethod(ui->revolveAngle, "setFocus", Qt::QueuedConnection); @@ -320,7 +320,7 @@ void TaskRevolutionParameters::setCheckboxes(PartDesign::Revolution::RevolMethod ui->buttonFace->setChecked(true); } } - else if (mode == PartDesign::Revolution::RevolMethod::TwoDimensions) { + else if (mode == PartDesign::Revolution::RevolMethod::TwoAngles) { isRevolveAngleVisible = true; isRevolveAngle2Visible = true; isReversedEnabled = true; @@ -624,7 +624,7 @@ void TaskRevolutionParameters::onModeChanged(int index) : &(getObject()->Type); switch (static_cast(index)) { - case PartDesign::Revolution::RevolMethod::Dimension: + case PartDesign::Revolution::RevolMethod::Angle: propEnum->setValue("Angle"); break; case PartDesign::Revolution::RevolMethod::ToLast: @@ -636,7 +636,7 @@ void TaskRevolutionParameters::onModeChanged(int index) case PartDesign::Revolution::RevolMethod::ToFace: propEnum->setValue("UpToFace"); break; - case PartDesign::Revolution::RevolMethod::TwoDimensions: + case PartDesign::Revolution::RevolMethod::TwoAngles: propEnum->setValue("TwoAngles"); break; } @@ -809,12 +809,12 @@ void TaskRevolutionParameters::setGizmoVisibility() auto type = static_cast(ui->changeMode->currentIndex()); switch (type) { - case PartDesign::Revolution::RevolMethod::Dimension: + case PartDesign::Revolution::RevolMethod::Angle: gizmoContainer->visible = true; rotationGizmo->setVisibility(true); rotationGizmo2->setVisibility(false); break; - case PartDesign::Revolution::RevolMethod::TwoDimensions: + case PartDesign::Revolution::RevolMethod::TwoAngles: gizmoContainer->visible = true; rotationGizmo->setVisibility(true); rotationGizmo2->setVisibility(true); diff --git a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui index e858efa474..903e59de14 100644 --- a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui @@ -27,7 +27,7 @@ - Dimension + Angle @@ -139,7 +139,7 @@ 10.000000000000000 - 60.000000000000000 + 0.000000000000000 From df5c75d6b008a29d01bb1d59e1f19733b6a1d7ae Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 17 Feb 2025 12:27:20 +0100 Subject: [PATCH 7/9] PD: Improve groove feature The only difference between groove and revolution is that for the former the revolved face is removed from the base shape instead of added. Thus, the code of the Revolution and Groove classes should be almost identical. This allows it in a further step to refactor the code and make a common base class. This fixes issue 18842. --- src/Mod/PartDesign/App/FeatureGroove.cpp | 328 +++++++++++++---------- src/Mod/PartDesign/App/FeatureGroove.h | 12 +- 2 files changed, 188 insertions(+), 152 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureGroove.cpp b/src/Mod/PartDesign/App/FeatureGroove.cpp index d9b1f57167..fd042cdcd5 100644 --- a/src/Mod/PartDesign/App/FeatureGroove.cpp +++ b/src/Mod/PartDesign/App/FeatureGroove.cpp @@ -25,10 +25,9 @@ # include # include # include -# include -# include # include - +# include +# include #include #include @@ -36,7 +35,6 @@ #include "FeatureGroove.h" #include "Mod/Part/App/TopoShapeOpCode.h" - using namespace PartDesign; namespace PartDesign { @@ -47,7 +45,7 @@ const char* Groove::TypeEnums[]= {"Angle", "ThroughAll", "UpToFirst", "UpToFace" PROPERTY_SOURCE(PartDesign::Groove, PartDesign::ProfileBased) -const App::PropertyAngle::Constraints Groove::floatAngle = { Base::toDegrees(Precision::Angular()), 360.0, 1.0 }; +const App::PropertyAngle::Constraints Groove::floatAngle = { 0.0, 360.0, 1.0 }; Groove::Groove() { @@ -58,10 +56,10 @@ Groove::Groove() ADD_PROPERTY_TYPE(Base, (Base::Vector3d(0.0f,0.0f,0.0f)), "Groove", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Base"); ADD_PROPERTY_TYPE(Axis, (Base::Vector3d(0.0f,1.0f,0.0f)), "Groove", App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden), "Axis"); ADD_PROPERTY_TYPE(Angle, (360.0),"Groove", App::Prop_None, "Angle"); - ADD_PROPERTY_TYPE(Angle2, (60.0), "Groove", App::Prop_None, "Groove length in 2nd direction"); + ADD_PROPERTY_TYPE(Angle2, (0.0), "Groove", App::Prop_None, "Groove length in 2nd direction"); ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Groove", App::Prop_None, "Face where groove will end"); Angle.setConstraints(&floatAngle); - ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Groove", (App::PropertyType)(App::Prop_None), "Reference axis of Groove"); + ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Groove", (App::Prop_None), "Reference axis of groove"); } short Groove::mustExecute() const @@ -81,57 +79,63 @@ App::DocumentObjectExecReturn *Groove::execute() { if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } + constexpr double maxDegree = 360.0; + auto method = methodFromString(Type.getValueAsString()); + // Validate parameters - double angle = Angle.getValue(); - if (angle > 360.0) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too large")); - - angle = Base::toRadians(angle); - if (angle < Precision::Angular()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of groove too small")); - - // Reverse angle if selected - if (Reversed.getValue() && !Midplane.getValue()) - angle *= (-1.0); - - TopoShape sketchshape; - try { - sketchshape = getTopoShapeVerifiedFace(); - } catch (const Base::Exception& e) { - return new App::DocumentObjectExecReturn(e.what()); + double angleDeg = Angle.getValue(); + if (angleDeg > maxDegree) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Angle of groove too large")); } - // if the Base property has a valid shape, fuse the prism into it + double angle = Base::toRadians(angleDeg); + if (angle < Precision::Angular() && method == RevolMethod::Angle) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Angle of groove too small")); + } + + double angle2 = Base::toRadians(Angle2.getValue()); + if (std::fabs(angle + angle2) < Precision::Angular() && method == RevolMethod::TwoAngles) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Angles of groove nullify each other")); + } + + TopoShape sketchshape = getTopoShapeVerifiedFace(); + + // if the Base property has a valid shape, fuse the AddShape into it TopoShape base; try { base = getBaseTopoShape(); } catch (const Base::Exception&) { - std::string text(QT_TRANSLATE_NOOP("Exception", "The requested feature cannot be created. The reason may be that:\n" - " - the active Body does not contain a base shape, so there is no\n" - " material to be removed;\n" - " - the selected sketch does not belong to the active Body.")); - return new App::DocumentObjectExecReturn(text); + // fall back to support (for legacy features) } - updateAxis(); - - // get revolve axis - Base::Vector3d b = Base.getValue(); - gp_Pnt pnt(b.x,b.y,b.z); - Base::Vector3d v = Axis.getValue(); - gp_Dir dir(v.x,v.y,v.z); + // update Axis from ReferenceAxis + try { + updateAxis(); + } + catch (const Base::Exception& e) { + return new App::DocumentObjectExecReturn(e.what()); + } try { - if (sketchshape.isNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed")); + // get revolve axis + Base::Vector3d b = Base.getValue(); + gp_Pnt pnt(b.x, b.y, b.z); + Base::Vector3d v = Axis.getValue(); - // Rotate the face by half the angle to get Groove symmetric to sketch plane - if (Midplane.getValue()) { - gp_Trsf mov; - mov.SetRotation(gp_Ax1(pnt, dir), Base::toRadians(Angle.getValue()) * (-1.0) / 2.0); - TopLoc_Location loc(mov); - sketchshape.move(loc); + if (v.IsNull()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Reference axis is invalid")); + } + + gp_Dir dir(v.x, v.y, v.z); + + if (sketchshape.isNull()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed")); } this->positionByPrevious(); @@ -144,66 +148,112 @@ App::DocumentObjectExecReturn *Groove::execute() // Check distance between sketchshape and axis - to avoid failures and crashes TopExp_Explorer xp; xp.Init(sketchshape.getShape(), TopAbs_FACE); - for (;xp.More(); xp.Next()) { - if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current()))) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch")); - } - - // revolve the face to a solid - TopoShape result(0); - try { - result.makeElementRevolve(sketchshape, gp_Ax1(pnt, dir), angle); - }catch(Standard_Failure &) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!")); - } - this->AddSubShape.setValue(result); - - if(base.isNull()) { - Shape.setValue(getSolid(result)); - return App::DocumentObject::StdReturn; - } - - result.Tag = -getID(); - TopoShape boolOp(0); - - try { - const char *maker; - switch (getAddSubType()) { - case Additive: - maker = Part::OpCodes::Fuse; - break; -// case Intersecting: -// maker = Part::OpCodes::Common; -// break; - default: - maker = Part::OpCodes::Cut; + for (; xp.More(); xp.Next()) { + if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current()))) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch")); } -// this->fixShape(result); - boolOp.makeElementBoolean(maker, {base,result}); - }catch(Standard_Failure &) { - return new App::DocumentObjectExecReturn("Failed to cut base feature"); } - TopoShape solid = this->getSolid(boolOp); - if (solid.isNull()) - return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); - // store shape before refinement - this->rawShape = boolOp; - boolOp = refineShapeIfActive(boolOp); - if (!isSingleSolidRuleSatisfied(boolOp.getShape())) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: enable 'Allow Compound' in the active body.")); + // Create a fresh support even when base exists so that it can be used for patterns + TopoShape result(0); + TopoShape supportface(0); + try { + supportface = getSupportFace(); } - boolOp = getSolid(boolOp); - Shape.setValue(boolOp); + catch(...) { + // do nothing, null shape is handled below + } + + supportface.move(invObjLoc); + + if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst) { + TopoShape upToFace; + if (method == RevolMethod::ToFace) { + getUpToFaceFromLinkSub(upToFace, UpToFace); + upToFace.move(invObjLoc); + } + else { + throw Base::RuntimeError( + "ProfileBased: Revolution up to first/last is not yet supported"); + } + + if (Reversed.getValue()) { + dir.Reverse(); + } + + TopExp_Explorer Ex(supportface.getShape(), TopAbs_WIRE); + if (!Ex.More()) { + supportface = TopoDS_Face(); + } + + try { + result = base.makeElementRevolution(base, + TopoDS::Face(sketchshape.getShape()), + gp_Ax1(pnt, dir), + TopoDS::Face(supportface.getShape()), + TopoDS::Face(upToFace.getShape()), + nullptr, + Part::RevolMode::CutFromBase, + Standard_True); + } + catch (Standard_Failure&) { + return new App::DocumentObjectExecReturn("Could not revolve the sketch!"); + } + } + else { + bool midplane = Midplane.getValue(); + bool reversed = Reversed.getValue(); + generateRevolution(result, + sketchshape.getShape(), + gp_Ax1(pnt, dir), + angle, + angle2, + midplane, + reversed, + method); + } + + if (!result.isNull()) { + // store shape before refinement + this->rawShape = result; + result = refineShapeIfActive(result); + // set the additive shape property for later usage in e.g. pattern + this->AddSubShape.setValue(result); + + if (!base.isNull()) { + result = base.makeElementCut(result); + // store shape before refinement + this->rawShape = result; + result = refineShapeIfActive(result); + } + if (!isSingleSolidRuleSatisfied(result.getShape())) { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: enable 'Allow Compound' in the active body.")); + } + result = getSolid(result); + this->Shape.setValue(result); + } + else { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!")); + } + + // eventually disable some settings that are not valid for the current method + updateProperties(method); + return App::DocumentObject::StdReturn; } catch (Standard_Failure& e) { - if (std::string(e.GetMessageString()) == "TopoDS::Face") - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not create face from sketch.\n" - "Intersecting sketch entities in a sketch are not allowed.")); - else + if (std::string(e.GetMessageString()) == "TopoDS::Face") { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", + "Could not create face from sketch.\n" + "Intersecting sketch entities in a sketch are not allowed.")); + } + else { return new App::DocumentObjectExecReturn(e.GetMessageString()); + } } catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); @@ -212,7 +262,12 @@ App::DocumentObjectExecReturn *Groove::execute() bool Groove::suggestReversed() { - updateAxis(); + try { + updateAxis(); + } catch (const Base::Exception&) { + return false; + } + return ProfileBased::getReversedAngle(Base.getValue(), Axis.getValue()) > 0.0; } @@ -224,16 +279,14 @@ void Groove::updateAxis() Base::Vector3d dir; getAxis(pcReferenceAxis, subReferenceAxis, base, dir, ForbiddenAxis::NotParallelWithNormal); - if (dir.Length() > Precision::Confusion()) { - Base.setValue(base.x,base.y,base.z); - Axis.setValue(dir.x,dir.y,dir.z); - } + Base.setValue(base.x,base.y,base.z); + Axis.setValue(dir.x,dir.y,dir.z); } Groove::RevolMethod Groove::methodFromString(const std::string& methodStr) { if (methodStr == "Angle") - return RevolMethod::Dimension; + return RevolMethod::Angle; if (methodStr == "UpToLast") return RevolMethod::ToLast; if (methodStr == "ThroughAll") @@ -243,14 +296,13 @@ Groove::RevolMethod Groove::methodFromString(const std::string& methodStr) if (methodStr == "UpToFace") return RevolMethod::ToFace; if (methodStr == "TwoAngles") - return RevolMethod::TwoDimensions; + return RevolMethod::TwoAngles; throw Base::ValueError("Groove:: No such method"); - return RevolMethod::Dimension; } -void Groove::generateRevolution(TopoDS_Shape& revol, - const TopoDS_Shape& sketchshape, +void Groove::generateRevolution(TopoShape& revol, + const TopoShape& sketchshape, const gp_Ax1& axis, const double angle, const double angle2, @@ -258,11 +310,11 @@ void Groove::generateRevolution(TopoDS_Shape& revol, const bool reversed, RevolMethod method) { - if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) { + if (method == RevolMethod::Angle || method == RevolMethod::TwoAngles || method == RevolMethod::ThroughAll) { double angleTotal = angle; double angleOffset = 0.; - if (method == RevolMethod::TwoDimensions) { + if (method == RevolMethod::TwoAngles) { // Rotate the face by `angle2`/`angle` to get "second" angle angleTotal += angle2; angleOffset = angle2 * -1.0; @@ -271,43 +323,41 @@ void Groove::generateRevolution(TopoDS_Shape& revol, angleTotal = 2 * std::numbers::pi; } else if (midplane) { - // Rotate the face by half the angle to get Groove symmetric to sketch plane + // Rotate the face by half the angle to get Revolution symmetric to sketch plane angleOffset = -angle / 2; } if (fabs(angleTotal) < Precision::Angular()) throw Base::ValueError("Cannot create a revolution with zero angle."); - TopoDS_Shape from = sketchshape; - if (method == RevolMethod::TwoDimensions || midplane) { - gp_Trsf mov; - mov.SetRotation(axis, angleOffset); - TopLoc_Location loc(mov); - from.Move(loc); + gp_Ax1 revolAx(axis); + if (reversed) { + revolAx.Reverse(); } - else if (reversed) { - angleTotal *= -1.0; + + TopoShape from = sketchshape; + if (method == RevolMethod::TwoAngles || midplane) { + gp_Trsf mov; + mov.SetRotation(revolAx, angleOffset); + TopLoc_Location loc(mov); + from.move(loc); } // revolve the face to a solid // BRepPrimAPI is the only option that allows use of this shape for patterns. // See https://forum.freecadweb.org/viewtopic.php?f=8&t=70185&p=611673#p611673. - BRepPrimAPI_MakeRevol RevolMaker(from, axis, angleTotal); - - if (!RevolMaker.IsDone()) - throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!"); - else - revol = RevolMaker.Shape(); - } - else { + revol = from; + revol = revol.makeElementRevolve(revolAx,angleTotal); + revol.Tag = -getID(); + } else { std::stringstream str; - str << "ProfileBased: Internal error: Unknown method for generateGroove()"; + str << "ProfileBased: Internal error: Unknown method for generateRevolution()"; throw Base::RuntimeError(str.str()); } } -void Groove::generateRevolution(TopoDS_Shape& revol, - const TopoDS_Shape& baseshape, +void Groove::generateRevolution(TopoShape& revol, + const TopoShape& baseshape, const TopoDS_Shape& profileshape, const TopoDS_Face& supportface, const TopoDS_Face& uptoface, @@ -317,20 +367,8 @@ void Groove::generateRevolution(TopoDS_Shape& revol, Standard_Boolean Modify) { if (method == RevolMethod::ToFirst || method == RevolMethod::ToFace || method == RevolMethod::ToLast) { - BRepFeat_MakeRevol RevolMaker; - TopoDS_Shape base = baseshape; - for (TopExp_Explorer xp(profileshape, TopAbs_FACE); xp.More(); xp.Next()) { - RevolMaker.Init(base, xp.Current(), supportface, axis, Mode, Modify); - RevolMaker.Perform(uptoface); - if (!RevolMaker.IsDone()) - throw Base::RuntimeError("ProfileBased: Up to face: Could not revolve the sketch!"); - - base = RevolMaker.Shape(); - if (Mode == RevolMode::None) - Mode = RevolMode::FuseWithBase; - } - - revol = base; + revol = revol.makeElementRevolution(baseshape, profileshape, axis, supportface, uptoface, nullptr, + static_cast(Mode), Modify, nullptr); } else { std::stringstream str; @@ -348,7 +386,7 @@ void Groove::updateProperties(RevolMethod method) bool isMidplaneEnabled = false; bool isReversedEnabled = false; bool isUpToFaceEnabled = false; - if (method == RevolMethod::Dimension) { + if (method == RevolMethod::Angle) { isAngleEnabled = true; isMidplaneEnabled = true; isReversedEnabled = !Midplane.getValue(); @@ -367,7 +405,7 @@ void Groove::updateProperties(RevolMethod method) isReversedEnabled = true; isUpToFaceEnabled = true; } - else if (method == RevolMethod::TwoDimensions) { + else if (method == RevolMethod::TwoAngles) { isAngleEnabled = true; isAngle2Enabled = true; isReversedEnabled = true; @@ -380,7 +418,5 @@ void Groove::updateProperties(RevolMethod method) UpToFace.setReadOnly(!isUpToFaceEnabled); } - } - diff --git a/src/Mod/PartDesign/App/FeatureGroove.h b/src/Mod/PartDesign/App/FeatureGroove.h index 8e9c2d40c6..9856fc1e12 100644 --- a/src/Mod/PartDesign/App/FeatureGroove.h +++ b/src/Mod/PartDesign/App/FeatureGroove.h @@ -69,12 +69,12 @@ public: bool suggestReversed(); enum class RevolMethod { - Dimension, + Angle, ThroughAll, ToLast = ThroughAll, ToFirst, ToFace, - TwoDimensions + TwoAngles }; protected: @@ -95,8 +95,8 @@ protected: /** * Generates a [groove] of the input sketchshape and stores it in the given \a revol. */ - void generateRevolution(TopoDS_Shape& revol, - const TopoDS_Shape& sketchshape, + void generateRevolution(TopoShape& revol, + const TopoShape& sketchshape, const gp_Ax1& ax1, const double angle, const double angle2, @@ -108,8 +108,8 @@ protected: * Generates a [groove] of the input \a profileshape. * It will be a stand-alone solid created with BRepFeat_MakeRevol. */ - void generateRevolution(TopoDS_Shape& revol, - const TopoDS_Shape& baseshape, + void generateRevolution(TopoShape& revol, + const TopoShape& baseshape, const TopoDS_Shape& profileshape, const TopoDS_Face& supportface, const TopoDS_Face& uptoface, From ab60695ef9345e8704f7cbe6e3e244f5906e8442 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 27 Jan 2025 00:47:01 +0100 Subject: [PATCH 8/9] PD: Show commands in task panel when selecting (sub) shape binders This fixes issue 19098 --- src/Mod/PartDesign/Gui/Workbench.cpp | 51 ++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index e4187d3201..8fbd77b60b 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -296,10 +296,10 @@ void Workbench::activated() "PartDesign_Hole", "PartDesign_Revolution", "PartDesign_Groove", - "PartDesign_AdditivePipe", - "PartDesign_SubtractivePipe", "PartDesign_AdditiveLoft", "PartDesign_SubtractiveLoft", + "PartDesign_AdditivePipe", + "PartDesign_SubtractivePipe", "PartDesign_AdditiveHelix", "PartDesign_SubtractiveHelix", nullptr}; @@ -310,6 +310,53 @@ void Workbench::activated() "PartDesign_Body" )); + const char* Sketches[] = { + "PartDesign_AdditiveLoft", + "PartDesign_SubtractiveLoft", + "PartDesign_AdditivePipe", + "PartDesign_SubtractivePipe", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT Sketcher::SketchObject COUNT 2..", + Sketches, + "Modeling tools", + "PartDesign_Body" + )); + + const char* ShapeBinder[] = { + "PartDesign_Pad", + "PartDesign_Pocket", + "PartDesign_Revolution", + "PartDesign_Groove", + "PartDesign_AdditiveLoft", + "PartDesign_SubtractiveLoft", + "PartDesign_AdditivePipe", + "PartDesign_SubtractivePipe", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT PartDesign::ShapeBinder COUNT 1", + ShapeBinder, + "Modeling tools", + "PartDesign_Body" + )); + + const char* SubShapeBinder[] = { + "PartDesign_Pad", + "PartDesign_Pocket", + "PartDesign_Revolution", + "PartDesign_Groove", + "PartDesign_AdditiveLoft", + "PartDesign_SubtractiveLoft", + "PartDesign_AdditivePipe", + "PartDesign_SubtractivePipe", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT PartDesign::SubShapeBinder COUNT 1", + SubShapeBinder, + "Modeling tools", + "PartDesign_Body" + )); + const char* Transformed[] = { "PartDesign_Mirrored", "PartDesign_LinearPattern", From 20a4dc38391daddf56d15ad802f0c5f600b6dc7c Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 27 Jan 2025 10:21:32 +0100 Subject: [PATCH 9/9] PD: Restore GUI commands for PartDesign datum objects With PR 19114 the commands to create PD datum objects are replaced with the counterparts of Part. However, PD datum objects are much more powerful than the Part datum objects. (see e.g. https://forum.freecad.org/viewtopic.php?p=806960) So, this change brings these commands back to the task panel. --- src/Mod/PartDesign/Gui/Workbench.cpp | 91 ++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 8fbd77b60b..f66d6bc046 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -162,6 +162,19 @@ void Workbench::activated() "PartDesign_Body" )); + const char* Vertex1[] = { + "PartDesign_Point", + "PartDesign_Line", + "PartDesign_Plane", + "PartDesign_CoordinateSystem", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT Part::Feature SUBELEMENT Vertex COUNT 1..", + Vertex1, + "Datum objects", + "PartDesign_CoordinateSystem" + )); + const char* Edge[] = { "PartDesign_Fillet", "PartDesign_Chamfer", @@ -177,6 +190,19 @@ void Workbench::activated() "PartDesign_Body" )); + const char* Edge1[] = { + "PartDesign_Point", + "PartDesign_Line", + "PartDesign_Plane", + "PartDesign_CoordinateSystem", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT Part::Feature SUBELEMENT Edge COUNT 1..", + Edge1, + "Datum objects", + "PartDesign_CoordinateSystem" + )); + const char* Face[] = { "PartDesign_NewSketch", "PartDesign_Fillet", @@ -195,6 +221,19 @@ void Workbench::activated() "PartDesign_Body" )); + const char* Face1[] = { + "PartDesign_Point", + "PartDesign_Line", + "PartDesign_Plane", + "PartDesign_CoordinateSystem", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT Part::Feature SUBELEMENT Face COUNT 1", + Face1, + "Datum objects", + "PartDesign_CoordinateSystem" + )); + const char* Body[] = { "PartDesign_NewSketch", nullptr}; @@ -228,6 +267,7 @@ void Workbench::activated() "Helper Tools", "PartDesign_Body" )); + const char* Plane2[] = { "PartDesign_NewSketch", "Part_DatumPoint", @@ -242,6 +282,32 @@ void Workbench::activated() "PartDesign_Body" )); + const char* Plane3[] = { + "PartDesign_Point", + "PartDesign_Line", + "PartDesign_Plane", + "PartDesign_CoordinateSystem", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT App::Plane COUNT 1", + Plane3, + "Datum objects", + "PartDesign_CoordinateSystem" + )); + + const char* Plane4[] = { + "PartDesign_Point", + "PartDesign_Line", + "PartDesign_Plane", + "PartDesign_CoordinateSystem", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT PartDesign::Plane COUNT 1", + Plane4, + "Datum objects", + "PartDesign_CoordinateSystem" + )); + const char* Line[] = { "Part_DatumPoint", "Part_DatumLine", @@ -254,6 +320,18 @@ void Workbench::activated() "PartDesign_Body" )); + const char* Line1[] = { + "PartDesign_Point", + "PartDesign_Line", + "PartDesign_Plane", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT PartDesign::Line COUNT 1", + Line1, + "Datum objects", + "PartDesign_CoordinateSystem" + )); + const char* Point[] = { "Part_DatumPoint", "Part_DatumLine", @@ -267,6 +345,19 @@ void Workbench::activated() "PartDesign_Body" )); + const char* Point1[] = { + "PartDesign_Point", + "PartDesign_Line", + "PartDesign_Plane", + "PartDesign_CoordinateSystem", + nullptr}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT PartDesign::Point COUNT 1", + Point1, + "Datum objects", + "PartDesign_CoordinateSystem" + )); + const char* NoSel[] = { "PartDesign_Body", nullptr};