From a2c5788a98be64dd8c64acbea0b3ad435166e44c Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 31 Jan 2025 15:06:59 +0100 Subject: [PATCH] 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, } } -