PartDesign: Fix hole centered on point edge case (#21257)

* Light refactor of getTopoShape function

* Fix hole edge case

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update src/Mod/Part/App/PartFeature.cpp

Co-authored-by: Kacper Donat <kadet1090@gmail.com>

* Update src/Mod/Part/App/PartFeature.cpp

Co-authored-by: Kacper Donat <kadet1090@gmail.com>

* Update src/Mod/Part/App/PartFeature.cpp

Co-authored-by: Kacper Donat <kadet1090@gmail.com>

* Update src/Mod/Part/App/PartFeature.cpp

Co-authored-by: Kacper Donat <kadet1090@gmail.com>

* Refactor simplifyCompound()

* Use Base::Flags<GetShapeOption>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Shorten enum name and move it from class scope to namespace scope

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Kacper Donat <kadet1090@gmail.com>
This commit is contained in:
theo-vt
2025-05-29 16:37:54 -04:00
committed by GitHub
parent 17e56f6570
commit 13e7952ccc
52 changed files with 449 additions and 223 deletions

View File

@@ -69,7 +69,7 @@ App::DocumentObjectExecReturn* FeatureBase::execute()
QT_TRANSLATE_NOOP("Exception", "BaseFeature must be a Part::Feature"));
}
auto shape = Part::Feature::getTopoShape(BaseFeature.getValue());
auto shape = Part::Feature::getTopoShape(BaseFeature.getValue(), Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform);
if (!shape.countSubShapes(TopAbs_SOLID)) {
shape = shape.makeElementSolid();
}

View File

@@ -107,7 +107,7 @@ App::DocumentObjectExecReturn *Boolean::execute()
std::vector<TopoShape> shapes;
shapes.push_back(baseTopShape);
for(auto it=tools.begin(); it<tools.end(); ++it) {
auto shape = getTopoShape(*it);
auto shape = getTopoShape(*it, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform);
if (shape.isNull())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception","Tool shape is null"));
shapes.push_back(shape);

View File

@@ -514,7 +514,12 @@ App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions opt
if (sub.empty() && subs.size() > 1) {
continue;
}
TopoShape shape = Part::Feature::getTopoShape(obj, sub.c_str(), true);
TopoShape shape = Part::Feature::getTopoShape(obj,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
sub.c_str());
if (shape.isNull()) {
FC_ERR(getFullName()
<< ": failed to get profile shape " << obj->getFullName() << "." << sub);

View File

@@ -1855,7 +1855,11 @@ static gp_Pnt toPnt(gp_Vec dir)
App::DocumentObjectExecReturn* Hole::execute()
{
TopoShape profileshape = getProfileShape();
TopoShape profileshape =
getProfileShape( Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform
| Part::ShapeOption::DontSimplifyCompound);
// Find the base shape
TopoShape base;

View File

@@ -77,13 +77,17 @@ Loft::getSectionShape(const char *name,
auto subName = subs.empty() ? "" : subs.front();
auto useEntireSketch = obj->isDerivedFrom<Part::Part2DObject>() && subName.find("Vertex") != 0;
if (subs.empty() || std::ranges::find(subs, std::string()) != subs.end() || useEntireSketch ) {
shapes.push_back(Part::Feature::getTopoShape(obj));
shapes.push_back(Part::Feature::getTopoShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform));
if (shapes.back().isNull())
FC_THROWM(Part::NullShapeException, "Failed to get shape of "
<< name << " " << App::SubObjectT(obj, "").getSubObjectFullName(obj->getDocument()->getName()));
} else {
for (const auto &sub : subs) {
shapes.push_back(Part::Feature::getTopoShape(obj, sub.c_str(), /*needSubElement*/true));
shapes.push_back(Part::Feature::getTopoShape(obj,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
sub.c_str()));
if (shapes.back().isNull())
FC_THROWM(Part::NullShapeException, "Failed to get shape of " << name << " "
<< App::SubObjectT(obj, sub.c_str()).getSubObjectFullName(obj->getDocument()->getName()));

View File

@@ -184,13 +184,18 @@ TopoShape ProfileBased::getTopoShapeVerifiedFace(bool silent,
TopoShape shape;
if (AllowMultiFace.getValue()) {
if (subs.empty()) {
shape = Part::Feature::getTopoShape(obj);
shape = Part::Feature::getTopoShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform);
}
else {
std::vector<TopoShape> shapes;
for (auto& sub : subs) {
auto subshape =
Part::Feature::getTopoShape(obj, sub.c_str(), /*needSubElement*/ true);
auto subshape = Part::Feature::getTopoShape(obj,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
sub.c_str());
if (subshape.isNull()) {
FC_THROWM(Base::CADKernelError,
"Sub shape not found: " << obj->getFullName() << "." << sub);
@@ -207,7 +212,11 @@ TopoShape ProfileBased::getTopoShapeVerifiedFace(bool silent,
sub = subs[0];
}
}
shape = Part::Feature::getTopoShape(obj, sub.c_str(), !sub.empty());
shape = Part::Feature::getTopoShape(obj,
(sub.empty() ? Part::ShapeOption::NoFlag : Part::ShapeOption::NeedSubElement)
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
sub.c_str());
}
if (shape.isNull()) {
if (silent) {
@@ -404,19 +413,20 @@ TopoDS_Shape ProfileBased::getVerifiedFace(bool silent) const {
return TopoDS_Face();
}
TopoShape ProfileBased::getProfileShape() const
TopoShape ProfileBased::getProfileShape(Part::ShapeOptions subShapeOptions) const
{
TopoShape shape;
const auto& subs = Profile.getSubValues();
auto profile = Profile.getValue();
if (subs.empty()) {
shape = Part::Feature::getTopoShape(profile);
shape = Part::Feature::getTopoShape(profile, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform);
}
else {
std::vector<TopoShape> shapes;
for (auto& sub : subs) {
shapes.push_back(
Part::Feature::getTopoShape(profile, sub.c_str(), /* needSubElement */ true));
shapes.push_back(Part::Feature::getTopoShape(profile,
subShapeOptions,
sub.c_str()));
}
shape = TopoShape(shape.Tag).makeElementCompound(shapes);
}
@@ -567,9 +577,11 @@ TopoShape ProfileBased::getTopoShapeSupportFace() const
const auto& Support = sketch->AttachmentSupport;
App::DocumentObject* ref = Support.getValue();
shape = Part::Feature::getTopoShape(
ref,
Support.getSubValues().size() ? Support.getSubValues()[0].c_str() : "",
true);
ref,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
Support.getSubValues().empty() ? "" : Support.getSubValues()[0].c_str());
}
if (!shape.isNull()) {
if (shape.shapeType(true) != TopAbs_FACE) {
@@ -663,7 +675,13 @@ void ProfileBased::getUpToFaceFromLinkSub(TopoShape& upToFace, const App::Proper
}
const auto& subs = refFace.getSubValues();
upToFace = Part::Feature::getTopoShape(ref, subs.size() ? subs[0].c_str() : nullptr, true);
upToFace = Part::Feature::getTopoShape(
ref,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
subs.empty() ? nullptr : subs[0].c_str());
if (!upToFace.hasSubShape(TopAbs_FACE)) {
throw Base::ValueError("SketchBased: Up to face: Failed to extract face");
}
@@ -687,7 +705,12 @@ int ProfileBased::getUpToShapeFromLinkSubList(TopoShape& upToShape, const App::P
auto subStrings = subSet.second;
if (subStrings.empty() || subStrings[0].empty()) {
TopoShape baseShape = Part::Feature::getTopoShape(ref, nullptr, true);
TopoShape baseShape = Part::Feature::getTopoShape(ref,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform);
for (auto face : baseShape.getSubTopoShapes(TopAbs_FACE)){
faceList.push_back(face);
ret ++;
@@ -695,7 +718,13 @@ int ProfileBased::getUpToShapeFromLinkSubList(TopoShape& upToShape, const App::P
}
else {
for (auto &subString : subStrings){
auto shape = Part::Feature::getTopoShape(ref, subString.c_str(), true);
auto shape = Part::Feature::getShape(
ref,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
subString.c_str());
TopoShape face = shape;
face = face.makeElementFace();
if (face.isNull()) {
@@ -1419,7 +1448,7 @@ Base::Vector3d ProfileBased::getProfileNormal() const {
if (shape.hasSubShape(TopAbs_EDGE)) {
// Find the first planar face that contains the edge, and return the plane normal
TopoShape objShape = Part::Feature::getTopoShape(obj);
TopoShape objShape = Part::Feature::getTopoShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform);
for (int idx : objShape.findAncestors(shape.getSubShape(TopAbs_EDGE, 1), TopAbs_FACE)) {
if (objShape.getSubTopoShape(TopAbs_FACE, idx).findPlane(pln)) {
gp_Dir dir = pln.Axis().Direction();

View File

@@ -131,7 +131,11 @@ public:
virtual Base::Vector3d getProfileNormal() const;
TopoShape getProfileShape() const;
// Use Part::ShapeOptions enum to pass flags
TopoShape getProfileShape(Part::ShapeOptions subShapeOptions =
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform) const;
/// retrieves the number of axes in the linked sketch (defined as construction lines)
int getSketchAxisCount() const;

View File

@@ -670,7 +670,11 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
for (const auto& sub : subs) {
++subidx;
try {
auto shape = Part::Feature::getTopoShape(obj, sub.c_str(), true);
auto shape = Part::Feature::getTopoShape(obj,
Part::ShapeOption::NeedSubElement
| Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform,
sub.c_str());
if (!shape.isNull()) {
shapes.push_back(shape);
shapeOwners.emplace_back(sidx, subidx);

View File

@@ -151,7 +151,8 @@ void TaskDressUpParameters::addAllEdges(QListWidget* widget)
if (!base) {
return;
}
int count = Part::Feature::getTopoShape(base).countSubShapes(TopAbs_EDGE);
int count = Part::Feature::getTopoShape(base, Part::ShapeOption::ResolveLink
| Part::ShapeOption::Transform).countSubShapes(TopAbs_EDGE);
auto subValues = pcDressUp->Base.getSubValues(false);
std::size_t len = subValues.size();
for (int i = 0; i < count; ++i) {