diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 711e1ffaf3..47cba1fd62 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -258,6 +258,7 @@ void Document::exportGraphviz(std::ostream& out) const buildAdjacencyList(); addEdges(); markCycles(); + markOutOfScopeLinks(); } /** @@ -663,6 +664,7 @@ void Document::exportGraphviz(std::ostream& out) const edgeAttrMap[edge]["ltail"] = getClusterName(docObj); if (GraphList[*It2]) edgeAttrMap[edge]["lhead"] = getClusterName(*It2); + } } @@ -767,6 +769,24 @@ void Document::exportGraphviz(std::ostream& out) const for (auto ei = out_edges.begin(), ei_end = out_edges.end(); ei != ei_end; ++ei) edgeAttrMap[ei->second]["color"] = "red"; } + + void markOutOfScopeLinks() { + + const boost::property_map::type& edgeAttrMap = boost::get(boost::edge_attribute, DepList); + + for( auto obj : objects) { + + std::vector invalids; + GeoFeatureGroupExtension::getInvalidLinkObjects(obj, invalids); + //isLinkValid returns true for non-link properties + for(auto linkedObj : invalids) { + + auto res = edge(GlobalVertexList[getId(obj)], GlobalVertexList[getId(linkedObj)], DepList); + if(res.second) + edgeAttrMap[res.first]["color"] = "red"; + } + } + } const struct DocumentP* d; Graph DepList; diff --git a/src/App/GeoFeatureGroupExtension.cpp b/src/App/GeoFeatureGroupExtension.cpp index 062d9d1c4c..6674c42faa 100644 --- a/src/App/GeoFeatureGroupExtension.cpp +++ b/src/App/GeoFeatureGroupExtension.cpp @@ -210,47 +210,63 @@ void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) { } -std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLinks(DocumentObject* obj, LinkScope scope) { +std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLinks(const DocumentObject* obj, LinkScope scope) { //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()) && - static_cast(prop)->getScope() == scope) { - - result.push_back(static_cast(prop)->getValue()); - } - else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId()) && - static_cast(prop)->getScope() == scope) { - - auto vec = static_cast(prop)->getValues(); - result.insert(result.end(), vec.begin(), vec.end()); - } - else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSub::getClassTypeId()) && - static_cast(prop)->getScope() == scope) { - - result.push_back(static_cast(prop)->getValue()); - } - else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSubList::getClassTypeId()) && - static_cast(prop)->getScope() == scope) { - - auto vec = static_cast(prop)->getValues(); - result.insert(result.end(), vec.begin(), vec.end()); - } + + auto vec = getScopedObjectsFromLink(prop, scope); + 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::getScopedObjectsFromLink(App::Property* prop, LinkScope scope) { -void GeoFeatureGroupExtension::getCSOutList(App::DocumentObject* obj, std::vector< DocumentObject* >& vec) { + std::vector< App::DocumentObject* > result; + + if(prop->getTypeId().isDerivedFrom(App::PropertyLink::getClassTypeId()) && + static_cast(prop)->getScope() == scope) { + + result.push_back(static_cast(prop)->getValue()); + } + + if(prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId()) && + static_cast(prop)->getScope() == scope) { + + auto vec = static_cast(prop)->getValues(); + result.insert(result.end(), vec.begin(), vec.end()); + } + + if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSub::getClassTypeId()) && + static_cast(prop)->getScope() == scope) { + + result.push_back(static_cast(prop)->getValue()); + } + + if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSubList::getClassTypeId()) && + static_cast(prop)->getScope() == scope) { + + auto vec = static_cast(prop)->getValues(); + result.insert(result.end(), vec.begin(), vec.end()); + } + + //it is important to remove all nullptrs + result.erase(std::remove(result.begin(), result.end(), nullptr), result.end()); + return result; +} + + + +void GeoFeatureGroupExtension::getCSOutList(const App::DocumentObject* obj, std::vector< DocumentObject* >& vec) { if(!obj) return; @@ -287,7 +303,7 @@ void GeoFeatureGroupExtension::getCSOutList(App::DocumentObject* obj, std::vecto vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); } -void GeoFeatureGroupExtension::getCSInList(DocumentObject* obj, std::vector< DocumentObject* >& vec) { +void GeoFeatureGroupExtension::getCSInList(const DocumentObject* obj, std::vector< DocumentObject* >& vec) { if(!obj) return; @@ -328,7 +344,7 @@ void GeoFeatureGroupExtension::getCSInList(DocumentObject* obj, std::vector< Doc } -void GeoFeatureGroupExtension::getCSRelevantLinks(DocumentObject* obj, std::vector< DocumentObject* >& vec ) { +void GeoFeatureGroupExtension::getCSRelevantLinks(const DocumentObject* obj, std::vector< DocumentObject* >& vec ) { //get all out links getCSOutList(obj, vec); @@ -348,14 +364,55 @@ void GeoFeatureGroupExtension::getCSRelevantLinks(DocumentObject* obj, std::vect vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); } -bool GeoFeatureGroupExtension::areLinksValid(DocumentObject* obj) { +bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj) { + //no cross CS link for local links. + std::vector list; + obj->getPropertyList(list); + for(App::Property* prop : list) { + if(!isLinkValid(prop)) + return false; + } + + return true; +} + +bool GeoFeatureGroupExtension::isLinkValid(App::Property* prop) { + + //get the object that holds the property + if(!prop->getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId())) + return true; //this link comes not from a document object, scopes are meaningless + auto obj = static_cast(prop->getContainer()); + + //no cross CS link for local links. + auto result = getScopedObjectsFromLink(prop, LinkScope::Local); + auto group = obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) ? obj : getGroupOfObject(obj); + for(auto link : result) { + if(getGroupOfObject(link) != group) + return false; + } + + //for links with scope SubGroup we need to check if all features are part of subgroups + if(group) { + result = getScopedObjectsFromLink(prop, LinkScope::Child); + auto groupExt = group->getExtensionByType(); + for(auto link : result) { + if(!groupExt->hasObject(link, true)) + return false; + } + } + + return true; +} + +void GeoFeatureGroupExtension::getInvalidLinkObjects(const DocumentObject* obj, std::vector< DocumentObject* >& vec) { + //no cross CS link for local links. auto result = getScopedObjectsFromLinks(obj, LinkScope::Local); auto group = obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) ? obj : getGroupOfObject(obj); for(auto link : result) { - if(getGroupOfObject(link) != group) - return false; + if(getGroupOfObject(link) != group) + vec.push_back(link); } //for links with scope SubGroup we need to check if all features are part of subgroups @@ -363,14 +420,13 @@ bool GeoFeatureGroupExtension::areLinksValid(DocumentObject* obj) { result = getScopedObjectsFromLinks(obj, LinkScope::Child); auto groupExt = group->getExtensionByType(); for(auto link : result) { - if(!groupExt->hasObject(link, true)) - return false; + if(!groupExt->hasObject(link, true)) + vec.push_back(link); } } - - return true; } + // Python feature --------------------------------------------------------- namespace App { diff --git a/src/App/GeoFeatureGroupExtension.h b/src/App/GeoFeatureGroupExtension.h index 6042ee0e2c..ba0a9a3bc4 100644 --- a/src/App/GeoFeatureGroupExtension.h +++ b/src/App/GeoFeatureGroupExtension.h @@ -102,21 +102,30 @@ public: /// Collects GeoFeatureGroup relevant objects that are linked from the given one. That means all linked objects /// including their links (recursively) except GeoFeatureGroups, where the recursion stops. Expressions /// links are ignored. An exception is thrown when there are dependency loops. - static void getCSOutList(App::DocumentObject* obj, std::vector& vec); + static void getCSOutList(const App::DocumentObject* obj, std::vector& vec); /// Collects GeoFeatureGroup relevant objects that link to the given one. That means all objects /// including their parents (recursively) except GeoFeatureGroups, where the recursion stops. Expression /// links are ignored. An exception is thrown when there are dependency loops. - static void getCSInList(App::DocumentObject* obj, std::vector& vec); + static void getCSInList(const App::DocumentObject* obj, std::vector& vec); /// Collects 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 void getCSRelevantLinks(App::DocumentObject* obj, std::vector& vec); + static void getCSRelevantLinks(const App::DocumentObject* obj, std::vector& vec); /// Checks if the links of the given object comply with all GeoFeatureGroup requrirements, that means /// if normal links are only withing the parent GeoFeatureGroup. - static bool areLinksValid(App::DocumentObject* obj); + static bool areLinksValid(const App::DocumentObject* obj); + /// Checks if the given link complies with all GeoFeatureGroup requrirements, that means + /// if normal links are only withing the parent GeoFeatureGroup. + static bool isLinkValid(App::Property* link); + //Returns all objects that are wrongly linked from this object, meaning which are out of scope of the + //links of obj + static void getInvalidLinkObjects(const App::DocumentObject* obj, std::vector& vec); + private: Base::Placement recursiveGroupPlacement(GeoFeatureGroupExtension* group); - static std::vector getScopedObjectsFromLinks(App::DocumentObject*, LinkScope scope = LinkScope::Local); + static std::vector getScopedObjectsFromLinks(const App::DocumentObject*, LinkScope scope = LinkScope::Local); + static std::vector getScopedObjectsFromLink(App::Property*, LinkScope scope = LinkScope::Local); + }; typedef ExtensionPythonT> GeoFeatureGroupExtensionPython;