From d008bec5b0e4aca613a30f5081a86987a2f4137d Mon Sep 17 00:00:00 2001 From: mpv Date: Tue, 7 Apr 2020 14:17:01 +0300 Subject: [PATCH] Implementation of mechanism of publication into the SHAPERSTUDY that propagates modification of results by 1:1 simple features. --- .../ConnectorPlugin_PublishToStudyFeature.py | 103 +++++++++++----- src/Model/Model_ResultBody.cpp | 38 ++++++ src/Model/Model_ResultBody.h | 3 + src/ModelAPI/ModelAPI_ResultBody.h | 3 + src/ModelAPI/ModelAPI_Tools.cpp | 115 ++++++++++++++++++ src/ModelAPI/ModelAPI_Tools.h | 5 + 6 files changed, 236 insertions(+), 31 deletions(-) diff --git a/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py b/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py index 07aa22005..aca23d564 100644 --- a/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py +++ b/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py @@ -58,6 +58,24 @@ class PublishToStudyFeature(ModelAPI.ModelAPI_Feature): def initAttributes(self): pass + ## Computes unique entry for result: partId:featureId[number of result in feature] + def computeEntry(self, theRes): + aDoc = theRes.document() + aFeat = aDoc.feature(theRes) + aFeatId = str(aFeat.data().featureId()) + aRootDoc = ModelAPI.ModelAPI_Session.get().moduleDocument() + aPartFeat = ModelAPI.findPartFeature(aRootDoc, aDoc) + aPartFeatureId = str(aPartFeat.data().featureId()) + aResIndex = 0 + for aRes in aFeat.results(): + if aRes.data().isEqual(theRes.data()): + break + aResIndex += 1 + if aResIndex != 0: + aFeatId += ":" + str(aResIndex) + anEntry = aPartFeatureId + ":" + aFeatId + return anEntry + ## Exports all shapes and groups into the GEOM module. def execute(self): aSession = ModelAPI.ModelAPI_Session.get() @@ -77,7 +95,7 @@ class PublishToStudyFeature(ModelAPI.ModelAPI_Feature): # collect all processed internal entries to break the link of unprocessed later allProcessed = [] - # iterate all parts and all results to publish them in SHAPER_STUDY + # iterate all parts and all results to find all alive items for aPartId in range(aPartSet.size(model.ModelAPI_ResultPart_group())): aPartObject = aPartSet.object(model.ModelAPI_ResultPart_group(), aPartId) aPartRes = ModelAPI.modelAPI_ResultPart(ModelAPI.modelAPI_Result(aPartObject)) @@ -87,18 +105,62 @@ class PublishToStudyFeature(ModelAPI.ModelAPI_Feature): break aPartFeatureId = aPartSet.feature(aPartRes).data().featureId() # Collects all features of exported results to find results of the same features and extend id. - # Map from feature index to index of result. If index is zero (initial), no surrfix to entry is added. - aFeaturesIndices = {} for aResId in range(aPartDoc.size(model.ModelAPI_ResultBody_group())): aResObject = aPartDoc.object(model.ModelAPI_ResultBody_group(), aResId) aRes = model.objectToResult(aResObject) - aResFeatureId = str(aPartDoc.feature(aRes).data().featureId()) - if aResFeatureId in aFeaturesIndices: - aFeaturesIndices[aResFeatureId] += 1 - aResFeatureId += ":" + str(aFeaturesIndices[aResFeatureId]) - else: - aFeaturesIndices[aResFeatureId] = 0 - aSSEntry = str(aPartFeatureId) + ":" + aResFeatureId + aSSEntry = self.computeEntry(aRes) + allProcessed.append(aSSEntry) + + # process all SHAPER-STUDY shapes to find dead of evolved + allNewReferences = [] # entries of new objects that are now referenced by evolved + aSOIter = SHAPERSTUDY_utils.getStudy().NewChildIterator(aComponent) + while aSOIter.More(): + aSO = aSOIter.Value() + aSOIter.Next() # here because there is continue inside the loop + anIOR = aSO.GetIOR() + if len(anIOR): + anObj = salome.orb.string_to_object(anIOR) + if isinstance(anObj, SHAPERSTUDY_ORB._objref_SHAPER_Object): + anEntry = anObj.GetEntry() + if len(anEntry) == 0: + continue; + elif anEntry not in allProcessed: # found a removed shape: make it dead for the moment + # but before check this result was updated to the new result + anEvolutionRes = ModelAPI.singleEvolution(anEntry) + if anEvolutionRes: + # try to find the last evolution + aNewEntry = self.computeEntry(anEvolutionRes) + aNewRes = ModelAPI.singleEvolution(aNewEntry) + while aNewRes: + aNewEntry = self.computeEntry(aNewRes) + aNewRes = ModelAPI.singleEvolution(aNewEntry) + allNewReferences.append(aNewEntry) + anObj.SetEntry(aNewEntry) # set a new entry and a shape + anObj.SetShapeByStream(anEvolutionRes.shape().getShapeStream(False)) + aSO.SetAttrString("AttributeName", anEvolutionRes.data().name()) # also rename + else: # remove the reference - red node + aRes, aSO2 = aSO.FindSubObject(1) + if aRes: + aRes, aRef = aSO2.ReferencedObject() + if aRes: + aBuilder = SHAPERSTUDY_utils.getStudy().NewBuilder() + aBuilder.RemoveReference(aSO2) + + # iterate all parts and all results to publish them in SHAPER_STUDY + for aPartId in range(aPartSet.size(model.ModelAPI_ResultPart_group())): + aPartObject = aPartSet.object(model.ModelAPI_ResultPart_group(), aPartId) + aPartRes = ModelAPI.modelAPI_ResultPart(ModelAPI.modelAPI_Result(aPartObject)) + aPartDoc = aPartRes.partDoc() + if aPartDoc is None and aPartObject is not None: + break + aPartFeatureId = aPartSet.feature(aPartRes).data().featureId() + # Collects all features of exported results to find results of the same features and extend id. + for aResId in range(aPartDoc.size(model.ModelAPI_ResultBody_group())): + aResObject = aPartDoc.object(model.ModelAPI_ResultBody_group(), aResId) + aRes = model.objectToResult(aResObject) + aSSEntry = self.computeEntry(aRes) + if aSSEntry in allNewReferences: + continue aSShape = anEngine.FindOrCreateShape(aSSEntry) aSShape.SetShapeByStream(aRes.shape().getShapeStream(False)) if not aSShape.GetSO(): # publish in case it is a new shape @@ -116,27 +178,6 @@ class PublishToStudyFeature(ModelAPI.ModelAPI_Feature): # Fields self.processGroups(aRes, anEngine, aPartFeatureId, aSShape, True) - # process all SHAPER-STUDY shapes to find dead - aSOIter = SHAPERSTUDY_utils.getStudy().NewChildIterator(aComponent) - while aSOIter.More(): - aSO = aSOIter.Value() - aSOIter.Next() ### here because there is continue inside the loop! - anIOR = aSO.GetIOR() - if len(anIOR): - anObj = salome.orb.string_to_object(anIOR) - if isinstance(anObj, SHAPERSTUDY_ORB._objref_SHAPER_Object): - anEntry = anObj.GetEntry() - if len(anEntry) == 0: - continue; - elif anEntry not in allProcessed: # found a removed shape: make it dead for the moment - # remove the reference - red node - aRes, aSO2 = aSO.FindSubObject(1) - if aRes: - aRes, aRef = aSO2.ReferencedObject() - if aRes: - aBuilder = SHAPERSTUDY_utils.getStudy().NewBuilder() - aBuilder.RemoveReference(aSO2) - # Part of the "execute" method: processes the Groups of theRes result publication. # If theFields is true, the same is performed for Fields. def processGroups(self, theRes, theEngine, thePartFeatureId, theStudyShape, theFields): diff --git a/src/Model/Model_ResultBody.cpp b/src/Model/Model_ResultBody.cpp index 62b47c7cb..b7bf78229 100644 --- a/src/Model/Model_ResultBody.cpp +++ b/src/Model/Model_ResultBody.cpp @@ -31,10 +31,15 @@ #include #include +#include +#include +#include +#include #include #include #include #include +#include // if this attribute exists, the shape is connected topology Standard_GUID kIsConnectedTopology("e51392e0-3a4d-405d-8e36-bbfe19858ef5"); @@ -449,3 +454,36 @@ void Model_ResultBody::computeOldForSub(const GeomShapePtr& theSub, } } } + +void Model_ResultBody::modifiedSources(std::list& theSources) +{ + TDF_Label aLab = std::dynamic_pointer_cast(data())->shapeLab(); + TDF_LabelMap aResMap; + if (aLab.IsAttribute(TNaming_NamedShape::GetID())) { + for(TNaming_Iterator anIter(aLab); anIter.More(); anIter.Next()) { + TopoDS_Shape anOld = anIter.OldShape(); + if (!anOld.IsNull() && TNaming_Tool::HasLabel(aLab, anOld)) { + // searching where this old shape is a new shape + for(TNaming_SameShapeIterator aNewIter(anOld, aLab); aNewIter.More(); aNewIter.Next()) { + for(TNaming_Iterator aNSIter(aNewIter.Label()); aNSIter.More(); aNSIter.Next()) { + if (!aNSIter.NewShape().IsNull() && aNSIter.NewShape().IsSame(anOld)) { + aResMap.Add(aNewIter.Label()); + break; + } + } + } + } + } + } else if (aLab.IsAttribute(TDF_Reference::GetID())) { // check also simple reference to the origin + Handle(TDF_Reference) aRef; + aLab.FindAttribute(TDF_Reference::GetID(), aRef); + aResMap.Add(aRef->Get()); + } + + std::shared_ptr aDoc = std::dynamic_pointer_cast(document()); + for(TDF_MapIteratorOfLabelMap aLabIter(aResMap); aLabIter.More(); aLabIter.Next()) { + ResultPtr aRes = aDoc->resultByLab(aLabIter.Value()); + if (aRes.get()) + theSources.push_back(aRes); + } +} diff --git a/src/Model/Model_ResultBody.h b/src/Model/Model_ResultBody.h index d0a1f908e..d87918a98 100644 --- a/src/Model/Model_ResultBody.h +++ b/src/Model/Model_ResultBody.h @@ -113,6 +113,9 @@ public: /// Cleans cash related to the already stored elements MODEL_EXPORT virtual void cleanCash() override; + /// Returns in the set results that are stored as modified or generated in this result + MODEL_EXPORT virtual void modifiedSources(std::list& theSources) override; + protected: /// Makes a body on the given feature Model_ResultBody(); diff --git a/src/ModelAPI/ModelAPI_ResultBody.h b/src/ModelAPI/ModelAPI_ResultBody.h index 7b9b31778..6026388df 100644 --- a/src/ModelAPI/ModelAPI_ResultBody.h +++ b/src/ModelAPI/ModelAPI_ResultBody.h @@ -183,6 +183,9 @@ public: /// Cleans cash related to the already stored elements MODELAPI_EXPORT virtual void cleanCash() = 0; + /// Returns in the set results that are stored as modified or generated in this result + MODELAPI_EXPORT virtual void modifiedSources(std::list& theSources) = 0; + protected: /// Default constructor accessible only from Model_Objects MODELAPI_EXPORT ModelAPI_ResultBody(); diff --git a/src/ModelAPI/ModelAPI_Tools.cpp b/src/ModelAPI/ModelAPI_Tools.cpp index 8a19702d6..de4c97ccd 100644 --- a/src/ModelAPI/ModelAPI_Tools.cpp +++ b/src/ModelAPI/ModelAPI_Tools.cpp @@ -1102,6 +1102,121 @@ std::list referencedFeatures( return aResList; } +std::shared_ptr singleEvolution(const std::string theEntry) +{ + ResultPtr aRes; + std::size_t aFirstColon = theEntry.find(":"); + if (aFirstColon == std::string::npos) + return aRes; + // searching a part + std::string aPartIdStr = theEntry.substr(0, aFirstColon); + int aPartId = std::stoi(aPartIdStr); + SessionPtr aSession = ModelAPI_Session::get(); + DocumentPtr aRoot = aSession->moduleDocument(); + int aRootFeatures = aRoot->size(ModelAPI_Feature::group()); + FeaturePtr aPartFeat; + for(int aRootId = 0; aRootId < aRootFeatures; aRootId++) { + ObjectPtr aRootObj = aRoot->object(ModelAPI_Feature::group(), aRootId); + FeaturePtr aRootFeat = std::dynamic_pointer_cast(aRootObj); + if (aRootFeat.get()) { + DataPtr aData = aRootFeat->data(); + if (aData.get() && aData->isValid() && aData->featureId() == aPartId) { + aPartFeat = aRootFeat; + break; + } + } + } + if (!aPartFeat.get() || aPartFeat->results().empty()) + return aRes; + DocumentPtr aPartDoc = std::dynamic_pointer_cast(aPartFeat->firstResult())->partDoc(); + if (!aPartDoc.get()) + return aRes; + // searching a feature + std::string aFeatIdStr = theEntry.substr(aFirstColon + 1); + std::string aResIdStr; + std::size_t aSecondColon = aFeatIdStr.find(":"); + if (aSecondColon != std::string::npos) { + aResIdStr = aFeatIdStr.substr(aSecondColon + 1); + aFeatIdStr = aFeatIdStr.substr(0, aSecondColon); + } + int aFeatId = std::stoi(aFeatIdStr); + int aFeaturesNum = aPartDoc->size(ModelAPI_Feature::group()); + FeaturePtr aFeat; + for(int anId = 0; anId < aFeaturesNum; anId++) { + ObjectPtr anObj = aPartDoc->object(ModelAPI_Feature::group(), anId); + aFeat = std::dynamic_pointer_cast(anObj); + if (aFeat.get()) { + DataPtr aData = aFeat->data(); + if (aData.get() && aData->isValid() && aData->featureId() == aFeatId) + break; + aFeat.reset(); + } + } + if (!aFeat.get() || aFeat->results().empty()) + return aRes; + // searching for a result + int aResId = 0; + if (!aResIdStr.empty()) { + aResId = std::stoi(aResIdStr); + } + ResultPtr anOldRes; + std::list::const_iterator aResIt = aFeat->results().cbegin(); + for(; aResIt != aFeat->results().cend(); aResIt++) { + if (aResId == 0) { + anOldRes = *aResIt; + break; + } + aResId--; + } + if (!anOldRes.get() || !anOldRes->shape().get()) + return aRes; + // searching for a new result + std::list allRes; + allResults(aFeat, allRes); + std::list allObjs; + allObjs.push_back(aFeat); + for(std::list::iterator anIter = allRes.begin(); anIter != allRes.end(); anIter++) + allObjs.push_back(*anIter); + ModelAPI_ValidatorsFactory* aValidators = aSession->validators(); + FeaturePtr aConcealer; + for(std::list::iterator anOld = allObjs.begin(); anOld != allObjs.end(); anOld++) { + const std::set& aRefs = (*anOld)->data()->refsToMe(); + std::set::const_iterator aRefIt = aRefs.cbegin(); + for(; aRefIt != aRefs.end(); aRefIt++) { + FeaturePtr aRefFeat = std::dynamic_pointer_cast((*aRefIt)->owner()); + if (!aRefFeat.get()) + continue; + if (!aValidators->isConcealed(aRefFeat->getKind(), (*aRefIt)->id())) + continue; // use only concealed attributes + if (aConcealer.get() && aConcealer != aRefFeat) + return aRes; // multiple concealers + aConcealer = aRefFeat; + } + } + if (!aConcealer.get() || aConcealer->results().size() != 1) + return aRes; // no new features at all or concealer contains may result + if (!aConcealer->firstResult()->shape().get() || aConcealer->firstResult()->shape()->shapeType() != anOldRes->shape()->shapeType()) + return aRes; // shapes in old and new results are not of the same type + aRes = aConcealer->firstResult(); + + ResultBodyPtr aBody = std::dynamic_pointer_cast(aRes); + if (aBody.get()) { + std::list aSources; + aBody->modifiedSources(aSources); + if (aSources.empty()) + return ResultPtr(); // no sources of modification stored in the data tree + std::list::iterator aSourceIter = aSources.begin(); + for(; aSourceIter != aSources.end(); aSourceIter++) { + FeaturePtr aSourceFeat = aPartDoc->feature(*aSourceIter); + if (!aSourceFeat.get()) + return ResultPtr(); // invalid feature or feature from other document + if (aSourceFeat != aFeat) + return ResultPtr(); // modied by other feature + } + } + return aRes; +} + // LCOV_EXCL_STOP } // namespace ModelAPI_Tools diff --git a/src/ModelAPI/ModelAPI_Tools.h b/src/ModelAPI/ModelAPI_Tools.h index a90cd60fa..c1208e63f 100644 --- a/src/ModelAPI/ModelAPI_Tools.h +++ b/src/ModelAPI/ModelAPI_Tools.h @@ -300,6 +300,11 @@ MODELAPI_EXPORT std::list > referencedFeatures std::shared_ptr theTarget, const std::string& theFeatureKind, const bool theSortResults); +/*! Returns the result that was created from theEntry result: only this result is created, same shape type, + * only from this result the result is created. +* \param theEntry string that identifies a result (part feature id):(feature is)[:(result number in the feature)] +*/ +MODELAPI_EXPORT std::shared_ptr singleEvolution(const std::string theEntry); } #endif -- 2.39.2