DocumentObject: API implementation change of In/OutList

Added getInListEx() as a more efficient algorithm for recursive in
list. Replacing the original getInListRecursive().

Added out list object and name cache to improve default implementation
of getSubObject().
This commit is contained in:
Zheng, Lei
2019-07-04 19:14:30 +08:00
committed by wmayer
parent 00166c2346
commit 515d0c60cc
2 changed files with 182 additions and 62 deletions

View File

@@ -239,40 +239,38 @@ const char* DocumentObject::detachFromDocument()
return name ? name->c_str() : 0;
}
std::vector<DocumentObject*> DocumentObject::getOutList(void) const
{
std::vector<Property*> List;
std::vector<DocumentObject*> ret;
getPropertyList(List);
for (std::vector<Property*>::const_iterator It = List.begin();It != List.end(); ++It) {
if ((*It)->isDerivedFrom(PropertyLinkList::getClassTypeId())) {
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkList*>(*It)->getValues();
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
if (*It2)
ret.push_back(*It2);
}
}
else if ((*It)->isDerivedFrom(PropertyLinkSubList::getClassTypeId())) {
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkSubList*>(*It)->getValues();
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
if (*It2)
ret.push_back(*It2);
}
}
else if ((*It)->isDerivedFrom(PropertyLink::getClassTypeId())) {
if (static_cast<PropertyLink*>(*It)->getValue())
ret.push_back(static_cast<PropertyLink*>(*It)->getValue());
}
else if ((*It)->isDerivedFrom(PropertyLinkSub::getClassTypeId())) {
if (static_cast<PropertyLinkSub*>(*It)->getValue())
ret.push_back(static_cast<PropertyLinkSub*>(*It)->getValue());
}
const std::vector<DocumentObject*> &DocumentObject::getOutList() const {
if(!_outListCached) {
_outList.clear();
getOutList(0,_outList);
_outListCached = true;
}
return _outList;
}
// Get document objects that this document object relies on
ExpressionEngine.getDocumentObjectDeps(ret);
std::vector<DocumentObject*> DocumentObject::getOutList(int options) const
{
std::vector<DocumentObject*> res;
getOutList(options,res);
return res;
}
return ret;
void DocumentObject::getOutList(int options, std::vector<DocumentObject*> &res) const {
if(_outListCached && !options) {
res.insert(res.end(),_outList.begin(),_outList.end());
return;
}
std::vector<Property*> props;
getPropertyList(props);
bool noHidden = !!(options & OutListNoHidden);
bool noXLinked = !!(options & OutListNoXLinked);
for(auto prop : props) {
auto link = dynamic_cast<PropertyLinkBase*>(prop);
if(link && (!noXLinked || !PropertyXLink::supportXLink(prop)))
link->getLinks(res,noHidden);
}
if(!(options & OutListNoExpression))
ExpressionEngine.getLinks(res);
}
std::vector<App::DocumentObject*> DocumentObject::getOutListOfProperty(App::Property* prop) const
@@ -281,33 +279,9 @@ std::vector<App::DocumentObject*> DocumentObject::getOutListOfProperty(App::Prop
if (!prop || prop->getContainer() != this)
return ret;
if (prop->isDerivedFrom(PropertyLinkList::getClassTypeId())) {
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkList*>(prop)->getValues();
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
if (*It2)
ret.push_back(*It2);
}
}
else if (prop->isDerivedFrom(PropertyLinkSubList::getClassTypeId())) {
const std::vector<DocumentObject*> &OutList = static_cast<PropertyLinkSubList*>(prop)->getValues();
for (std::vector<DocumentObject*>::const_iterator It2 = OutList.begin();It2 != OutList.end(); ++It2) {
if (*It2)
ret.push_back(*It2);
}
}
else if (prop->isDerivedFrom(PropertyLink::getClassTypeId())) {
if (static_cast<PropertyLink*>(prop)->getValue())
ret.push_back(static_cast<PropertyLink*>(prop)->getValue());
}
else if (prop->isDerivedFrom(PropertyLinkSub::getClassTypeId())) {
if (static_cast<PropertyLinkSub*>(prop)->getValue())
ret.push_back(static_cast<PropertyLinkSub*>(prop)->getValue());
}
else if (prop == &ExpressionEngine) {
// Get document objects that this document object relies on
ExpressionEngine.getDocumentObjectDeps(ret);
}
auto link = dynamic_cast<PropertyLinkBase*>(prop);
if(link)
link->getLinks(ret);
return ret;
}
@@ -322,7 +296,7 @@ std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
#else // ifndef USE_OLD_DAG
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
const std::vector<App::DocumentObject*> &DocumentObject::getInList(void) const
{
return _inList;
}
@@ -330,6 +304,8 @@ std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
#endif // if USE_OLD_DAG
#if 0
void _getInListRecursive(std::set<DocumentObject*>& objSet,
const DocumentObject* obj,
const DocumentObject* checkObj, int depth)
@@ -352,7 +328,8 @@ std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const
// number of objects in document is a good estimate in result size
// int maxDepth = getDocument()->countObjects() +2;
int maxDepth = GetApplication().checkLinkDepth(0);
std::set<App::DocumentObject*> result;
std::vector<App::DocumentObject*> result;
result.reserve(maxDepth);
// using a rcursie helper to collect all InLists
_getInListRecursive(result, this, this, maxDepth);
@@ -362,6 +339,95 @@ std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const
return array;
}
#else
// The original algorithm is highly inefficient in some special case.
// Considering an object is linked by every other objects. After exculding this
// object, there is another object linked by every other of the remaining
// objects, and so on. The vector 'result' above will be of magnitude n^2.
// Even if we replace the vector with a set, we still need to visit that amount
// of objects. And this may not be the worst case. getInListEx() has no such
// problem.
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const {
std::set<App::DocumentObject*> inSet;
std::vector<App::DocumentObject*> res;
getInListEx(inSet,true,&res);
return res;
}
#endif
// More efficient algorithm to find the recursive inList of an object,
// including possible external parents. One shortcoming of this algorithm is
// it does not detect cyclic reference, althgouth it won't crash either.
void DocumentObject::getInListEx(std::set<App::DocumentObject*> &inSet,
bool recursive, std::vector<App::DocumentObject*> *inList) const
{
#ifdef USE_OLD_DAG
std::map<DocumentObject*,std::set<App::DocumentObject*> > outLists;
// Old DAG does not have pre-built InList, and must calculate The InList by
// going through all objects' OutLists. So we collect all objects and their
// outLists first here.
for(auto doc : GetApplication().getDocuments()) {
for(auto obj : doc->getObjects()) {
if(!obj || !obj->getNameInDocument() || obj==this)
continue;
const auto &outList = obj->getOutList();
outLists[obj].insert(outList.begin(),outList.end());
}
}
std::stack<DocumentObject*> pendings;
pendings.push(const_cast<DocumentObject*>(this));
while(pendings.size()) {
auto obj = pendings.top();
pendings.pop();
for(auto &v : outLists) {
if(v.first == obj) continue;
auto &outList = v.second;
// Check the outList to see if the object is there, and pend the
// object for recrusive check if it's not already in the inList
if(outList.find(obj)!=outList.end() &&
inSet.insert(v.first).second &&
recursive)
{
pendings.push(v.first);
}
}
}
#else // USE_OLD_DAG
if(!recursive) {
inSet.insert(_inList.begin(),_inList.end());
if(inList)
*inList = _inList;
return;
}
std::stack<DocumentObject*> pendings;
pendings.push(const_cast<DocumentObject*>(this));
while(pendings.size()) {
auto obj = pendings.top();
pendings.pop();
for(auto o : obj->getInList()) {
if(o && o->getNameInDocument() && inSet.insert(o).second) {
pendings.push(o);
if(inList)
inList->push_back(o);
}
}
}
#endif
}
std::set<App::DocumentObject*> DocumentObject::getInListEx(bool recursive) const {
std::set<App::DocumentObject*> ret;
getInListEx(ret,recursive);
return ret;
}
void _getOutListRecursive(std::set<DocumentObject*>& objSet,
const DocumentObject* obj,
const DocumentObject* checkObj, int depth)
@@ -420,8 +486,12 @@ bool _isInInListRecursive(const DocumentObject* act,
bool DocumentObject::isInInListRecursive(DocumentObject *linkTo) const
{
#if 0
int maxDepth = getDocument()->countObjects() + 2;
return _isInInListRecursive(this, linkTo, maxDepth);
#else
return this==linkTo || getInListEx(true).count(linkTo);
#endif
}
bool DocumentObject::isInInList(DocumentObject *linkTo) const
@@ -488,6 +558,7 @@ bool DocumentObject::testIfLinkDAGCompatible(DocumentObject *linkTo) const
bool DocumentObject::testIfLinkDAGCompatible(const std::vector<DocumentObject *> &linksTo) const
{
#if 0
Document* doc = this->getDocument();
if (!doc)
throw Base::RuntimeError("DocumentObject::testIfLinkIsDAG: object is not in any document.");
@@ -497,6 +568,14 @@ bool DocumentObject::testIfLinkDAGCompatible(const std::vector<DocumentObject *>
return false;
else
return true;
#else
auto inLists = getInListEx(true);
inLists.emplace(const_cast<DocumentObject*>(this));
for(auto obj : linksTo)
if(inLists.count(obj))
return false;
return true;
#endif
}
bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSubList &linksTo) const
@@ -620,6 +699,12 @@ void DocumentObject::onChanged(const Property* prop)
signalChanged(*this,*prop);
}
void DocumentObject::clearOutListCache() const {
_outList.clear();
_outListMap.clear();
_outListCached = false;
}
PyObject *DocumentObject::getPyObject(void)
{
if (PythonObject.is(Py::_None())) {