diff --git a/src/Mod/PartDesign/App/FeatureExtrude.cpp b/src/Mod/PartDesign/App/FeatureExtrude.cpp index 78bdd6164e..4225b79e4d 100644 --- a/src/Mod/PartDesign/App/FeatureExtrude.cpp +++ b/src/Mod/PartDesign/App/FeatureExtrude.cpp @@ -309,10 +309,13 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt std::string method2(Type2.getValueAsString()); // Validate parameters - double L = method == "ThroughAll" ? getThroughAllLength() : Length.getValue(); - double L2 = Sidemethod == "Two sides" - ? method2 == "ThroughAll" ? getThroughAllLength() : Length2.getValue() - : 0; + double L = method == "ThroughAll" ? getThroughAllLength() + : method == "Length" ? Length.getValue() + : 0.0; + double L2 = Sidemethod == "Two sides" ? method2 == "ThroughAll" ? getThroughAllLength() + : method2 == "Length" ? Length2.getValue() + : 0.0 + : 0.0; if ((Sidemethod == "One side" && method == "Length") || (Sidemethod == "Two sides" && method == "Length" && method2 == "Length")) { @@ -455,52 +458,167 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt prisms.push_back(prism1); } else if (Sidemethod == "Symmetric") { - L /= 2.0; - TopoShape prism1 = generateSingleExtrusionSide(sketchshape, - method, L, taper1, UpToFace, UpToShape, - dir, offset1, makeface, base); - prisms.push_back(prism1); + // For Length mode, we are not doing a mirror, but we extrude along the same axis + // in both directions as it is what users expect. + if (method == "Length") { + if (std::fabs(taper1) > Precision::Angular()) { + // TAPERED case: We must create two separate prisms and fuse them + // to ensure the taper originates correctly from the sketch plane in both + // directions. + L /= 2.0; + TopoShape prism1 = generateSingleExtrusionSide(sketchshape.makeElementCopy(), + method, + L, + taper1, + UpToFace, + UpToShape, + dir, + offset1, + makeface, + base); + if (!prism1.isNull() && !prism1.getShape().IsNull()) { + prisms.push_back(prism1); + } - // Prism 2 : Make a symmetric of prism1 - Base::Vector3d base = sketchshape.getBoundBox().GetCenter(); - gp_Ax2 axe(gp_Pnt(base.x, base.y, base.z), dir); - TopoShape prism2 = prism1.makeElementMirror(axe); - prisms.push_back(prism2); + gp_Dir dir2 = dir; + dir2.Reverse(); + TopoShape prism2 = generateSingleExtrusionSide(sketchshape.makeElementCopy(), + method, + L, + taper1, + UpToFace, + UpToShape, + dir2, + offset1, + makeface, + base); + if (!prism2.isNull() && !prism2.getShape().IsNull()) { + prisms.push_back(prism2); + } + } + else { + // NON-TAPERED case: We can optimize by creating a single prism. + // Translate the sketch to the start position (-L/2) and extrude by the full + // length L. + gp_Trsf start_transform; + start_transform.SetTranslation(gp_Vec(dir).Reversed() * (L / 2.0)); + TopoShape moved_sketch = sketchshape.makeElementCopy(); + moved_sketch.move(start_transform); + + TopoShape prism1 = generateSingleExtrusionSide(moved_sketch, + method, + L, + taper1, + UpToFace, + UpToShape, + dir, + offset1, + makeface, + base); + if (!prism1.isNull() && !prism1.getShape().IsNull()) { + prisms.push_back(prism1); + } + } + } + else { + // For "UpToFace", "UpToShape", etc., mirror the result. + TopoShape prism1 = generateSingleExtrusionSide(sketchshape, + method, L, taper1, UpToFace, UpToShape, + dir, offset1, makeface, base); + prisms.push_back(prism1); + + // Prism 2: Mirror prism1 across the sketch plane. + // The mirror plane's normal must be the sketch normal, not the extrusion direction. + gp_Dir sketchNormalDir(SketchVector.x, SketchVector.y, SketchVector.z); + sketchNormalDir.Transform(invTrsf); // Transform to global CS, like 'dir' was. + + Base::Vector3d sketchCenter = sketchshape.getBoundBox().GetCenter(); + gp_Ax2 mirrorPlane(gp_Pnt(sketchCenter.x, sketchCenter.y, sketchCenter.z), + sketchNormalDir); + TopoShape prism2 = prism1.makeElementMirror(mirrorPlane); + prisms.push_back(prism2); + } } else if (Sidemethod == "Two sides") { - TopoShape prism1 = generateSingleExtrusionSide(sketchshape.makeElementCopy(), - method, - L, - taper1, - UpToFace, - UpToShape, - dir, - offset1, - makeface, - base); - if (!prism1.isNull() && !prism1.getShape().IsNull()) { - prisms.push_back(prism1); - } - - // Side 2 double taper2 = TaperAngle2.getValue(); double offset2 = Offset2.getValue(); gp_Dir dir2 = dir; dir2.Reverse(); + bool noTaper = std::fabs(taper1) < Precision::Angular() + && std::fabs(taper2) < Precision::Angular(); + bool method1LengthBased = method == "Length" || method == "ThroughAll"; + bool method2LengthBased = method2 == "Length" || method2 == "ThroughAll"; - TopoShape prism2 = generateSingleExtrusionSide(sketchshape.makeElementCopy(), - method2, - L2, - taper2, - UpToFace2, - UpToShape2, - dir2, - offset2, - makeface, - base); - if (!prism2.isNull() && !prism2.getShape().IsNull()) { - prisms.push_back(prism2); + if (method1LengthBased && method2 != "UpToFirst" && noTaper) { + gp_Trsf start_transform; + start_transform.SetTranslation(gp_Vec(dir) * L); + + TopoShape moved_sketch = sketchshape.makeElementCopy(); + moved_sketch.move(start_transform); + TopoShape prism = generateSingleExtrusionSide(moved_sketch, + method2, + L + L2, + 0.0, + UpToFace2, + UpToShape2, + dir2, + offset2, + makeface, + base); + if (!prism.isNull() && !prism.getShape().IsNull()) { + prisms.push_back(prism); + } + } + else if (method2LengthBased && method != "UpToFirst" && noTaper) { + gp_Trsf start_transform; + start_transform.SetTranslation(gp_Vec(dir).Reversed() * L2); + + TopoShape moved_sketch = sketchshape.makeElementCopy(); + moved_sketch.move(start_transform); + TopoShape prism = generateSingleExtrusionSide(moved_sketch, + method, + L + L2, + 0.0, + UpToFace, + UpToShape, + dir, + offset1, + makeface, + base); + if (!prism.isNull() && !prism.getShape().IsNull()) { + prisms.push_back(prism); + } + } + else { + TopoShape prism1 = generateSingleExtrusionSide(sketchshape.makeElementCopy(), + method, + L, + taper1, + UpToFace, + UpToShape, + dir, + offset1, + makeface, + base); + if (!prism1.isNull() && !prism1.getShape().IsNull()) { + prisms.push_back(prism1); + } + + // Side 2 + TopoShape prism2 = generateSingleExtrusionSide(sketchshape.makeElementCopy(), + method2, + L2, + taper2, + UpToFace2, + UpToShape2, + dir2, + offset2, + makeface, + base); + if (!prism2.isNull() && !prism2.getShape().IsNull()) { + prisms.push_back(prism2); + } } }