From 70b9305f2eb434e5fc5bf8595088f0b10593d1a8 Mon Sep 17 00:00:00 2001 From: Alfredo Monclus Date: Fri, 23 Jan 2026 07:41:49 -0300 Subject: [PATCH 1/3] PD: hole use a compound instead of a fuse for threads --- src/Mod/PartDesign/App/FeatureHole.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureHole.cpp b/src/Mod/PartDesign/App/FeatureHole.cpp index 08975ec1a7..a12b9f7470 100644 --- a/src/Mod/PartDesign/App/FeatureHole.cpp +++ b/src/Mod/PartDesign/App/FeatureHole.cpp @@ -1960,21 +1960,20 @@ App::DocumentObjectExecReturn* Hole::execute() ); } - // Make thread if (Threaded.getValue() && ModelThread.getValue()) { TopoDS_Shape protoThread = makeThread(xDir, zDir, length); - // fuse the thread to the hole - FCBRepAlgoAPI_Fuse mkFuse(protoHole, protoThread); - if (!mkFuse.IsDone()) { - return new App::DocumentObjectExecReturn( - QT_TRANSLATE_NOOP("Exception", "Error: Adding the thread failed") - ); - } + TopoDS_Compound holeWithThread; + holeWithThread.Nullify(); + + BRep_Builder builder; + builder.MakeCompound(holeWithThread); + builder.Add(holeWithThread, protoHole); + builder.Add(holeWithThread, protoThread); // we reuse the name protoHole (only now it is threaded) - protoHole = mkFuse.Shape(); + protoHole = holeWithThread; } std::vector holes; auto compound = findHoles(holes, profileshape, protoHole); @@ -2169,6 +2168,7 @@ Base::Vector3d Hole::guessNormalDirection(const TopoShape& profileshape) const return getProfileNormal(); } + TopoShape Hole::findHoles( std::vector& holes, const TopoShape& profileshape, From 603fc4b3410b0d9706a30ae5d62f6d26015a3312 Mon Sep 17 00:00:00 2001 From: Alfredo Monclus Date: Wed, 28 Jan 2026 09:03:01 -0300 Subject: [PATCH 2/3] PD: hole speficify the tolerance in the boolean op this fixes issues in which the helix is the only thing being cut and the revolve is ignored --- src/Mod/PartDesign/App/FeatureHole.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureHole.cpp b/src/Mod/PartDesign/App/FeatureHole.cpp index a12b9f7470..f7055a0812 100644 --- a/src/Mod/PartDesign/App/FeatureHole.cpp +++ b/src/Mod/PartDesign/App/FeatureHole.cpp @@ -2009,7 +2009,12 @@ App::DocumentObjectExecReturn* Hole::execute() result = compound; } else { - result.makeElementBoolean(maker, {base, compound}); + result.makeElementBoolean( + maker, + {base, compound}, + getNameInDocument(), + Precision::Confusion() + ); } result = getSolid(result); retry = false; @@ -2031,7 +2036,12 @@ App::DocumentObjectExecReturn* Hole::execute() for (auto& hole : holes) { ++i; try { - result.makeElementBoolean(maker, {base, hole}); + result.makeElementBoolean( + maker, + {base, hole}, + getNameInDocument(), + Precision::Confusion() + ); } catch (Standard_Failure&) { std::string msg( From 928e5e0fbbdb439971c6c93ab5e88ce474ed9583 Mon Sep 17 00:00:00 2001 From: Alfredo Monclus Date: Wed, 28 Jan 2026 10:37:03 -0300 Subject: [PATCH 3/3] PD: preview substractive feature check for removed volume --- src/Mod/PartDesign/App/FeatureAddSub.cpp | 48 ++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureAddSub.cpp b/src/Mod/PartDesign/App/FeatureAddSub.cpp index 385165d120..959c13e7a8 100644 --- a/src/Mod/PartDesign/App/FeatureAddSub.cpp +++ b/src/Mod/PartDesign/App/FeatureAddSub.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "FeatureAddSub.h" #include "FeaturePy.h" @@ -75,7 +77,6 @@ void FeatureAddSub::getAddSubShape(Part::TopoShape& addShape, Part::TopoShape& s subShape = AddSubShape.getShape(); } } - void FeatureAddSub::updatePreviewShape() { const auto notifyWarning = [](const QString& message) { @@ -88,10 +89,43 @@ void FeatureAddSub::updatePreviewShape() // for subtractive shapes we want to also showcase removed volume, not only the tool if (addSubType == Subtractive) { TopoShape base = getBaseTopoShape(true).moved(getLocation().Inverted()); + const TopoShape& tool = AddSubShape.getShape(); - if (const TopoShape& addSubShape = AddSubShape.getShape(); !addSubShape.isEmpty()) { + if (!tool.isEmpty()) { try { - base.makeElementBoolean(Part::OpCodes::Common, {base, addSubShape}); + // Compute removed volume preview (for display) + TopoShape common; + common.makeElementBoolean( + Part::OpCodes::Common, + {base, tool}, + "Preview", + Precision::Confusion() + ); + + // does CUT change volume? + GProp_GProps propsBefore, propsAfter; + BRepGProp::VolumeProperties(base.getShape(), propsBefore); + + TopoShape cut; + cut.makeElementBoolean( + Part::OpCodes::Cut, + {base, tool}, + "PreviewCheck", + Precision::Confusion() + ); + + BRepGProp::VolumeProperties(cut.getShape(), propsAfter); + + const double removed = propsBefore.Mass() - propsAfter.Mass(); + + if (removed <= Precision::Confusion()) { + notifyWarning( + tr("Resulting shape is empty. That may indicate that no material will be " + "removed or a problem with the model.") + ); + } + PreviewShape.setValue(common); + return; } catch (Standard_Failure& e) { notifyWarning(QString::fromUtf8(e.GetMessageString())); @@ -99,14 +133,6 @@ void FeatureAddSub::updatePreviewShape() catch (Base::Exception& e) { notifyWarning(QString::fromStdString(e.getMessage())); } - - if (base.isEmpty()) { - notifyWarning( - tr("Resulting shape is empty. That may indicate that no material will be " - "removed or a problem with the model.") - ); - } - PreviewShape.setValue(base); return; }