From de31528dda48036b7c92f27fcbd2a301652cd3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Sat, 16 Sep 2017 15:47:07 +0200 Subject: [PATCH] PartDesign: Make Base Feature compatible with GeoFeatureGroup. fixes #0003080 The Original BaseFeature implementation had some serious issues with scoped links. It failed completely for e.g. sketches on the BaseFeature as it made a local link to refere to a out of body object. The only solution to make this work correctly is to add a proxy object into the body which is alloed to exactly that, to link outside oof the body. Something like shapebinder. --- src/Mod/Part/App/BodyBase.cpp | 11 +- src/Mod/PartDesign/App/AppPartDesign.cpp | 2 + src/Mod/PartDesign/App/Body.cpp | 78 ++- src/Mod/PartDesign/App/CMakeLists.txt | 2 + src/Mod/PartDesign/App/Feature.cpp | 15 + src/Mod/PartDesign/App/Feature.h | 3 + src/Mod/PartDesign/App/FeatureBase.cpp | 97 ++++ src/Mod/PartDesign/App/FeatureBase.h | 57 ++ src/Mod/PartDesign/Gui/AppPartDesignGui.cpp | 2 + src/Mod/PartDesign/Gui/CMakeLists.txt | 2 + .../PartDesign/Gui/Resources/PartDesign.qrc | 1 + .../icons/PartDesign_BaseFeature.svg | 503 ++++++++++++++++++ src/Mod/PartDesign/Gui/ViewProviderBase.cpp | 45 ++ src/Mod/PartDesign/Gui/ViewProviderBase.h | 46 ++ src/Mod/PartDesign/Gui/ViewProviderBody.cpp | 3 - 15 files changed, 820 insertions(+), 47 deletions(-) create mode 100644 src/Mod/PartDesign/App/FeatureBase.cpp create mode 100644 src/Mod/PartDesign/App/FeatureBase.h create mode 100644 src/Mod/PartDesign/Gui/Resources/icons/PartDesign_BaseFeature.svg create mode 100644 src/Mod/PartDesign/Gui/ViewProviderBase.cpp create mode 100644 src/Mod/PartDesign/Gui/ViewProviderBase.h diff --git a/src/Mod/Part/App/BodyBase.cpp b/src/Mod/Part/App/BodyBase.cpp index 3653b17f3d..e2d4397cfc 100644 --- a/src/Mod/Part/App/BodyBase.cpp +++ b/src/Mod/Part/App/BodyBase.cpp @@ -86,18 +86,21 @@ bool BodyBase::isAfter(const App::DocumentObject *feature, const App::DocumentOb } void BodyBase::onBeforeChange (const App::Property* prop) { - // If we are changing the base feature and tip point to it reset it + + //Tip can't point outside the body, hence no base feature tip + /*// If we are changing the base feature and tip point to it reset it if ( prop == &BaseFeature && BaseFeature.getValue() == Tip.getValue() && BaseFeature.getValue() ) { Tip.setValue( nullptr ); - } + }*/ Part::Feature::onBeforeChange ( prop ); } void BodyBase::onChanged (const App::Property* prop) { - // If the tip is zero and we are adding a base feature to the body set it to be the tip + //Tip can't point outside the body, hence no base feature tip + /*// If the tip is zero and we are adding a base feature to the body set it to be the tip if ( prop == &BaseFeature && !Tip.getValue() && BaseFeature.getValue() ) { Tip.setValue( BaseFeature.getValue () ); - } + }*/ Part::Feature::onChanged ( prop ); } diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp index 33e18f65c4..91963ecf83 100644 --- a/src/Mod/PartDesign/App/AppPartDesign.cpp +++ b/src/Mod/PartDesign/App/AppPartDesign.cpp @@ -58,6 +58,7 @@ #include "FeaturePipe.h" #include "FeatureLoft.h" #include "ShapeBinder.h" +#include "FeatureBase.h" namespace PartDesign { extern PyObject* initModule(); @@ -146,6 +147,7 @@ PyMOD_INIT_FUNC(_PartDesign) PartDesign::Wedge ::init(); PartDesign::AdditiveWedge ::init(); PartDesign::SubtractiveWedge ::init(); + PartDesign::FeatureBase ::init(); PyMOD_Return(mod); } diff --git a/src/Mod/PartDesign/App/Body.cpp b/src/Mod/PartDesign/App/Body.cpp index 3646b78bb9..953752d656 100644 --- a/src/Mod/PartDesign/App/Body.cpp +++ b/src/Mod/PartDesign/App/Body.cpp @@ -47,6 +47,7 @@ #include "ShapeBinder.h" #include "Body.h" +#include "FeatureBase.h" #include "BodyPy.h" using namespace PartDesign; @@ -109,8 +110,7 @@ void Body::onChanged(const App::Property *prop) short Body::mustExecute() const { - if ( Tip.isTouched() || - BaseFeature.isTouched() ) { + if ( Tip.isTouched() ) { return 1; } return Part::BodyBase::mustExecute(); @@ -133,18 +133,13 @@ App::DocumentObject* Body::getPrevFeature(App::DocumentObject *start) const App::DocumentObject* Body::getPrevSolidFeature(App::DocumentObject *start) { - App::DocumentObject* baseFeature = BaseFeature.getValue(); - if ( !start ) { // default to tip start = Tip.getValue(); } if ( !start ) { // No Tip return nullptr; - } else if ( start == baseFeature ) { // The base feature always considered as the first solid - return nullptr; } - if (!hasObject(start)) return nullptr; @@ -156,15 +151,13 @@ App::DocumentObject* Body::getPrevSolidFeature(App::DocumentObject *start) if (rvIt != features.rend()) { // the solid found in model list return *rvIt; - } else { // if solid is not present in the list the previous one is baseFeature - return baseFeature; // return it either it's set or nullptr } + + return nullptr; } App::DocumentObject* Body::getNextSolidFeature(App::DocumentObject *start) { - App::DocumentObject* baseFeature = BaseFeature.getValue(); - if ( !start ) { // default to tip start = Tip.getValue(); } @@ -178,14 +171,9 @@ App::DocumentObject* Body::getNextSolidFeature(App::DocumentObject *start) const std::vector & features = Group.getValues(); std::vector::const_iterator startIt; - if ( start == baseFeature ) { - // Handle the base feature, it's always considered to be solid - startIt = features.begin(); - } else { - startIt = std::find ( features.begin(), features.end(), start ); - assert ( startIt != features.end() ); - startIt++; - } + startIt = std::find ( features.begin(), features.end(), start ); + assert ( startIt != features.end() ); + startIt++; if (startIt == features.end() ) { // features list is empty return nullptr; @@ -195,9 +183,9 @@ App::DocumentObject* Body::getNextSolidFeature(App::DocumentObject *start) if (rvIt != features.end()) { // the solid found in model list return *rvIt; - } else { - return nullptr; } + + return nullptr; } bool Body::isAfterInsertPoint(App::DocumentObject* feature) { @@ -299,18 +287,8 @@ std::vector< App::DocumentObject* > Body::addObjects(std::vector< App::DocumentO void Body::insertObject(App::DocumentObject* feature, App::DocumentObject* target, bool after) { - if (target) { - if (target == BaseFeature.getValue()) { - // Handle the insertion relative to the base feature in a special way - if (after) { - target = nullptr; - } else { - throw Base::Exception("Body: impossible to insert before the base object"); - } - } else if (!hasObject (target)) { - // Check if the target feature belongs to the body - throw Base::Exception("Body: the feature we should insert relative to is not part of that body"); - } + if (target && !hasObject (target)) { + throw Base::Exception("Body: the feature we should insert relative to is not part of that body"); } //ensure that all origin links are ok @@ -420,8 +398,7 @@ App::DocumentObjectExecReturn *Body::execute(void) Part::TopoShape tipShape; if ( tip ) { - if ( !tip->getTypeId().isDerivedFrom ( PartDesign::Feature::getClassTypeId() ) - && tip != BaseFeature.getValue () ) { + if ( !tip->getTypeId().isDerivedFrom ( PartDesign::Feature::getClassTypeId() ) ) { return new App::DocumentObjectExecReturn ( "Linked object is not a PartDesign feature" ); } @@ -454,11 +431,32 @@ void Body::onSettingDocument() { void Body::onChanged (const App::Property* prop) { if ( prop == &BaseFeature ) { - App::DocumentObject *baseFeature = BaseFeature.getValue(); - App::DocumentObject *nextSolid = getNextSolidFeature ( baseFeature ); - if ( nextSolid ) { - assert ( nextSolid->isDerivedFrom ( PartDesign::Feature::getClassTypeId () ) ); - static_cast(nextSolid)->BaseFeature.setValue( baseFeature ); + FeatureBase* bf = nullptr; + 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")); + insertObject(bf, first, false); + if(!Tip.getValue()) + Tip.setValue(bf); + } + else + bf = static_cast(first); + } + + if(bf && (bf->BaseFeature.getValue() != BaseFeature.getValue())) + bf->BaseFeature.setValue(BaseFeature.getValue()); + } + 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); } } diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt index 844edfa281..c9e223cfc8 100644 --- a/src/Mod/PartDesign/App/CMakeLists.txt +++ b/src/Mod/PartDesign/App/CMakeLists.txt @@ -37,6 +37,8 @@ SET(Features_SRCS FeatureSolid.h Body.cpp Body.h + FeatureBase.h + FeatureBase.cpp ) SOURCE_GROUP("Features" FILES ${Features_SRCS}) diff --git a/src/Mod/PartDesign/App/Feature.cpp b/src/Mod/PartDesign/App/Feature.cpp index 4f491122f3..bcee81ff59 100644 --- a/src/Mod/PartDesign/App/Feature.cpp +++ b/src/Mod/PartDesign/App/Feature.cpp @@ -55,6 +55,7 @@ Feature::Feature() { ADD_PROPERTY(BaseFeature,(0)); Placement.setStatus(App::Property::Hidden, true); + BaseFeature.setStatus(App::Property::Hidden, true); } short Feature::mustExecute() const @@ -176,6 +177,20 @@ TopoDS_Shape Feature::makeShapeFromPlane(const App::DocumentObject* obj) return builder.Shape(); } +Body* Feature::getFeatureBody() { + + auto list = getInList(); + for (auto in : list) { + if(in->isDerivedFrom(Body::getClassTypeId()) && //is Body? + static_cast(in)->hasObject(this)) { //is part of this Body? + + return static_cast(in); + } + } + + return nullptr; +}; + }//namespace PartDesign namespace App { diff --git a/src/Mod/PartDesign/App/Feature.h b/src/Mod/PartDesign/App/Feature.h index 698b0133c8..0689d849e4 100644 --- a/src/Mod/PartDesign/App/Feature.h +++ b/src/Mod/PartDesign/App/Feature.h @@ -56,6 +56,9 @@ public: /// Check whether the given feature is a datum feature static bool isDatum(const App::DocumentObject* feature); + /// Returns the body the feature is in, or none + Body* getFeatureBody(); + /** * Returns the BaseFeature property's object (if any) * @param silent if couldn't determine the base feature and silent == true, diff --git a/src/Mod/PartDesign/App/FeatureBase.cpp b/src/Mod/PartDesign/App/FeatureBase.cpp new file mode 100644 index 0000000000..abe1a3d0e5 --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureBase.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (c) 2017 Stefan Tröger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include +#include +#include "Body.h" +#include "FeatureBase.h" +#include "FeaturePy.h" + +namespace PartDesign { + + +PROPERTY_SOURCE(PartDesign::FeatureBase,PartDesign::Feature) + +FeatureBase::FeatureBase() +{ + BaseFeature.setScope(App::LinkScope::Global); + BaseFeature.setStatus(App::Property::Hidden, false); +} + +Part::Feature* FeatureBase::getBaseObject(bool) const { + + return nullptr; +} + +short int FeatureBase::mustExecute(void) const { + + if(BaseFeature.isTouched()) + return 1; + + return Part::Feature::mustExecute(); +} + + +App::DocumentObjectExecReturn* FeatureBase::execute(void) { + + if(!BaseFeature.getValue()) + return new App::DocumentObjectExecReturn("BaseFeature link is not set"); + + if(!BaseFeature.getValue()->isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("BaseFeature must be a Part::Feature"); + + auto shape = static_cast(BaseFeature.getValue())->Shape.getValue(); + if(shape.IsNull()) + return new App::DocumentObjectExecReturn("BaseFeature has a empty shape"); + + Shape.setValue(shape); + + return StdReturn; +} + +void FeatureBase::onChanged(const App::Property* prop) { + + // the BaseFeature property should track the Body BaseFeature and vice-versa + if (prop == &BaseFeature) { + + auto body = getFeatureBody(); + if(!body) + return; + + if (BaseFeature.getValue() && body->BaseFeature.getValue() != BaseFeature.getValue()) { + body->BaseFeature.setValue(BaseFeature.getValue()); + } + } + + Part::Feature::onChanged(prop); +} + + + +}//namespace PartDesign + diff --git a/src/Mod/PartDesign/App/FeatureBase.h b/src/Mod/PartDesign/App/FeatureBase.h new file mode 100644 index 0000000000..3516d8c5b5 --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureBase.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (c) 2017 Stefan Tröger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PARTDESIGN_FeatureBase_H +#define PARTDESIGN_FeatureBase_H + +#include +#include "Feature.h" + + +/// Base class of all additive features in PartDesign +namespace PartDesign +{ + +class PartDesignExport FeatureBase : public PartDesign::Feature +{ + PROPERTY_HEADER(PartDesign::FeatureBase); + +public: + FeatureBase(); + + virtual short int mustExecute(void) const; + + virtual Part::Feature* getBaseObject(bool silent=false) const; + + virtual const char* getViewProviderName() const { + return "PartDesignGui::ViewProviderBase"; + } + + virtual void onChanged(const App::Property* prop); + virtual App::DocumentObjectExecReturn* execute(void); +}; + +} //namespace PartDesign + + +#endif // PARTDESIGN_FeatureBase_H diff --git a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp index 76f3d178b3..7fcb2d0d06 100644 --- a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp +++ b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp @@ -62,6 +62,7 @@ #include "ViewProviderPipe.h" #include "ViewProviderLoft.h" #include "ViewProviderShapeBinder.h" +#include "ViewProviderBase.h" // use a different name to CreateCommand() void CreatePartDesignCommands(void); @@ -154,6 +155,7 @@ PyMOD_INIT_FUNC(PartDesignGui) PartDesignGui::ViewProviderPrimitive ::init(); PartDesignGui::ViewProviderPipe ::init(); PartDesignGui::ViewProviderLoft ::init(); + PartDesignGui::ViewProviderBase ::init(); // add resources and reloads the translators loadPartDesignResource(); diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index 8fcb7e8e3c..fcb61e2489 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -158,6 +158,8 @@ SET(PartDesignGuiViewProvider_SRCS ViewProviderPipe.cpp ViewProviderLoft.h ViewProviderLoft.cpp + ViewProviderBase.h + ViewProviderBase.cpp ) SOURCE_GROUP("ViewProvider" FILES ${PartDesignGuiViewProvider_SRCS}) diff --git a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc index cdff546919..7630713cce 100644 --- a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc +++ b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc @@ -49,6 +49,7 @@ icons/PartDesign_Subtractive_Ellipsoid.svg icons/PartDesign_Subtractive_Prism.svg icons/PartDesign_Subtractive_Wedge.svg + icons/PartDesign_BaseFeature.svg translations/PartDesign_af.qm translations/PartDesign_de.qm translations/PartDesign_fi.qm diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_BaseFeature.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_BaseFeature.svg new file mode 100644 index 0000000000..27cf1a671b --- /dev/null +++ b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_BaseFeature.svg @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [triplus] + + + PartDesignWorkbench + 2016-02-26 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/PartDesign/Gui/Resources/icons/PartDesignWorkbench.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/PartDesign/Gui/ViewProviderBase.cpp b/src/Mod/PartDesign/Gui/ViewProviderBase.cpp new file mode 100644 index 0000000000..505c64075b --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderBase.cpp @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (c) 2017 Stefan Tröger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "ViewProviderBase.h" + + +using namespace PartDesignGui; + +PROPERTY_SOURCE(PartDesignGui::ViewProviderBase,PartDesignGui::ViewProvider) + +ViewProviderBase::ViewProviderBase() +{ + sPixmap = "PartDesign_BaseFeature.svg"; +} + +ViewProviderBase::~ViewProviderBase() +{ + +} + diff --git a/src/Mod/PartDesign/Gui/ViewProviderBase.h b/src/Mod/PartDesign/Gui/ViewProviderBase.h new file mode 100644 index 0000000000..07a5da0988 --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderBase.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (c) 2017 Stefan Tröger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_ViewProviderBase_H +#define PARTGUI_ViewProviderBase_H + +#include "ViewProvider.h" + +namespace PartDesignGui { + +class PartDesignGuiExport ViewProviderBase : public ViewProvider +{ + PROPERTY_HEADER(PartDesignGui::ViewProviderBase); + +public: + /// constructor + ViewProviderBase(); + /// destructor + virtual ~ViewProviderBase(); + +}; + +} // namespace PartDesignGui + + +#endif // PARTGUI_ViewProviderBase_H diff --git a/src/Mod/PartDesign/Gui/ViewProviderBody.cpp b/src/Mod/PartDesign/Gui/ViewProviderBody.cpp index b8654d22e7..591318fd9d 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderBody.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderBody.cpp @@ -200,9 +200,6 @@ std::vector ViewProviderBody::claimChildren(void)const if (body->Origin.getValue()) { // Clame for the Origin Result.push_back (body->Origin.getValue()); } - if (body->BaseFeature.getValue()) { // Clame for the base feature - Result.push_back (body->BaseFeature.getValue()); - } // claim for rest content not claimed by any other features std::remove_copy_if (model.begin(), model.end(), std::back_inserter (Result),