diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp index c9118aa464..92f28e9fdf 100644 --- a/src/Mod/PartDesign/App/AppPartDesign.cpp +++ b/src/Mod/PartDesign/App/AppPartDesign.cpp @@ -88,6 +88,8 @@ PyMOD_INIT_FUNC(_PartDesign) // clang-format off PartDesign::Feature ::init(); PartDesign::FeaturePython ::init(); + PartDesign::FeatureRefine ::init(); + PartDesign::FeatureRefinePython ::init(); PartDesign::Solid ::init(); PartDesign::FeatureAddSub ::init(); PartDesign::FeatureAddSubPython ::init(); diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt index 214520ebcd..64845a2dce 100644 --- a/src/Mod/PartDesign/App/CMakeLists.txt +++ b/src/Mod/PartDesign/App/CMakeLists.txt @@ -95,6 +95,8 @@ SET(FeaturesSketchBased_SRCS FeatureRevolution.h FeatureGroove.cpp FeatureGroove.h + FeatureRefine.cpp + FeatureRefine.h FeatureAddSub.cpp FeatureAddSub.h FeatureHole.h diff --git a/src/Mod/PartDesign/App/FeatureAddSub.cpp b/src/Mod/PartDesign/App/FeatureAddSub.cpp index 3a2bf63e06..4c09474ac2 100644 --- a/src/Mod/PartDesign/App/FeatureAddSub.cpp +++ b/src/Mod/PartDesign/App/FeatureAddSub.cpp @@ -39,13 +39,11 @@ namespace PartDesign { extern bool getPDRefineModelParameter(); -PROPERTY_SOURCE(PartDesign::FeatureAddSub, PartDesign::Feature) +PROPERTY_SOURCE(PartDesign::FeatureAddSub, PartDesign::FeatureRefine) FeatureAddSub::FeatureAddSub() { ADD_PROPERTY(AddSubShape,(TopoDS_Shape())); - ADD_PROPERTY_TYPE(Refine,(0),"Part Design",(App::PropertyType)(App::Prop_None),"Refine shape (clean up redundant edges) after adding/subtracting"); - this->Refine.setValue(getPDRefineModelParameter()); } FeatureAddSub::Type FeatureAddSub::getAddSubType() @@ -60,48 +58,6 @@ short FeatureAddSub::mustExecute() const return PartDesign::Feature::mustExecute(); } - -bool FeatureAddSub::onlyHasToRefine() const -{ - if( ! Refine.isTouched()){ - return false; - } - if (rawShape.isNull()){ - return false; - } - std::vector propList; - getPropertyList(propList); - for (auto prop : propList){ - if (prop != &Refine - /*&& prop != &SuppressedShape*/ - && prop->isTouched()){ - return false; - } - } - return true; -} - - - -TopoShape FeatureAddSub::refineShapeIfActive(const TopoShape& oldShape, const RefineErrorPolicy onError) const -{ - if (this->Refine.getValue()) { - TopoShape shape(oldShape); - // this->fixShape(shape); // Todo: Not clear that this is required - try{ - return shape.makeElementRefine(); - } - catch (Standard_Failure& err) { - if(onError == RefineErrorPolicy::Warn){ - Base::Console().Warning((std::string("Refine failed: ") + err.GetMessageString()).c_str()); - } else { - throw; - } - } - } - return oldShape; -} - void FeatureAddSub::getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShape) { if (addSubType == Additive) diff --git a/src/Mod/PartDesign/App/FeatureAddSub.h b/src/Mod/PartDesign/App/FeatureAddSub.h index b7948bf952..fc5cc7e1cc 100644 --- a/src/Mod/PartDesign/App/FeatureAddSub.h +++ b/src/Mod/PartDesign/App/FeatureAddSub.h @@ -24,13 +24,13 @@ #ifndef PARTDESIGN_FeatureAdditive_H #define PARTDESIGN_FeatureAdditive_H -#include "Feature.h" +#include "FeatureRefine.h" /// Base class of all additive features in PartDesign namespace PartDesign { -class PartDesignExport FeatureAddSub : public PartDesign::Feature +class PartDesignExport FeatureAddSub : public PartDesign::FeatureRefine { PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::FeatureAddSub); @@ -40,11 +40,6 @@ public: Subtractive }; - enum class RefineErrorPolicy { - Raise = 0, - Warn - }; - FeatureAddSub(); Type getAddSubType(); @@ -54,17 +49,10 @@ public: virtual void getAddSubShape(Part::TopoShape &addShape, Part::TopoShape &subShape); Part::PropertyPartShape AddSubShape; - App::PropertyBool Refine; protected: Type addSubType{Additive}; - - //store the shape before refinement - TopoShape rawShape; - - bool onlyHasToRefine() const; - TopoShape refineShapeIfActive(const TopoShape& oldShape, const RefineErrorPolicy onError = RefineErrorPolicy::Raise) const; }; using FeatureAddSubPython = App::FeaturePythonT; diff --git a/src/Mod/PartDesign/App/FeatureBoolean.cpp b/src/Mod/PartDesign/App/FeatureBoolean.cpp index ee260cb7e5..465ab8a841 100644 --- a/src/Mod/PartDesign/App/FeatureBoolean.cpp +++ b/src/Mod/PartDesign/App/FeatureBoolean.cpp @@ -53,9 +53,6 @@ Boolean::Boolean() ADD_PROPERTY(Type,((long)0)); Type.setEnums(TypeEnums); - ADD_PROPERTY_TYPE(Refine,(0),"Part Design",(App::PropertyType)(App::Prop_None),"Refine shape (clean up redundant edges) after adding/subtracting"); - this->Refine.setValue(getPDRefineModelParameter()); - ADD_PROPERTY_TYPE(UsePlacement,(0),"Part Design",(App::PropertyType)(App::Prop_None),"Apply the placement of the second ( tool ) object"); this->UsePlacement.setValue(false); @@ -187,25 +184,4 @@ void Boolean::handleChangedPropertyName(Base::XMLReader &reader, const char * Ty } } - -// FIXME: This method ( and the Refine property it depends on ) is redundant with the exact same -// thing in FeatureAddSub, but cannot reasonably be moved up an inheritance level to Feature as -// there are inheritors like FeatureBox for which a refine Property does not make sense. A -// solution like moving Refine and refineShapeIfActive to a new FeatureRefine class that sits -// between Feature and FeatureBoolean / FeatureAddSub is a possibility, or maybe [ew!] hiding the -// property in Feature and only enabling it in the places it is relevant. -TopoShape Boolean::refineShapeIfActive(const TopoShape& oldShape) const -{ - if (this->Refine.getValue()) { - try { - return oldShape.makeElementRefine(); - } - catch (Standard_Failure&) { - return oldShape; - } - } - - return oldShape; -} - } diff --git a/src/Mod/PartDesign/App/FeatureBoolean.h b/src/Mod/PartDesign/App/FeatureBoolean.h index 2810574605..5fcffdac2b 100644 --- a/src/Mod/PartDesign/App/FeatureBoolean.h +++ b/src/Mod/PartDesign/App/FeatureBoolean.h @@ -26,7 +26,7 @@ #include #include -#include "Feature.h" +#include "FeatureRefine.h" namespace PartDesign @@ -36,7 +36,7 @@ namespace PartDesign * Abstract superclass of all features that are created by transformation of another feature * Transformations are translation, rotation and mirroring */ -class PartDesignExport Boolean : public PartDesign::Feature, public App::GeoFeatureGroupExtension +class PartDesignExport Boolean : public PartDesign::FeatureRefine, public App::GeoFeatureGroupExtension { PROPERTY_HEADER_WITH_EXTENSIONS(PartDesign::Boolean); @@ -46,7 +46,6 @@ public: /// The type of the boolean operation App::PropertyEnumeration Type; - App::PropertyBool Refine; App::PropertyBool UsePlacement; /** @name methods override feature */ @@ -63,7 +62,6 @@ public: protected: void handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName) override; - TopoShape refineShapeIfActive(const TopoShape&) const; private: diff --git a/src/Mod/PartDesign/App/FeatureChamfer.cpp b/src/Mod/PartDesign/App/FeatureChamfer.cpp index 406e094d4a..9a0bfab347 100644 --- a/src/Mod/PartDesign/App/FeatureChamfer.cpp +++ b/src/Mod/PartDesign/App/FeatureChamfer.cpp @@ -104,11 +104,7 @@ short Chamfer::mustExecute() const App::DocumentObjectExecReturn *Chamfer::execute() { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } // NOTE: Normally the Base property and the BaseFeature property should point to the same object. // The only difference is that the Base property also stores the edges that are to be chamfered diff --git a/src/Mod/PartDesign/App/FeatureExtrude.cpp b/src/Mod/PartDesign/App/FeatureExtrude.cpp index e2deb3f82b..73659cc6bd 100644 --- a/src/Mod/PartDesign/App/FeatureExtrude.cpp +++ b/src/Mod/PartDesign/App/FeatureExtrude.cpp @@ -468,11 +468,8 @@ void FeatureExtrude::setupObject() App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions options) { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } + bool makeface = options.testFlag(ExtrudeOption::MakeFace); bool fuse = options.testFlag(ExtrudeOption::MakeFuse); diff --git a/src/Mod/PartDesign/App/FeatureFillet.cpp b/src/Mod/PartDesign/App/FeatureFillet.cpp index 9524cd6bb6..25d60c2778 100644 --- a/src/Mod/PartDesign/App/FeatureFillet.cpp +++ b/src/Mod/PartDesign/App/FeatureFillet.cpp @@ -65,11 +65,8 @@ short Fillet::mustExecute() const App::DocumentObjectExecReturn *Fillet::execute() { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } + Part::TopoShape baseShape; try { diff --git a/src/Mod/PartDesign/App/FeatureGroove.cpp b/src/Mod/PartDesign/App/FeatureGroove.cpp index 24e3fa6e57..66f27fdcd3 100644 --- a/src/Mod/PartDesign/App/FeatureGroove.cpp +++ b/src/Mod/PartDesign/App/FeatureGroove.cpp @@ -81,11 +81,7 @@ short Groove::mustExecute() const App::DocumentObjectExecReturn *Groove::execute() { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } // Validate parameters double angle = Angle.getValue(); diff --git a/src/Mod/PartDesign/App/FeatureHelix.cpp b/src/Mod/PartDesign/App/FeatureHelix.cpp index 303bfbc197..94cfab1c86 100644 --- a/src/Mod/PartDesign/App/FeatureHelix.cpp +++ b/src/Mod/PartDesign/App/FeatureHelix.cpp @@ -126,12 +126,7 @@ short Helix::mustExecute() const App::DocumentObjectExecReturn* Helix::execute() { - - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape, RefineErrorPolicy::Warn); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } // Validate and normalize parameters HelixMode mode = static_cast(Mode.getValue()); diff --git a/src/Mod/PartDesign/App/FeatureLoft.cpp b/src/Mod/PartDesign/App/FeatureLoft.cpp index 378ba308ee..05cb21c004 100644 --- a/src/Mod/PartDesign/App/FeatureLoft.cpp +++ b/src/Mod/PartDesign/App/FeatureLoft.cpp @@ -112,11 +112,7 @@ Loft::getSectionShape(const char *name, App::DocumentObjectExecReturn *Loft::execute() { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } std::vector wires; try { diff --git a/src/Mod/PartDesign/App/FeaturePipe.cpp b/src/Mod/PartDesign/App/FeaturePipe.cpp index a2ee7cf313..f5010885cf 100644 --- a/src/Mod/PartDesign/App/FeaturePipe.cpp +++ b/src/Mod/PartDesign/App/FeaturePipe.cpp @@ -104,11 +104,7 @@ short Pipe::mustExecute() const App::DocumentObjectExecReturn *Pipe::execute() { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } auto getSectionShape = [](App::DocumentObject* feature, const std::vector& subs) -> TopoDS_Shape { diff --git a/src/Mod/PartDesign/App/FeaturePrimitive.cpp b/src/Mod/PartDesign/App/FeaturePrimitive.cpp index e7a52f76b2..21cc034d8d 100644 --- a/src/Mod/PartDesign/App/FeaturePrimitive.cpp +++ b/src/Mod/PartDesign/App/FeaturePrimitive.cpp @@ -68,11 +68,7 @@ FeaturePrimitive::FeaturePrimitive() App::DocumentObjectExecReturn* FeaturePrimitive::execute(const TopoDS_Shape& primitive) { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } try { //transform the primitive in the correct coordinance diff --git a/src/Mod/PartDesign/App/FeatureRefine.cpp b/src/Mod/PartDesign/App/FeatureRefine.cpp new file mode 100644 index 0000000000..b56e9c5ba5 --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureRefine.cpp @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif + +#include +#include +#include +#include + +#include "FeatureRefine.h" +#include "FeaturePy.h" + + +using namespace PartDesign; + +namespace PartDesign +{ +PROPERTY_SOURCE(PartDesign::FeatureRefine, PartDesign::Feature) + +FeatureRefine::FeatureRefine() +{ + ADD_PROPERTY_TYPE(Refine, + (0), + "Part Design", + (App::PropertyType)(App::Prop_None), + "Refine shape (clean up redundant edges) after operations"); + // init Refine property + Base::Reference hGrp = App::GetApplication() + .GetUserParameter() + .GetGroup("BaseApp") + ->GetGroup("Preferences") + ->GetGroup("Mod/PartDesign"); + this->Refine.setValue(hGrp->GetBool("RefineModel", true)); +} + +bool FeatureRefine::onlyHaveRefined() +{ + if (!Refine.isTouched()) { + return false; + } + if (rawShape.isNull()) { + return false; + } + std::vector propList; + getPropertyList(propList); + for (auto prop : propList) { + if (prop != &Refine + /*&& prop != &SuppressedShape*/ + && prop->isTouched()) { + return false; + } + } + TopoShape result = refineShapeIfActive(rawShape); + Shape.setValue(result); + return true; +} +TopoShape FeatureRefine::refineShapeIfActive(const TopoShape& oldShape, + const RefineErrorPolicy onError) const +{ + if (!this->Refine.getValue()) { + return oldShape; + } + TopoShape shape(oldShape); + try { + return shape.makeElementRefine(); + } + catch (Standard_Failure& err) { + if (onError == RefineErrorPolicy::Warn) { + Base::Console().Warning( + fmt::format("Refine failed: {}", err.GetMessageString()).c_str()); + } + else { + throw; + } + } + return oldShape; +} + +} // namespace PartDesign + + +namespace App +{ +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(PartDesign::FeatureRefinePython, PartDesign::FeatureRefine) +template<> +const char* PartDesign::FeatureRefinePython::getViewProviderName() const +{ + return "PartDesignGui::ViewProviderPython"; +} +template<> +PyObject* PartDesign::FeatureRefinePython::getPyObject() +{ + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new FeaturePythonPyT(this), true); + } + return Py::new_reference_to(PythonObject); +} +/// @endcond + +// explicit template instantiation +template class PartDesignExport FeaturePythonT; +} // namespace App diff --git a/src/Mod/PartDesign/App/FeatureRefine.h b/src/Mod/PartDesign/App/FeatureRefine.h new file mode 100644 index 0000000000..52b790b13a --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureRefine.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + + +#ifndef PARTDESIGN_FeatureRefine_H +#define PARTDESIGN_FeatureRefine_H + +#include "Feature.h" + +/// Base class of all features that can be refined PartDesign +namespace PartDesign +{ + +class PartDesignExport FeatureRefine : public PartDesign::Feature +{ + PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::FeatureRefine); + +public: + + enum class RefineErrorPolicy { + Raise = 0, + Warn + }; + + FeatureRefine(); + + App::PropertyBool Refine; + +protected: + //store the shape before refinement + TopoShape rawShape; + + + /** + * Check if the feature *only* requires the refinement operation, and do that refinement if so. + * Typically called as the first operation in a subclass's `execute()` method to provide an + * early exit if no other parameters have been changed (so the base feature is still + * up-to-date). + * + * @return true if the refine was done and that was the only thing needed, or false if further + * computation is necessary. + */ + bool onlyHaveRefined(); + TopoShape refineShapeIfActive(const TopoShape& oldShape, const RefineErrorPolicy onError = RefineErrorPolicy::Raise) const; +}; + +using FeatureRefinePython = App::FeaturePythonT; + +} //namespace PartDesign + + +#endif // PARTDESIGN_FeatureRefine_H diff --git a/src/Mod/PartDesign/App/FeatureRevolution.cpp b/src/Mod/PartDesign/App/FeatureRevolution.cpp index 0f9cca70cb..d60b1f6558 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.cpp +++ b/src/Mod/PartDesign/App/FeatureRevolution.cpp @@ -80,11 +80,8 @@ short Revolution::mustExecute() const App::DocumentObjectExecReturn* Revolution::execute() { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } + // Validate parameters // All angles are in radians unless explicitly stated diff --git a/src/Mod/PartDesign/App/FeatureThickness.cpp b/src/Mod/PartDesign/App/FeatureThickness.cpp index 002fcf0700..7be41c2ded 100644 --- a/src/Mod/PartDesign/App/FeatureThickness.cpp +++ b/src/Mod/PartDesign/App/FeatureThickness.cpp @@ -65,11 +65,7 @@ int16_t Thickness::mustExecute() const { } App::DocumentObjectExecReturn *Thickness::execute() { - if (onlyHasToRefine()){ - TopoShape result = refineShapeIfActive(rawShape); - Shape.setValue(result); - return App::DocumentObject::StdReturn; - } + if (onlyHaveRefined()) { return App::DocumentObject::StdReturn; } // Base shape Part::TopoShape TopShape; diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index b7f18afaae..50fb6ff218 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -57,7 +57,7 @@ namespace PartDesign { extern bool getPDRefineModelParameter(); -PROPERTY_SOURCE(PartDesign::Transformed, PartDesign::Feature) +PROPERTY_SOURCE(PartDesign::Transformed, PartDesign::FeatureRefine) std::array transformModeEnums = {"Transform tool shapes", "Transform body", @@ -71,14 +71,6 @@ Transformed::Transformed() ADD_PROPERTY(TransformMode, (static_cast(Mode::TransformToolShapes))); TransformMode.setEnums(transformModeEnums.data()); - - ADD_PROPERTY_TYPE(Refine, - (0), - "Part Design", - (App::PropertyType)(App::Prop_None), - "Refine shape (clean up redundant edges) after adding/subtracting"); - - this->Refine.setValue(getPDRefineModelParameter()); } void Transformed::positionBySupport() @@ -351,15 +343,6 @@ App::DocumentObjectExecReturn* Transformed::execute() return App::DocumentObject::StdReturn; } - -TopoShape Transformed::refineShapeIfActive(const TopoShape& oldShape) const -{ - if (this->Refine.getValue()) { - return oldShape.makeElementRefine(); - } - return oldShape; -} - TopoDS_Shape Transformed::getRemainingSolids(const TopoDS_Shape& shape) { BRep_Builder builder; diff --git a/src/Mod/PartDesign/App/FeatureTransformed.h b/src/Mod/PartDesign/App/FeatureTransformed.h index 81ac61786f..3fe27e8851 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.h +++ b/src/Mod/PartDesign/App/FeatureTransformed.h @@ -27,7 +27,7 @@ #include #include -#include "Feature.h" +#include "FeatureRefine.h" namespace PartDesign @@ -37,7 +37,7 @@ namespace PartDesign * Abstract superclass of all features that are created by transformation of another feature * Transformations are translation, rotation and mirroring */ -class PartDesignExport Transformed: public PartDesign::Feature +class PartDesignExport Transformed: public PartDesign::FeatureRefine { PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::Transformed); @@ -106,8 +106,6 @@ protected: App::Property* prop) override; virtual void positionBySupport(); - TopoShape refineShapeIfActive(const TopoShape&) const; - TopoDS_Shape refineShapeIfActive(const TopoDS_Shape&) const; static TopoDS_Shape getRemainingSolids(const TopoDS_Shape&); private: diff --git a/src/Mod/PartDesign/App/ShapeBinder.cpp b/src/Mod/PartDesign/App/ShapeBinder.cpp index 895473d183..510480c459 100644 --- a/src/Mod/PartDesign/App/ShapeBinder.cpp +++ b/src/Mod/PartDesign/App/ShapeBinder.cpp @@ -397,8 +397,6 @@ SubShapeBinder::~SubShapeBinder() { void SubShapeBinder::setupObject() { _Version.setValue(2); checkPropertyStatus(); - - this->Refine.setValue(getPDRefineModelParameter()); } App::DocumentObject* SubShapeBinder::getSubObject(const char* subname, PyObject** pyObj, diff --git a/src/Mod/PartDesign/App/ShapeBinder.h b/src/Mod/PartDesign/App/ShapeBinder.h index a32841b3bd..29d2342f39 100644 --- a/src/Mod/PartDesign/App/ShapeBinder.h +++ b/src/Mod/PartDesign/App/ShapeBinder.h @@ -29,6 +29,7 @@ #include #include #include +#include "FeatureRefine.h" namespace PartDesign { @@ -41,7 +42,7 @@ namespace PartDesign */ // TODO Add better documentation (2015-09-11, Fat-Zer) -class PartDesignExport ShapeBinder : public Part::Feature +class PartDesignExport ShapeBinder : public PartDesign::FeatureRefine { PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::ShapeBinder); diff --git a/src/Mod/PartDesign/PartDesignTests/TestInvoluteGear.py b/src/Mod/PartDesign/PartDesignTests/TestInvoluteGear.py index c109ec969e..5ac7c9729f 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestInvoluteGear.py +++ b/src/Mod/PartDesign/PartDesignTests/TestInvoluteGear.py @@ -26,7 +26,7 @@ from math import pi, tan, cos, acos import FreeCAD Quantity = FreeCAD.Units.Quantity # FIXME from FreeCAD.Units import Quantity doesn't work from FreeCAD import Vector -from Part import makeCircle, Precision +from Part import makeCircle, Precision, Solid import InvoluteGearFeature FIXTURE_PATH = pathlib.Path(__file__).parent / "Fixtures" @@ -265,7 +265,7 @@ class TestInvoluteGear(unittest.TestCase): pocket.Reversed = True # need to "pocket upwards" into the cylinder pocket.Type = 'ThroughAll' self.assertSuccessfulRecompute() - self.assertSolid(pocket.Shape) + self.assertSolid(Solid(pocket.Shape)) # Can be a compound, make that into a Solid if needed. def testRecomputeExternalGearFromV020(self): FreeCAD.closeDocument(self.Doc.Name) # this was created in setUp(self) diff --git a/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py b/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py index fe7098ef9b..f75ebc6df8 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py +++ b/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py @@ -20,6 +20,7 @@ #*************************************************************************** import unittest +import math import FreeCAD import TestSketcherApp @@ -74,26 +75,14 @@ class TestMultiTransform(unittest.TestCase): # Make first offset cube Pad PadSketch = Doc.addObject('Sketcher::SketchObject', 'SketchPad') Body.addObject(PadSketch) - TestSketcherApp.CreateRectangleSketch(PadSketch, (0, 0), (10, 10)) + xw = yw = zw = 10 + TestSketcherApp.CreateRectangleSketch(PadSketch, (0, 0), (xw, yw)) Doc.recompute() Pad = Doc.addObject("PartDesign::Pad", "Pad") Body.addObject(Pad) Pad.Profile = PadSketch - Pad.Length = 10 + Pad.Length = zw Doc.recompute() - - PadSketch2 = Doc.addObject('Sketcher::SketchObject', 'SketchPad') - PadSketch2.AttachmentSupport = (Pad, ('Face6',)) - Body.addObject(PadSketch2) - TestSketcherApp.CreateRectangleSketch(PadSketch, (9, 9), (1, 1)) - Doc.recompute() - Pad2 = Doc.addObject("PartDesign::Pad", "Pad2") - Body.addObject(Pad2) - Pad2.Profile = PadSketch2 - Pad2.Length = 10 - Doc.recompute() - - MultiTransform = Doc.addObject("PartDesign::MultiTransform","MultiTransform") Doc.recompute() MultiTransform.Originals = [Pad] @@ -102,27 +91,43 @@ class TestMultiTransform(unittest.TestCase): Doc.recompute() Mirrored = Doc.addObject("PartDesign::Mirrored","Mirrored") Mirrored.MirrorPlane = (Doc.getObject('XY_Plane'), ['']) + Mirrored.Refine = True Body.addObject(Mirrored) Mirrored2 = Doc.addObject("PartDesign::Mirrored","Mirrored") Mirrored2.MirrorPlane = (Doc.getObject('XZ_Plane'), [""]) + Mirrored2.Refine = True Body.addObject(Mirrored2) MultiTransform.Transformations = [Mirrored,Mirrored2] + MultiTransform.Refine = True Doc.recompute() Fillet = Doc.addObject("PartDesign::Fillet","Fillet") - Fillet.Base = (MultiTransform, ['Face'+str(i+1) for i in range(2)]) - Fillet.Radius = 3 + Fillet.Base = (MultiTransform, [ "Face1", "Face2" ]) + radius = 3 + Fillet.Radius = radius Body.addObject(Fillet) - # Add a fillet here. - # Now do that copy thing... + # Broken out calculation of volume with two adjacent filleted faces = 5 long edges, 2 short edges, + # 2 fully rounded corners and 4 corners with only 2 fillets meeting + cubeVolume = xw * yw * zw * 2 * 2 # Mirrored and mirrored again. + filletOuter = radius ** 2 * ( xw - radius * 2 ) # Volume of the rect prisms the fillets are in. + filletCorner = radius ** 3 # Volume of the rect prism corners + qRoundArea = math.pi * radius ** 2 / 4 # Area of the quarter round fillet profile + filletPrism = qRoundArea * ( xw - radius * 2 ) # Volume of fillet minus corners + fillet3Corner = math.pi * radius ** 3 * 4 / 3 / 8 # Volume of a fully rounded corner ( Sphere / 8 ) + fillet2Corner = radius ** 2 * 2 # Volume of corner with two fillets intersecting + fillet1Corner = math.pi * radius ** 2 / 4 * radius # Volume of corner with stopped single fillet + extraFillet = qRoundArea * xw # extra fillet in a mirrored direction + filletOuterExt = radius ** 2 * 10 # extra rect prim surrounding fillet + rectBox = cubeVolume - (4 + 3) * (filletOuter + filletCorner ) - 5 * filletOuterExt + filletCorner + fillets = ( 4 + 3 ) * filletPrism + 5 * extraFillet + fillet3Corner * 2 + fillet2Corner * 4 + fillet1Corner * 0 + volume = rectBox + fillets + # Act Link = Doc.addObject('App::Link','Link001') Link.setLink(Doc.Body) Link.Label='Body001' - # Act - # There are properties on those objects with values - # Link.addProperty("App::PropertyInteger","test2","Table2") + Doc.recompute() # Assert - self.assertAlmostEqual(Body.Shape.Volume, 990) - self.assertAlmostEqual(Link.Shape.Volume, 990) + self.assertAlmostEqual(abs(Body.Shape.Volume), volume, 6) + self.assertAlmostEqual(abs(Link.Shape.Volume), volume, 6) def testMultiTransformBody(self): pass diff --git a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py index f7a456c0b9..42ca8352be 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py +++ b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py @@ -834,6 +834,7 @@ class TestTopologicalNamingProblem(unittest.TestCase): body = self.Doc.addObject("PartDesign::Body", "Body") box = self.Doc.addObject("PartDesign::AdditiveBox", "Box") body.addObject(box) + self.Doc.recompute() sketch = self.Doc.addObject("Sketcher::SketchObject", "Sketch") sketch.AttachmentSupport = (box, "Face6") sketch.MapMode = "FlatFace" @@ -852,10 +853,9 @@ class TestTopologicalNamingProblem(unittest.TestCase): self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 32) self.assertEqual(body.Shape.ElementMapSize, 32) self.assertEqual(sketch.Shape.ElementMapSize, 2) - self.assertEqual(hole.Shape.ElementMapSize, 32) - # self.assertNotEqual(hole.Shape.ElementReverseMap['Vertex1'],"Vertex1") # NewName, not OldName + self.assertNotEqual(body.Shape.ElementReverseMap['Vertex1'],"Vertex1") # NewName, not OldName self.assertEqual( - self.countFacesEdgesVertexes(hole.Shape.ElementReverseMap), (7, 15, 10) + self.countFacesEdgesVertexes(body.Shape.ElementReverseMap), (7, 15, 10) ) volume = 1000 - 10 * math.pi * 3 * 3 self.assertAlmostEqual(hole.Shape.Volume, volume)