From 53ff196c2eee598200119d2f28182d4956fc0f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Thu, 9 Feb 2017 17:22:41 +0100 Subject: [PATCH] GeoFeatureGroup: add object adds relevant links --- src/App/GeoFeatureGroupExtension.cpp | 145 ++++++++++++++++++++++++--- src/App/GeoFeatureGroupExtension.h | 13 +++ src/Mod/Test/Document.py | 34 ++++++- 3 files changed, 179 insertions(+), 13 deletions(-) diff --git a/src/App/GeoFeatureGroupExtension.cpp b/src/App/GeoFeatureGroupExtension.cpp index 85843bdcfb..c8867119cb 100644 --- a/src/App/GeoFeatureGroupExtension.cpp +++ b/src/App/GeoFeatureGroupExtension.cpp @@ -30,6 +30,8 @@ #include #include "GeoFeatureGroupExtension.h" +#include "OriginFeature.h" +#include "Origin.h" //#include "GeoFeatureGroupPy.h" //#include "FeaturePythonPyImp.h" @@ -109,24 +111,143 @@ Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(GeoFeatureGrou return group->placement().getValue(); } -void GeoFeatureGroupExtension::addObject(App::DocumentObject* obj) { +void GeoFeatureGroupExtension::addObject(App::DocumentObject* object) { - if(!allowObject(obj)) + if(!allowObject(object)) return; - //only one geofeaturegroup per object. This is the reason why we need to override addObject, - //we need to check here for GeoFeatureGroups only. It is allowed to be at the same time in a - //GeoFeatureGroup and a Group - auto *group = App::GeoFeatureGroupExtension::getGroupOfObject(obj); - if(group && group != getExtendedObject()) - group->getExtensionByType()->removeObject(obj); + //cross CoordinateSystem links are not allowed, so we need to move the whole link group + auto links = getCSRelevantLinks(object); + links.push_back(object); - if (!hasObject(obj)) { - std::vector grp = Group.getValues(); - grp.push_back(obj); - Group.setValues(grp); + std::vector grp = Group.getValues(); + for( auto obj : links) { + //only one geofeaturegroup per object. + auto *group = App::GeoFeatureGroupExtension::getGroupOfObject(obj); + if(group && group != getExtendedObject()) + group->getExtensionByType()->removeObject(obj); + + if (!hasObject(obj)) + grp.push_back(obj); } + + Group.setValues(grp); } +std::vector< DocumentObject* > GeoFeatureGroupExtension::getObjectsFromLinks(DocumentObject* obj) { + + //we get all linked objects. We can't use outList() as this includes the links from expressions + std::vector< App::DocumentObject* > result; + std::vector list; + obj->getPropertyList(list); + for(App::Property* prop : list) { + if(prop->getTypeId().isDerivedFrom(App::PropertyLink::getClassTypeId())) + result.push_back(static_cast(prop)->getValue()); + else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId())) { + auto vec = static_cast(prop)->getValues(); + result.insert(result.end(), vec.begin(), vec.end()); + } + else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSub::getClassTypeId())) + result.push_back(static_cast(prop)->getValue()); + else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSubList::getClassTypeId())) { + auto vec = static_cast(prop)->getValues(); + result.insert(result.end(), vec.begin(), vec.end()); + } + } + + //clear all null objects and douplicates + result.erase(std::remove(result.begin(), result.end(), nullptr), result.end()); + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + + return result; +} + + +std::vector< DocumentObject* > GeoFeatureGroupExtension::getCSOutList(App::DocumentObject* obj) { + + if(!obj) + return std::vector< App::DocumentObject* >(); + + //if the object is a geofeaturegroup than all dependencies belong to that CS, we don't want them + if(obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) + return std::vector< App::DocumentObject* >(); + + //we get all linked objects. We can't use outList() as this includes the links from expressions + auto result = getObjectsFromLinks(obj); + + //we remove all links to origin features and origins, they belong to a CS too and can't be moved + result.erase(std::remove_if(result.begin(), result.end(), [](App::DocumentObject* obj)->bool { + return (obj->isDerivedFrom(App::OriginFeature::getClassTypeId()) || + obj->isDerivedFrom(App::Origin::getClassTypeId())); + }), result.end()); + + //collect all dependencies of those objects + std::vector< App::DocumentObject* > links; + for(App::DocumentObject *obj : result) { + auto vec = getCSOutList(obj); + links.insert(links.end(), vec.begin(), vec.end()); + } + + if (!links.empty()) { + result.insert(result.end(), links.begin(), links.end()); + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + } + + return result; +} + +std::vector< DocumentObject* > GeoFeatureGroupExtension::getCSInList(DocumentObject* obj) { + + if(!obj) + return std::vector< App::DocumentObject* >(); + + //we get all objects that link to it + std::vector< App::DocumentObject* > result; + + //search the inlist for objects that have non-expression links to us + for(App::DocumentObject* parent : obj->getInList()) { + + //not interested in other groups + if(parent->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) + continue; + + //check if the link is real or if it is a expression one (could also be both, so it is not + //enough to check the expressions) + auto res = getObjectsFromLinks(parent); + if(std::find(res.begin(), res.end(), obj) != res.end()) + result.push_back(parent); + } + + //clear all douplicates + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + + //collect all links to those objects + std::vector< App::DocumentObject* > links; + for(App::DocumentObject *obj : result) { + auto vec = getCSInList(obj); + links.insert(links.end(), vec.begin(), vec.end()); + } + + if (!links.empty()) { + result.insert(result.end(), links.begin(), links.end()); + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + } + + return result; +} + +std::vector< DocumentObject* > GeoFeatureGroupExtension::getCSRelevantLinks(DocumentObject* obj) { + + auto vec1 = getCSInList(obj); + auto vec2 = getCSOutList(obj); + + vec1.insert(vec1.end(), vec2.begin(), vec2.end()); + return vec1; +} + // Python feature --------------------------------------------------------- diff --git a/src/App/GeoFeatureGroupExtension.h b/src/App/GeoFeatureGroupExtension.h index 77d1b729ee..abab0fa047 100644 --- a/src/App/GeoFeatureGroupExtension.h +++ b/src/App/GeoFeatureGroupExtension.h @@ -98,8 +98,21 @@ public: virtual void addObject(DocumentObject* obj); + /// returns GeoFeatureGroup relevant objects that are linked from the given one. That meas all linked objects + /// including their linkes (recursively) except GeoFeatureGroups, where the recursion stops. Expressions + /// links are ignored. + static std::vector getCSOutList(App::DocumentObject* obj); + ///returns GeoFeatureGroup relevant objects that link to the given one. That meas all objects + /// including their parents (recursively) except GeoFeatureGroups, where the recursion stops. Expression + /// links are ignored + static std::vector getCSInList(App::DocumentObject* obj); + /// Returns all links that are relevant for the coordinate system, meaning all recursive links to + /// obj and from obj excluding expressions and stopping the recursion at other geofeaturegroups. + /// The result is the combination of CSOutList and CSInList. + static std::vector getCSRelevantLinks(App::DocumentObject* obj); private: Base::Placement recursiveGroupPlacement(GeoFeatureGroupExtension* group); + static std::vector getObjectsFromLinks(App::DocumentObject*); }; typedef ExtensionPythonT> GeoFeatureGroupExtensionPython; diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 3ae2c8def6..f8fa39d39b 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -836,7 +836,39 @@ class UndoRedoCases(unittest.TestCase): self.Doc.removeObject("Group") self.Doc.removeObject("Label_2") self.Doc.removeObject("Label_3") - + + def testGroupAndGeoFeatureGroup(self): + + # an object can only be in one group at once, that must be enforced + obj1 = self.Doc.addObject("App::FeatureTest","obj1") + grp1 = self.Doc.addObject("App::DocumentObjectGroup","Group1") + grp2 = self.Doc.addObject("App::DocumentObjectGroup","Group2") + grp1.addObject(obj1) + self.failUnless(grp1.hasObject(obj1)) + grp2.addObject(obj1) + self.failUnless(grp1.hasObject(obj1)==False) + self.failUnless(grp2.hasObject(obj1)) + + # an object is allowed to be in a group and a geofeaturegroup + prt1 = self.Doc.addObject("App::Part","Part1") + prt2 = self.Doc.addObject("App::Part","Part2") + + prt1.addObject(grp2) + self.failUnless(grp2.hasObject(obj1)) + self.failUnless(prt1.hasObject(grp2)) + self.failUnless(prt1.hasObject(obj1)) + + #it is not allowed to be in 2 geofeaturegroups + prt2.addObject(grp2) + self.failUnless(grp2.hasObject(obj1)) + self.failUnless(prt1.hasObject(grp2)==False) + self.failUnless(prt1.hasObject(obj1)==False) + self.failUnless(prt2.hasObject(grp2)) + self.failUnless(prt2.hasObject(obj1)) + + #adding the object to a geofeaturegroup, but not its group, should handle it automatically when used + #addObject + def tearDown(self): # closing doc