From 145947e6d750640981490df467435a8521626477 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Tue, 13 Jan 2026 06:52:43 +0100 Subject: [PATCH] Part: Fix Part_Extrude taper angle regression for internal faces (#26781) * Part: Fix Part_Extrude taper angle regression for internal faces The taper angle for holes (inner wires) in `Part_Extrude` was not being negated after the toponaming refactor, causing internal faces to taper in the wrong direction compared to v0.21.2. The original makeDraft function correctly handled inner wires by: - Negating taper for all inner wires in Part_Extrude - Negating taper only for multi-edge inner wires in PartDesign This logic was controlled via an isPartDesign function parameter. When makeElementDraft was introduced for toponaming support, it was designed to use innerTaperAngleFwd/innerTaperAngleRev fields that were never ported from realthunder's branch. The cleanup (commit 709eab3018) removed references to these non-existent fields, leaving makeElementDraft with no inner wire taper handling at all. So, this fix ports the makeDraft inner wire logic to makeElementDraft by adding the flag, and counting wires and then triggering proper angle flip depending on the flag/wire situation. --------- Co-authored-by: Chris Hennes --- src/Mod/Part/App/ExtrusionHelper.cpp | 25 ++++++++++++++++------- src/Mod/Part/App/ExtrusionHelper.h | 11 ++++++++++ src/Mod/Part/App/FeatureExtrusion.cpp | 18 +++++++++++++++- src/Mod/Part/App/FeatureExtrusion.h | 3 +++ src/Mod/PartDesign/App/FeatureExtrude.cpp | 1 + 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/Mod/Part/App/ExtrusionHelper.cpp b/src/Mod/Part/App/ExtrusionHelper.cpp index a8548d27ea..f0ee6fe993 100644 --- a/src/Mod/Part/App/ExtrusionHelper.cpp +++ b/src/Mod/Part/App/ExtrusionHelper.cpp @@ -615,13 +615,24 @@ void ExtrusionHelper::makeElementDraft( Standard_Failure::Raise("Failed to make drafted extrusion"); } std::vector inner; - TopoShape innerWires(0); - innerWires.makeElementCompound( - wires, - "", - TopoShape::SingleShapeCompoundCreationPolicy::returnShape - ); - makeElementDraft(params, innerWires, inner, hasher); + // process each inner wire individually to check edge count + // Inner wires (holes) need negated taper angles + for (auto& innerWire : wires) { + ExtrusionParameters innerParams = params; + + int numEdges = 0; + TopExp_Explorer xp(innerWire.getShape(), TopAbs_EDGE); + while (xp.More()) { + numEdges++; + xp.Next(); + } + + if (numEdges > 1 || params.innerWireTaper == InnerWireTaper::Inverted) { + innerParams.taperAngleFwd = -params.taperAngleFwd; + innerParams.taperAngleRev = -params.taperAngleRev; + } + makeElementDraft(innerParams, innerWire, inner, hasher); + } if (inner.empty()) { Standard_Failure::Raise("Failed to make drafted extrusion with inner hole"); } diff --git a/src/Mod/Part/App/ExtrusionHelper.h b/src/Mod/Part/App/ExtrusionHelper.h index 61953cb718..cde4328ff6 100644 --- a/src/Mod/Part/App/ExtrusionHelper.h +++ b/src/Mod/Part/App/ExtrusionHelper.h @@ -25,6 +25,7 @@ #ifndef PART_EXTRUSIONHELPER_H #define PART_EXTRUSIONHELPER_H +#include #include #include #include @@ -39,6 +40,15 @@ namespace Part class TopoShape; +/** + * @brief Controls how taper is applied to inner wires (holes) during extrusion. + */ +enum class InnerWireTaper : std::uint8_t +{ + Inverted, + SameAsOuter, +}; + /** * @brief The ExtrusionParameters struct is supposed to be filled with final * extrusion parameters, after resolving links, applying mode logic, @@ -53,6 +63,7 @@ struct ExtrusionParameters double taperAngleFwd {0}; // in radians double taperAngleRev {0}; std::string faceMakerClass; + InnerWireTaper innerWireTaper {InnerWireTaper::Inverted}; }; class PartExport ExtrusionHelper diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index 438dfe5fc8..f1f81d7903 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include "FeatureExtrusion.h" @@ -53,6 +54,7 @@ using namespace Part; PROPERTY_SOURCE(Part::Extrusion, Part::Feature) const char* Extrusion::eDirModeStrings[] = {"Custom", "Edge", "Normal", nullptr}; +const char* Extrusion::eInnerWireTaperStrings[] = {"Inverted", "SameAsOuter", nullptr}; namespace { @@ -136,6 +138,9 @@ Extrusion::Extrusion() ADD_PROPERTY_TYPE(FaceMakerMode, (3L), "Extrude", App::Prop_None, "If Solid is true, this sets the facemaker class to use when converting wires to faces. Otherwise, ignored."); FaceMakerMode.setEnums(MakerEnums); + ADD_PROPERTY_TYPE(InnerWireTaper, (static_cast(InnerWireTaper::Inverted)), "Compatibility", App::Prop_Hidden, + "Controls taper direction for inner wires (holes)."); + InnerWireTaper.setEnums(eInnerWireTaperStrings); // clang-format on } @@ -144,7 +149,7 @@ short Extrusion::mustExecute() const if (Base.isTouched() || Dir.isTouched() || DirMode.isTouched() || DirLink.isTouched() || LengthFwd.isTouched() || LengthRev.isTouched() || Solid.isTouched() || Reversed.isTouched() || Symmetric.isTouched() || TaperAngle.isTouched() - || TaperAngleRev.isTouched() || FaceMakerClass.isTouched()) { + || TaperAngleRev.isTouched() || FaceMakerClass.isTouched() || InnerWireTaper.isTouched()) { return 1; } return 0; @@ -267,6 +272,7 @@ ExtrusionParameters Extrusion::computeFinalParameters() } result.faceMakerClass = this->FaceMakerClass.getValue(); + result.innerWireTaper = static_cast(this->InnerWireTaper.getValue()); return result; } @@ -495,6 +501,16 @@ void Extrusion::onDocumentRestored() restoreFaceMakerMode(this); } +void Extrusion::Restore(Base::XMLReader& reader) +{ + Part::Feature::Restore(reader); + + // for 1.0 the inner wire taper was not inverted due to a bug + if (Base::getVersion(reader.ProgramVersion) == Base::Version::v1_0) { + InnerWireTaper.setValue(static_cast(Part::InnerWireTaper::SameAsOuter)); + } +} + void Part::Extrusion::onChanged(const App::Property* prop) { if (prop == &FaceMakerMode) { diff --git a/src/Mod/Part/App/FeatureExtrusion.h b/src/Mod/Part/App/FeatureExtrusion.h index 2fa025bc3c..732602ced5 100644 --- a/src/Mod/Part/App/FeatureExtrusion.h +++ b/src/Mod/Part/App/FeatureExtrusion.h @@ -58,6 +58,7 @@ public: App::PropertyAngle TaperAngleRev; App::PropertyString FaceMakerClass; App::PropertyEnumeration FaceMakerMode; + App::PropertyEnumeration InnerWireTaper; /** @name methods override feature */ //@{ @@ -108,6 +109,7 @@ public: static Base::Vector3d calculateShapeNormal(const App::PropertyLink& shapeLink); void onDocumentRestored() override; + void Restore(Base::XMLReader& reader) override; public: // mode enumerations enum eDirMode @@ -117,6 +119,7 @@ public: // mode enumerations dmNormal }; static const char* eDirModeStrings[]; + static const char* eInnerWireTaperStrings[]; protected: void setupObject() override; diff --git a/src/Mod/PartDesign/App/FeatureExtrude.cpp b/src/Mod/PartDesign/App/FeatureExtrude.cpp index 41f36ed19f..430eafb6ef 100644 --- a/src/Mod/PartDesign/App/FeatureExtrude.cpp +++ b/src/Mod/PartDesign/App/FeatureExtrude.cpp @@ -912,6 +912,7 @@ TopoShape FeatureExtrude::generateSingleExtrusionSide( Part::ExtrusionParameters params; params.taperAngleFwd = Base::toRadians(taperAngleDeg); + params.innerWireTaper = Part::InnerWireTaper::SameAsOuter; if (std::fabs(params.taperAngleFwd) >= Precision::Angular() || std::fabs(params.taperAngleRev) >= Precision::Angular()) {