+ if (!(*anObjIter)->data()->isValid())
+ continue;
+#ifdef DEB_UPDATE
+ std::cout<<">>> in event updated "<<(*anObjIter)->groupName()<<
+ " "<<(*anObjIter)->data()->name()<<std::endl;
+#endif
+ if ((*anObjIter)->groupName() == ModelAPI_ResultParameter::group()) {
+ myIsParamUpdated = true;
+ }
+ // on undo/redo, abort do not update persistent features
+ FeaturePtr anUpdated = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIter);
+ if (anUpdated.get()) {
+ if (addModified(anUpdated, FeaturePtr()))
+ aSomeModified = true;
+ if (myUpdateBlocked) { // execute this feature anyway to show the current result
+ /*if (!anUpdated->isStable() && anUpdated->results().size() && (
+ anUpdated->firstResult()->groupName() == ModelAPI_ResultBody::group() ||
+ anUpdated->firstResult()->groupName() == ModelAPI_ResultPart::group())) {
+ if (aFactory->validate(anUpdated)) {
+ executeFeature(anUpdated);
+ redisplayWithResults(anUpdated, ModelAPI_StateNothing, false);
+ static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+ aLoop->flush(EVENT_DISP);
+ }
+ }*/
+ }
+ } else {
+ // process the updated result as update of features that refers to this result
+ const std::set<std::shared_ptr<ModelAPI_Attribute> >&
+ aRefs = (*anObjIter)->data()->refsToMe();
+ std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.cbegin();
+ FeaturePtr aReason;
+ ResultPtr aReasonResult = std::dynamic_pointer_cast<ModelAPI_Result>(*anObjIter);
+ if (aReasonResult.get())
+ aReason = (*anObjIter)->document()->feature(aReasonResult);
+ for(; aRefIter != aRefs.cend(); aRefIter++) {
+ if (!(*aRefIter)->owner()->data()->isValid())
+ continue;
+ FeaturePtr anUpdated = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
+ if (anUpdated.get()) {
+ if (addModified(anUpdated, aReason))
+ aSomeModified = true;
+ }
+ }
+ }
+ }
+ // this event is for solver update, not here, do not react immediately
+ if (aSomeModified) {
+ processFeatures();
+ }
+ } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent ||
+ theMessage->eventID() == kOpStartEvent) {
+ myIsPreviewBlocked = false;
+
+ if (theMessage->eventID() == kOpFinishEvent) {// if update is blocked, skip
+ myIsFinish = true;
+ // add features that wait for finish as modified
+ std::map<std::shared_ptr<ModelAPI_Feature>, std::set<std::shared_ptr<ModelAPI_Feature> > >::
+ iterator aFeature = myProcessOnFinish.begin();
+ for(; aFeature != myProcessOnFinish.end(); aFeature++) {
+ if (aFeature->first->data()->isValid()) {// there may be already removed while wait
+ if (aFeature->second.empty()) {
+ addModified(aFeature->first, FeaturePtr());
+ continue;
+ }
+ std::set<std::shared_ptr<ModelAPI_Feature> >::iterator aReasons;
+ for(aReasons = aFeature->second.begin(); aReasons != aFeature->second.end(); aReasons++){
+ addModified(aFeature->first, *aReasons);
+ }
+ }
+ }
+ myIsFinish = false;
+ }
+ // processed features must be only on finish, so clear anyway (to avoid re-import on load)
+ myProcessOnFinish.clear();
+
+ // #2156: current must be sketch, left after the macro execution
+ DocumentPtr anActiveDoc = ModelAPI_Session::get()->activeDocument();
+ FeaturePtr aCurrent;
+ if (anActiveDoc.get())
+ aCurrent = anActiveDoc->currentFeature(false);
+
+ if (!(theMessage->eventID() == kOpStartEvent)) {
+ processFeatures(false);
+ }
+
+ if (anActiveDoc.get() && aCurrent.get() && aCurrent->data()->isValid()) {
+ if (anActiveDoc->currentFeature(false) != aCurrent &&
+ ModelAPI_Tools::compositeOwner(anActiveDoc->currentFeature(false)) == aCurrent)
+ anActiveDoc->setCurrentFeature(aCurrent, false); // #2156 make the current feature back
+ }
+
+ // remove all macros before clearing all created
+ std::set<FeaturePtr>::iterator anUpdatedIter = myWaitForFinish.begin();
+ while(anUpdatedIter != myWaitForFinish.end()) {
+ FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anUpdatedIter);
+ if (aFeature.get()) {
+ // remove macro on finish
+ if (aFeature->isMacro()) {
+ aFeature->document()->removeFeature(aFeature);
+ myWaitForFinish.erase(aFeature);
+ }
+ // to avoid the map update problems on "remove"
+ if (myWaitForFinish.find(aFeature) == myWaitForFinish.end()) {
+ anUpdatedIter = myWaitForFinish.begin();
+ } else {
+ anUpdatedIter++;
+ }
+ } else {
+ anUpdatedIter++;
+ }
+ }
+ // 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(kRedisplayEvent);
+
+ // in the end of transaction everything is updated, so clear the old objects
+ //myIsParamUpdated = false; // to avoid problems in sprocket.py parameter update
+ 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
+ }
+}
+
+void Model_Update::processFeatures(const bool theFlushRedisplay)
+{
+ // perform update of everything if it is not performed right now or any preview is blocked
+ if (!myIsProcessed && !myIsPreviewBlocked) {
+ myIsProcessed = true;
+ #ifdef DEB_UPDATE
+ std::cout<<"****** Start processing"<<std::endl;
+ #endif
+
+ while(!myModified.empty()) {
+ processFeature(myModified.begin()->first);
+ }
+ myIsProcessed = false;
+
+ // 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);
+
+ // flush to update display
+ if (theFlushRedisplay) {
+ static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+ aLoop->flush(EVENT_DISP);
+ }
+ #ifdef DEB_UPDATE
+ std::cout<<"****** End processing"<<std::endl;
+ #endif
+ myProcessed.clear();
+ }
+}
+
+// collects all the features 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();
+
+ if (!theFeature->data()->isValid()) { // deleted feature, just remove from all containers
+ if (myModified.find(theFeature) != myModified.end())
+ myModified.erase(theFeature);
+ return false;
+ }
+
+ if (theFeature->isPersistentResult()) {
+ if (!std::dynamic_pointer_cast<Model_Document>((theFeature)->document())->executeFeatures())
+ return false;
+ }
+
+ if (myProcessed.find(theFeature) == myProcessed.end()) {
+ myProcessed[theFeature] = 0;
+ } else {
+ int aCount = myProcessed[theFeature];
+ if (aCount > 100) {
+ // too many repetition of processing (in VS it may crash on 330 with stack overflow)
+ Events_InfoMessage("Model_Update",
+ "Feature '%1' is updated in infinitive loop").arg(theFeature->data()->name()).send();
+ // to stop iteration
+ myModified.clear();
+ return false;
+ }
+ myProcessed[theFeature] = aCount + 1;
+ }
+
+ // check this feature is not yet checked or processed
+ bool aIsModified = myModified.find(theFeature) != myModified.end();
+ if (!aIsModified && myIsFinish) { // get info about the modification for features without preview
+ if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
+ aIsModified = true;
+ std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
+ // contains itself, so, we don't know which was the reason and the reason is any
+ aNewSet.insert(theFeature);
+ myModified[theFeature] = aNewSet;
+ }
+ }
+
+#ifdef DEB_UPDATE
+ std::cout<<"* Process feature "<<theFeature->name()<<std::endl;
+#endif
+
+ // update the sketch plane before the sketch sub-elements are recomputed
+ // (otherwise sketch will update plane, modify subs, after executed, but with old subs edges)
+ if (aIsModified && theFeature->getKind() == "Sketch") {
+#ifdef DEB_UPDATE
+ std::cout << "****** Update sketch args " << theFeature->name() << std::endl;
+#endif
+ AttributeSelectionPtr anExtSel = theFeature->selection("External");
+ if (anExtSel.get()) {
+ ResultPtr aContext = anExtSel->context();
+ if (aContext.get() && aContext->document().get()) {
+ FeaturePtr anExtBase = aContext->document()->feature(aContext);
+ if (anExtBase.get()) {
+ processFeature(anExtBase);
+ }
+ std::shared_ptr<GeomDataAPI_Point> anOrigin =
+ std::dynamic_pointer_cast<GeomDataAPI_Point>(theFeature->attribute("Origin"));
+ double anOX = anOrigin->x(), anOY = anOrigin->y(), anOZ = anOrigin->z();
+ std::shared_ptr<GeomDataAPI_Dir> aDir =
+ std::dynamic_pointer_cast<GeomDataAPI_Dir>(theFeature->attribute("DirX"));
+ double aDX = aDir->x(), aDY = aDir->y(), aDZ = aDir->z();
+ std::shared_ptr<GeomDataAPI_Dir> aNorm =
+ std::dynamic_pointer_cast<GeomDataAPI_Dir>(theFeature->attribute("Norm"));
+ double aNX = aNorm->x(), aNY = aNorm->y(), aNZ = aNorm->z();
+ // update sketch plane
+ updateArguments(theFeature);
+ theFeature->attributeChanged("External"); // to recompute origin, direction and normal
+ // check it is updated, so all must be changed
+ if (anOrigin->x() != anOX || anOrigin->y() != anOY || anOrigin->z() != anOZ ||
+ aDir->x() != aDX || aDir->y() != aDY || aDir->z() != aDZ ||
+ aNorm->x() != aNX || aNorm->y() != aNY || aNorm->z() != aNZ)
+ {
+ std::set<FeaturePtr> aWholeR;
+ allReasons(theFeature, aWholeR);
+ std::set<FeaturePtr>::iterator aRIter = aWholeR.begin();
+ for (; aRIter != aWholeR.end(); aRIter++) {
+ if ((*aRIter)->data()->selection("External").get())
+ (*aRIter)->attributeChanged("External");