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 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()) 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, diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp index a2faf08082..67112f1531 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); 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, } } - 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/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; 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 diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index e4187d3201..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}; @@ -296,10 +387,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 +401,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",