[PartDesign] Still a helix fix (#12977)

* [PartDesign] Still a helix fix

If we don't break the helix path at each turns we get a vaild path for
MakePipe (solid) even with an angle.

* Decrease helix tests requirements
This commit is contained in:
Florian Foinant-Willig
2024-03-25 18:31:20 +01:00
committed by GitHub
parent 283fc1183c
commit 4beeafbedd
3 changed files with 13 additions and 81 deletions

View File

@@ -232,79 +232,12 @@ App::DocumentObjectExecReturn* Helix::execute()
// generate the helix path
TopoDS_Shape path = generateHelixPath();
TopoDS_Shape auxpath = generateHelixPath(1.0);
// 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);
mkPS.Build();
result = mkPS.Shape();
} else {
std::vector<std::vector<TopoDS_Wire>> wiresections;
for (TopoDS_Wire& wire : wires)
wiresections.emplace_back(1, wire);
//build all shells
std::vector<TopoDS_Shape> shells;
std::vector<TopoDS_Wire> frontwires, backwires;
for (std::vector<TopoDS_Wire>& 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();
}
}
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();
BRepClass3d_SolidClassifier SC(result);
SC.PerformInfinitePoint(Precision::Confusion());
@@ -403,7 +336,7 @@ void Helix::updateAxis()
Axis.setValue(dir.x, dir.y, dir.z);
}
TopoDS_Shape Helix::generateHelixPath(double startOffset0)
TopoDS_Shape Helix::generateHelixPath()
{
double turns = Turns.getValue();
double height = Height.getValue();
@@ -449,7 +382,7 @@ TopoDS_Shape Helix::generateHelixPath(double startOffset0)
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(startOffset0 + profileCenter * axisVector - baseVector * axisVector);
double startOffset = 10000.0 * std::fabs(baseVector * axisVector);
if (radius < Precision::Confusion()) {
// in this case ensure that axis is not in the sketch plane
@@ -466,8 +399,7 @@ TopoDS_Shape Helix::generateHelixPath(double startOffset0)
radiusTop = radius + height * tan(Base::toRadians(angle));
//build the helix path
//TopoShape helix = TopoShape().makeLongHelix(pitch, height, radius, angle, leftHanded);
TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1, leftHanded);
TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1000, leftHanded);
/*
* The helix wire is created with the axis coinciding with z-axis and the start point at (radius, 0, 0)

View File

@@ -80,7 +80,7 @@ protected:
void updateAxis();
/// generate helix and move it to the right location.
TopoDS_Shape generateHelixPath(double startOffset0 = 0.0);
TopoDS_Shape generateHelixPath();
// project shape on plane. Used for detecting self intersection.
TopoDS_Shape projectShape(const TopoDS_Shape& input, const gp_Ax2& plane);

View File

@@ -87,15 +87,15 @@ class TestHelix(unittest.TestCase):
helix.Angle = 0
helix.Mode = 1
self.Doc.recompute()
self.assertAlmostEqual(helix.Shape.Volume, 78.95687956849457,places=5)
self.assertAlmostEqual(helix.Shape.Volume, 78.957,places=3)
helix.Angle = 25
self.Doc.recompute()
self.assertAlmostEqual(helix.Shape.Volume, 134.17450779511307,places=5)
self.assertAlmostEqual(helix.Shape.Volume, 134.17,places=2)
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.63088079046352,places=5)
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, 388285.4117047924,places=5)
self.assertAlmostEqual(helix.Shape.Volume/1e5, 3.8828,places=4)
def tearDown(self):
FreeCAD.closeDocument("PartDesignTestHelix")