Salome HOME
Fix for the issue #1755 : for now the sketch solver receives all modified entities...
[modules/shaper.git] / src / Model / Model_Update.cpp
index 8f5359772248484064731113012ef1be47f7f02e..bb00695b2f8fe2b726a2ab875535d059871d1e20 100755 (executable)
 #include <GeomDataAPI_Point2D.h>
 #include <Events_Loop.h>
 #include <Events_LongOp.h>
-#include <Events_Error.h>
+#include <Events_InfoMessage.h>
 #include <Config_PropManager.h>
 
 using namespace std;
 
 Model_Update MY_UPDATER_INSTANCE;  /// the only one instance initialized on load of the library
-#define DEB_UPDATE
+//#define DEB_UPDATE
 
 Model_Update::Model_Update()
 {
@@ -56,6 +56,8 @@ Model_Update::Model_Update()
   aLoop->registerListener(this, kPreviewBlockedEvent);
   static const Events_ID kPreviewRequestedEvent = aLoop->eventByName(EVENT_PREVIEW_REQUESTED);
   aLoop->registerListener(this, kPreviewRequestedEvent);
+  static const Events_ID kReorderEvent = aLoop->eventByName(EVENT_ORDER_UPDATED);
+  aLoop->registerListener(this, kReorderEvent);
 
   //  Config_PropManager::findProp("Model update", "automatic_rebuild")->value() == "true";
   myIsParamUpdated = false;
@@ -69,9 +71,19 @@ bool Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
   if (!theFeature->data()->isValid())
     return false; // delete an extrusion created on the sketch
 
-  if (theFeature->isPersistentResult()) {
-    if (!std::dynamic_pointer_cast<Model_Document>((theFeature)->document())->executeFeatures())
+  bool isNotExecuted = theFeature->isPersistentResult() &&
+    !std::dynamic_pointer_cast<Model_Document>((theFeature)->document())->executeFeatures();
+  if (isNotExecuted) {
+    if (!theReason.get()) // no reason => no construction reason
       return false;
+    if (myNotPersistentRefs.find(theFeature) == myNotPersistentRefs.end()) {
+      myNotPersistentRefs[theFeature].insert(theReason);
+    } else {
+      std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
+      aNewSet.insert(theReason);
+      myNotPersistentRefs[theFeature] = aNewSet;
+    }
+    return false;
   }
 
   // update arguments for "apply button" state change
@@ -93,13 +105,21 @@ bool Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
     static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
     aFactory->validate(theFeature); // need to be validated to update the "Apply" state if not previewed
 
+    // to redisplay split's arguments presentation, even result is not computed
+    if (!theFeature->isPreviewNeeded()) {
+      static Events_Loop* aLoop = Events_Loop::loop();
+      static const Events_ID kRedisplayEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+      ModelAPI_EventCreator::get()->sendUpdated(theFeature, kRedisplayEvent);
+      aLoop->flush(kRedisplayEvent);
+    }
+
     if (!myIsPreviewBlocked)
       return true;
   }
   if (myModified.find(theFeature) != myModified.end()) {
     if (theReason.get()) {
 #ifdef DEB_UPDATE
-      std::cout<<"*** Add already modified "<<theFeature->name()<<std::endl;
+      //std::cout<<"*** Add already modified "<<theFeature->name()<<" reason "<<theReason->name()<<std::endl;
 #endif
       myModified[theFeature].insert(theReason);
     }
@@ -109,16 +129,21 @@ bool Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
   bool aIsDisabled = theFeature->isDisabled();
   if (!aIsDisabled) {
     std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
-    if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
+    if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated ||
+        theFeature->data()->execState() == ModelAPI_StateInvalidArgument) { // issue 1519
       // do not forget that in this case all were the reasons
       aNewSet.insert(theFeature);
     } else {
       if (theReason.get())
         aNewSet.insert(theReason);
     }
-    myModified[theFeature] = aNewSet;
+      myModified[theFeature] = aNewSet;
 #ifdef DEB_UPDATE
-    std::cout<<"*** Add modified "<<theFeature->name()<<std::endl;
+    if (theReason.get()) {
+      //std::cout<<"*** Add modified "<<theFeature->name()<<" reason "<<theReason->name()<<std::endl;
+    } else {
+      //std::cout<<"*** Add modified "<<theFeature->name()<<std::endl;
+    }
 #endif
   } else { // will be updated during the finish of the operation, or when it becomes enabled
     if (theFeature->data()->execState() == ModelAPI_StateDone)
@@ -126,7 +151,7 @@ bool Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
     else 
       return true; // do not need iteration deeply if it is already marked as modified or so
 #ifdef DEB_UPDATE
-    std::cout<<"*** Set modified state "<<theFeature->name()<<std::endl;
+    //std::cout<<"*** Set modified state "<<theFeature->name()<<std::endl;
 #endif
   }
   // clear processed and fill modified recursively
@@ -178,6 +203,8 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
   static const Events_ID kStabilityEvent = aLoop->eventByName(EVENT_STABILITY_CHANGED);
   static const Events_ID kPreviewBlockedEvent = aLoop->eventByName(EVENT_PREVIEW_BLOCKED);
   static const Events_ID kPreviewRequestedEvent = aLoop->eventByName(EVENT_PREVIEW_REQUESTED);
+  static const Events_ID kReorderEvent = aLoop->eventByName(EVENT_ORDER_UPDATED);
+  static const Events_ID kRedisplayEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
 
 #ifdef DEB_UPDATE
   std::cout<<"****** Event "<<theMessage->eventID().eventText()<<std::endl;
@@ -205,8 +232,13 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
     const std::set<ObjectPtr>& anObjs = aMsg->objects();
     std::set<ObjectPtr>::const_iterator anObjIter = anObjs.cbegin();
     for(; anObjIter != anObjs.cend(); anObjIter++) {
-      if (std::dynamic_pointer_cast<Model_Document>((*anObjIter)->document())->executeFeatures())
-        ModelAPI_EventCreator::get()->sendUpdated(*anObjIter, kUpdatedEvent);
+      if (std::dynamic_pointer_cast<Model_Document>((*anObjIter)->document())->executeFeatures()) {
+        if ((*anObjIter)->groupName() == ModelAPI_Feature::group()) { // results creation means enabling, not update
+          ModelAPI_EventCreator::get()->sendUpdated(*anObjIter, kUpdatedEvent);
+        } else {
+          ModelAPI_EventCreator::get()->sendUpdated(*anObjIter, kRedisplayEvent);
+        }
+      }
     }
     return;
   }
@@ -220,7 +252,7 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
       if (!(*anObjIter)->data()->isValid())
         continue;
 #ifdef DEB_UPDATE
-      std::cout<<">>> in event updated "<<(*anObjIter)->data()->name()<<std::endl;
+      std::cout<<">>> in event updated "<<(*anObjIter)->groupName()<<" "<<(*anObjIter)->data()->name()<<std::endl;
 #endif
       if ((*anObjIter)->groupName() == ModelAPI_ResultParameter::group()) {
         myIsParamUpdated = true;
@@ -289,11 +321,16 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
     // the redisplay signal should be flushed in order to erase the feature presentation in the viewer
     // if should be done after removeFeature() of document,
     // by this reason, upper processFeatures() do not perform this flush
-    Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
+    Events_Loop::loop()->flush(kRedisplayEvent);
 
     // in the end of transaction everything is updated, so clear the old objects
     myIsParamUpdated = false;
     myWaitForFinish.clear();
+  } else if (theMessage->eventID() == kReorderEvent) {
+    std::shared_ptr<ModelAPI_OrderUpdatedMessage> aMsg = 
+      std::dynamic_pointer_cast<ModelAPI_OrderUpdatedMessage>(theMessage);
+    if (aMsg->reordered().get())
+      addModified(aMsg->reordered(), aMsg->reordered()); // to update all attributes
   }
 }
 
@@ -311,10 +348,10 @@ void Model_Update::processFeatures(const bool theFlushRedisplay)
     }
     myIsProcessed = false;
 
-    // flush updates just before "myModification" increment: to distinguish
-    // updates by "execute" produced by this updater and other updates, coming outside,
-    // which are really important for "processEvent" of this updater
+    // to update the object browser if something is updated/created during executions
     static Events_Loop* aLoop = Events_Loop::loop();
+    static const Events_ID kCreatedEvent= aLoop->eventByName(EVENT_OBJECT_CREATED);
+    aLoop->flush(kCreatedEvent);
     static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
     aLoop->flush(kUpdatedEvent);
 
@@ -330,6 +367,41 @@ void Model_Update::processFeatures(const bool theFlushRedisplay)
   }
 }
 
+// collects all the feautres this feature depends on: reasons
+static void allReasons(FeaturePtr theFeature, std::set<FeaturePtr>& theReasons) {
+  std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > > aDeps;
+  theFeature->data()->referencesToObjects(aDeps);
+  std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > >::iterator
+    anAttrsIter = aDeps.begin();
+  for(; anAttrsIter != aDeps.end(); anAttrsIter++) {
+    if (theFeature->attribute(anAttrsIter->first)->isArgument()) {
+      std::list<std::shared_ptr<ModelAPI_Object> >::iterator aDepIter = anAttrsIter->second.begin();
+      for(; aDepIter != anAttrsIter->second.end(); aDepIter++) {
+        FeaturePtr aDepFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(*aDepIter);
+        if (!aDepFeat.get()) { // so, it depends on the result and process the feature owner of it
+          ResultPtr aDepRes = std::dynamic_pointer_cast<ModelAPI_Result>(*aDepIter);
+          if (aDepRes.get()) {
+            aDepFeat = (*aDepIter)->document()->feature(aDepRes);
+          }
+        }
+        if (aDepFeat.get() && aDepFeat->data()->isValid()) {
+          theReasons.insert(aDepFeat);
+        }
+      }
+    }
+  }
+  if (theFeature->getKind() == "Part") { // part is not depended on its subs directly, but subs must be iterated anyway
+    CompositeFeaturePtr aPart = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
+    int aNum = aPart->numberOfSubs();
+    for(int a = 0; a < aNum; a++) {
+      FeaturePtr aSub = aPart->subFeature(a);
+      if (aSub.get() && aSub->data()->isValid()) {
+        theReasons.insert(aSub);
+      }
+    }
+  }
+}
+
 bool Model_Update::processFeature(FeaturePtr theFeature)
 {
   static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
@@ -350,8 +422,8 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
   } else {
     int aCount = myProcessed[theFeature];
     if (aCount > 100) { // too many repetition of processing (in VS it may crash on 330 with stack overflow)
-      Events_Error::send(
-        "Feature '" + theFeature->data()->name() + "' is updated in infinitive loop");
+      Events_InfoMessage("Model_Update",
+        "Feature '%1' is updated in infinitive loop").arg(theFeature->data()->name()).send();
       return false;
     }
     myProcessed[theFeature] = aCount + 1;
@@ -389,6 +461,10 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
       }
     }
     updateArguments(theFeature);
+    // send event that sketch is prepared to be recomputed
+    static Events_ID& anID = Events_Loop::eventByName("SketchPrepared");
+    std::shared_ptr<Events_Message> aMsg(new Events_Message(anID, this));
+    Events_Loop::loop()->send(aMsg);
   }
 
   if (!aIsModified) { // no modification is needed
@@ -402,56 +478,34 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
 
   bool isReferencedInvalid = false;
   // check all features this feature depended on (recursive call of updateFeature)
-  std::set<std::shared_ptr<ModelAPI_Feature> >& aReasons = myModified[theFeature];
-  if (aReasons.find(theFeature) == aReasons.end()) {
-    std::set<std::shared_ptr<ModelAPI_Feature> >::iterator aReasonIter = aReasons.begin();
-    for(; aReasonIter != aReasons.end(); aReasonIter++) {
-      if (*aReasonIter != theFeature) {
-        if (processFeature(*aReasonIter))
-          aIsModified = true;
-        if ((*aReasonIter)->data()->execState() == ModelAPI_StateInvalidArgument)
-          isReferencedInvalid = true;
-      }
-    }
-  } else { // check all features this feature depended on because here which one was modified is unknown
-    std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > > aDeps;
-    theFeature->data()->referencesToObjects(aDeps);
-    std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > >::iterator
-      anAttrsIter = aDeps.begin();
-    for(; anAttrsIter != aDeps.end(); anAttrsIter++) {
-      if (theFeature->attribute(anAttrsIter->first)->isArgument()) {
-        std::list<std::shared_ptr<ModelAPI_Object> >::iterator aDepIter = anAttrsIter->second.begin();
-        for(; aDepIter != anAttrsIter->second.end(); aDepIter++) {
-          FeaturePtr aDepFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(*aDepIter);
-          if (!aDepFeat.get()) { // so, it depends on the result and process the feature owner of it
-            ResultPtr aDepRes = std::dynamic_pointer_cast<ModelAPI_Result>(*aDepIter);
-            if (aDepRes.get()) {
-              aDepFeat = (*aDepIter)->document()->feature(aDepRes);
-            }
-          }
-          if (aDepFeat.get()) {
-            if (processFeature(aDepFeat))
-              aIsModified = true;
-            if (aDepFeat->data()->execState() == ModelAPI_StateInvalidArgument)
-              isReferencedInvalid = true;
-          }
-        }
-      }
-    }
-    if (theFeature->getKind() == "Part") { // part is not depended on its subs directly, but subs must be iterated anyway
-      CompositeFeaturePtr aPart = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
-      int aNum = aPart->numberOfSubs();
-      for(int a = 0; a < aNum; a++) {
-        FeaturePtr aSub = aPart->subFeature(a);
-        if (aSub.get()) {
-          if (processFeature(aSub))
-            aIsModified = true;
-          if (aSub->data()->execState() == ModelAPI_StateInvalidArgument)
-            isReferencedInvalid = true;
-        }
-      }
+  std::set<FeaturePtr>& aReasons = myModified[theFeature];
+  bool allSubsUsed = aReasons.find(theFeature) != aReasons.end();
+  if (allSubsUsed) { // add all subs in aReasons and temporary remove "theFeature" to avoid processing itself
+    allReasons(theFeature, aReasons);
+    aReasons.erase(theFeature);
+  }
+  // take reasons one by one (they may be added during the feature process (circle by the radius of sketch)
+  std::set<FeaturePtr> aProcessedReasons;
+  while(!aReasons.empty()) {
+    FeaturePtr aReason = *(aReasons.begin());
+#ifdef DEB_UPDATE
+    //cout<<theFeature->name()<<" process next reason "<<aReason->name()<<endl;
+#endif
+    if (aReason != theFeature && (aReason)->data()->isValid()) {
+      if (processFeature(aReason))
+        aIsModified = true;
+      if (aReason->data()->execState() == ModelAPI_StateInvalidArgument)
+        isReferencedInvalid = true;
     }
+    // searching for the next not used reason
+    aProcessedReasons.insert(aReason);
+    aReasons.erase(aReason);
+  }
+  // restore the modified reasons: they will be used in the update of arguments
+  if (allSubsUsed) { // restore theFeature in this set
+    aProcessedReasons.insert(theFeature);
   }
+  myModified[theFeature] = aProcessedReasons;
 
   // do not execute the composite that contains the current
   bool isPostponedMain = false;
@@ -462,21 +516,27 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
     isPostponedMain = aCurrentOwner.get() && aCompos->isSub(aCurrentOwner);
   }
 
-  #ifdef DEB_UPDATE
-    std::cout<<"Update args "<<theFeature->name()<<std::endl;
-  #endif
+#ifdef DEB_UPDATE
+  std::cout<<"Update args "<<theFeature->name()<<std::endl;
+#endif
+  // TestImport.py : after arguments are updated, theFeature may be removed
+  if (!theFeature->data()->isValid())
+    return false;
   // Update selection and parameters attributes first, before sub-features analysis (sketch plane).
   updateArguments(theFeature);
 
   // add this feature to the processed right now to be able remove it from this list on
   // update signal during this feature execution
   myModified.erase(theFeature);
+  if (myNotPersistentRefs.find(theFeature) != myNotPersistentRefs.end())
+    myNotPersistentRefs.erase(theFeature);
   if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated)
     theFeature->data()->execState(ModelAPI_StateDone);
 
   // this checking must be after the composite feature sub-elements processing:
   // composite feature status may depend on it's subelements
-  if (theFeature->data()->execState() == ModelAPI_StateInvalidArgument || isReferencedInvalid) {
+  if ((theFeature->data()->execState() == ModelAPI_StateInvalidArgument || isReferencedInvalid) && 
+    theFeature->getKind() != "Part") { // don't disable Part because it will make disabled all the features (performance and problems with the current feature)
   #ifdef DEB_UPDATE
     std::cout<<"Invalid args "<<theFeature->name()<<std::endl;
   #endif
@@ -693,18 +753,33 @@ bool Model_Update::isReason(std::shared_ptr<ModelAPI_Feature>& theFeature,
 {
   std::map<std::shared_ptr<ModelAPI_Feature>, std::set<std::shared_ptr<ModelAPI_Feature> > >
     ::iterator aReasonsIt = myModified.find(theFeature);
-  if (aReasonsIt == myModified.end())
-    return false; // this case only for not-previewed items update state, nothing is changed in args for it
-  if (aReasonsIt->second.find(theFeature) != aReasonsIt->second.end())
-    return true; // any is reason if it contains itself
-  FeaturePtr aReasFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(theReason);
-  if (!aReasFeat.get()) { // try to get feature of this result
-    ResultPtr aReasRes = std::dynamic_pointer_cast<ModelAPI_Result>(theReason);
-    if (aReasRes.get())
-      aReasFeat = theReason->document()->feature(aReasRes);
-  }
-  return aReasonsIt->second.find(aReasFeat) != aReasonsIt->second.end();
+  if (aReasonsIt != myModified.end()) {
+    if (aReasonsIt->second.find(theFeature) != aReasonsIt->second.end())
+      return true; // any is reason if it contains itself
+    FeaturePtr aReasFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(theReason);
+    if (!aReasFeat.get()) { // try to get feature of this result
+      ResultPtr aReasRes = std::dynamic_pointer_cast<ModelAPI_Result>(theReason);
+      if (aReasRes.get())
+        aReasFeat = theReason->document()->feature(aReasRes);
+    }
+    if (aReasonsIt->second.find(aReasFeat) != aReasonsIt->second.end())
+      return true;
+  }
+  // another try: postponed modification by not-persistences
+  std::map<std::shared_ptr<ModelAPI_Feature>, std::set<std::shared_ptr<ModelAPI_Feature> > >
+    ::iterator aNotPersist = myNotPersistentRefs.find(theFeature);
+  if (aNotPersist != myNotPersistentRefs.end()) {
+    FeaturePtr aReasFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(theReason);
+    if (!aReasFeat.get()) { // try to get feature of this result
+      ResultPtr aReasRes = std::dynamic_pointer_cast<ModelAPI_Result>(theReason);
+      if (aReasRes.get())
+        aReasFeat = theReason->document()->feature(aReasRes);
+    }
+    if (aNotPersist->second.find(aReasFeat) != aNotPersist->second.end())
+      return true;
+  }
 
+  return false; // this case only for not-previewed items update state, nothing is changed in args for it
 }
 
 void Model_Update::executeFeature(FeaturePtr theFeature)
@@ -724,8 +799,8 @@ void Model_Update::executeFeature(FeaturePtr theFeature)
     }
   } catch(...) {
     aState = ModelAPI_StateExecFailed;
-    Events_Error::send(
-      "Feature " + theFeature->getKind() + " has failed during the execution");
+    Events_InfoMessage("Model_Update",
+      "Feature %1 has failed during the execution").arg(theFeature->getKind()).send();
   }
   // The macro feature has to be deleted in any case even its execution is failed 
   myWaitForFinish.insert(theFeature);