DAG: Change object graph handling to be bidirectional
This commit is contained in:
parent
93262e98b4
commit
665eb63d4c
|
@ -58,11 +58,17 @@ recompute path. Also enables more complicated dependencies beyond trees.
|
|||
# include <bitset>
|
||||
#endif
|
||||
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include <boost/graph/subgraph.hpp>
|
||||
#include <boost/graph/graphviz.hpp>
|
||||
|
||||
#if USE_OLD_DAG
|
||||
#include <boost/graph/topological_sort.hpp>
|
||||
#include <boost/graph/depth_first_search.hpp>
|
||||
#include <boost/graph/dijkstra_shortest_paths.hpp>
|
||||
#include <boost/graph/visitors.hpp>
|
||||
#include <boost/graph/graphviz.hpp>
|
||||
#endif //USE_OLD_DAG
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <unordered_set>
|
||||
|
@ -138,15 +144,17 @@ struct DocumentP
|
|||
DocumentObject* activeObject;
|
||||
Transaction *activeUndoTransaction;
|
||||
int iTransactionMode;
|
||||
std::map<Vertex,DocumentObject*> vertexMap;
|
||||
bool rollback;
|
||||
bool undoing; ///< document in the middle of undo or redo
|
||||
std::bitset<32> StatusBits;
|
||||
int iUndoMode;
|
||||
unsigned int UndoMemSize;
|
||||
unsigned int UndoMaxStackSize;
|
||||
#if USE_OLD_DAG
|
||||
DependencyList DepList;
|
||||
std::map<DocumentObject*,Vertex> VertexObjectList;
|
||||
std::map<Vertex,DocumentObject*> vertexMap;
|
||||
#endif //USE_OLD_DAG
|
||||
|
||||
DocumentP() {
|
||||
activeObject = 0;
|
||||
|
@ -211,7 +219,7 @@ void Document::exportGraphviz(std::ostream& out) const
|
|||
{
|
||||
/* Typedefs for a graph with graphviz attributes */
|
||||
typedef std::map<std::string, std::string> GraphvizAttributes;
|
||||
typedef subgraph< adjacency_list<vecS, vecS, directedS,
|
||||
typedef boost::subgraph< adjacency_list<vecS, vecS, directedS,
|
||||
property<vertex_attribute_t, GraphvizAttributes>,
|
||||
property<edge_index_t, int, property<edge_attribute_t, GraphvizAttributes> >,
|
||||
property<graph_name_t, std::string,
|
||||
|
@ -1667,6 +1675,7 @@ std::vector<App::DocumentObject*> Document::getInList(const DocumentObject* me)
|
|||
return result;
|
||||
}
|
||||
|
||||
#if USE_OLD_DAG
|
||||
namespace boost {
|
||||
// recursive helper function to get all dependencies
|
||||
void out_edges_recursive(const Vertex& v, const DependencyList& g, std::set<Vertex>& out)
|
||||
|
@ -1753,29 +1762,6 @@ Document::getDependencyList(const std::vector<App::DocumentObject*>& objs) const
|
|||
return ary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Signal that object identifiers, typically a property or document object has been renamed.
|
||||
*
|
||||
* This function iterates through all document object in the document, and calls its
|
||||
* renameObjectIdentifiers functions.
|
||||
*
|
||||
* @param paths Map with current and new names
|
||||
*/
|
||||
|
||||
void Document::renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> &paths)
|
||||
{
|
||||
std::map<App::ObjectIdentifier, App::ObjectIdentifier> extendedPaths;
|
||||
|
||||
std::map<App::ObjectIdentifier, App::ObjectIdentifier>::const_iterator it = paths.begin();
|
||||
while (it != paths.end()) {
|
||||
extendedPaths[it->first.canonicalPath()] = it->second.canonicalPath();
|
||||
++it;
|
||||
}
|
||||
|
||||
for (std::vector<DocumentObject*>::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it)
|
||||
(*it)->renameObjectIdentifiers(extendedPaths);
|
||||
}
|
||||
|
||||
void Document::_rebuildDependencyList(void)
|
||||
{
|
||||
d->VertexObjectList.clear();
|
||||
|
@ -1809,13 +1795,62 @@ void Document::_rebuildDependencyList(void)
|
|||
}
|
||||
}
|
||||
|
||||
void Document::recompute()
|
||||
#else
|
||||
|
||||
std::vector<App::DocumentObject*> Document::getDependencyList(const std::vector<App::DocumentObject*>& objs) const
|
||||
{
|
||||
std::vector<App::DocumentObject*> dep;
|
||||
for (auto obj : objs){
|
||||
if(!obj)
|
||||
continue;
|
||||
std::vector<App::DocumentObject*> objDep = obj->getOutListRecursive();
|
||||
dep.insert(dep.end(), objDep.begin(), objDep.end());
|
||||
dep.push_back(obj);
|
||||
}
|
||||
|
||||
// remove duplicate entries and resize the vector
|
||||
std::sort(dep.begin(), dep.end());
|
||||
auto newEnd = std::unique(dep.begin(), dep.end());
|
||||
dep.resize(std::distance(dep.begin(), newEnd));
|
||||
|
||||
return dep;
|
||||
}
|
||||
#endif // USE_OLD_DAG
|
||||
|
||||
|
||||
/**
|
||||
* @brief Signal that object identifiers, typically a property or document object has been renamed.
|
||||
*
|
||||
* This function iterates through all document object in the document, and calls its
|
||||
* renameObjectIdentifiers functions.
|
||||
*
|
||||
* @param paths Map with current and new names
|
||||
*/
|
||||
|
||||
void Document::renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> &paths)
|
||||
{
|
||||
std::map<App::ObjectIdentifier, App::ObjectIdentifier> extendedPaths;
|
||||
|
||||
std::map<App::ObjectIdentifier, App::ObjectIdentifier>::const_iterator it = paths.begin();
|
||||
while (it != paths.end()) {
|
||||
extendedPaths[it->first.canonicalPath()] = it->second.canonicalPath();
|
||||
++it;
|
||||
}
|
||||
|
||||
for (std::vector<DocumentObject*>::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it)
|
||||
(*it)->renameObjectIdentifiers(extendedPaths);
|
||||
}
|
||||
|
||||
#if USE_OLD_DAG
|
||||
int Document::recompute()
|
||||
{
|
||||
int objectCount = 0;
|
||||
|
||||
// The 'SkipRecompute' flag can be (tmp.) set to avoid to many
|
||||
// time expensive recomputes
|
||||
bool skip = testStatus(Document::SkipRecompute);
|
||||
if (skip)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
// delete recompute log
|
||||
for (std::vector<App::DocumentObjectExecReturn*>::iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
|
||||
|
@ -1835,7 +1870,7 @@ void Document::recompute()
|
|||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Document::recompute: " << e.what() << std::endl;
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// caching vertex to DocObject
|
||||
|
@ -1908,8 +1943,9 @@ void Document::recompute()
|
|||
if ( _recomputeFeature(Cur)) {
|
||||
// if somthing happen break execution of recompute
|
||||
d->vertexMap.clear();
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
++objectCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1921,8 +1957,92 @@ void Document::recompute()
|
|||
d->vertexMap.clear();
|
||||
|
||||
signalRecomputed(*this);
|
||||
|
||||
return objectCount;
|
||||
}
|
||||
|
||||
#else // USE_OLD_DAG
|
||||
|
||||
std::vector<App::DocumentObject*> Document::topologicalSort() const
|
||||
{
|
||||
// topological sort algorithm described here: https://de.wikipedia.org/wiki/Topologische_Sortierung#Algorithmus_f.C3.BCr_das_Topologische_Sortieren
|
||||
vector < App::DocumentObject* > ret;
|
||||
ret.reserve(d->objectArray.size());
|
||||
map < App::DocumentObject*,int > countMap;
|
||||
|
||||
for (auto objectIt : d->objectArray)
|
||||
countMap[objectIt] = objectIt->getInList().size();
|
||||
|
||||
auto rootObjeIt = find_if(countMap.begin(), countMap.end(), [](pair < App::DocumentObject*, int > count)->bool {
|
||||
return count.second == 0;
|
||||
});
|
||||
|
||||
if (rootObjeIt == countMap.end()){
|
||||
cerr << "Document::topologicalSort: cyclic dependency detected (no root object)" << endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (rootObjeIt != countMap.end()){
|
||||
rootObjeIt->second = rootObjeIt->second - 1;
|
||||
for (auto outListIt : rootObjeIt->first->getOutList()){
|
||||
auto outListMapIt = countMap.find(outListIt);
|
||||
outListMapIt->second = outListMapIt->second - 1;
|
||||
}
|
||||
ret.push_back(rootObjeIt->first);
|
||||
|
||||
rootObjeIt = find_if(countMap.begin(), countMap.end(), [](pair < App::DocumentObject*, int > count)->bool {
|
||||
return count.second == 0;
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Document::recompute()
|
||||
{
|
||||
int objectCount = 0;
|
||||
// delete recompute log
|
||||
for( auto LogEntry: _RecomputeLog)
|
||||
delete LogEntry;
|
||||
_RecomputeLog.clear();
|
||||
|
||||
// get the sorted vector of all objects in the document and go though it from the end
|
||||
vector<DocumentObject*> topoSortedObjects = topologicalSort();
|
||||
|
||||
if (topoSortedObjects.size() != d->objectArray.size()){
|
||||
cerr << "App::Document::recompute(): topological sort fails, invalid DAG!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (auto objIt = topoSortedObjects.rbegin(); objIt != topoSortedObjects.rend(); ++objIt){
|
||||
// ask the object if it should be recomputed
|
||||
if ((*objIt)->mustExecute() == 1){
|
||||
objectCount++;
|
||||
if (_recomputeFeature(*objIt)) {
|
||||
// if something happen break execution of recompute
|
||||
return -1;
|
||||
}
|
||||
else{
|
||||
(*objIt)->purgeTouched();
|
||||
// set all dependent object touched to force recompute
|
||||
for (auto inObjIt : (*objIt)->getInList())
|
||||
inObjIt->touch();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#ifdef FC_DEBUG
|
||||
// check if all objects are recalculated which were thouched
|
||||
for (auto objectIt : d->objectArray)
|
||||
if (objectIt->isTouched())
|
||||
cerr << "Document::recompute(): " << objectIt->getNameInDocument() << " still touched after recompute" << endl;
|
||||
#endif
|
||||
|
||||
return objectCount;
|
||||
}
|
||||
|
||||
#endif // USE_OLD_DAG
|
||||
|
||||
const char * Document::getErrorDescription(const App::DocumentObject*Obj) const
|
||||
{
|
||||
for (std::vector<App::DocumentObjectExecReturn*>::const_iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
|
||||
|
@ -2182,6 +2302,7 @@ void Document::remObject(const char* sName)
|
|||
signalTransactionRemove(*pos->second, 0);
|
||||
}
|
||||
|
||||
#if USE_OLD_DAG
|
||||
if (!d->vertexMap.empty()) {
|
||||
// recompute of document is running
|
||||
for (std::map<Vertex,DocumentObject*>::iterator it = d->vertexMap.begin(); it != d->vertexMap.end(); ++it) {
|
||||
|
@ -2191,7 +2312,8 @@ void Document::remObject(const char* sName)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //USE_OLD_DAG
|
||||
|
||||
// Before deleting we must nullify all dependant objects
|
||||
breakDependency(pos->second, true);
|
||||
|
||||
|
@ -2565,3 +2687,14 @@ PyObject * Document::getPyObject(void)
|
|||
{
|
||||
return Py::new_reference_to(DocumentPythonObject);
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> Document::getRootObjects() const
|
||||
{
|
||||
std::vector < App::DocumentObject* > ret;
|
||||
|
||||
for (auto objectIt : d->objectArray)
|
||||
if (objectIt->getInList().size() == 0)
|
||||
ret.push_back(objectIt);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -37,8 +37,6 @@
|
|||
#include <stack>
|
||||
|
||||
#include <boost/signals.hpp>
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
|
||||
|
||||
namespace Base {
|
||||
class Writer;
|
||||
|
@ -246,8 +244,8 @@ public:
|
|||
void setClosable(bool);
|
||||
/// check whether the document can be closed
|
||||
bool isClosable() const;
|
||||
/// Recompute all touched features
|
||||
void recompute();
|
||||
/// Recompute all touched features and return the amount of recalculated features
|
||||
int recompute();
|
||||
/// Recompute only one feature
|
||||
void recomputeFeature(DocumentObject* Feat);
|
||||
/// get the error log from the recompute run
|
||||
|
@ -311,10 +309,15 @@ public:
|
|||
std::vector<App::DocumentObject*> getInList(const DocumentObject* me) const;
|
||||
/// Get a complete list of all objects the given objects depend on. The list
|
||||
/// also contains the given objects!
|
||||
/// deprecated! Use In- and OutList mimic in the DocumentObject instead!
|
||||
std::vector<App::DocumentObject*> getDependencyList
|
||||
(const std::vector<App::DocumentObject*>&) const;
|
||||
// set Changed
|
||||
//void setChanged(DocumentObject* change);
|
||||
/// get a list of topological sorted objects (https://en.wikipedia.org/wiki/Topological_sorting)
|
||||
std::vector<App::DocumentObject*> topologicalSort() const;
|
||||
/// get all root objects (objects no other one reference too)
|
||||
std::vector<App::DocumentObject*> getRootObjects() const;
|
||||
//@}
|
||||
|
||||
/// Function called to signal that an object identifier has been renamed
|
||||
|
@ -352,8 +355,10 @@ protected:
|
|||
/// helper which Recompute only this feature
|
||||
bool _recomputeFeature(DocumentObject* Feat);
|
||||
void _clearRedos();
|
||||
#if USE_OLD_DAG
|
||||
/// refresh the internal dependency graph
|
||||
void _rebuildDependencyList(void);
|
||||
#endif
|
||||
std::string getTransientDirectoryName(const std::string& uuid, const std::string& filename) const;
|
||||
|
||||
|
||||
|
|
|
@ -182,6 +182,7 @@ std::vector<DocumentObject*> DocumentObject::getOutList(void) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if USE_OLD_DAG
|
||||
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
|
||||
{
|
||||
if (_pDoc)
|
||||
|
@ -190,6 +191,79 @@ std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
|
|||
return std::vector<App::DocumentObject*>();
|
||||
}
|
||||
|
||||
#else // if USE_OLD_DAG
|
||||
|
||||
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
|
||||
{
|
||||
return _inList;
|
||||
}
|
||||
|
||||
#endif // if USE_OLD_DAG
|
||||
|
||||
|
||||
void _getInListRecursive(std::vector<DocumentObject*>& objSet, const DocumentObject* obj, const DocumentObject* checkObj, int depth)
|
||||
{
|
||||
for (const auto objIt : obj->getInList()){
|
||||
// if the check object is in the recursive inList we have a cycle!
|
||||
if (objIt == checkObj || depth <= 0){
|
||||
std::cerr << "DocumentObject::getInListRecursive(): cyclic dependency detected!"<<std::endl;
|
||||
throw Base::Exception("DocumentObject::getInListRecursive(): cyclic dependency detected!");
|
||||
}
|
||||
|
||||
objSet.push_back(objIt);
|
||||
_getInListRecursive(objSet, objIt, checkObj,depth-1);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
std::vector<App::DocumentObject*> result;
|
||||
result.reserve(maxDepth);
|
||||
|
||||
// using a rcursie helper to collect all InLists
|
||||
_getInListRecursive(result, this, this, maxDepth);
|
||||
|
||||
// remove duplicate entries and resize the vector
|
||||
std::sort(result.begin(), result.end());
|
||||
auto newEnd = std::unique(result.begin(), result.end());
|
||||
result.resize(std::distance(result.begin(), newEnd));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _getOutListRecursive(std::vector<DocumentObject*>& objSet, const DocumentObject* obj, const DocumentObject* checkObj, int depth)
|
||||
{
|
||||
for (const auto objIt : obj->getOutList()){
|
||||
// if the check object is in the recursive inList we have a cycle!
|
||||
if (objIt == checkObj || depth <= 0){
|
||||
std::cerr << "DocumentObject::getOutListRecursive(): cyclic dependency detected!" << std::endl;
|
||||
throw Base::Exception("DocumentObject::getOutListRecursive(): cyclic dependency detected!");
|
||||
}
|
||||
objSet.push_back(objIt);
|
||||
_getOutListRecursive(objSet, objIt, checkObj,depth-1);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> DocumentObject::getOutListRecursive(void) const
|
||||
{
|
||||
// number of objects in document is a good estimate in result size
|
||||
int maxDepth = getDocument()->countObjects() + 2;
|
||||
std::vector<App::DocumentObject*> result;
|
||||
result.reserve(maxDepth);
|
||||
|
||||
// using a recursive helper to collect all OutLists
|
||||
_getOutListRecursive(result, this, this, maxDepth);
|
||||
|
||||
// remove duplicate entries and resize the vector
|
||||
std::sort(result.begin(), result.end());
|
||||
auto newEnd = std::unique(result.begin(), result.end());
|
||||
result.resize(std::distance(result.begin(), newEnd));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DocumentObjectGroup* DocumentObject::getGroup() const
|
||||
{
|
||||
return dynamic_cast<DocumentObjectGroup*>(GroupExtension::getGroupOfObject(this));
|
||||
|
@ -229,6 +303,67 @@ bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSub &linkTo) const
|
|||
return this->testIfLinkDAGCompatible(linkTo_in_vector);
|
||||
}
|
||||
|
||||
#if USE_OLD_DAG
|
||||
#else
|
||||
bool DocumentObject::_isInInListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const
|
||||
{
|
||||
if (std::find(_inList.begin(), _inList.end(), test) != _inList.end())
|
||||
return true;
|
||||
|
||||
for (auto obj : _inList){
|
||||
// if the check object is in the recursive inList we have a cycle!
|
||||
if (obj == checkObj || depth <= 0){
|
||||
std::cerr << "DocumentObject::getOutListRecursive(): cyclic dependency detected!" << std::endl;
|
||||
throw Base::Exception("DocumentObject::getOutListRecursive(): cyclic dependency detected!");
|
||||
}
|
||||
|
||||
if (_isInInListRecursive(obj, test, checkObj, depth - 1))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DocumentObject::isInInListRecursive(DocumentObject *linkTo) const
|
||||
{
|
||||
return _isInInListRecursive(this, linkTo, this, getDocument()->countObjects());
|
||||
}
|
||||
|
||||
bool DocumentObject::isInInList(DocumentObject *linkTo) const
|
||||
{
|
||||
if (std::find(_inList.begin(), _inList.end(), linkTo) != _inList.end())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DocumentObject::_isInOutListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const
|
||||
{
|
||||
std::vector <DocumentObject*> outList = act->getOutList();
|
||||
|
||||
if (std::find(outList.begin(), outList.end(), test) != outList.end())
|
||||
return true;
|
||||
|
||||
for (auto obj : outList){
|
||||
// if the check object is in the recursive inList we have a cycle!
|
||||
if (obj == checkObj || depth <= 0){
|
||||
std::cerr << "DocumentObject::isInOutListRecursive(): cyclic dependency detected!" << std::endl;
|
||||
throw Base::Exception("DocumentObject::isInOutListRecursive(): cyclic dependency detected!");
|
||||
}
|
||||
|
||||
if (_isInOutListRecursive(obj, test, checkObj, depth - 1))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DocumentObject::isInOutListRecursive(DocumentObject *linkTo) const
|
||||
{
|
||||
return _isInOutListRecursive(this, linkTo, this, getDocument()->countObjects());
|
||||
}
|
||||
#endif //USE_OLD_DAG
|
||||
|
||||
void DocumentObject::onLostLinkToObject(DocumentObject*)
|
||||
{
|
||||
|
||||
|
@ -405,3 +540,17 @@ void DocumentObject::unsetupObject()
|
|||
for(auto ext : vector)
|
||||
ext->onExtendedUnsetupObject();
|
||||
}
|
||||
|
||||
#if USE_OLD_DAG
|
||||
#else
|
||||
void App::DocumentObject::_removeBackLink(DocumentObject* rmfObj)
|
||||
{
|
||||
_inList.erase(std::remove(_inList.begin(), _inList.end(), rmfObj), _inList.end());
|
||||
}
|
||||
|
||||
void App::DocumentObject::_addBackLink(DocumentObject* newObje)
|
||||
{
|
||||
if ( std::find(_inList.begin(), _inList.end(), newObje) == _inList.end() )
|
||||
_inList.push_back(newObje);
|
||||
}
|
||||
#endif //USE_OLD_DAG
|
|
@ -127,12 +127,37 @@ public:
|
|||
void setStatus(ObjectStatus pos, bool on) {StatusBits.set((size_t)pos, on);}
|
||||
//@}
|
||||
|
||||
/** DAG handling
|
||||
This part of the interface deals with viewing the document as
|
||||
an DAG (directed acyclic graph).
|
||||
*/
|
||||
//@{
|
||||
/// returns a list of objects this object is pointing to by Links
|
||||
std::vector<App::DocumentObject*> getOutList(void) const;
|
||||
/// returns a list of objects this object is pointing to by Links and all further descended
|
||||
std::vector<App::DocumentObject*> getOutListRecursive(void) const;
|
||||
/// get all objects link to this object
|
||||
std::vector<App::DocumentObject*> getInList(void) const;
|
||||
/// get all objects link directly or indirectly to this object
|
||||
std::vector<App::DocumentObject*> getInListRecursive(void) const;
|
||||
/// get group if object is part of a group, otherwise 0 is returned
|
||||
DocumentObjectGroup* getGroup() const;
|
||||
#if USE_OLD_DAG
|
||||
#else
|
||||
/// test if this object is in the InList and recursive further down
|
||||
bool isInInListRecursive(DocumentObject* objToTest) const;
|
||||
/// test if this object is directly (non recursive) in the InList
|
||||
bool isInInList(DocumentObject* objToTest) const;
|
||||
/// test if the given object is in the OutList and recursive further down
|
||||
bool isInOutListRecursive(DocumentObject* objToTest) const;
|
||||
/// test if this object is directly (non recursive) in the OutList
|
||||
bool isInOutList(DocumentObject* objToTest) const;
|
||||
/// internal, used by ProperyLink to maintain DAG back links
|
||||
void _removeBackLink(DocumentObject*);
|
||||
/// internal, used by ProperyLink to maintain DAG back links
|
||||
void _addBackLink(DocumentObject*);
|
||||
#endif //USE_OLD_DAG
|
||||
//@}
|
||||
|
||||
/**
|
||||
* @brief testIfLinkIsDAG tests a link that is about to be created for
|
||||
|
@ -149,7 +174,6 @@ public:
|
|||
bool testIfLinkDAGCompatible(App::PropertyLinkSubList &linksTo) const;
|
||||
bool testIfLinkDAGCompatible(App::PropertyLinkSub &linkTo) const;
|
||||
|
||||
|
||||
public:
|
||||
/** mustExecute
|
||||
* We call this method to check if the object was modified to
|
||||
|
@ -259,6 +283,15 @@ protected: // attributes
|
|||
|
||||
// pointer to the document name string (for performance)
|
||||
const std::string *pcNameInDocument;
|
||||
|
||||
private:
|
||||
// Back pointer to all the fathers in a DAG of the document
|
||||
// this is used by the document (via friend) to have a effective DAG handling
|
||||
std::vector<App::DocumentObject*> _inList;
|
||||
// helper for isInInListRecursive()
|
||||
bool _isInInListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const;
|
||||
// helper for isInOutListRecursive()
|
||||
bool _isInOutListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const;
|
||||
};
|
||||
|
||||
class AppExport ObjectStatusLocker
|
||||
|
|
|
@ -61,12 +61,24 @@
|
|||
</Documentation>
|
||||
<Parameter Name="OutList" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="OutListRecursive" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all objects this object links to recursivly.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="OutListRecursive" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="InList" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all objects which link to this object.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="InList" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="InListRecursive" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of all objects which link to this object recursivly.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="InListRecursive" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Name" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return the internal name of this object</UserDocu>
|
||||
|
|
|
@ -207,6 +207,22 @@ Py::List DocumentObjectPy::getInList(void) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
Py::List DocumentObjectPy::getInListRecursive(void) const
|
||||
{
|
||||
Py::List ret;
|
||||
try{
|
||||
|
||||
std::vector<DocumentObject*> list = getDocumentObjectPtr()->getInListRecursive();
|
||||
|
||||
for (std::vector<DocumentObject*>::iterator It = list.begin(); It != list.end(); ++It)
|
||||
ret.append(Py::Object((*It)->getPyObject(), true));
|
||||
|
||||
}catch (const Base::Exception& e) {
|
||||
throw Py::IndexError(e.what());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Py::List DocumentObjectPy::getOutList(void) const
|
||||
{
|
||||
Py::List ret;
|
||||
|
@ -218,6 +234,24 @@ Py::List DocumentObjectPy::getOutList(void) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
Py::List DocumentObjectPy::getOutListRecursive(void) const
|
||||
{
|
||||
Py::List ret;
|
||||
try {
|
||||
|
||||
std::vector<DocumentObject*> list = getDocumentObjectPtr()->getOutListRecursive();
|
||||
|
||||
// creat the python list for the output
|
||||
for (std::vector<DocumentObject*>::iterator It = list.begin(); It != list.end(); ++It)
|
||||
ret.append(Py::Object((*It)->getPyObject(), true));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
throw Py::IndexError(e.what());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject* DocumentObjectPy::setExpression(PyObject * args)
|
||||
{
|
||||
char * path = NULL;
|
||||
|
|
|
@ -151,6 +151,18 @@ Both parameters are optional.</UserDocu>
|
|||
</Documentation>
|
||||
<Parameter Name="Objects" Type="List" />
|
||||
</Attribute>
|
||||
<Attribute Name="ToplogicalSortedObjects" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>The list of object of this document in topological sorted order</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="ToplogicalSortedObjects" Type="List" />
|
||||
</Attribute>
|
||||
<Attribute Name="RootObjects" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>The list of root object of this document</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="RootObjects" Type="List" />
|
||||
</Attribute>
|
||||
<Attribute Name="UndoMode" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>The Undo mode of the Document (0 = no Undo, 1 = Undo/Redo)</UserDocu>
|
||||
|
|
|
@ -490,6 +490,38 @@ Py::List DocumentPy::getObjects(void) const
|
|||
return res;
|
||||
}
|
||||
|
||||
Py::List DocumentPy::getToplogicalSortedObjects(void) const
|
||||
{
|
||||
#if USE_OLD_DAG == 0
|
||||
std::vector<DocumentObject*> objs = getDocumentPtr()->topologicalSort();
|
||||
Py::List res;
|
||||
|
||||
for (std::vector<DocumentObject*>::const_iterator It = objs.begin(); It != objs.end(); ++It)
|
||||
//Note: Here we must force the Py::Object to own this Python object as getPyObject() increments the counter
|
||||
res.append(Py::Object((*It)->getPyObject(), true));
|
||||
|
||||
return res;
|
||||
#else
|
||||
return Py::List();
|
||||
#endif
|
||||
}
|
||||
|
||||
Py::List DocumentPy::getRootObjects(void) const
|
||||
{
|
||||
#if USE_OLD_DAG == 0
|
||||
std::vector<DocumentObject*> objs = getDocumentPtr()->getRootObjects();
|
||||
Py::List res;
|
||||
|
||||
for (std::vector<DocumentObject*>::const_iterator It = objs.begin(); It != objs.end(); ++It)
|
||||
//Note: Here we must force the Py::Object to own this Python object as getPyObject() increments the counter
|
||||
res.append(Py::Object((*It)->getPyObject(), true));
|
||||
|
||||
return res;
|
||||
#else
|
||||
return Py::List();
|
||||
#endif
|
||||
}
|
||||
|
||||
Py::Int DocumentPy::getUndoMode(void) const
|
||||
{
|
||||
return Py::Int(getDocumentPtr()->getUndoMode());
|
||||
|
|
|
@ -122,10 +122,49 @@ void PropertyExpressionEngine::Paste(const Property &from)
|
|||
const PropertyExpressionEngine * fromee = static_cast<const PropertyExpressionEngine*>(&from);
|
||||
|
||||
AtomicPropertyChange signaller(*this);
|
||||
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain backlinks
|
||||
ExpressionMap::const_iterator i = expressions.begin();
|
||||
while (i != expressions.end()) {
|
||||
std::set<ObjectIdentifier> deps;
|
||||
i->second.expression->getDeps(deps);
|
||||
|
||||
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
|
||||
while (j != deps.end()) {
|
||||
const ObjectIdentifier & p = *j;
|
||||
DocumentObject* docObj = p.getDocumentObject();
|
||||
|
||||
if (docObj)
|
||||
docObj->_removeBackLink(static_cast<App::DocumentObject*>(getContainer()));
|
||||
|
||||
++j;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
#endif
|
||||
expressions.clear();
|
||||
|
||||
for (ExpressionMap::const_iterator it = fromee->expressions.begin(); it != fromee->expressions.end(); ++it) {
|
||||
expressions[it->first] = ExpressionInfo(boost::shared_ptr<Expression>(it->second.expression->copy()), it->second.comment.c_str());
|
||||
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain backlinks
|
||||
std::set<ObjectIdentifier> deps;
|
||||
it->second.expression->getDeps(deps);
|
||||
|
||||
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
|
||||
while (j != deps.end()) {
|
||||
const ObjectIdentifier & p = *j;
|
||||
DocumentObject* docObj = p.getDocumentObject();
|
||||
|
||||
if (docObj)
|
||||
docObj->_addBackLink(static_cast<App::DocumentObject*>(getContainer()));
|
||||
|
||||
++j;
|
||||
}
|
||||
#endif
|
||||
|
||||
expressionChanged(it->first);
|
||||
}
|
||||
|
||||
|
@ -364,11 +403,43 @@ void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, boost::sh
|
|||
|
||||
AtomicPropertyChange signaller(*this);
|
||||
expressions[usePath] = ExpressionInfo(expr, comment);
|
||||
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain the backlinks in the documentobject graph datastructure
|
||||
std::set<ObjectIdentifier> deps;
|
||||
expr->getDeps(deps);
|
||||
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
|
||||
while (j != deps.end()) {
|
||||
const ObjectIdentifier & p = *j;
|
||||
DocumentObject* docObj = p.getDocumentObject();
|
||||
if (docObj)
|
||||
docObj->_addBackLink(static_cast<App::DocumentObject*>(getContainer()));
|
||||
|
||||
++j;
|
||||
}
|
||||
#endif
|
||||
|
||||
expressionChanged(usePath);
|
||||
}
|
||||
else {
|
||||
AtomicPropertyChange signaller(*this);
|
||||
expressions.erase(usePath);
|
||||
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain the backlinks in the documentobject graph datastructure
|
||||
std::set<ObjectIdentifier> deps;
|
||||
expressions[usePath].expression->getDeps(deps);
|
||||
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
|
||||
while (j != deps.end()) {
|
||||
const ObjectIdentifier & p = *j;
|
||||
DocumentObject* docObj = p.getDocumentObject();
|
||||
if (docObj)
|
||||
docObj->_removeBackLink(static_cast<App::DocumentObject*>(getContainer()));
|
||||
|
||||
++j;
|
||||
}
|
||||
#endif
|
||||
|
||||
expressionChanged(usePath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ using namespace Base;
|
|||
using namespace std;
|
||||
|
||||
|
||||
|
||||
/// helper function for DAG back link handling
|
||||
void _maintainBackLink(DocumentObject *me, DocumentObject *oldObj, DocumentObject *newObj);
|
||||
|
||||
//**************************************************************************
|
||||
//**************************************************************************
|
||||
|
@ -75,7 +76,17 @@ PropertyLink::~PropertyLink()
|
|||
|
||||
void PropertyLink::setValue(App::DocumentObject * lValue)
|
||||
{
|
||||
if (lValue == _pcLink)
|
||||
return; // nothing to do
|
||||
|
||||
aboutToSetValue();
|
||||
#if USE_OLD_DAG==0
|
||||
// maintain the back link in the DocumentObject class
|
||||
if(_pcLink)
|
||||
_pcLink->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
if(lValue)
|
||||
lValue->_addBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
_pcLink=lValue;
|
||||
hasSetValue();
|
||||
}
|
||||
|
@ -163,9 +174,10 @@ Property *PropertyLink::Copy(void) const
|
|||
|
||||
void PropertyLink::Paste(const Property &from)
|
||||
{
|
||||
aboutToSetValue();
|
||||
_pcLink = dynamic_cast<const PropertyLink&>(from)._pcLink;
|
||||
hasSetValue();
|
||||
if(!from.isDerivedFrom(PropertyLink::getClassTypeId()))
|
||||
throw Base::Exception("Incompatible proeprty to paste to");
|
||||
|
||||
setValue(static_cast<const PropertyLink&>(from)._pcLink);
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
|
@ -200,17 +212,37 @@ int PropertyLinkList::getSize(void) const
|
|||
|
||||
void PropertyLinkList::setValue(DocumentObject* lValue)
|
||||
{
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain the back link in the DocumentObject class
|
||||
for(auto *obj : _lValueList)
|
||||
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
if(lValue)
|
||||
lValue->_addBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
|
||||
if (lValue){
|
||||
aboutToSetValue();
|
||||
_lValueList.resize(1);
|
||||
_lValueList[0] = lValue;
|
||||
hasSetValue();
|
||||
}
|
||||
else {
|
||||
aboutToSetValue();
|
||||
_lValueList.clear();
|
||||
hasSetValue();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyLinkList::setValues(const std::vector<DocumentObject*>& lValue)
|
||||
{
|
||||
aboutToSetValue();
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain the back link in the DocumentObject class
|
||||
for(auto *obj : _lValueList)
|
||||
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
for(auto *obj : lValue)
|
||||
obj->_addBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
_lValueList = lValue;
|
||||
hasSetValue();
|
||||
}
|
||||
|
@ -314,9 +346,7 @@ Property *PropertyLinkList::Copy(void) const
|
|||
|
||||
void PropertyLinkList::Paste(const Property &from)
|
||||
{
|
||||
aboutToSetValue();
|
||||
_lValueList = dynamic_cast<const PropertyLinkList&>(from)._lValueList;
|
||||
hasSetValue();
|
||||
setValues(dynamic_cast<const PropertyLinkList&>(from)._lValueList);
|
||||
}
|
||||
|
||||
unsigned int PropertyLinkList::getMemSize(void) const
|
||||
|
@ -351,7 +381,16 @@ PropertyLinkSub::~PropertyLinkSub()
|
|||
|
||||
void PropertyLinkSub::setValue(App::DocumentObject * lValue, const std::vector<std::string> &SubList)
|
||||
{
|
||||
if(lValue == _pcLinkSub)
|
||||
return; //nothing to do
|
||||
|
||||
aboutToSetValue();
|
||||
#if USE_OLD_DAG == 0
|
||||
if(_pcLinkSub)
|
||||
_pcLinkSub->_removeBackLink(static_cast<App::DocumentObject*>(getContainer()));
|
||||
if(lValue)
|
||||
lValue->_addBackLink(static_cast<App::DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
_pcLinkSub=lValue;
|
||||
_cSubList = SubList;
|
||||
hasSetValue();
|
||||
|
@ -505,10 +544,7 @@ Property *PropertyLinkSub::Copy(void) const
|
|||
|
||||
void PropertyLinkSub::Paste(const Property &from)
|
||||
{
|
||||
aboutToSetValue();
|
||||
_pcLinkSub = dynamic_cast<const PropertyLinkSub&>(from)._pcLinkSub;
|
||||
_cSubList = dynamic_cast<const PropertyLinkSub&>(from)._cSubList;
|
||||
hasSetValue();
|
||||
setValue(dynamic_cast<const PropertyLinkSub&>(from)._pcLinkSub, dynamic_cast<const PropertyLinkSub&>(from)._cSubList);
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
|
@ -544,6 +580,14 @@ int PropertyLinkSubList::getSize(void) const
|
|||
|
||||
void PropertyLinkSubList::setValue(DocumentObject* lValue,const char* SubName)
|
||||
{
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain backlinks
|
||||
for(auto *obj : _lValueList)
|
||||
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
if(lValue)
|
||||
lValue->_addBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
|
||||
if (lValue) {
|
||||
aboutToSetValue();
|
||||
_lValueList.resize(1);
|
||||
|
@ -561,9 +605,22 @@ void PropertyLinkSubList::setValue(DocumentObject* lValue,const char* SubName)
|
|||
}
|
||||
|
||||
void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,const std::vector<const char*>& lSubNames)
|
||||
{
|
||||
{
|
||||
if (lValue.size() != lSubNames.size())
|
||||
throw Base::Exception("PropertyLinkSubList::setValues: size of subelements list != size of objects list");
|
||||
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain backlinks. _lValueList can contain items multiple times, but we trust the document
|
||||
//object to ensure that this works
|
||||
for(auto *obj : _lValueList)
|
||||
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
|
||||
//maintain backlinks. lValue can contain items multiple times, but we trust the document
|
||||
//object to ensure that the backlink is only added once
|
||||
for(auto *obj : lValue)
|
||||
obj->_addBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
|
||||
aboutToSetValue();
|
||||
_lValueList = lValue;
|
||||
_lSubList.resize(lSubNames.size());
|
||||
|
@ -577,6 +634,19 @@ void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,c
|
|||
{
|
||||
if (lValue.size() != lSubNames.size())
|
||||
throw Base::Exception("PropertyLinkSubList::setValues: size of subelements list != size of objects list");
|
||||
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain backlinks. _lValueList can contain items multiple times, but we trust the document
|
||||
//object to ensure that this works
|
||||
for(auto *obj : _lValueList)
|
||||
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
|
||||
//maintain backlinks. lValue can contain items multiple times, but we trust the document
|
||||
//object to ensure that the backlink is only added once
|
||||
for(auto *obj : lValue)
|
||||
obj->_addBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
|
||||
aboutToSetValue();
|
||||
_lValueList = lValue;
|
||||
_lSubList = lSubNames;
|
||||
|
@ -585,6 +655,18 @@ void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,c
|
|||
|
||||
void PropertyLinkSubList::setValue(DocumentObject* lValue, const std::vector<string> &SubList)
|
||||
{
|
||||
#if USE_OLD_DAG == 0
|
||||
//maintain backlinks. _lValueList can contain items multiple times, but we trust the document
|
||||
//object to ensure that this works
|
||||
for(auto *obj : _lValueList)
|
||||
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
|
||||
//maintain backlinks. lValue can contain items multiple times, but we trust the document
|
||||
//object to ensure that the backlink is only added once
|
||||
if(lValue)
|
||||
lValue->_addBackLink(static_cast<DocumentObject*>(getContainer()));
|
||||
#endif
|
||||
|
||||
aboutToSetValue();
|
||||
std::size_t size = SubList.size();
|
||||
this->_lValueList.clear();
|
||||
|
@ -850,10 +932,7 @@ Property *PropertyLinkSubList::Copy(void) const
|
|||
|
||||
void PropertyLinkSubList::Paste(const Property &from)
|
||||
{
|
||||
aboutToSetValue();
|
||||
_lValueList = dynamic_cast<const PropertyLinkSubList&>(from)._lValueList;
|
||||
_lSubList = dynamic_cast<const PropertyLinkSubList&>(from)._lSubList;
|
||||
hasSetValue();
|
||||
setValues(dynamic_cast<const PropertyLinkSubList&>(from)._lValueList, dynamic_cast<const PropertyLinkSubList&>(from)._lSubList);
|
||||
}
|
||||
|
||||
unsigned int PropertyLinkSubList::getMemSize (void) const
|
||||
|
|
|
@ -342,5 +342,6 @@ typedef unsigned __int64 uint64_t;
|
|||
//# define _PreComp_ // use precompiled header
|
||||
#endif
|
||||
|
||||
#define USE_OLD_DAG 0
|
||||
|
||||
#endif //FC_CONFIG_H
|
||||
|
|
Loading…
Reference in New Issue
Block a user