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 c31ebeeee6)
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 <chennes@pioneerlibrarysystem.org>
This commit is contained in:
tetektoza
2026-01-13 06:52:43 +01:00
committed by GitHub
parent 5792b1173c
commit bb48de8265
5 changed files with 50 additions and 8 deletions

View File

@@ -615,13 +615,24 @@ void ExtrusionHelper::makeElementDraft(
Standard_Failure::Raise("Failed to make drafted extrusion");
}
std::vector<TopoShape> 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");
}

View File

@@ -25,6 +25,7 @@
#ifndef PART_EXTRUSIONHELPER_H
#define PART_EXTRUSIONHELPER_H
#include <cstdint>
#include <list>
#include <vector>
#include <gp_Dir.hxx>
@@ -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

View File

@@ -41,6 +41,7 @@
#include <App/Document.h>
#include <Base/Exception.h>
#include <Base/ProgramVersion.h>
#include <Base/Tools.h>
#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<long>(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<Part::InnerWireTaper>(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<long>(Part::InnerWireTaper::SameAsOuter));
}
}
void Part::Extrusion::onChanged(const App::Property* prop)
{
if (prop == &FaceMakerMode) {

View File

@@ -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;

View File

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