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()) {