From e4ed0d883f513f333484c32a774882ef11a2b964 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 11 May 2024 14:17:02 +0200 Subject: [PATCH] PartDesign: Introduce ability to disable single-solid rule This adds "SingleSolidRuleMode" enum that controls if PartDesign will enforce singular solid. By default the single-solid is enforced so nothing changes for the user, it must be explicitly disabled by setting new Allow Compound boolean property on a given body. Default for this value is controled using user parameter under Mod/PartDesign/AllowCompoundDefault --- src/Mod/Part/App/BodyBase.h | 3 +-- src/Mod/PartDesign/App/Body.cpp | 30 ++++++++++++++++++++------ src/Mod/PartDesign/App/Body.h | 1 + src/Mod/PartDesign/App/Feature.cpp | 34 ++++++++++++++++++++++++++---- src/Mod/PartDesign/App/Feature.h | 7 ++++-- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/Mod/Part/App/BodyBase.h b/src/Mod/Part/App/BodyBase.h index 0ac0c9c056..f9edbcf991 100644 --- a/src/Mod/Part/App/BodyBase.h +++ b/src/Mod/Part/App/BodyBase.h @@ -47,9 +47,8 @@ public: /** * The final feature of the body it is associated with. * Note: tip may either point to the BaseFeature or to some feature inside the Group list. - * in case it points to the model the PartDesign::Body guaranties that it is a solid. */ - App::PropertyLink Tip; + App::PropertyLink Tip; /** * A base object of the body, serves as a base object for the first feature of the body. diff --git a/src/Mod/PartDesign/App/Body.cpp b/src/Mod/PartDesign/App/Body.cpp index 0e41160ca7..ff92107892 100644 --- a/src/Mod/PartDesign/App/Body.cpp +++ b/src/Mod/PartDesign/App/Body.cpp @@ -40,7 +40,17 @@ using namespace PartDesign; PROPERTY_SOURCE(PartDesign::Body, Part::BodyBase) Body::Body() { - _GroupTouched.setStatus(App::Property::Output,true); + ADD_PROPERTY_TYPE(AllowCompound, (false), "Experimental", App::Prop_None, "Allow multiple solids in Body (experimental)"); + + _GroupTouched.setStatus(App::Property::Output, true); + + static Base::Reference hGrp = App::GetApplication() + .GetUserParameter() + .GetGroup("BaseApp/Preferences/Mod/PartDesign"); + + auto allowCompoundDefaultValue = hGrp->GetBool("AllowCompoundDefault", false); + + ADD_PROPERTY(AllowCompound, (allowCompoundDefaultValue)); } /* @@ -428,7 +438,7 @@ void Body::onSettingDocument() { Part::BodyBase::onSettingDocument(); } -void Body::onChanged (const App::Property* prop) { +void Body::onChanged(const App::Property* prop) { // we neither load a project nor perform undo/redo if (!this->isRestoring() && this->getDocument() @@ -438,7 +448,6 @@ void Body::onChanged (const App::Property* prop) { auto first = Group.getValues().empty() ? nullptr : Group.getValues().front(); if (BaseFeature.getValue()) { - //setup the FeatureBase if needed if (!first || !first->isDerivedFrom(FeatureBase::getClassTypeId())) { bf = static_cast(getDocument()->addObject("PartDesign::FeatureBase", "BaseFeature")); @@ -452,17 +461,26 @@ void Body::onChanged (const App::Property* prop) { } } - if (bf && (bf->BaseFeature.getValue() != BaseFeature.getValue())) + if (bf && (bf->BaseFeature.getValue() != BaseFeature.getValue())) { bf->BaseFeature.setValue(BaseFeature.getValue()); + } } - else if( prop == &Group ) { - + else if (prop == &Group) { //if the FeatureBase was deleted we set the BaseFeature link to nullptr if (BaseFeature.getValue() && (Group.getValues().empty() || !Group.getValues().front()->isDerivedFrom(FeatureBase::getClassTypeId()))) { BaseFeature.setValue(nullptr); } } + else if (prop == &AllowCompound) { + // As disallowing compounds can break the model we need to recompute the whole tree. + // This will inform user about first place where there is more than one solid. + if (!AllowCompound.getValue()) { + for (auto feature : getFullModel()) { + feature->enforceRecompute(); + } + } + } } Part::BodyBase::onChanged(prop); diff --git a/src/Mod/PartDesign/App/Body.h b/src/Mod/PartDesign/App/Body.h index fbb8ae879b..e36f751960 100644 --- a/src/Mod/PartDesign/App/Body.h +++ b/src/Mod/PartDesign/App/Body.h @@ -41,6 +41,7 @@ class PartDesignExport Body : public Part::BodyBase PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::Body); public: + App::PropertyBool AllowCompound; /// True if this body feature is active or was active when the document was last closed //App::PropertyBool IsActive; diff --git a/src/Mod/PartDesign/App/Feature.cpp b/src/Mod/PartDesign/App/Feature.cpp index 0b283a3383..fe6e1eeadd 100644 --- a/src/Mod/PartDesign/App/Feature.cpp +++ b/src/Mod/PartDesign/App/Feature.cpp @@ -91,10 +91,17 @@ short Feature::mustExecute() const // TODO: Toponaming April 2024 Deprecated in favor of TopoShape method. Remove when possible. TopoDS_Shape Feature::getSolid(const TopoDS_Shape& shape) { - if (shape.IsNull()) + if (shape.IsNull()) { Standard_Failure::Raise("Shape is null"); + } + + // If single solid rule is not enforced we simply return the shape as is + if (singleSolidRuleMode() != Feature::SingleSolidRuleMode::Enforced) { + return shape; + } + TopExp_Explorer xp; - xp.Init(shape,TopAbs_SOLID); + xp.Init(shape, TopAbs_SOLID); if (xp.More()) { return xp.Current(); } @@ -107,12 +114,19 @@ TopoShape Feature::getSolid(const TopoShape& shape) if (shape.isNull()) { throw Part::NullShapeException("Null shape"); } + + // If single solid rule is not enforced we simply return the shape as is + if (singleSolidRuleMode() != Feature::SingleSolidRuleMode::Enforced) { + return shape; + } + int count = shape.countSubShapes(TopAbs_SOLID); - if(count) { - auto res = shape.getSubTopoShape(TopAbs_SOLID,1); + if (count) { + auto res = shape.getSubTopoShape(TopAbs_SOLID, 1); res.fixSolidOrientation(); return res; } + return shape; } @@ -153,12 +167,24 @@ int Feature::countSolids(const TopoDS_Shape& shape, TopAbs_ShapeEnum type) bool Feature::isSingleSolidRuleSatisfied(const TopoDS_Shape& shape, TopAbs_ShapeEnum type) { + if (singleSolidRuleMode() == Feature::SingleSolidRuleMode::Disabled) { + return true; + } + int solidCount = countSolids(shape, type); return solidCount <= 1; } +Feature::SingleSolidRuleMode Feature::singleSolidRuleMode() +{ + auto body = getFeatureBody(); + auto areCompoundSolidsAllowed = body->AllowCompound.getValue(); + + return areCompoundSolidsAllowed ? SingleSolidRuleMode::Disabled : SingleSolidRuleMode::Enforced; +} + const gp_Pnt Feature::getPointFromFace(const TopoDS_Face& f) { if (!f.Infinite()) { diff --git a/src/Mod/PartDesign/App/Feature.h b/src/Mod/PartDesign/App/Feature.h index 91e1b9189a..916aa03100 100644 --- a/src/Mod/PartDesign/App/Feature.h +++ b/src/Mod/PartDesign/App/Feature.h @@ -52,6 +52,8 @@ class PartDesignExport Feature : public Part::Feature, public App::SuppressibleE public: Feature(); + enum SingleSolidRuleMode { Disabled = 0, Enforced = 1 }; + /// Base feature which this feature will be fused into or cut out of App::PropertyLink BaseFeature; App::PropertyLinkHidden _Body; @@ -96,14 +98,15 @@ protected: * Get a solid of the given shape. If no solid is found an exception is raised. */ // TODO: Toponaming April 2024 Deprecated in favor of TopoShape method. Remove when possible. - static TopoDS_Shape getSolid(const TopoDS_Shape&); + TopoDS_Shape getSolid(const TopoDS_Shape&); TopoShape getSolid(const TopoShape&); static int countSolids(const TopoDS_Shape&, TopAbs_ShapeEnum type = TopAbs_SOLID); /** * Checks if the single-solid body rule is fulfilled. */ - static bool isSingleSolidRuleSatisfied(const TopoDS_Shape&, TopAbs_ShapeEnum type = TopAbs_SOLID); + bool isSingleSolidRuleSatisfied(const TopoDS_Shape&, TopAbs_ShapeEnum type = TopAbs_SOLID); + SingleSolidRuleMode singleSolidRuleMode(); /// Grab any point from the given face static const gp_Pnt getPointFromFace(const TopoDS_Face& f);