From 84e25ed38dc22b0190dd7b550d358e67bab92ec6 Mon Sep 17 00:00:00 2001 From: Florian Foinant-Willig Date: Mon, 5 Aug 2024 17:40:00 +0200 Subject: [PATCH] [PD] Revert 316506f, back to shell algo for some helix cases (#15678) * [PD] Revert 316506f, back to shell algo for some helix cases * fix test accuracy --- src/Mod/PartDesign/App/FeatureHelix.cpp | 83 +++++++++++++++++-- src/Mod/PartDesign/App/FeatureHelix.h | 2 +- .../PartDesign/PartDesignTests/TestHelix.py | 4 +- 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureHelix.cpp b/src/Mod/PartDesign/App/FeatureHelix.cpp index 3aa6e0c9e9..3b61cecbce 100644 --- a/src/Mod/PartDesign/App/FeatureHelix.cpp +++ b/src/Mod/PartDesign/App/FeatureHelix.cpp @@ -232,12 +232,78 @@ App::DocumentObjectExecReturn* Helix::execute() // generate the helix path TopoDS_Shape path = generateHelixPath(); + TopoDS_Shape auxpath = generateHelixPath(1.0); - TopoDS_Shape face = Part::FaceMakerCheese::makeFace(wires); - face.Move(invObjLoc); - BRepOffsetAPI_MakePipe mkPS(TopoDS::Wire(path), face, GeomFill_Trihedron::GeomFill_IsFrenet, Standard_False); - mkPS.Build(); - result = mkPS.Shape(); + // Use MakePipe for frenet ( Angle is 0 ) calculations, faster than MakePipeShell + if ( Angle.getValue() == 0 ) { + TopoDS_Shape face = Part::FaceMakerCheese::makeFace(wires); + face.Move(invObjLoc); + BRepOffsetAPI_MakePipe mkPS(TopoDS::Wire(path), face, GeomFill_Trihedron::GeomFill_IsFrenet, Standard_False); + result = mkPS.Shape(); + } else { + std::vector> wiresections; + for (TopoDS_Wire& wire : wires) + wiresections.emplace_back(1, wire); + + //build all shells + std::vector shells; + std::vector frontwires, backwires; + for (std::vector& wires : wiresections) { + + BRepOffsetAPI_MakePipeShell mkPS(TopoDS::Wire(path)); + + // Frenet mode doesn't place the face quite right on an angled helix, so + // use the auxiliary spine to force that. + mkPS.SetMode(TopoDS::Wire(auxpath), true); // this is for auxiliary + + for (TopoDS_Wire& wire : wires) { + wire.Move(invObjLoc); + mkPS.Add(wire); + } + + if (!mkPS.IsReady()) + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Could not build")); + mkPS.Build(); + + shells.push_back(mkPS.Shape()); + + if (!mkPS.Shape().Closed()) { + // // shell is not closed - use simulate to get the end wires + TopTools_ListOfShape sim; + mkPS.Simulate(2, sim); + + frontwires.push_back(TopoDS::Wire(sim.First())); + backwires.push_back(TopoDS::Wire(sim.Last())); + } + BRepBuilderAPI_MakeSolid mkSolid; + + if (!frontwires.empty()) { + // build the end faces, sew the shell and build the final solid + TopoDS_Shape front = Part::FaceMakerCheese::makeFace(frontwires); + TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires); + + BRepBuilderAPI_Sewing sewer; + sewer.SetTolerance(Precision::Confusion()); + sewer.Add(front); + sewer.Add(back); + + for (TopoDS_Shape& s : shells) + sewer.Add(s); + sewer.Perform(); + mkSolid.Add(TopoDS::Shell(sewer.SewedShape())); + } + else { + // shells are already closed - add them directly + for (TopoDS_Shape& s : shells) { + mkSolid.Add(TopoDS::Shell(s)); + } + } + if (!mkSolid.IsDone()) + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Result is not a solid")); + + result = mkSolid.Shape(); + } + } BRepClass3d_SolidClassifier SC(result); SC.PerformInfinitePoint(Precision::Confusion()); @@ -335,7 +401,7 @@ void Helix::updateAxis() Axis.setValue(dir.x, dir.y, dir.z); } -TopoDS_Shape Helix::generateHelixPath() +TopoDS_Shape Helix::generateHelixPath(double startOffset0) { double turns = Turns.getValue(); double height = Height.getValue(); @@ -381,7 +447,7 @@ TopoDS_Shape Helix::generateHelixPath() bool turned = axisOffset < 0; // since the factor does not only change the radius but also the path position, we must shift its offset back // using the square of the factor - double startOffset = 10000.0 * std::fabs(baseVector * axisVector); + double startOffset = 10000.0 * std::fabs(startOffset0 + profileCenter * axisVector - baseVector * axisVector); if (radius < Precision::Confusion()) { // in this case ensure that axis is not in the sketch plane @@ -398,7 +464,8 @@ TopoDS_Shape Helix::generateHelixPath() radiusTop = radius + height * tan(Base::toRadians(angle)); //build the helix path - TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1000, leftHanded); + //TopoShape helix = TopoShape().makeLongHelix(pitch, height, radius, angle, leftHanded); + TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1, leftHanded); /* * The helix wire is created with the axis coinciding with z-axis and the start point at (radius, 0, 0) diff --git a/src/Mod/PartDesign/App/FeatureHelix.h b/src/Mod/PartDesign/App/FeatureHelix.h index 3a4a3a7cb7..531ded92d6 100644 --- a/src/Mod/PartDesign/App/FeatureHelix.h +++ b/src/Mod/PartDesign/App/FeatureHelix.h @@ -80,7 +80,7 @@ protected: void updateAxis(); /// generate helix and move it to the right location. - TopoDS_Shape generateHelixPath(); + TopoDS_Shape generateHelixPath(double startOffset0 = 0.0); // project shape on plane. Used for detecting self intersection. TopoDS_Shape projectShape(const TopoDS_Shape& input, const gp_Ax2& plane); diff --git a/src/Mod/PartDesign/PartDesignTests/TestHelix.py b/src/Mod/PartDesign/PartDesignTests/TestHelix.py index fb5c917928..d89b3cb1a2 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestHelix.py +++ b/src/Mod/PartDesign/PartDesignTests/TestHelix.py @@ -95,7 +95,7 @@ class TestHelix(unittest.TestCase): profileSketch.addGeometry(Part.Circle(FreeCAD.Vector(2, 0, 0), FreeCAD.Vector(0,0,1), 0.5) ) self.Doc.recompute() - self.assertAlmostEqual(helix.Shape.Volume, 100.63,places=2) + self.assertAlmostEqual(helix.Shape.Volume, 100.63, places=2) def testRectangle(self): @@ -174,7 +174,7 @@ class TestHelix(unittest.TestCase): helix.Mode = 0 helix.Reversed = True self.Doc.recompute() - self.assertAlmostEqual(helix.Shape.Volume/1e5, 3.8828,places=4) + self.assertAlmostEqual(helix.Shape.Volume/1e5, 3.8828, places=3) def tearDown(self): FreeCAD.closeDocument("PartDesignTestHelix")