diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 3ac0753d23..faaf50a10b 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -2232,22 +2232,25 @@ int Document::recompute() for (auto objIt = topoSortedObjects.rbegin(); objIt != topoSortedObjects.rend(); ++objIt){ // ask the object if it should be recomputed - if ((*objIt)->isTouched() || (*objIt)->mustExecute() == 1){ + bool doRecompute = false; + if ((*objIt)->mustRecompute()) { + doRecompute = true; objectCount++; if (_recomputeFeature(*objIt)) { // if something happened break execution of recompute return -1; } - else{ - (*objIt)->purgeTouched(); - // set all dependent object touched to force recompute - for (auto inObjIt : (*objIt)->getInList()) - inObjIt->touch(); - } + } + + if ((*objIt)->isTouched() || doRecompute) { + (*objIt)->purgeTouched(); + // force recompute of all dependent objects + for (auto inObjIt : (*objIt)->getInList()) + inObjIt->enforceRecompute(); } } #ifdef FC_DEBUG - // check if all objects are recalculated which were thouched + // check if all objects are recalculated which were touched for (auto objectIt : d->objectArray) { if (objectIt->isTouched()) cerr << "Document::recompute(): " << objectIt->getNameInDocument() << " still touched after recompute" << endl; diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index d07ad1bb9b..2446931b3b 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -110,19 +110,65 @@ bool DocumentObject::recomputeFeature() return isValid(); } +/** + * @brief Set this document object touched. + * Touching a document object does not mean to recompute it, it only means that + * other document objects that link it (i.e. its InList) will be recomputed. + * If it should be forced to recompute a document object then use + * \ref enforceRecompute() instead. + */ +void DocumentObject::touch(void) +{ + StatusBits.set(ObjectStatus::Touch); +} + +/** + * @brief Check whether the document object is touched or not. + * @return true if document object is touched, false if not. + */ +bool DocumentObject::isTouched() const +{ + return ExpressionEngine.isTouched() || StatusBits.test(ObjectStatus::Touch); +} + +/** + * @brief Enforces this document object to be recomputed. + * This can be useful to recompute the feature without + * having to change one of its input properties. + */ +void DocumentObject::enforceRecompute(void) +{ + StatusBits.set(ObjectStatus::Enforce); + StatusBits.set(ObjectStatus::Touch); +} + +/** + * @brief Check whether the document object must be recomputed or not. + * This means that the 'Enforce' flag is set or that \ref mustExecute() + * returns a value > 0. + * @return true if document object must be recomputed, false if not. + */ +bool DocumentObject::mustRecompute(void) const +{ + if (StatusBits.test(ObjectStatus::Enforce)) + return true; + + return mustExecute() > 0; +} + short DocumentObject::mustExecute(void) const { - if(isTouched()) + if (ExpressionEngine.isTouched()) return 1; //ask all extensions auto vector = getExtensionsDerivedFromType(); for(auto ext : vector) { - if(ext->extensionMustExecute()) + if (ext->extensionMustExecute()) return 1; } + return 0; - } const char* DocumentObject::getStatusString(void) const @@ -503,21 +549,6 @@ std::vector DocumentObject::getPySubObjects(const std::vector(); } -void DocumentObject::touch(void) -{ - StatusBits.set(ObjectStatus::Touch); -} - -/** - * @brief Check whether the document object is touched or not. - * @return true if document object is touched, false if not. - */ - -bool DocumentObject::isTouched() const -{ - return ExpressionEngine.isTouched() || StatusBits.test(ObjectStatus::Touch); -} - void DocumentObject::Save (Base::Writer &writer) const { if (this->getNameInDocument()) diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index 9b72651312..9917ec2b39 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -51,6 +51,7 @@ enum ObjectStatus { Remove = 5, PythonCall = 6, Destroy = 7, + Enforce = 8, Expand = 16 }; @@ -105,13 +106,18 @@ public: /** Set the property touched -> changed, cause recomputation in Update() */ //@{ - /// set this feature touched (cause recomputation on depndend features) + /// set this document object touched (cause recomputation on dependent features) void touch(void); - /// test if this feature is touched + /// test if this document object is touched bool isTouched(void) const; - /// reset this feature touched + /// Enforce this document object to be recomputed + void enforceRecompute(); + /// Test if this document object must be recomputed + bool mustRecompute(void) const; + /// reset this document object touched void purgeTouched(void) { StatusBits.reset(ObjectStatus::Touch); + StatusBits.reset(ObjectStatus::Enforce); setPropertyStatus(0,false); } /// set this feature to error @@ -187,7 +193,10 @@ public: * If we must recompute the object - to call the method execute(). * 0: no recompution is needed * 1: recompution needed - * -1: the document examine all links of this object and if one is touched -> recompute + * + * @remark If an object is marked as 'touched' then this does not + * necessarily mean that it will be recomputed. It only means that all + * objects that link it (i.e. its InList) will be recomputed. */ virtual short mustExecute(void) const; diff --git a/src/App/DocumentObjectPy.xml b/src/App/DocumentObjectPy.xml index ddc960c41a..4622b71666 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -45,6 +45,11 @@ Mark the object as unchanged + + + Mark the object for recompute + + Register an expression for a property diff --git a/src/App/DocumentObjectPyImp.cpp b/src/App/DocumentObjectPyImp.cpp index 4416f6df7e..2f91d8823a 100644 --- a/src/App/DocumentObjectPyImp.cpp +++ b/src/App/DocumentObjectPyImp.cpp @@ -146,6 +146,14 @@ PyObject* DocumentObjectPy::purgeTouched(PyObject * args) Py_Return; } +PyObject* DocumentObjectPy::enforceRecompute(PyObject * args) +{ + if (!PyArg_ParseTuple(args, "")) // convert args: Python->C + return NULL; // NULL triggers exception + getDocumentObjectPtr()->enforceRecompute(); + Py_Return; +} + Py::List DocumentObjectPy::getState(void) const { DocumentObject* object = this->getDocumentObjectPtr(); diff --git a/src/App/FeatureTest.cpp b/src/App/FeatureTest.cpp index ceb346fdde..b8e385365b 100644 --- a/src/App/FeatureTest.cpp +++ b/src/App/FeatureTest.cpp @@ -121,6 +121,14 @@ FeatureTest::~FeatureTest() } +short FeatureTest::mustExecute(void) const +{ + if (isTouched()) + return 1; + + return DocumentObject::mustExecute(); +} + DocumentObjectExecReturn *FeatureTest::execute(void) { diff --git a/src/App/FeatureTest.h b/src/App/FeatureTest.h index 2cb65bd38e..cf5a0eff14 100644 --- a/src/App/FeatureTest.h +++ b/src/App/FeatureTest.h @@ -104,6 +104,7 @@ public: /** @name methods override Feature */ //@{ + virtual short mustExecute(void) const; /// recalculate the Feature virtual DocumentObjectExecReturn *execute(void); /// returns the type name of the ViewProvider diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index a674b66c7d..4bd71a932d 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -348,7 +348,7 @@ void TreeWidget::onMarkRecompute() App::Document* doc = docitem->document()->getDocument(); std::vector obj = doc->getObjects(); for (std::vector::iterator it = obj.begin(); it != obj.end(); ++it) - (*it)->touch(); + (*it)->enforceRecompute(); } // mark all selected objects else { @@ -357,7 +357,7 @@ void TreeWidget::onMarkRecompute() if ((*it)->type() == ObjectType) { DocumentObjectItem* objitem = static_cast(*it); App::DocumentObject* obj = objitem->object()->getObject(); - obj->touch(); + obj->enforceRecompute(); } } } @@ -1574,9 +1574,9 @@ void DocumentObjectItem::testStatus() // if status has changed then continue int currentStatus = - ((pObject->isError() ? 1 : 0) << 2) | - ((pObject->mustExecute() == 1 ? 1 : 0) << 1) | - (viewObject->isShow() ? 1 : 0); + ((pObject->isError() ? 1 : 0) << 2) | + ((pObject->mustRecompute() == 1 ? 1 : 0) << 1) | + (viewObject->isShow() ? 1 : 0); if (previousStatus == currentStatus) return; previousStatus = currentStatus; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 8879dff1f8..a6fd1fa4b1 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -142,6 +142,17 @@ SketchObject::~SketchObject() delete analyser; } +short SketchObject::mustExecute() const +{ + if (Geometry.isTouched()) + return 1; + if (Constraints.isTouched()) + return 1; + if (ExternalGeometry.isTouched()) + return 1; + return Part2DObject::mustExecute(); +} + App::DocumentObjectExecReturn *SketchObject::execute(void) { try { diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index e1e9cfca61..8cb5df55b8 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -66,6 +66,7 @@ public: App ::PropertyLinkSubList ExternalGeometry; /** @name methods override Feature */ //@{ + short mustExecute() const; /// recalculate the Feature (if no recompute is needed see also solve() and solverNeedsUpdate boolean) App::DocumentObjectExecReturn *execute(void);