From c57b0044525edb117d2b10cca0a931eecb5681f5 Mon Sep 17 00:00:00 2001 From: mpv Date: Wed, 23 Jan 2019 12:14:08 +0300 Subject: [PATCH] High level objects history implementation for Intersection and Compound features. Updates due to unit tests. --- src/BuildPlugin/BuildPlugin_Compound.cpp | 2 +- .../FeaturesPlugin_Intersection.cpp | 3 +- src/FeaturesPlugin/Test/Test1876.py | 2 +- src/FeaturesPlugin/Test/Test1942.py | 14 +- src/Model/Model_AttributeSelection.cpp | 47 +++++- src/Model/Model_BodyBuilder.cpp | 146 +++++++++++------- src/Model/Model_ResultBody.cpp | 25 ++- src/Model/Model_ResultBody.h | 5 +- src/Model/Model_Update.cpp | 2 +- 9 files changed, 175 insertions(+), 71 deletions(-) diff --git a/src/BuildPlugin/BuildPlugin_Compound.cpp b/src/BuildPlugin/BuildPlugin_Compound.cpp index 6d6c0c79f..68997c988 100644 --- a/src/BuildPlugin/BuildPlugin_Compound.cpp +++ b/src/BuildPlugin/BuildPlugin_Compound.cpp @@ -76,7 +76,7 @@ void BuildPlugin_Compound::execute() int anIndexToRemove = 0; if (aCopyCompound) { ResultBodyPtr aResultBody = document()->createBody(data(), anIndexToRemove++); - aResultBody->store(aCopyCompound); + aResultBody->storeModified(anOriginalShapes, aCopyCompound, aCopyAlgo); aResultBody->loadModifiedShapes(aCopyAlgo, aCompound, GeomAPI_Shape::VERTEX); aResultBody->loadModifiedShapes(aCopyAlgo, aCompound, GeomAPI_Shape::EDGE); aResultBody->loadModifiedShapes(aCopyAlgo, aCompound, GeomAPI_Shape::FACE); diff --git a/src/FeaturesPlugin/FeaturesPlugin_Intersection.cpp b/src/FeaturesPlugin/FeaturesPlugin_Intersection.cpp index 93a02c79f..471e0cff1 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Intersection.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Intersection.cpp @@ -83,7 +83,6 @@ void FeaturesPlugin_Intersection::execute() setResult(aResultBody, aResultIndex); aResultIndex++; - // remove the rest results if there were produced in the previous pass removeResults(aResultIndex); } @@ -100,7 +99,7 @@ void FeaturesPlugin_Intersection::loadNamingDS(ResultBodyPtr theResultBody, return; } - theResultBody->storeModified(theObjects.front(), aResultShape); + theResultBody->storeModified(theObjects, aResultShape, theMakeShape); const int aShapeTypesNb = 3; const GeomAPI_Shape::ShapeType aShapeTypes[aShapeTypesNb] = {GeomAPI_Shape::VERTEX, diff --git a/src/FeaturesPlugin/Test/Test1876.py b/src/FeaturesPlugin/Test/Test1876.py index 4b01039f9..47709b5f5 100644 --- a/src/FeaturesPlugin/Test/Test1876.py +++ b/src/FeaturesPlugin/Test/Test1876.py @@ -69,4 +69,4 @@ assert(aList1.value(1).value().shapeTypeStr() == "SOLID") assert(aList2.value(0).value().shapeTypeStr() == "SOLID") assert(aList2.value(1).value().shapeTypeStr() == "SOLID") assert(aList1.value(0).value().isSame(aList2.value(0).value()) or aList1.value(1).value().isSame(aList2.value(0).value()) or -aList1.value(1).value().isSame(aList2.value(0).value()) or aList1.value(1).value().isSame(aList2.value(1).value())) +aList1.value(0).value().isSame(aList2.value(1).value()) or aList1.value(1).value().isSame(aList2.value(1).value())) diff --git a/src/FeaturesPlugin/Test/Test1942.py b/src/FeaturesPlugin/Test/Test1942.py index ada970bc5..efd3de9c8 100644 --- a/src/FeaturesPlugin/Test/Test1942.py +++ b/src/FeaturesPlugin/Test/Test1942.py @@ -184,7 +184,19 @@ Plane_5 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/Axis_4"), Face_1_objects = [model.selection("EDGE", "Sketch_1/SketchArc_2_2"), model.selection("EDGE", "Sketch_1/SketchLine_4"), model.selection("EDGE", "Sketch_1/SketchLine_5"), model.selection("EDGE", "Sketch_1/SketchLine_6"), model.selection("EDGE", "Sketch_1/SketchLine_7"), model.selection("EDGE", "Sketch_1/SketchLine_8"), model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2")] Face_1 = model.addFace(Part_1_doc, Face_1_objects) Intersection_1 = model.addIntersection(Part_1_doc, [model.selection("SOLID", "Recover_1_1"), model.selection("FACE", "Face_1_1")]) -Group_1_objects = [model.selection("VERTEX", "[Intersection_1_1_3/Intersection_1_1_3&Face_1_1/Edge_3][weak_name_2]"), model.selection("VERTEX", "[Intersection_1_1_1/Intersection_1_1_1][weak_name_2]"), model.selection("VERTEX", "[Intersection_1_1_9/Intersection_1_1_9&Face_1_1/Edge_7][weak_name_1]"), model.selection("VERTEX", "[Intersection_1_1_7/Intersection_1_1_7&Face_1_1/Edge_5][weak_name_2]"), model.selection("VERTEX", "[Intersection_1_1_4/Intersection_1_1_4][weak_name_2]"), model.selection("VERTEX", "[Intersection_1_1_1/Intersection_1_1_1][weak_name_1]"), model.selection("VERTEX", "[Intersection_1_1_2/Intersection_1_1_2&Face_1_1/Edge_2][weak_name_1]"), model.selection("VERTEX", "[Intersection_1_1_4/Intersection_1_1_4][weak_name_1]"), model.selection("VERTEX", "[Intersection_1_1_6/Intersection_1_1_6&Face_1_1/Edge_4][weak_name_2]"), model.selection("VERTEX", "[Intersection_1_1_10/Intersection_1_1_10&Face_1_1/Edge_8][weak_name_1]"), model.selection("VERTEX", "[Intersection_1_1_8/Intersection_1_1_8&Face_1_1/Edge_6][weak_name_1]")] +Group_1_objects = [ +model.selection("VERTEX", "[Intersection_1_1_3/Intersection_1_1_3&Face_1_1/Edge_3][weak_name_2]"), +model.selection("VERTEX", "[Intersection_1_1_1/Intersection_1_1_1][weak_name_2]"), +model.selection("VERTEX", "[Intersection_1_1_9/Intersection_1_1_9&Face_1_1/Edge_7][weak_name_1]"), +model.selection("VERTEX", "[Intersection_1_1_7/Intersection_1_1_7&Face_1_1/Edge_5][weak_name_2]"), +model.selection("VERTEX", "[Intersection_1_1_4/Intersection_1_1_4][weak_name_2]"), +model.selection("VERTEX", "[Intersection_1_1_1/Intersection_1_1_1][weak_name_1]"), +model.selection("VERTEX", "[Intersection_1_1_2/Intersection_1_1_2&Face_1_1/Edge_2][weak_name_1]"), +model.selection("VERTEX", "[Intersection_1_1_4/Intersection_1_1_4][weak_name_1]"), +model.selection("VERTEX", "[Intersection_1_1_6/Intersection_1_1_6&Face_1_1/Edge_4][weak_name_2]"), +model.selection("VERTEX", "[Intersection_1_1_10/Intersection_1_1_10&Face_1_1/Edge_8][weak_name_1]"), +model.selection("VERTEX", "[Intersection_1_1_8/Intersection_1_1_8&Face_1_1/Edge_6][weak_name_1]")] + Group_1 = model.addGroup(Part_1_doc, Group_1_objects) Group_2_objects = [model.selection("EDGE", "Intersection_1_1_7"), model.selection("EDGE", "Intersection_1_1_6"), model.selection("EDGE", "Intersection_1_1_3"), model.selection("EDGE", "Intersection_1_1_11"), model.selection("EDGE", "Intersection_1_1_5"), model.selection("EDGE", "Intersection_1_1_1"), model.selection("EDGE", "Intersection_1_1_4"), model.selection("EDGE", "Intersection_1_1_2"), model.selection("EDGE", "Intersection_1_1_10"), model.selection("EDGE", "Intersection_1_1_9"), model.selection("EDGE", "Intersection_1_1_8")] Group_2 = model.addGroup(Part_1_doc, Group_2_objects) diff --git a/src/Model/Model_AttributeSelection.cpp b/src/Model/Model_AttributeSelection.cpp index 6940cdbe1..d4ce0fcd6 100644 --- a/src/Model/Model_AttributeSelection.cpp +++ b/src/Model/Model_AttributeSelection.cpp @@ -1155,6 +1155,32 @@ void Model_AttributeSelection::computeValues( } } if (aToFindPart == 2 && !aNewToOld.IsEmpty()) { + // also iterate the whole old shape to find not-modified shapes that contain this old + TopoDS_Shape anOldContShape = theOldContext->shape()->impl(); + NCollection_Map aNewTypes; // types of shapes to iterate + TopTools_DataMapOfShapeShape::Iterator aNewTypeIter(aNewToOld); + for(; aNewTypeIter.More(); aNewTypeIter.Next()) { + if (aNewTypeIter.Key().ShapeType() != theValShape.ShapeType()) + aNewTypes.Add(aNewTypeIter.Key().ShapeType()); + } + NCollection_Map::Iterator aTypeIter(aNewTypes); + for(; aTypeIter.More(); aTypeIter.Next()) { + for(TopExp_Explorer anExp(anOldContShape, aTypeIter.Value()); anExp.More(); anExp.Next()) { + TopoDS_Shape anOld = anExp.Current(); + if (aNewToOld.IsBound(anOld) || anOlds.Contains(anOld)) // this was modified + continue; + TopExp_Explorer aValExp(anOld, theValShape.ShapeType()); + for(; aValExp.More(); aValExp.Next()) { + const TopoDS_Shape& anUnchanged = aValExp.Current(); + if (anUnchanged.IsSame(theValShape)) { + aNewToOld.Bind(anOld, anOld); + anOlds.Add(anOld); + break; + } + } + } + } + // map of sub-shapes -> number of occurrences of these shapes in containers NCollection_DataMap aSubs; TopTools_DataMapOfShapeShape::Iterator aContIter(aNewToOld); @@ -1174,12 +1200,29 @@ void Model_AttributeSelection::computeValues( aSubsIter(aSubs); for(; aSubsIter.More(); aSubsIter.Next()) { if (aSubsIter.Value().Size() == aCountInOld) { - theShapes.Append(aSubsIter.Key()); + TopoDS_Shape anOld = aSubsIter.Key(); + // check this exists in the new shape + TopExp_Explorer aNew(aNewContShape, anOld.ShapeType()); + for (; aNew.More(); aNew.Next()) { + if (aNew.Current().IsSame(anOld)) + break; + } + if (aNew.More()) + theShapes.Append(anOld); } } } if (theShapes.IsEmpty()) { // nothing was changed - theShapes.Append(aWasWholeContext ? TopoDS_Shape() : theValShape); + if (aWasWholeContext) + theShapes.Append(TopoDS_Shape()); + else { // if theValShape exists in new context, add it without changes, otherwise - nothing + for (TopExp_Explorer aNew(aNewContShape, theValShape.ShapeType()); aNew.More(); aNew.Next()){ + if (aNew.Current().IsSame(theValShape)) { + theShapes.Append(theValShape); + break; + } + } + } } } diff --git a/src/Model/Model_BodyBuilder.cpp b/src/Model/Model_BodyBuilder.cpp index 893b4fc1c..a0f074c75 100755 --- a/src/Model/Model_BodyBuilder.cpp +++ b/src/Model/Model_BodyBuilder.cpp @@ -120,6 +120,53 @@ Model_BodyBuilder::Model_BodyBuilder(ModelAPI_Object* theOwner) { } +/// Checks that shape is presented in the tree with not-selection evolution +/// In theOriginalLabel it returns label where NS of old sub-shape is stored +static bool isShapeInTree(const TDF_Label& theAccess1, const TDF_Label& theAccess2, + TopoDS_Shape theShape, TDF_Label& theOriginalLabel) +{ + bool aResult = TNaming_Tool::HasLabel(theAccess1, theShape); + if (aResult) { //check evolution and a label of this shape + for(TNaming_SameShapeIterator aShapes(theShape, theAccess1); aShapes.More(); aShapes.Next()) + { + static Handle(TNaming_NamedShape) aNS; + if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) { + if (aNS->Evolution() != TNaming_SELECTED) { + theOriginalLabel = aNS->Label(); + return true; + } + } + } + } + if (!theAccess2.IsNull()) { + static const TDF_Label anEmpty; + return isShapeInTree(theAccess2, anEmpty, theShape, theOriginalLabel); + } + return false; +} + +/// Stores entry to the external label in the entries list at this label +static void storeExternalReference(const TDF_Label& theExternal, const TDF_Label theThis) +{ + // store information about the external document reference to restore old shape on open + if (!theExternal.IsNull() && !theExternal.Root().IsEqual(theThis.Root())) { + Handle(TDataStd_ExtStringList) anEntries; + if (!theThis.FindAttribute(kEXTERNAL_SHAPE_REF, anEntries)) { + anEntries = TDataStd_ExtStringList::Set(theThis, kEXTERNAL_SHAPE_REF); + } + TCollection_AsciiString anEntry; + TDF_Tool::Entry(theExternal, anEntry); + // check it already contains this entry + TDataStd_ListOfExtendedString::Iterator anIter(anEntries->List()); + for(; anIter.More(); anIter.Next()) + if (anIter.Value() == anEntry) + break; + if (!anIter.More()) { + anEntries->Append(anEntry); + } + } +} + void Model_BodyBuilder::store(const GeomShapePtr& theShape, const bool theIsStoreSameShapes) { @@ -174,8 +221,7 @@ void Model_BodyBuilder::storeGenerated(const GeomShapePtr& theFromShape, // clean builders if (theIsCleanStored) clean(); - // store the new shape as primitive - TNaming_Builder aBuilder(aShapeLab); + TNaming_Builder* aBuilder = builder(0); if (!theFromShape || !theToShape) return; // bad shape TopoDS_Shape aShapeBasis = theFromShape->impl(); @@ -184,16 +230,35 @@ void Model_BodyBuilder::storeGenerated(const GeomShapePtr& theFromShape, TopoDS_Shape aShapeNew = theToShape->impl(); if (aShapeNew.IsNull()) return; // null shape inside - aBuilder.Generated(aShapeBasis, aShapeNew); + + // There is no sense to write history if old shape does not exist in the document. + TDF_Label anAccess2 = std::dynamic_pointer_cast( + ModelAPI_Session::get()->moduleDocument())->generalLabel(); + TDF_Label anOriginalLabel; + if (!isShapeInTree(aData->shapeLab(), anAccess2, aShapeBasis, anOriginalLabel)) { + if (aBuilder->NamedShape()->Get().IsNull()) { // store as primitive if alone anyway + aBuilder->Generated(aShapeNew); + } + } else { + if (aBuilder->NamedShape()->Evolution() == TNaming_PRIMITIVE) { // erase primitive before + myBuilders.erase(0); + aBuilder = builder(0); + } + + aBuilder->Generated(aShapeBasis, aShapeNew); + // store information about the external document reference to restore old shape on open + storeExternalReference(anOriginalLabel, aBuilder->NamedShape()->Label()); + } + // register name - if(!aBuilder.NamedShape()->IsEmpty()) { + if(!aBuilder->NamedShape()->IsEmpty()) { Handle(TDataStd_Name) anAttr; - if(aBuilder.NamedShape()->Label().FindAttribute(TDataStd_Name::GetID(),anAttr)) { + if(aBuilder->NamedShape()->Label().FindAttribute(TDataStd_Name::GetID(),anAttr)) { std::string aName (TCollection_AsciiString(anAttr->Get()).ToCString()); if(!aName.empty()) { std::shared_ptr aDoc = std::dynamic_pointer_cast(document()); - aDoc->addNamingName(aBuilder.NamedShape()->Label(), aName); + aDoc->addNamingName(aBuilder->NamedShape()->Label(), aName); } } } @@ -245,7 +310,6 @@ void Model_BodyBuilder::storeModified(const GeomShapePtr& theOldShape, if (aData) { // clean builders if (theIsCleanStored) clean(); - // store the new shape as primitive TNaming_Builder* aBuilder = builder(0); if (!theOldShape || !theNewShape) return; // bad shape @@ -255,7 +319,26 @@ void Model_BodyBuilder::storeModified(const GeomShapePtr& theOldShape, TopoDS_Shape aShapeNew = theNewShape->impl(); if (aShapeNew.IsNull()) return; // null shape inside - aBuilder->Modify(aShapeOld, aShapeNew); + + // There is no sense to write history if old shape does not exist in the document. + TDF_Label anAccess2 = std::dynamic_pointer_cast( + ModelAPI_Session::get()->moduleDocument())->generalLabel(); + TDF_Label anOriginalLabel; + if (!isShapeInTree(aData->shapeLab(), anAccess2, aShapeOld, anOriginalLabel)) { + if (aBuilder->NamedShape()->Get().IsNull()) { // store as primitive if alone anyway + aBuilder->Generated(aShapeNew); + } + } else { + if (aBuilder->NamedShape()->Evolution() == TNaming_PRIMITIVE) { // erase primitive before + myBuilders.erase(0); + aBuilder = builder(0); + } + + aBuilder->Modify(aShapeOld, aShapeNew); + // store information about the external document reference to restore old shape on open + storeExternalReference(anOriginalLabel, aBuilder->NamedShape()->Label()); + } + if(!aBuilder->NamedShape()->IsEmpty()) { Handle(TDataStd_Name) anAttr; if(aBuilder->NamedShape()->Label().FindAttribute(TDataStd_Name::GetID(), anAttr)) { @@ -482,53 +565,6 @@ static void keepTopLevelShapes(ListOfShape& theShapes, } } -/// Checks that shape is presented in the tree with not-selection evolution -/// In theOriginalLabel it returns label where NS of old sub-shape is stored -static bool isShapeInTree(const TDF_Label& theAccess1, const TDF_Label& theAccess2, - TopoDS_Shape theShape, TDF_Label& theOriginalLabel) -{ - bool aResult = TNaming_Tool::HasLabel(theAccess1, theShape); - if (aResult) { //check evolution and a label of this shape - for(TNaming_SameShapeIterator aShapes(theShape, theAccess1); aShapes.More(); aShapes.Next()) - { - static Handle(TNaming_NamedShape) aNS; - if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) { - if (aNS->Evolution() != TNaming_SELECTED) { - theOriginalLabel = aNS->Label(); - return true; - } - } - } - } - if (!theAccess2.IsNull()) { - static const TDF_Label anEmpty; - return isShapeInTree(theAccess2, anEmpty, theShape, theOriginalLabel); - } - return false; -} - -/// Stores entry to the external label in the entries list at this label -static void storeExternalReference(const TDF_Label& theExternal, const TDF_Label theThis) -{ - // store information about the external document reference to restore old shape on open - if (!theExternal.IsNull() && !theExternal.Root().IsEqual(theThis.Root())) { - Handle(TDataStd_ExtStringList) anEntries; - if (!theThis.FindAttribute(kEXTERNAL_SHAPE_REF, anEntries)) { - anEntries = TDataStd_ExtStringList::Set(theThis, kEXTERNAL_SHAPE_REF); - } - TCollection_AsciiString anEntry; - TDF_Tool::Entry(theExternal, anEntry); - // check it already contains this entry - TDataStd_ListOfExtendedString::Iterator anIter(anEntries->List()); - for(; anIter.More(); anIter.Next()) - if (anIter.Value() == anEntry) - break; - if (!anIter.More()) { - anEntries->Append(anEntry); - } - } -} - void Model_BodyBuilder::loadModifiedShapes(const GeomMakeShapePtr& theAlgo, const GeomShapePtr& theOldShape, const GeomAPI_Shape::ShapeType theShapeTypeToExplore, diff --git a/src/Model/Model_ResultBody.cpp b/src/Model/Model_ResultBody.cpp index 8368220f9..3d28be7c6 100644 --- a/src/Model/Model_ResultBody.cpp +++ b/src/Model/Model_ResultBody.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -256,7 +257,7 @@ void Model_ResultBody::updateSubs(const std::shared_ptr& theThisS if (!aShape->isEqual(anOldSubShape)) { if (myAlgo.get()) { std::list anOldForSub; - computeOldForSub(aShape, anOldForSub); + computeOldForSub(aShape, myOlds, anOldForSub); myIsGenerated ? aSub->storeGenerated(anOldForSub, aShape, myAlgo) : aSub->storeModified(anOldForSub, aShape, myAlgo); } else { @@ -341,15 +342,27 @@ void Model_ResultBody::cleanCash() } } -void Model_ResultBody::computeOldForSub( - const GeomShapePtr& theSubShape, std::list& theOldForSub) +void Model_ResultBody::computeOldForSub(const GeomShapePtr& theSub, + const std::list& theAllOlds, std::list& theOldForSub) { - std::list::iterator aRootOlds = myOlds.begin(); - for(; aRootOlds != myOlds.end(); aRootOlds++) { + std::list::const_iterator aRootOlds = theAllOlds.cbegin(); + for(; aRootOlds != theAllOlds.cend(); aRootOlds++) { ListOfShape aNews; myIsGenerated ? myAlgo->generated(*aRootOlds, aNews) : myAlgo->modified(*aRootOlds, aNews); + // MakeShape may return alone old shape if there is no history information for this input + if (aNews.size() == 1 && aNews.front()->isEqual(*aRootOlds)) + aNews.clear(); + if (aNews.empty()) { // try to iterate to sub-elements (for intersection of solids this is face) + std::list theAllSubOlds; + for(GeomAPI_ShapeIterator aSubOld(*aRootOlds); aSubOld.more(); aSubOld.next()) { + GeomShapePtr aSub = aSubOld.current(); + if (aSub.get() && !aSub->isNull()) + theAllSubOlds.push_back(aSub); + } + computeOldForSub(theSub, theAllSubOlds, theOldForSub); + } for(ListOfShape::iterator aNewIter = aNews.begin(); aNewIter != aNews.end(); aNewIter++) { - if (theSubShape->isSame(*aNewIter)) { // found old that was used for new theSubShape creation + if (theSub->isSame(*aNewIter)) { // found old that was used for new theSubShape creation theOldForSub.push_back(*aRootOlds); break; } diff --git a/src/Model/Model_ResultBody.h b/src/Model/Model_ResultBody.h index e34d1989b..a810f2ea0 100644 --- a/src/Model/Model_ResultBody.h +++ b/src/Model/Model_ResultBody.h @@ -123,8 +123,9 @@ protected: // Checks the state of children and parents to send events of creation/erase when needed void updateConcealment(); - /// Adds to theOldForSub only old shapes that where used for theSubShape creation - void computeOldForSub(const GeomShapePtr& theSubShape, std::list& theOldForSub); + /// Adds to theOldForSub only old shapes that where used for theSub creation + void computeOldForSub(const GeomShapePtr& theSub, + const std::list& theAllOlds, std::list& theOldForSub); friend class Model_Objects; }; diff --git a/src/Model/Model_Update.cpp b/src/Model/Model_Update.cpp index db6fc41bc..1f4001e78 100755 --- a/src/Model/Model_Update.cpp +++ b/src/Model/Model_Update.cpp @@ -589,7 +589,7 @@ bool Model_Update::processFeature(FeaturePtr theFeature) double aNX = aNorm->x(), aNY = aNorm->y(), aNZ = aNorm->z(); // update sketch plane updateArguments(theFeature); - //theFeature->attributeChanged("External"); // to recompute origin, direction and normal + 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 || -- 2.39.2