X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModel%2FModel_Objects.cpp;h=c8e1ca72f1e6f0738d49b03a5084697a04c0ba82;hb=a94fc319f2aa64b43c9a73b5ff7063923648faec;hp=0eab9cf32f8d988b05dc0c321e01f80cbfa253bb;hpb=c39f725bfe98395871c5853dacb89ab1a6ecc6f2;p=modules%2Fshaper.git diff --git a/src/Model/Model_Objects.cpp b/src/Model/Model_Objects.cpp index 0eab9cf32..c8e1ca72f 100644 --- a/src/Model/Model_Objects.cpp +++ b/src/Model/Model_Objects.cpp @@ -1,8 +1,22 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D - -// File: Model_Objects.cxx -// Created: 15 May 2015 -// Author: Mikhail PONIKAROV +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// #include #include @@ -12,13 +26,16 @@ #include #include #include +#include #include +#include #include #include #include +#include #include -#include +#include #include #include @@ -29,6 +46,8 @@ #include #include #include +#include +#include static const int TAG_OBJECTS = 2; // tag of the objects sub-tree (features, results) @@ -49,7 +68,8 @@ void Model_Objects::setOwner(DocumentPtr theDoc) { myDoc = theDoc; // update all fields and recreate features and result objects if needed - synchronizeFeatures(false, true, true); + TDF_LabelList aNoUpdated; + synchronizeFeatures(aNoUpdated, true, true, true, true); myHistory.clear(); } @@ -57,16 +77,27 @@ Model_Objects::~Model_Objects() { // delete all features of this document Events_Loop* aLoop = Events_Loop::loop(); - NCollection_DataMap::Iterator aFeaturesIter(myFeatures); - for(; aFeaturesIter.More(); aFeaturesIter.Next()) { + // erase one by one to avoid access from the feature destructor itself from he map + // blocks the flush signals to avoid the temporary objects visualization in the viewer + // they should not be shown in order to do not lose highlight by erasing them + bool isActive = aLoop->activateFlushes(false); + + while(!myFeatures.IsEmpty()) { + NCollection_DataMap::Iterator aFeaturesIter(myFeatures); FeaturePtr aFeature = aFeaturesIter.Value(); static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); ModelAPI_EventCreator::get()->sendDeleted(myDoc, ModelAPI_Feature::group()); ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP); - aFeature->eraseResults(); + aFeature->removeResults(0, false); aFeature->erase(); + myFeatures.UnBind(aFeaturesIter.Key()); } - myFeatures.Clear(); + myHistory.clear(); + aLoop->activateFlushes(isActive); + // erase update, because features are destroyed and update should not performed for them anywhere + aLoop->eraseMessages(Events_Loop::eventByName(EVENT_OBJECT_CREATED)); + aLoop->eraseMessages(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + // deleted and redisplayed is correctly performed: they know that features are destroyed aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY)); @@ -109,7 +140,7 @@ void Model_Objects::addFeature(FeaturePtr theFeature, const FeaturePtr theAfterT // in initData it creates new features, appeared later than this TDF_Label aPrevFeateureLab; if (theAfterThis.get()) { // searching for the previous feature label - std::shared_ptr aPrevData = + std::shared_ptr aPrevData = std::dynamic_pointer_cast(theAfterThis->data()); if (aPrevData.get()) { aPrevFeateureLab = aPrevData->label().Father(); @@ -117,15 +148,24 @@ void Model_Objects::addFeature(FeaturePtr theFeature, const FeaturePtr theAfterT } AddToRefArray(aFeaturesLab, aFeatureLab, aPrevFeateureLab); - initData(theFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS); // keep the feature ID to restore document later correctly TDataStd_Comment::Set(aFeatureLab, theFeature->getKind().c_str()); myFeatures.Bind(aFeatureLab, theFeature); - // event: feature is added + // must be before the event sending: for OB the feature is already added + updateHistory(ModelAPI_Feature::group()); + // do not change the order: + // initData() + // sendUpdated() + // during python script with fillet constraint feature data should be + // initialized before using it in GUI + + // must be after binding to the map because of "Box" macro feature that + // creates other features in "initData" + initData(theFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS); + // event: feature is added, mist be before "initData" to update OB correctly on Duplicate: + // first new part, then the content static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED); ModelAPI_EventCreator::get()->sendUpdated(theFeature, anEvent); - theFeature->setDisabled(false); // by default created feature is enabled - updateHistory(ModelAPI_Feature::group()); } else { // make feature has not-null data anyway theFeature->setData(Model_Data::invalidData()); theFeature->setDoc(myDoc); @@ -135,7 +175,7 @@ void Model_Objects::addFeature(FeaturePtr theFeature, const FeaturePtr theAfterT /// Appends to the array of references a new referenced label. /// If theIndex is not -1, removes element at this index, not theReferenced. /// \returns the index of removed element -static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, +static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, const int theIndex = -1) { int aResult = -1; // no returned @@ -169,15 +209,17 @@ void Model_Objects::refsToFeature(FeaturePtr theFeature, { // check the feature: it must have no depended objects on it // the dependencies can be in the feature results - std::list::const_iterator aResIter = theFeature->results().cbegin(); - for(; aResIter != theFeature->results().cend(); aResIter++) { + std::list aResults; + ModelAPI_Tools::allResults(theFeature, aResults); + std::list::const_iterator aResIter = aResults.cbegin(); + for (; aResIter != aResults.cend(); aResIter++) { ResultPtr aResult = (*aResIter); - std::shared_ptr aData = - std::dynamic_pointer_cast(aResult->data()); + std::shared_ptr aData = + std::dynamic_pointer_cast(aResult->data()); if (aData.get() != NULL) { const std::set& aRefs = aData->refsToMe(); std::set::const_iterator aRefIt = aRefs.begin(), aRefLast = aRefs.end(); - for(; aRefIt != aRefLast; aRefIt++) { + for (; aRefIt != aRefLast; aRefIt++) { FeaturePtr aFeature = std::dynamic_pointer_cast((*aRefIt)->owner()); if (aFeature.get() != NULL) theRefs.insert(aFeature); @@ -185,12 +227,12 @@ void Model_Objects::refsToFeature(FeaturePtr theFeature, } } // the dependencies can be in the feature itself - std::shared_ptr aData = + std::shared_ptr aData = std::dynamic_pointer_cast(theFeature->data()); - if (aData && !aData->refsToMe().empty()) { + if (aData.get() && !aData->refsToMe().empty()) { const std::set& aRefs = aData->refsToMe(); std::set::const_iterator aRefIt = aRefs.begin(), aRefLast = aRefs.end(); - for(; aRefIt != aRefLast; aRefIt++) { + for (; aRefIt != aRefLast; aRefIt++) { FeaturePtr aFeature = std::dynamic_pointer_cast((*aRefIt)->owner()); if (aFeature.get() != NULL) theRefs.insert(aFeature); @@ -198,23 +240,23 @@ void Model_Objects::refsToFeature(FeaturePtr theFeature, } if (!theRefs.empty() && isSendError) { - Events_Error::send( - "Feature '" + theFeature->data()->name() + "' is used and can not be deleted"); + Events_InfoMessage("Model_Objects", + "Feature '%1' is used and can not be deleted").arg(theFeature->data()->name()).send(); } } void Model_Objects::removeFeature(FeaturePtr theFeature) { std::shared_ptr aData = std::static_pointer_cast(theFeature->data()); - if (aData && aData->isValid()) { + if (aData.get() && aData->isValid()) { // checking that the sub-element of composite feature is removed: if yes, inform the owner std::set > aRefs; refsToFeature(theFeature, aRefs, false); std::set >::iterator aRefIter = aRefs.begin(); for(; aRefIter != aRefs.end(); aRefIter++) { - std::shared_ptr aComposite = + std::shared_ptr aComposite = std::dynamic_pointer_cast(*aRefIter); - if (aComposite.get()) { + if (aComposite.get() && aComposite->isSub(theFeature)) { aComposite->removeFeature(theFeature); } } @@ -237,24 +279,50 @@ void Model_Objects::removeFeature(FeaturePtr theFeature) RemoveFromRefArray(featuresLabel(), aFeatureLabel); // event: feature is deleted ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group()); - // the redisplay signal should be flushed in order to erase the feature presentation in the viewer - Events_Loop::loop()->flush(EVENT_DISP); updateHistory(ModelAPI_Feature::group()); } } +void Model_Objects::eraseAllFeatures() +{ + static Events_ID kDispEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY); + static const ModelAPI_EventCreator* kCreator = ModelAPI_EventCreator::get(); + // make all features invalid (like deleted) + NCollection_DataMap::Iterator aFIter(myFeatures); + for(; aFIter.More(); aFIter.Next()) { + FeaturePtr aFeature = aFIter.Value(); + std::list aResList; + ModelAPI_Tools::allResults(aFeature, aResList); + std::list::iterator aRIter = aResList.begin(); + for(; aRIter != aResList.end(); aRIter++) { + ResultPtr aRes = *aRIter; + if (aRes && aRes->data()->isValid()) { + kCreator->sendDeleted(myDoc, aRes->groupName()); + kCreator->sendUpdated(aRes, kDispEvent); + aRes->setData(aRes->data()->invalidPtr()); + + } + } + kCreator->sendUpdated(aFeature, kDispEvent); + aFeature->setData(aFeature->data()->invalidPtr()); + } + kCreator->sendDeleted(myDoc, ModelAPI_Feature::group()); + myFeatures.Clear(); // just remove features without modification of DS + updateHistory(ModelAPI_Feature::group()); +} + void Model_Objects::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) { TDF_Label aFeaturesLab = featuresLabel(); Handle(TDataStd_ReferenceArray) aRefs; if (!aFeaturesLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) return; - TDF_Label anAfterLab, aMovedLab = + TDF_Label anAfterLab, aMovedLab = std::dynamic_pointer_cast(theMoved->data())->label().Father(); if (theAfterThis.get()) anAfterLab = std::dynamic_pointer_cast(theAfterThis->data())->label().Father(); - Handle(TDataStd_HLabelArray1) aNewArray = + Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(), aRefs->Upper()); int aPassedMovedFrom = 0; // the prev feature location is found and passed int aPassedMovedTo = 0; // the feature is added and this location is passed @@ -278,10 +346,10 @@ void Model_Objects::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) if (!aPassedMovedFrom || !aPassedMovedTo) {// not found: unknown situation if (!aPassedMovedFrom) { static std::string aMovedFromError("The moved feature is not found"); - Events_Error::send(aMovedFromError); + Events_InfoMessage("Model_Objects", aMovedFromError).send(); } else { static std::string aMovedToError("The 'after' feature for movement is not found"); - Events_Error::send(aMovedToError); + Events_InfoMessage("Model_Objects", aMovedToError).send(); } return; } @@ -289,13 +357,15 @@ void Model_Objects::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) aRefs->SetInternalArray(aNewArray); // update the feature and the history clearHistory(theMoved); - static Events_ID EVENT_UPD = Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED); - ModelAPI_EventCreator::get()->sendUpdated(theMoved, EVENT_UPD); + // make sure all (selection) attributes of moved feature will be updated + static Events_ID kUpdateSelection = Events_Loop::loop()->eventByName(EVENT_UPDATE_SELECTION); + ModelAPI_EventCreator::get()->sendUpdated(theMoved, kUpdateSelection, false); + ModelAPI_EventCreator::get()->sendReordered(theMoved); } void Model_Objects::clearHistory(ObjectPtr theObj) { - if (theObj) { + if (theObj.get()) { const std::string aGroup = theObj->groupName(); std::map >::iterator aHIter = myHistory.find(aGroup); if (aHIter != myHistory.end()) @@ -304,7 +374,7 @@ void Model_Objects::clearHistory(ObjectPtr theObj) FeaturePtr aFeature = std::dynamic_pointer_cast(theObj); std::string aResultGroup = featureResultGroup(aFeature); if (!aResultGroup.empty()) { - std::map >::iterator aHIter = + std::map >::iterator aHIter = myHistory.find(aResultGroup); if (aHIter != myHistory.end()) myHistory.erase(aHIter); // erase from map => this means that it is not synchronized @@ -317,8 +387,7 @@ void Model_Objects::createHistory(const std::string& theGroupID) { std::map >::iterator aHIter = myHistory.find(theGroupID); if (aHIter == myHistory.end()) { - myHistory[theGroupID] = std::vector(); - std::vector& aResult = myHistory[theGroupID]; + std::vector aResult = std::vector(); // iterate the array of references and get feature by feature from the array bool isFeature = theGroupID == ModelAPI_Feature::group(); Handle(TDataStd_ReferenceArray) aRefs; @@ -326,24 +395,35 @@ void Model_Objects::createHistory(const std::string& theGroupID) for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) { FeaturePtr aFeature = feature(aRefs->Value(a)); if (aFeature.get()) { + // if feature is in sub-component, remove it from history: + // it is in sub-tree of sub-component + bool isSub = ModelAPI_Tools::compositeOwner(aFeature).get() != NULL; if (isFeature) { // here may be also disabled features - if (aFeature->isInHistory()) { + if (!isSub && aFeature->isInHistory()) { aResult.push_back(aFeature); } } else if (!aFeature->isDisabled()) { // iterate all results of not-disabled feature - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.begin(); - for (; aRIter != aResults.cend(); aRIter++) { - ResultPtr aRes = *aRIter; - if (aRes->groupName() != theGroupID) break; // feature have only same group results - if (!aRes->isDisabled() && aRes->isInHistory() && !aRes->isConcealed()) { - aResult.push_back(*aRIter); + // construction results of sub-features should not be in the tree + if (!isSub || theGroupID != ModelAPI_ResultConstruction::group()) { + // do not use reference to the list here since results can be changed by "isConcealed" + const std::list > aResults = aFeature->results(); + std::list >::const_iterator + aRIter = aResults.begin(); + for (; aRIter != aResults.cend(); aRIter++) { + ResultPtr aRes = *aRIter; + if (aRes->groupName() != theGroupID) break; // feature have only same group results + if (!aRes->isDisabled() && aRes->isInHistory() && !aRes->isConcealed()) { + aResult.push_back(*aRIter); + } } } } } } } + // to be sure that isConcealed did not update the history (issue 1089) during the iteration + if (myHistory.find(theGroupID) == myHistory.end()) + myHistory[theGroupID] = aResult; } } @@ -370,18 +450,40 @@ ObjectPtr Model_Objects::object(TDF_Label theLabel) { // try feature by label FeaturePtr aFeature = feature(theLabel); - if (aFeature) + if (aFeature.get()) return feature(theLabel); TDF_Label aFeatureLabel = theLabel.Father().Father(); // let's suppose it is result aFeature = feature(aFeatureLabel); - if (aFeature) { + bool isSubResult = false; + if (!aFeature.get() && aFeatureLabel.Depth() > 1) { // let's suppose this is sub-result of result + aFeatureLabel = aFeatureLabel.Father().Father(); + aFeature = feature(aFeatureLabel); + isSubResult = true; + } + if (aFeature.get()) { const std::list >& aResults = aFeature->results(); std::list >::const_iterator aRIter = aResults.cbegin(); for (; aRIter != aResults.cend(); aRIter++) { - std::shared_ptr aResData = std::dynamic_pointer_cast( - (*aRIter)->data()); - if (aResData->label().Father().IsEqual(theLabel)) - return *aRIter; + if (isSubResult) { + ResultCompSolidPtr aCompRes = std::dynamic_pointer_cast(*aRIter); + if (aCompRes.get()) { + int aNumSubs = aCompRes->numberOfSubs(); + for(int a = 0; a < aNumSubs; a++) { + ResultPtr aSub = aCompRes->subResult(a); + if (aSub.get()) { + std::shared_ptr aSubData = std::dynamic_pointer_cast( + aSub->data()); + if (aSubData->label().Father().IsEqual(theLabel)) + return aSub; + } + } + } + } else { + std::shared_ptr aResData = std::dynamic_pointer_cast( + (*aRIter)->data()); + if (aResData->label().Father().IsEqual(theLabel)) + return *aRIter; + } } } return FeaturePtr(); // not found @@ -399,11 +501,38 @@ std::shared_ptr Model_Objects::objectByName( const std::string& theGroupID, const std::string& theName) { createHistory(theGroupID); - std::vector& allObjs = myHistory[theGroupID]; - std::vector::iterator anObjIter = allObjs.begin(); - for(; anObjIter != allObjs.end(); anObjIter++) { - if ((*anObjIter)->data()->name() == theName) - return *anObjIter; + if (theGroupID == ModelAPI_Feature::group()) { // searching among features (in history or not) + std::list > allObjs = allFeatures(); + std::list >::iterator anObjIter = allObjs.begin(); + for(; anObjIter != allObjs.end(); anObjIter++) { + if ((*anObjIter)->data()->name() == theName) + return *anObjIter; + } + } else { // searching among results (concealed or not) + std::list > allObjs = allFeatures(); + std::list >::iterator anObjIter = allObjs.begin(); + for(; anObjIter != allObjs.end(); anObjIter++) { + const std::list >& aResults = (*anObjIter)->results(); + std::list >::const_iterator aRIter = aResults.cbegin(); + for (; aRIter != aResults.cend(); aRIter++) { + if (aRIter->get() && (*aRIter)->groupName() == theGroupID) { + if ((*aRIter)->data()->name() == theName) + return *aRIter; + ResultCompSolidPtr aCompRes = + std::dynamic_pointer_cast(*aRIter); + if (aCompRes.get()) { + int aNumSubs = aCompRes->numberOfSubs(); + for(int a = 0; a < aNumSubs; a++) { + ResultPtr aSub = aCompRes->subResult(a); + if (aSub.get() && aSub->groupName() == theGroupID) { + if (aSub->data()->name() == theName) + return aSub; + } + } + } + } + } + } } // not found return ObjectPtr(); @@ -426,7 +555,7 @@ const int Model_Objects::index(std::shared_ptr theObject) int Model_Objects::size(const std::string& theGroupID) { createHistory(theGroupID); - return myHistory[theGroupID].size(); + return int(myHistory[theGroupID].size()); } void Model_Objects::allResults(const std::string& theGroupID, std::list& theResults) @@ -442,9 +571,10 @@ void Model_Objects::allResults(const std::string& theGroupID, std::listgroupName() != theGroupID) break; // feature have only same group results - if (aRes->isInHistory() && !aRes->isConcealed()) { + // iterate also concealed: ALL RESULTS (for translation parts undo/redo management) + //if (aRes->isInHistory() && !aRes->isConcealed()) { theResults.push_back(*aRIter); - } + //} } } } @@ -463,7 +593,7 @@ void Model_Objects::setUniqueName(FeaturePtr theFeature) return; // not needed, name is already defined std::string aName; // result // first count all features of such kind to start with index = count + 1 - int aNumObjects = 0; + int aNumObjects = -1; // this feature is already in this map NCollection_DataMap::Iterator aFIter(myFeatures); for (; aFIter.More(); aFIter.Next()) { if (aFIter.Value()->getKind() == theFeature->getKind()) @@ -484,6 +614,7 @@ void Model_Objects::setUniqueName(FeaturePtr theFeature) isSameName = (*aRIter)->data()->name() == aName; } } + if (isSameName) { aNumObjects++; std::stringstream aNameStream; @@ -505,14 +636,24 @@ void Model_Objects::initData(ObjectPtr theObj, TDF_Label theLab, const int theTa theObj->setDoc(myDoc); theObj->setData(aData); FeaturePtr aFeature = std::dynamic_pointer_cast(theObj); - if (aFeature) { + if (aFeature.get()) { setUniqueName(aFeature); // must be before "initAttributes" because duplicate part uses name } theObj->initAttributes(); } +std::shared_ptr Model_Objects::featureById(const int theId) +{ + if (theId > 0) { + TDF_Label aLab = featuresLabel().FindChild(theId, Standard_False); + return feature(aLab); + } + return std::shared_ptr(); // not found +} + void Model_Objects::synchronizeFeatures( - const bool theMarkUpdated, const bool theUpdateReferences, const bool theFlush) + const TDF_LabelList& theUpdated, const bool theUpdateReferences, + const bool theExecuteFeatures, const bool theOpen, const bool theFlush) { Model_Document* anOwner = std::dynamic_pointer_cast(myDoc).get(); if (!anOwner) // this may happen on creation of document: nothing there, so nothing to synchronize @@ -527,6 +668,17 @@ void Model_Objects::synchronizeFeatures( static Events_ID aToHideEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); bool isActive = aLoop->activateFlushes(false); + // collect all updated labels map + TDF_LabelMap anUpdatedMap; + TDF_ListIteratorOfLabelList anUpdatedIter(theUpdated); + for(; anUpdatedIter.More(); anUpdatedIter.Next()) { + TDF_Label& aFeatureLab = anUpdatedIter.Value(); + while(aFeatureLab.Depth() > 3) + aFeatureLab = aFeatureLab.Father(); + if (myFeatures.IsBound(aFeatureLab)) + anUpdatedMap.Add(aFeatureLab); + } + // update all objects by checking are they on labels or not std::set aNewFeatures, aKeptFeatures; TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID()); @@ -538,25 +690,44 @@ void Model_Objects::synchronizeFeatures( aFeature = std::dynamic_pointer_cast(ModelAPI_Session::get())->createFeature( TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get()) .ToCString(), anOwner); - if (!aFeature) { // somethig is wrong, most probably, the opened document has invalid structure - Events_Error::send("Invalid type of object in the document"); + if (!aFeature.get()) { + // somethig is wrong, most probably, the opened document has invalid structure + Events_InfoMessage("Model_Objects", "Invalid type of object in the document").send(); aLabIter.Value()->Label().ForgetAllAttributes(); continue; } + aFeature->init(); // this must be before "setData" to redo the sketch line correctly myFeatures.Bind(aFeatureLabel, aFeature); aNewFeatures.insert(aFeature); initData(aFeature, aFeatureLabel, TAG_FEATURE_ARGUMENTS); updateHistory(aFeature); - aFeature->setDisabled(false); // by default created feature is enabled (this allows to recreate the results before "setCurrent" is called) // event: model is updated ModelAPI_EventCreator::get()->sendUpdated(aFeature, aCreateEvent); } else { // nothing is changed, both iterators are incremented aFeature = myFeatures.Find(aFeatureLabel); aKeptFeatures.insert(aFeature); - if (theMarkUpdated) { + if (anUpdatedMap.Contains(aFeatureLabel)) { + if (!theOpen) { // on abort/undo/redo reinitialize attributes if something is changed + std::list > anAttrs = + aFeature->data()->attributes(""); + std::list >::iterator anAttr = anAttrs.begin(); + for(; anAttr != anAttrs.end(); anAttr++) + (*anAttr)->reinit(); + } ModelAPI_EventCreator::get()->sendUpdated(aFeature, anUpdateEvent); + if (aFeature->getKind() == "Parameter") { + // if parameters are changed, update the results (issue 937) + const std::list >& aResults = aFeature->results(); + std::list >::const_iterator aRIter = aResults.begin(); + for (; aRIter != aResults.cend(); aRIter++) { + std::shared_ptr aRes = *aRIter; + if (aRes->data()->isValid() && !aRes->isDisabled()) { + ModelAPI_EventCreator::get()->sendUpdated(aRes, anUpdateEvent); + } + } + } } } } @@ -576,7 +747,9 @@ void Model_Objects::synchronizeFeatures( ModelAPI_EventCreator::get()->sendUpdated(aFeature, aRedispEvent); updateHistory(aFeature); aFeature->erase(); - // unbind after the "erase" call: on abort sketch is removes sub-objects that corrupts aFIter + + // unbind after the "erase" call: on abort sketch + // is removes sub-objects that corrupts aFIter myFeatures.UnBind(aFIter.Key()); // reinitialize iterator because unbind may corrupt the previous order in the map aFIter.Initialize(myFeatures); @@ -587,123 +760,175 @@ void Model_Objects::synchronizeFeatures( if (theUpdateReferences) { synchronizeBackRefs(); } - // update results of the features (after features created because they may be connected, like sketch and sub elements) - // After synchronisation of back references because sketch must be set in sub-elements before "execute" by updateResults - std::list aComposites; // composites must be updated after their subs (issue 360) + // update results of the features (after features created because + // they may be connected, like sketch and sub elements) + // After synchronisation of back references because sketch + // must be set in sub-elements before "execute" by updateResults + std::set aProcessed; // composites must be updated after their subs (issue 360) TDF_ChildIDIterator aLabIter2(featuresLabel(), TDataStd_Comment::GetID()); for (; aLabIter2.More(); aLabIter2.Next()) { TDF_Label aFeatureLabel = aLabIter2.Value()->Label(); if (myFeatures.IsBound(aFeatureLabel)) { // a new feature is inserted FeaturePtr aFeature = myFeatures.Find(aFeatureLabel); - if (std::dynamic_pointer_cast(aFeature).get()) - aComposites.push_back(aFeature); - updateResults(aFeature); + updateResults(aFeature, aProcessed); } } - std::list::iterator aComposite = aComposites.begin(); - for(; aComposite != aComposites.end(); aComposite++) { - updateResults(*aComposite); - } - - // the synchronize should be done after updateResults in order to correct back references of updated results + // the synchronize should be done after updateResults + // in order to correct back references of updated results if (theUpdateReferences) { synchronizeBackRefs(); } - if (theMarkUpdated) { // this means there is no control what was modified => remove history cash + if (!theUpdated.IsEmpty()) { + // this means there is no control what was modified => remove history cash myHistory.clear(); } - anOwner->executeFeatures() = false; + if (theExecuteFeatures) + anOwner->executeFeatures() = false; aLoop->activateFlushes(isActive); if (theFlush) { - aLoop->flush(aCreateEvent); aLoop->flush(aDeleteEvent); + // delete should be emitted before create to reacts to aborted feature + aLoop->flush(aCreateEvent); aLoop->flush(anUpdateEvent); + aLoop->flush(aCreateEvent); // after update of features, there could be results created + aLoop->flush(aDeleteEvent); // or deleted aLoop->flush(aRedispEvent); aLoop->flush(aToHideEvent); } - anOwner->executeFeatures() = true; + if (theExecuteFeatures) + anOwner->executeFeatures() = true; } -void Model_Objects::synchronizeBackRefs() +/// synchronises back references for the given object basing on the collected data +void Model_Objects::synchronizeBackRefsForObject(const std::set& theNewRefs, + ObjectPtr theObject) { - // keeps the concealed flags of result to catch the change and create created/deleted events - std::list > aConcealed; - // first cycle: erase all data about back-references - NCollection_DataMap::Iterator aFeatures(myFeatures); - for(; aFeatures.More(); aFeatures.Next()) { - FeaturePtr aFeature = aFeatures.Value(); - std::shared_ptr aFData = - std::dynamic_pointer_cast(aFeature->data()); - if (aFData) { - aFData->eraseBackReferences(); + if (!theObject.get() || !theObject->data()->isValid()) + return; // invalid + std::shared_ptr aData = std::dynamic_pointer_cast(theObject->data()); + // iterate new list to compare with curent + std::set::iterator aNewIter = theNewRefs.begin(); + for(; aNewIter != theNewRefs.end(); aNewIter++) { + if (aData->refsToMe().find(*aNewIter) == aData->refsToMe().end()) { + FeaturePtr aRefFeat = std::dynamic_pointer_cast((*aNewIter)->owner()); + aData->addBackReference(aRefFeat, (*aNewIter)->id()); } - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.begin(); - for (; aRIter != aResults.cend(); aRIter++) { - std::shared_ptr aResData = - std::dynamic_pointer_cast((*aRIter)->data()); - if (aResData) { - aConcealed.push_back(std::pair(*aRIter, (*aRIter)->isConcealed())); - aResData->eraseBackReferences(); - } + } + if (theNewRefs.size() != aData->refsToMe().size()) { // some back ref must be removed + std::set::iterator aCurrentIter = aData->refsToMe().begin(); + while(aCurrentIter != aData->refsToMe().end()) { + if (theNewRefs.find(*aCurrentIter) == theNewRefs.end()) { + // for external references from other documents this system + // is not working: refs are collected from + // different Model_Objects, so before remove check this + // external object exists and still referenced + bool aLeaveIt = false; + if ((*aCurrentIter)->owner().get() && (*aCurrentIter)->owner()->document() != myDoc && + (*aCurrentIter)->owner()->data().get() && (*aCurrentIter)->owner()->data()->isValid()) { + std::list > > > aRefs; + (*aCurrentIter)->owner()->data()->referencesToObjects(aRefs); + std::list >>>::iterator + aRefIter = aRefs.begin(); + for(; aRefIter != aRefs.end(); aRefIter++) { + if ((*aCurrentIter)->id() == aRefIter->first) { + std::list >::iterator anOIt; + for(anOIt = aRefIter->second.begin(); anOIt != aRefIter->second.end(); anOIt++) { + if (*anOIt == theObject) { + aLeaveIt = true; + } + } + } + } + } + if (!aLeaveIt) { + aData->removeBackReference(*aCurrentIter); + aCurrentIter = aData->refsToMe().begin(); // reinitialize iteration after delete + } else aCurrentIter++; + } else aCurrentIter++; } } + aData->updateConcealmentFlag(); +} - // second cycle: set new back-references: only features may have reference, iterate only them - ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators(); - for(aFeatures.Initialize(myFeatures); aFeatures.More(); aFeatures.Next()) { +void Model_Objects::synchronizeBackRefs() +{ + // collect all back references in the separated container: to update everything at once, + // without additional Concealment switchin on and off: only the final modification + + // referenced (slave) objects to referencing attirbutes + std::map > allRefs; + NCollection_DataMap::Iterator aFeatures(myFeatures); + for(; aFeatures.More(); aFeatures.Next()) { FeaturePtr aFeature = aFeatures.Value(); - std::shared_ptr aFData = - std::dynamic_pointer_cast(aFeature->data()); - if (aFData) { + std::shared_ptr aFData = std::dynamic_pointer_cast(aFeature->data()); + if (aFData.get()) { std::list > > aRefs; aFData->referencesToObjects(aRefs); - std::list > >::iterator - aRefsIter = aRefs.begin(); - for(; aRefsIter != aRefs.end(); aRefsIter++) { - std::list::iterator aRefTo = aRefsIter->second.begin(); - for(; aRefTo != aRefsIter->second.end(); aRefTo++) { + std::list > >::iterator aRefsIt = aRefs.begin(); + for(; aRefsIt != aRefs.end(); aRefsIt++) { + std::list::iterator aRefTo = aRefsIt->second.begin(); + for(; aRefTo != aRefsIt->second.end(); aRefTo++) { if (*aRefTo) { - std::shared_ptr aRefData = - std::dynamic_pointer_cast((*aRefTo)->data()); - aRefData->addBackReference(aFeature, aRefsIter->first); // here the Concealed flag is updated - // update enable/disable status: the nested status must be equal to the composite - CompositeFeaturePtr aComp = - std::dynamic_pointer_cast(aFeature); - if (aComp.get()) { - FeaturePtr aReferenced = std::dynamic_pointer_cast(*aRefTo); - if (aReferenced.get()) { - aReferenced->setDisabled(aComp->isDisabled()); - } + std::map >::iterator aFound = allRefs.find(*aRefTo); + if (aFound == allRefs.end()) { + allRefs[*aRefTo] = std::set(); + aFound = allRefs.find(*aRefTo); } + aFound->second.insert(aFeature->data()->attribute(aRefsIt->first)); } } } } } - std::list >::iterator aCIter = aConcealed.begin(); - for(; aCIter != aConcealed.end(); aCIter++) { - if (aCIter->first->isConcealed() != aCIter->second) { // somethign is changed => produce event - if (aCIter->second) { // was concealed become not => creation event - static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED); - ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, anEvent); - } else { // was not concealed become concealed => delete event - ModelAPI_EventCreator::get()->sendDeleted(myDoc, aCIter->first->groupName()); - // redisplay for the viewer (it must be disappeared also) - static Events_ID EVENT_DISP = - Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY); - ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, EVENT_DISP); + // second iteration: just compare back-references with existing in features and results + for(aFeatures.Initialize(myFeatures); aFeatures.More(); aFeatures.Next()) { + FeaturePtr aFeature = aFeatures.Value(); + static std::set anEmpty; + std::map >::iterator aFound = allRefs.find(aFeature); + if (aFound == allRefs.end()) { // not found => erase all back references + synchronizeBackRefsForObject(anEmpty, aFeature); + } else { + synchronizeBackRefsForObject(aFound->second, aFeature); + allRefs.erase(aFound); // to check that all refs are counted + } + // also for results + std::list aResults; + ModelAPI_Tools::allResults(aFeature, aResults); + std::list::iterator aRIter = aResults.begin(); + for(; aRIter != aResults.cend(); aRIter++) { + aFound = allRefs.find(*aRIter); + if (aFound == allRefs.end()) { // not found => erase all back references + synchronizeBackRefsForObject(anEmpty, *aRIter); + } else { + synchronizeBackRefsForObject(aFound->second, *aRIter); + allRefs.erase(aFound); // to check that all refs are counted } } } + for(aFeatures.Initialize(myFeatures); aFeatures.More(); aFeatures.Next()) { + FeaturePtr aFeature = aFeatures.Value(); + std::list aResults; + ModelAPI_Tools::allResults(aFeature, aResults); + // update the concealment status for disply in isConcealed of ResultBody + std::list::iterator aRIter = aResults.begin(); + for(; aRIter != aResults.cend(); aRIter++) { + (*aRIter)->isConcealed(); + } + } + // the rest all refs means that feature references to the external document feature: + // process also them + std::map >::iterator anExtIter = allRefs.begin(); + for(; anExtIter != allRefs.end(); anExtIter++) { + synchronizeBackRefsForObject(anExtIter->second, anExtIter->first); + } } TDF_Label Model_Objects::resultLabel( - const std::shared_ptr& theFeatureData, const int theResultIndex) + const std::shared_ptr& theFeatureData, const int theResultIndex) { - const std::shared_ptr& aData = + const std::shared_ptr& aData = std::dynamic_pointer_cast(theFeatureData); return aData->label().Father().FindChild(TAG_FEATURE_RESULTS).FindChild(theResultIndex + 1); } @@ -712,12 +937,16 @@ void Model_Objects::storeResult(std::shared_ptr theFeatureData, std::shared_ptr theResult, const int theResultIndex) { + theResult->init(); theResult->setDoc(myDoc); initData(theResult, resultLabel(theFeatureData, theResultIndex), TAG_FEATURE_ARGUMENTS); - if (theResult->data()->name().empty()) { // if was not initialized, generate event and set a name + if (theResult->data()->name().empty()) { + // if was not initialized, generate event and set a name std::stringstream aNewName; aNewName<name(); - if (theResultIndex > 0) // if there are several results, add unique prefix starting from second + // if there are several results (issue #899: any number of result), + // add unique prefix starting from second + if (theResultIndex > 0 || theResult->groupName() == ModelAPI_ResultBody::group()) aNewName<<"_"<data()->setName(aNewName.str()); } @@ -730,10 +959,10 @@ std::shared_ptr Model_Objects::createConstruction( TDataStd_Comment::Set(aLab, ModelAPI_ResultConstruction::group().c_str()); ObjectPtr anOldObject = object(aLab); std::shared_ptr aResult; - if (anOldObject) { + if (anOldObject.get()) { aResult = std::dynamic_pointer_cast(anOldObject); } - if (!aResult) { + if (!aResult.get()) { aResult = std::shared_ptr(new Model_ResultConstruction); storeResult(theFeatureData, aResult, theIndex); } @@ -744,14 +973,27 @@ std::shared_ptr Model_Objects::createBody( const std::shared_ptr& theFeatureData, const int theIndex) { TDF_Label aLab = resultLabel(theFeatureData, theIndex); - TDataStd_Comment::Set(aLab, ModelAPI_ResultBody::group().c_str()); - ObjectPtr anOldObject = object(aLab); + // for feature create compsolid, but for result sub create body: + // only one level of recursion is supported now + ResultPtr aResultOwner = std::dynamic_pointer_cast(theFeatureData->owner()); + ObjectPtr anOldObject; + if (aResultOwner.get()) { + TDataStd_Comment::Set(aLab, ModelAPI_ResultBody::group().c_str()); + } else { // in compsolid (higher level result) old object probably may be found + TDataStd_Comment::Set(aLab, ModelAPI_ResultCompSolid::group().c_str()); + anOldObject = object(aLab); + } std::shared_ptr aResult; - if (anOldObject) { + if (anOldObject.get()) { aResult = std::dynamic_pointer_cast(anOldObject); } - if (!aResult) { - aResult = std::shared_ptr(new Model_ResultBody); + if (!aResult.get()) { + // create compsolid anyway; if it is compsolid, it will create sub-bodies internally + if (aResultOwner.get()) { + aResult = std::shared_ptr(new Model_ResultBody); + } else { + aResult = std::shared_ptr(new Model_ResultCompSolid); + } storeResult(theFeatureData, aResult, theIndex); } return aResult; @@ -764,10 +1006,10 @@ std::shared_ptr Model_Objects::createPart( TDataStd_Comment::Set(aLab, ModelAPI_ResultPart::group().c_str()); ObjectPtr anOldObject = object(aLab); std::shared_ptr aResult; - if (anOldObject) { + if (anOldObject.get()) { aResult = std::dynamic_pointer_cast(anOldObject); } - if (!aResult) { + if (!aResult.get()) { aResult = std::shared_ptr(new Model_ResultPart); storeResult(theFeatureData, aResult, theIndex); } @@ -775,18 +1017,11 @@ std::shared_ptr Model_Objects::createPart( } std::shared_ptr Model_Objects::copyPart( - const std::shared_ptr& theOldPart, - const std::shared_ptr& theOrigin, const int theIndex) + const std::shared_ptr& theOrigin, + const std::shared_ptr& theFeatureData, const int theIndex) { - std::shared_ptr aResult; - if (theOldPart.get()) { - aResult = std::dynamic_pointer_cast(theOldPart); - } - if (!aResult) { - aResult = std::shared_ptr(new Model_ResultPart); - aResult->setDoc(myDoc); - aResult->setData(theOrigin->data()); - } + std::shared_ptr aResult = createPart(theFeatureData, theIndex); + aResult->data()->reference(Model_ResultPart::BASE_REF_ID())->setValue(theOrigin); return aResult; } @@ -797,16 +1032,33 @@ std::shared_ptr Model_Objects::createGroup( TDataStd_Comment::Set(aLab, ModelAPI_ResultGroup::group().c_str()); ObjectPtr anOldObject = object(aLab); std::shared_ptr aResult; - if (anOldObject) { + if (anOldObject.get()) { aResult = std::dynamic_pointer_cast(anOldObject); } - if (!aResult) { + if (!aResult.get()) { aResult = std::shared_ptr(new Model_ResultGroup(theFeatureData)); storeResult(theFeatureData, aResult, theIndex); } return aResult; } +std::shared_ptr Model_Objects::createField( + const std::shared_ptr& theFeatureData, const int theIndex) +{ + TDF_Label aLab = resultLabel(theFeatureData, theIndex); + TDataStd_Comment::Set(aLab, ModelAPI_ResultField::group().c_str()); + ObjectPtr anOldObject = object(aLab); + std::shared_ptr aResult; + if (anOldObject.get()) { + aResult = std::dynamic_pointer_cast(anOldObject); + } + if (!aResult.get()) { + aResult = std::shared_ptr(new Model_ResultField(theFeatureData)); + storeResult(theFeatureData, aResult, theIndex); + } + return aResult; +} + std::shared_ptr Model_Objects::createParameter( const std::shared_ptr& theFeatureData, const int theIndex) { @@ -814,10 +1066,10 @@ std::shared_ptr Model_Objects::createParameter( TDataStd_Comment::Set(aLab, ModelAPI_ResultParameter::group().c_str()); ObjectPtr anOldObject = object(aLab); std::shared_ptr aResult; - if (anOldObject) { + if (anOldObject.get()) { aResult = std::dynamic_pointer_cast(anOldObject); } - if (!aResult) { + if (!aResult.get()) { aResult = std::shared_ptr(new Model_ResultParameter); storeResult(theFeatureData, aResult, theIndex); } @@ -828,9 +1080,14 @@ std::shared_ptr Model_Objects::feature( const std::shared_ptr& theResult) { std::shared_ptr aData = std::dynamic_pointer_cast(theResult->data()); - if (aData) { + if (aData.get()) { TDF_Label aFeatureLab = aData->label().Father().Father().Father(); - return feature(aFeatureLab); + FeaturePtr aFeature = feature(aFeatureLab); + if (!aFeature.get() && aFeatureLab.Depth() > 1) { // this may be sub-result of result + aFeatureLab = aFeatureLab.Father().Father(); + aFeature = feature(aFeatureLab); + } + return aFeature; } return FeaturePtr(); } @@ -851,8 +1108,22 @@ std::string Model_Objects::featureResultGroup(FeaturePtr theFeature) return anEmpty; // not found } -void Model_Objects::updateResults(FeaturePtr theFeature) +void Model_Objects::updateResults(FeaturePtr theFeature, std::set& theProcessed) { + if (theProcessed.find(theFeature) != theProcessed.end()) + return; + theProcessed.insert(theFeature); + // for composites update subs recursively (sketch elements results are needed for the sketch) + CompositeFeaturePtr aComp = std::dynamic_pointer_cast(theFeature); + if (aComp.get() && aComp->getKind() != "Part") { // don't go inside of parts sub-features + // update subs of composites first + int aSubNum = aComp->numberOfSubs(); + for(int a = 0; a < aSubNum; a++) { + FeaturePtr aSub = aComp->subFeature(a); + updateResults(aComp->subFeature(a), theProcessed); + } + } + // for not persistent is will be done by parametric updater automatically //if (!theFeature->isPersistentResult()) return; // check the existing results and remove them if there is nothing on the label @@ -860,7 +1131,8 @@ void Model_Objects::updateResults(FeaturePtr theFeature) while(aResIter != theFeature->results().cend()) { ResultPtr aBody = std::dynamic_pointer_cast(*aResIter); if (aBody.get()) { - if (!aBody->data()->isValid()) { + std::shared_ptr aData = std::dynamic_pointer_cast(aBody->data()); + if (!aData.get() || !aData->isValid() || (!aBody->isDisabled() && aData->isDeleted())) { // found a disappeared result => remove it theFeature->eraseResultFromList(aBody); // start iterate from beginning because iterator is corrupted by removing @@ -874,7 +1146,7 @@ void Model_Objects::updateResults(FeaturePtr theFeature) if (!theFeature->data() || !theFeature->data()->isValid() || theFeature->isDisabled()) return; // check that results are presented on all labels - int aResSize = theFeature->results().size(); + int aResSize = int(theFeature->results().size()); TDF_ChildIterator aLabIter(resultLabel(theFeature->data(), 0).Father()); for(; aLabIter.More(); aLabIter.Next()) { // here must be GUID of the feature @@ -884,28 +1156,29 @@ void Model_Objects::updateResults(FeaturePtr theFeature) TDF_Label anArgLab = aLabIter.Value(); Handle(TDataStd_Comment) aGroup; if (anArgLab.FindAttribute(TDataStd_Comment::GetID(), aGroup)) { - if (aGroup->Get() == ModelAPI_ResultBody::group().c_str()) { + if (aGroup->Get() == ModelAPI_ResultBody::group().c_str() || + aGroup->Get() == ModelAPI_ResultCompSolid::group().c_str()) { aNewBody = createBody(theFeature->data(), aResIndex); } else if (aGroup->Get() == ModelAPI_ResultPart::group().c_str()) { - std::shared_ptr aNewP = createPart(theFeature->data(), aResIndex); + std::shared_ptr aNewP = createPart(theFeature->data(), aResIndex); theFeature->setResult(aNewP, aResIndex); if (!aNewP->partDoc().get()) - theFeature->execute(); // create the part result: it is better to restore the previous result if it is possible - break; + // create the part result: it is better to restore the previous result if it is possible + theFeature->execute(); } else if (aGroup->Get() == ModelAPI_ResultConstruction::group().c_str()) { theFeature->execute(); // construction shapes are needed for sketch solver - break; } else if (aGroup->Get() == ModelAPI_ResultGroup::group().c_str()) { aNewBody = createGroup(theFeature->data(), aResIndex); + } else if (aGroup->Get() == ModelAPI_ResultField::group().c_str()) { + aNewBody = createField(theFeature->data(), aResIndex); } else if (aGroup->Get() == ModelAPI_ResultParameter::group().c_str()) { theFeature->attributeChanged("expression"); // just produce a value - break; } else { - Events_Error::send(std::string("Unknown type of result is found in the document:") + - TCollection_AsciiString(aGroup->Get()).ToCString()); + Events_InfoMessage("Model_Objects", "Unknown type of result is found in the document:") + .arg(TCollection_AsciiString(aGroup->Get()).ToCString()).send(); } } - if (aNewBody) { + if (aNewBody && !aNewBody->data()->isDeleted()) { theFeature->setResult(aNewBody, aResIndex); } } @@ -919,9 +1192,10 @@ ResultPtr Model_Objects::findByName(const std::string theName) FeaturePtr& aFeature = anObjIter.ChangeValue(); if (!aFeature.get() || aFeature->isDisabled()) // may be on close continue; - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.begin(); - for (; aRIter != aResults.cend(); aRIter++) { + std::list allResults; + ModelAPI_Tools::allResults(aFeature, allResults); + std::list::iterator aRIter = allResults.begin(); + for (; aRIter != allResults.cend(); aRIter++) { ResultPtr aRes = *aRIter; if (aRes.get() && aRes->data() && aRes->data()->isValid() && !aRes->isDisabled() && aRes->data()->name() == theName) { @@ -936,12 +1210,13 @@ ResultPtr Model_Objects::findByName(const std::string theName) FeaturePtr Model_Objects::nextFeature(FeaturePtr theCurrent, const bool theReverse) { std::shared_ptr aData = std::static_pointer_cast(theCurrent->data()); - if (aData && aData->isValid()) { + if (aData.get() && aData->isValid()) { TDF_Label aFeatureLabel = aData->label().Father(); Handle(TDataStd_ReferenceArray) aRefs; if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) { for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) { // iterate all existing features - if (aRefs->Value(a).IsEqual(aFeatureLabel)) { + TDF_Label aCurLab = aRefs->Value(a); + if (aCurLab.IsEqual(aFeatureLabel)) { a += theReverse ? -1 : 1; if (a >= aRefs->Lower() && a <= aRefs->Upper()) return feature(aRefs->Value(a)); @@ -971,6 +1246,31 @@ FeaturePtr Model_Objects::lastFeature() return FeaturePtr(); // no features at all } +bool Model_Objects::isLater(FeaturePtr theLater, FeaturePtr theCurrent) const +{ + std::shared_ptr aLaterD = std::static_pointer_cast(theLater->data()); + std::shared_ptr aCurrentD = std::static_pointer_cast(theCurrent->data()); + if (aLaterD.get() && aLaterD->isValid() && aCurrentD.get() && aCurrentD->isValid()) { + TDF_Label aLaterL = aLaterD->label().Father(); + TDF_Label aCurrentL = aCurrentD->label().Father(); + int aLaterI = -1, aCurentI = -1; // not found yet state + Handle(TDataStd_ReferenceArray) aRefs; + if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) { + for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) { // iterate all existing features + TDF_Label aCurLab = aRefs->Value(a); + if (aCurLab.IsEqual(aLaterL)) { + aLaterI = a; + } else if (aCurLab.IsEqual(aCurrentL)) { + aCurentI = a; + } else continue; + if (aLaterI != -1 && aCurentI != -1) // both are found + return aLaterI > aCurentI; + } + } + } + return false; // not found, or something is wrong +} + std::list > Model_Objects::allFeatures() { std::list > aResult;