From 6389de91d7671d02ca45d8b5d3c0f1fe2be2d863 Mon Sep 17 00:00:00 2001 From: Ekaterina Sukhareva Date: Fri, 23 Aug 2024 16:24:44 +0100 Subject: [PATCH] [CEA] Allow to keep result in SHAPER --- src/FeaturesPlugin/FeaturesPlugin_Copy.cpp | 15 +++- src/FeaturesPlugin/FeaturesPlugin_Copy.h | 12 ++- src/GeomAPI/GeomAPI_Shape.cpp | 69 +++++++++++++++ src/GeomAPI/GeomAPI_Shape.h | 15 +++- src/Model/Model_AttributeSelection.cpp | 37 ++++++++ src/Model/Model_AttributeSelection.h | 6 ++ src/Model/Model_AttributeSelectionList.cpp | 14 +++ src/Model/Model_AttributeSelectionList.h | 3 + src/Model/Model_Data.cpp | 6 +- src/Model/Model_Data.h | 2 +- src/Model/Model_Document.cpp | 17 ++++ src/Model/Model_Document.h | 3 + src/Model/Model_Objects.cpp | 3 +- src/Model/Model_ResultBody.cpp | 45 ++++++++++ src/Model/Model_ResultBody.h | 13 ++- src/Model/Model_Tools.cpp | 32 +++++++ src/Model/Model_Tools.h | 9 ++ src/Model/Model_Update.cpp | 13 ++- src/Model/Model_Update.h | 1 + src/ModelAPI/ModelAPI_AttributeSelection.h | 9 ++ .../ModelAPI_AttributeSelectionList.h | 6 ++ src/ModelAPI/ModelAPI_Data.h | 2 +- src/ModelAPI/ModelAPI_Result.cpp | 54 ++++++++++++ src/ModelAPI/ModelAPI_Result.h | 45 ++++++++++ src/ModelAPI/ModelAPI_ResultBody.h | 6 ++ src/ModelAPI/ModelAPI_Tools.cpp | 81 ++++++++++++++++++ src/ModelAPI/ModelAPI_Tools.h | 22 +++++ src/ModelHighAPI/ModelHighAPI_Dumper.cpp | 74 +++++++++++----- src/PartSet/PartSet_IconFactory.cpp | 18 ++-- src/XGUI/XGUI_ContextMenuMgr.cpp | 11 +++ src/XGUI/XGUI_Workshop.cpp | 8 ++ src/XGUI/XGUI_pictures.qrc | 10 +++ src/XGUI/pictures/compound_persistent.png | Bin 0 -> 884 bytes .../pictures/compoundofsolids_persistent.png | Bin 0 -> 757 bytes src/XGUI/pictures/compsolid_persistent.png | Bin 0 -> 771 bytes src/XGUI/pictures/edge_persistent.png | Bin 0 -> 670 bytes src/XGUI/pictures/face_persistent.png | Bin 0 -> 757 bytes src/XGUI/pictures/shell_persistent.png | Bin 0 -> 779 bytes src/XGUI/pictures/solid_persistent.png | Bin 0 -> 738 bytes src/XGUI/pictures/vertex_persistent.png | Bin 0 -> 820 bytes src/XGUI/pictures/wire_persistent.png | Bin 0 -> 720 bytes 41 files changed, 616 insertions(+), 45 deletions(-) create mode 100755 src/XGUI/pictures/compound_persistent.png create mode 100755 src/XGUI/pictures/compoundofsolids_persistent.png create mode 100755 src/XGUI/pictures/compsolid_persistent.png create mode 100755 src/XGUI/pictures/edge_persistent.png create mode 100755 src/XGUI/pictures/face_persistent.png create mode 100755 src/XGUI/pictures/shell_persistent.png create mode 100755 src/XGUI/pictures/solid_persistent.png create mode 100755 src/XGUI/pictures/vertex_persistent.png create mode 100755 src/XGUI/pictures/wire_persistent.png diff --git a/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp b/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp index e1110270f..f357f66c5 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp @@ -33,6 +33,12 @@ void FeaturesPlugin_Copy::initAttributes() { data()->addAttribute(OBJECTS(), ModelAPI_AttributeSelectionList::typeId()); data()->addAttribute(NUMBER(), ModelAPI_AttributeInteger::typeId()); + + AttributeIntegerPtr aNameNumAttr = std::dynamic_pointer_cast( + data()->addAttribute(NAME_SUFFIX(), ModelAPI_AttributeInteger::typeId())); + if (!aNameNumAttr->isInitialized()){ + aNameNumAttr->setValue(0); + } } static GeomShapePtr shapeOfSelection(AttributeSelectionPtr theSel) { @@ -77,7 +83,8 @@ void FeaturesPlugin_Copy::execute() std::wstring aBaseName = aSel->context() ? aSel->context()->data()->name() : aSel->contextFeature()->firstResult()->data()->name(); std::wstring aName; - int anInd = 0; + int aNameNum = integer(NAME_SUFFIX())->value(); + int anInd = aNameNum; do { anInd++; std::wostringstream aNameStr; @@ -88,12 +95,12 @@ void FeaturesPlugin_Copy::execute() std::shared_ptr aResultBody = document()->createBody(data(), aResultIndex); - aResultBody->data()->setName(aName); + aResultBody->data()->setName(aName, false); // to make sub-results also names with a similar name temporarily rename the feature std::wstring anOrigName = name(); - data()->setName(aBaseName); + data()->setName(aBaseName, false); aResultBody->store(aResult); - data()->setName(anOrigName); + data()->setName(anOrigName, false); aResultBody->loadFirstLevel(aResult, "Copy"); setResult(aResultBody, aResultIndex++); } diff --git a/src/FeaturesPlugin/FeaturesPlugin_Copy.h b/src/FeaturesPlugin/FeaturesPlugin_Copy.h index 49bcc06e7..b213f0aa6 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Copy.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Copy.h @@ -28,8 +28,8 @@ /// \ingroup Plugins /// \brief This feature copies the selected results and sub-results (for the whole feature selected /// all results of this feature are copied). The referenced arguments of this feature are -/// not concealed. The difference with ”Recover” feature is that not concealed results may -/// be selected and in the history behavior: the “Move to the End” of groups will move to +/// not concealed. The difference with �Recover� feature is that not concealed results may +/// be selected and in the history behavior: the �Move to the End� of groups will move to /// all copy-results. class FeaturesPlugin_Copy : public ModelAPI_Feature, public ModelAPI_FeatureCopyInterface @@ -62,6 +62,14 @@ public: return MY_NUMBER; } + /// Integer attribute that contains the number of copies of original object that was created before + /// (used for creating copies of persistent object) + inline static const std::string NAME_SUFFIX() + { + static std::string MY_NAME_NUMBER("name_suffix"); + return MY_NAME_NUMBER; + } + /// Performs the algorithm and stores results it in the data structure. FEATURESPLUGIN_EXPORT virtual void execute(); diff --git a/src/GeomAPI/GeomAPI_Shape.cpp b/src/GeomAPI/GeomAPI_Shape.cpp index 3fb717b75..046da23db 100644 --- a/src/GeomAPI/GeomAPI_Shape.cpp +++ b/src/GeomAPI/GeomAPI_Shape.cpp @@ -110,9 +110,78 @@ bool GeomAPI_Shape::isSameGeometry(const std::shared_ptr theShape return face()->isSameGeometry(theShape); else if (isEdge()) return edge()->isSameGeometry(theShape); + else if (isVertex()) + return vertex()->isEqual(theShape); + else + { + //check only bounding boxes and amounts of subshapes + return isProbablySameGeometry(theShape); + } return false; } +bool GeomAPI_Shape::isSameSize(const std::shared_ptr theShape) const +{ + if (!theShape.get()) + return false; + + if(theShape->shapeType() != shapeType()) + return false; + + double aMinX, aMinY, aMinZ, aMaxX, aMaxY, aMaxZ; + computeSize(aMinX, aMinY, aMinZ, aMaxX, aMaxY, aMaxZ); + + double aSMinX, aSMinY, aSMinZ, aSMaxX, aSMaxY, aSMaxZ; + theShape->computeSize(aSMinX, aSMinY, aSMinZ, aSMaxX, aSMaxY, aSMaxZ); + + if(fabs(aMinX-aSMinX) > Precision::Confusion() || + fabs(aMinY-aSMinY) > Precision::Confusion() || + fabs(aMinZ-aSMinZ) > Precision::Confusion() || + fabs(aMaxX-aSMaxX) > Precision::Confusion() || + fabs(aMaxY-aSMaxY) > Precision::Confusion() || + fabs(aMaxZ-aSMaxZ) > Precision::Confusion()){ + return false; + } + return true; + +} + +bool GeomAPI_Shape::isSameAmountOfSubs(const std::shared_ptr theShape) const +{ + if (!theShape.get()) + return false; + + if(theShape->shapeType() != shapeType()) + return false; + + + if(theShape->subShapes(GeomAPI_Shape::COMPOUND).size() != subShapes(GeomAPI_Shape::COMPOUND).size() || + theShape->subShapes(GeomAPI_Shape::COMPSOLID).size() != subShapes(GeomAPI_Shape::COMPSOLID).size() || + theShape->subShapes(GeomAPI_Shape::SOLID).size() != subShapes(GeomAPI_Shape::SOLID).size() || + theShape->subShapes(GeomAPI_Shape::SHELL).size() != subShapes(GeomAPI_Shape::SHELL).size() || + theShape->subShapes(GeomAPI_Shape::FACE).size() != subShapes(GeomAPI_Shape::FACE).size() || + theShape->subShapes(GeomAPI_Shape::WIRE).size() != subShapes(GeomAPI_Shape::WIRE).size() || + theShape->subShapes(GeomAPI_Shape::EDGE).size() != subShapes(GeomAPI_Shape::EDGE).size() || + theShape->subShapes(GeomAPI_Shape::VERTEX).size() != subShapes(GeomAPI_Shape::VERTEX).size()){ + return false; + } + return true; +} + +bool GeomAPI_Shape::isProbablySameGeometry(const std::shared_ptr theShape) const +{ + if (!theShape.get()) + return false; + + if(theShape->shapeType() != shapeType()) + return false; + + if(!isSameAmountOfSubs(theShape) || !isSameSize(theShape)) + return false; + + return true; +} + bool GeomAPI_Shape::isVertex() const { const TopoDS_Shape& aShape = const_cast(this)->impl(); diff --git a/src/GeomAPI/GeomAPI_Shape.h b/src/GeomAPI/GeomAPI_Shape.h index e69709262..edfe32774 100644 --- a/src/GeomAPI/GeomAPI_Shape.h +++ b/src/GeomAPI/GeomAPI_Shape.h @@ -78,10 +78,23 @@ public: GEOMAPI_EXPORT virtual bool isSame(const std::shared_ptr theShape) const; - /// Returns \c true if shapes have same underlying geometry + /// Returns \c true if shapes have same underlying geometry (vertex,edge,face only) GEOMAPI_EXPORT virtual bool isSameGeometry(const std::shared_ptr theShape) const; + /// Returns \c true if bounding boxes of the shapes are equal (compared shapes should be the same type) + GEOMAPI_EXPORT + virtual bool isSameSize(const std::shared_ptr theShape) const; + + /// Returns \c true if shapes have the same amounts of each type of subshapes (compared shapes should be the same type) + GEOMAPI_EXPORT + virtual bool isSameAmountOfSubs(const std::shared_ptr theShape) const; + + /// Returns \c true if shapes (probably) have same underlying geometry (shell, solid, compound, compsolid) + /// Compare bounding boxes and amounts of subshapes + GEOMAPI_EXPORT + virtual bool isProbablySameGeometry(const std::shared_ptr theShape) const; + /// Returns whether the shape is a vertex GEOMAPI_EXPORT virtual bool isVertex() const; diff --git a/src/Model/Model_AttributeSelection.cpp b/src/Model/Model_AttributeSelection.cpp index 8ee38cbc8..a672008a7 100644 --- a/src/Model/Model_AttributeSelection.cpp +++ b/src/Model/Model_AttributeSelection.cpp @@ -74,6 +74,8 @@ #include #include +#include + //#define DEB_NAMING 1 #ifdef DEB_NAMING #include @@ -2222,3 +2224,38 @@ void Model_AttributeSelection::reset() ModelAPI_AttributeSelection::reset(); myRef.reset(); } + +bool Model_AttributeSelection::isCopyToSwallow() +{ + ResultPtr aContext = context(); + if (aContext.get()) + { + return ModelAPI_Tools::isCopyToSwallow(aContext); + } + return false; +} + +std::pair> Model_AttributeSelection::getOriginalContext() +{ + auto aThisPair = std::make_pair(context(), value()); + if(!isCopyToSwallow()) + return aThisPair; + + ResultPtr aCopyContext = context(); + ObjectPtr anOriginalObject = ModelAPI_Tools::getOriginalObject(aCopyContext); + + + ResultPtr anOriginaResult = std::dynamic_pointer_cast(anOriginalObject); //it should be original object + GeomShapePtr aGeomObj = value(); + GeomAPI_Shape::ShapeType aCurType = aGeomObj->shapeType(); + ListOfShape anOrigSubs = anOriginaResult->shape()->subShapes(aCurType,true); + for(auto anOrigSub: anOrigSubs) + { + if(anOrigSub->isSameGeometry(aGeomObj)) + { + return std::make_pair(anOriginaResult, anOrigSub); + } + } + + return aThisPair; +} \ No newline at end of file diff --git a/src/Model/Model_AttributeSelection.h b/src/Model/Model_AttributeSelection.h index 6b726748f..c5d8a008b 100644 --- a/src/Model/Model_AttributeSelection.h +++ b/src/Model/Model_AttributeSelection.h @@ -147,6 +147,12 @@ public: /// Resets attribute to deafult state MODEL_EXPORT virtual void reset(); + /// Return true if context of the atribute is a copy of persistent result + MODEL_EXPORT virtual bool isCopyToSwallow(); + + /// Return original context and shape of this copy atribute + MODEL_EXPORT virtual std::pair> getOriginalContext(); + protected: /// Objects are created for features automatically MODEL_EXPORT Model_AttributeSelection(TDF_Label& theLabel); diff --git a/src/Model/Model_AttributeSelectionList.cpp b/src/Model/Model_AttributeSelectionList.cpp index 3380b1f65..d3db4d757 100644 --- a/src/Model/Model_AttributeSelectionList.cpp +++ b/src/Model/Model_AttributeSelectionList.cpp @@ -44,6 +44,7 @@ #include #include + /// GUID for UAttribute that indicates the list has "To add all elements that share the same /// topology" flag enabled static const Standard_GUID kIS_GEOMETRICAL_SELECTION("f16987b6-e6c8-435c-99fa-03a7e0b06e83"); @@ -546,3 +547,16 @@ void Model_AttributeSelectionList::setFilters(FiltersFeaturePtr theFeature) // remove attribute if something is wrong myLab.ForgetAttribute(TDataStd_ReferenceList::GetID()); } + +bool Model_AttributeSelectionList::isContainCopiesToSwallow() +{ + for(int anIndex = size() - 1; anIndex >= 0; anIndex--) + { + AttributeSelectionPtr anAttr = value(anIndex); + if(anAttr->isCopyToSwallow()) + { + return true; + } + } + return false; +} diff --git a/src/Model/Model_AttributeSelectionList.h b/src/Model/Model_AttributeSelectionList.h index 82f62afc8..362d46808 100644 --- a/src/Model/Model_AttributeSelectionList.h +++ b/src/Model/Model_AttributeSelectionList.h @@ -126,6 +126,9 @@ public: /// Sets a selection filters feature if it is defined for this selection list MODEL_EXPORT virtual void setFilters(FiltersFeaturePtr theFeature); + /// Return true if contain at least one attr witch context is a copy of persistent result. + MODEL_EXPORT virtual bool isContainCopiesToSwallow(); + protected: /// Objects are created for features automatically MODEL_EXPORT Model_AttributeSelectionList(TDF_Label& theLabel); diff --git a/src/Model/Model_Data.cpp b/src/Model/Model_Data.cpp index 28237c24e..3d65946cb 100644 --- a/src/Model/Model_Data.cpp +++ b/src/Model/Model_Data.cpp @@ -121,7 +121,7 @@ std::wstring Model_Data::name() return L""; // not defined } -void Model_Data::setName(const std::wstring& theName) +void Model_Data::setName(const std::wstring& theName, bool isUserDefined) { bool isModified = false; std::wstring anOldName = name(); @@ -136,9 +136,9 @@ void Model_Data::setName(const std::wstring& theName) // check the name of result is defined by user // (name of result does not composed of the name of feature and the result index) - bool isUserDefined = true; + //bool isUserDefined = true; ResultPtr aResult = std::dynamic_pointer_cast(myObject); - if (aResult) { + if (aResult && isUserDefined) { std::wstring aDefaultName = ModelAPI_Tools::getDefaultName(aResult, false).first; isUserDefined = aDefaultName != theName; } diff --git a/src/Model/Model_Data.h b/src/Model/Model_Data.h index 82703d70c..0c7b411c1 100644 --- a/src/Model/Model_Data.h +++ b/src/Model/Model_Data.h @@ -105,7 +105,7 @@ class Model_Data : public ModelAPI_Data /// Returns the name of the feature visible by the user in the object browser MODEL_EXPORT virtual std::wstring name(); /// Defines the name of the feature visible by the user in the object browser - MODEL_EXPORT virtual void setName(const std::wstring& theName); + MODEL_EXPORT virtual void setName(const std::wstring& theName, bool isUserDefined = true); /// Return \c true if the object has been renamed by the user MODEL_EXPORT virtual bool hasUserDefinedName() const; /// Returns version of the feature (empty string if not applicable) diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index 5f313a855..cdcebe1e2 100644 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -833,6 +833,7 @@ bool Model_Document::finishOperation() setCurrentFeature(aMain, false); } myObjs->synchronizeBackRefs(); + processPersistentResult(true); Events_Loop* aLoop = Events_Loop::loop(); static const Events_ID kCreatedEvent = aLoop->eventByName(EVENT_OBJECT_CREATED); static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED); @@ -999,6 +1000,7 @@ void Model_Document::abortOperation() subDoc(*aSubIter)->abortOperation(); // references may be changed because they are set in attributes on the fly myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); + processPersistentResult(); } bool Model_Document::isOperation() const @@ -2363,3 +2365,18 @@ bool Model_Document::autoRecomutationState() const { return !generalLabel().FindChild(TAG_CURRENT_TRANSACTION).IsAttribute(kAutoRecomputationID); } + +bool Model_Document::processPersistentResult(const bool isAddedNewCopy) +{ + std::list aSubFeatures = allFeatures(); + for(auto& aFeat: aSubFeatures){ + if(aFeat->lastResult() && aFeat->lastResult()->isPersistent()){ + ResultBodyPtr aBody = std::dynamic_pointer_cast(aFeat->lastResult()); + if(aBody) + aBody->nullifyTempCopy(); + + if(isAddedNewCopy) + ModelAPI_Tools::increaseCopyAmount(aFeat->lastResult()); + } + } +} diff --git a/src/Model/Model_Document.h b/src/Model/Model_Document.h index 001758919..23352cad0 100644 --- a/src/Model/Model_Document.h +++ b/src/Model/Model_Document.h @@ -412,6 +412,9 @@ class Model_Document : public ModelAPI_Document /// Returns the current automatic recomputation flag: true means enabled bool autoRecomutationState() const; + /// Process persistent result: do some necessary actions with persistent results while finish/abort operation + bool processPersistentResult(const bool isAddedNewCopy = false); + friend class Model_Application; friend class Model_Session; friend class Model_Update; diff --git a/src/Model/Model_Objects.cpp b/src/Model/Model_Objects.cpp index 544b5bef8..f608219fe 100644 --- a/src/Model/Model_Objects.cpp +++ b/src/Model/Model_Objects.cpp @@ -1126,8 +1126,7 @@ static void collectReferences(std::shared_ptr theData, } } -void Model_Objects::synchronizeBackRefs() -{ +void Model_Objects::synchronizeBackRefs(){ // collect all back references in the separated container: to update everything at once, // without additional Concealment switching on and off: only the final modification diff --git a/src/Model/Model_ResultBody.cpp b/src/Model/Model_ResultBody.cpp index f6bc7d31a..b61716217 100644 --- a/src/Model/Model_ResultBody.cpp +++ b/src/Model/Model_ResultBody.cpp @@ -44,10 +44,18 @@ Standard_GUID kIsConnectedTopology("e51392e0-3a4d-405d-8e36-bbfe19858ef5"); // if this attribute exists, the connected topology flag must be recomputed Standard_GUID kUpdateConnectedTopology("01ef7a45-0bec-4266-b0b4-4aa570921818"); +// consts for creating copy +const std::string COPY_FEATURE = "Copy"; +const std::string COPY_ATTR_OBJECTS = "objects"; +const std::string COPY_ATTR_NUMBER = "number"; +const std::string COPY_ATTR_NAMESUF = "name_suffix"; + Model_ResultBody::Model_ResultBody() : ModelAPI_ResultBody() { myBuilder = new Model_BodyBuilder(this); myLastConcealed = false; + myTempCopy = nullptr; + myIsBlockedNewCopy = false; updateSubs(shape()); // in case of open, etc. } @@ -497,6 +505,43 @@ static void collectSubs( } } +std::shared_ptr Model_ResultBody::getCopyToSwallow() +{ + if(!isPersistent() || myIsBlockedNewCopy) + return nullptr; + + std::shared_ptr aDoc = std::dynamic_pointer_cast(document()); + if(!aDoc) + return nullptr; + + if(myTempCopy) + return myTempCopy; + + ResultPtr aCopyResult(nullptr); + FeaturePtr aCopyFeature = aDoc->addFeature(COPY_FEATURE); + if(aCopyFeature){ + myIsBlockedNewCopy = true; + ResultPtr context = std::dynamic_pointer_cast(shared_from_this()); + aCopyFeature->selectionList(COPY_ATTR_OBJECTS)->append(context, shape()); + aCopyFeature->integer(COPY_ATTR_NUMBER)->setValue(1); + aCopyFeature->integer(COPY_ATTR_NAMESUF)->setValue(ModelAPI_Tools::getCopyAmount(context)); + aCopyFeature->setInHistory(aCopyFeature,false); + aCopyFeature->execute(); + aCopyFeature->data()->execState(ModelAPI_StateDone); + aCopyResult = aCopyFeature->lastResult(); + ModelAPI_Tools::setIsCopyToSwallow(aCopyResult, true); + ModelAPI_Tools::setOriginalObject(aCopyResult, context); + myTempCopy = aCopyResult; + myIsBlockedNewCopy = false; + } + return myTempCopy; +} + +void Model_ResultBody::nullifyTempCopy() +{ + myTempCopy = nullptr; +} + void Model_ResultBody::computeOldForSub(const GeomShapePtr& theSub, const std::list& theAllOlds, std::list& theOldForSub) { diff --git a/src/Model/Model_ResultBody.h b/src/Model/Model_ResultBody.h index bda42cdf5..9dc28fd2e 100644 --- a/src/Model/Model_ResultBody.h +++ b/src/Model/Model_ResultBody.h @@ -36,7 +36,7 @@ * of result must be optimized. * Also provides a container of sub-body result in case it is compound or compsolid. */ -class Model_ResultBody : public ModelAPI_ResultBody +class Model_ResultBody : public ModelAPI_ResultBody, public std::enable_shared_from_this { /// Sub-bodies if this is compsolid or compound: zero-based index to subs std::vector mySubs; @@ -52,6 +52,10 @@ class Model_ResultBody : public ModelAPI_ResultBody bool myIsGenerated; /// Map from old shape to list of new shapes, cash for computeOldForSub method TopTools_DataMapOfShapeListOfShape myHistoryCash; + /// Store temporary copy of persistent result during one operation + std::shared_ptr myTempCopy; + /// Auxiliary, helps to prevent creating copy when it is prohibited (for example prevent recursive copy) + bool myIsBlockedNewCopy; public: @@ -123,6 +127,13 @@ public: /// Returns empty vector if not found. MODEL_EXPORT virtual const std::vector& findShapeColor(const std::wstring& theShapeName); + /// For persistent result - creates/return a copy of this result, so the copy can be swallowed + /// by the new feature instead of original result + MODEL_EXPORT virtual std::shared_ptr getCopyToSwallow(); + + MODEL_EXPORT virtual void nullifyTempCopy(); + + protected: /// Makes a body on the given feature Model_ResultBody(); diff --git a/src/Model/Model_Tools.cpp b/src/Model/Model_Tools.cpp index 4a86874e4..abab2b62d 100644 --- a/src/Model/Model_Tools.cpp +++ b/src/Model/Model_Tools.cpp @@ -19,11 +19,16 @@ #include #include +#include #include #include #include #include +#include +#include +#include +#include #include #include @@ -183,3 +188,30 @@ void Model_Tools::labelsOfCoordinates(std::set& theCoor } } } + +void Model_Tools::substituteSelectionWithCopy(FeaturePtr &theFeature, ResultPtr &theResult, AttributeSelectionPtr &theSelAttr) +{ + if(!theResult || !theResult->isPersistent() || !theSelAttr) + return; + + //if the result of the feature already a copy to be swallowed - skip + if(ModelAPI_Tools::isCopyToSwallow(theFeature->lastResult())) + return; + + ResultBodyPtr aBodyRes = std::dynamic_pointer_cast(theResult); + ResultPtr aCopyResult = aBodyRes->getCopyToSwallow(); + + if(aCopyResult){ + GeomShapePtr aGeomObj = theSelAttr->value(); + GeomAPI_Shape::ShapeType aCurType = aGeomObj->shapeType(); + ListOfShape aCopySubs = aCopyResult->shape()->subShapes(aCurType,true); + for(auto aCopySub: aCopySubs) + { + if(aCopySub->isSameGeometry(aGeomObj)) + { + theSelAttr->setValue(aCopyResult, aCopySub); + return; + } + } + } +} diff --git a/src/Model/Model_Tools.h b/src/Model/Model_Tools.h index bdd857e39..8de09e39c 100644 --- a/src/Model/Model_Tools.h +++ b/src/Model/Model_Tools.h @@ -24,6 +24,9 @@ #include #include +#include +#include +#include #include #include @@ -51,6 +54,12 @@ public: static void labelsOfCoordinates( std::set& theCoordinateLabels, Handle(TDF_RelocationTable) theRelocTable); + + /// Create a copy of persistent result and substitute the original selecttion attribute with a copy. + /// \param theFeature a feature that wants to swallow the result + /// \param theResult a persistent result to copy + /// \param theSelAttr an attribute to substitute + static void substituteSelectionWithCopy(FeaturePtr &theFeature, ResultPtr &theResult, AttributeSelectionPtr &theSelAttr); }; #endif diff --git a/src/Model/Model_Update.cpp b/src/Model/Model_Update.cpp index a2fb93b5a..f129ade60 100644 --- a/src/Model/Model_Update.cpp +++ b/src/Model/Model_Update.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -96,7 +97,6 @@ bool Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) { if (!theFeature->data()->isValid()) return false; // delete an extrusion created on the sketch - bool isNotExecuted = theFeature->isPersistentResult() && !std::dynamic_pointer_cast((theFeature)->document())->executeFeatures(); if (isNotExecuted) { @@ -889,7 +889,12 @@ void Model_Update::updateArguments(FeaturePtr theFeature) { ObjectPtr aContext = aSel->context(); // update argument only if the referenced object is ready to use if (aContext.get() && !aContext->isDisabled()) { + std::shared_ptr resultPtr = std::dynamic_pointer_cast(aContext); + if(resultPtr && resultPtr->isPersistent()){ + Model_Tools::substituteSelectionWithCopy(theFeature, resultPtr, aSel); + } if (isReason(theFeature, aContext)) { + if (!aSel->update()) { // this must be done on execution since it may be long operation bool isObligatory = !aFactory->isNotObligatory( theFeature->getKind(), theFeature->data()->id(aSel)) && @@ -927,6 +932,12 @@ void Model_Update::updateArguments(FeaturePtr theFeature) { std::dynamic_pointer_cast(aSel->value(a)); if (aSelAttr) { ObjectPtr aContext = aSelAttr->context(); + + std::shared_ptr resultPtr = std::dynamic_pointer_cast(aContext); + if(resultPtr && resultPtr->isPersistent()){ + Model_Tools::substituteSelectionWithCopy(theFeature, resultPtr, aSelAttr); + } + // update argument only if the referenced object is ready to use if (aContext.get() && !aContext->isDisabled()) { if (isReason(theFeature, aContext)) { diff --git a/src/Model/Model_Update.h b/src/Model/Model_Update.h index e4a09c9b6..92263030f 100644 --- a/src/Model/Model_Update.h +++ b/src/Model/Model_Update.h @@ -28,6 +28,7 @@ #include class ModelAPI_Object; +class ModelAPI_Result; class ModelAPI_Feature; class ModelAPI_CompositeFeature; class Model_Objects; diff --git a/src/ModelAPI/ModelAPI_AttributeSelection.h b/src/ModelAPI/ModelAPI_AttributeSelection.h index e8d3adffe..76ba6c0e9 100644 --- a/src/ModelAPI/ModelAPI_AttributeSelection.h +++ b/src/ModelAPI/ModelAPI_AttributeSelection.h @@ -23,6 +23,8 @@ #include "ModelAPI_Attribute.h" #include +#include + class GeomAPI_Edge; class GeomAPI_Pnt; @@ -128,6 +130,13 @@ class ModelAPI_AttributeSelection : public ModelAPI_Attribute /// Returns theRemove true if this attribute must be removed (become deleted) MODELAPI_EXPORT virtual void updateInHistory(bool& theRemove) = 0; + /// Return true if context of the atribute is a copy of persistent result + MODELAPI_EXPORT virtual bool isCopyToSwallow() = 0; + + /// Return original context and shape of this copy atribute + MODELAPI_EXPORT virtual std::pair> getOriginalContext() = 0; + + protected: /// Objects are created for features automatically MODELAPI_EXPORT ModelAPI_AttributeSelection(); diff --git a/src/ModelAPI/ModelAPI_AttributeSelectionList.h b/src/ModelAPI/ModelAPI_AttributeSelectionList.h index 3c25a0d0f..bc8ef3ecb 100644 --- a/src/ModelAPI/ModelAPI_AttributeSelectionList.h +++ b/src/ModelAPI/ModelAPI_AttributeSelectionList.h @@ -24,6 +24,9 @@ #include #include +#include +#include + class GeomAPI_Pnt; class GeomAPI_Shape; @@ -151,6 +154,9 @@ public: myMakeCopy = theFlag; } + /// Return true if contain at least one attr witch context is a copy of persistent result. + MODELAPI_EXPORT virtual bool isContainCopiesToSwallow() = 0; + protected: /// Default constructor MODELAPI_EXPORT ModelAPI_AttributeSelectionList() : ModelAPI_Attribute() diff --git a/src/ModelAPI/ModelAPI_Data.h b/src/ModelAPI/ModelAPI_Data.h index 8518d284d..5e4f21d19 100644 --- a/src/ModelAPI/ModelAPI_Data.h +++ b/src/ModelAPI/ModelAPI_Data.h @@ -75,7 +75,7 @@ class MODELAPI_EXPORT ModelAPI_Data /// Returns the name of the feature visible by the user in the object browser virtual std::wstring name() = 0; /// Defines the name of the feature visible by the user in the object browser - virtual void setName(const std::wstring& theName) = 0; + virtual void setName(const std::wstring& theName, bool isUserDefined = true) = 0; /// Return \c true if the object has been renamed by the user virtual bool hasUserDefinedName() const = 0; diff --git a/src/ModelAPI/ModelAPI_Result.cpp b/src/ModelAPI/ModelAPI_Result.cpp index 2c3bdc9f9..cf64c36e9 100644 --- a/src/ModelAPI/ModelAPI_Result.cpp +++ b/src/ModelAPI/ModelAPI_Result.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include @@ -52,6 +54,35 @@ void ModelAPI_Result::initAttributes() // Add the "Bring To Front" attribute to the Result base class, as we may support it in the future // for all type of results. Actually, only ResultGroups are supported. aData->addAttribute(BRING_TO_FRONT_ID(), ModelAPI_AttributeBoolean::typeId())->setIsArgument(false); + + AttributeBooleanPtr aIsPersistentAttr = std::dynamic_pointer_cast( + aData->addAttribute(IS_PERSISTENT_ID(), ModelAPI_AttributeBoolean::typeId())); + aIsPersistentAttr->setIsArgument(false); + if (!aIsPersistentAttr->isInitialized()){ + aIsPersistentAttr->setValue(false); + } + + AttributeBooleanPtr aCopyAttr = std::dynamic_pointer_cast( + aData->addAttribute(IS_COPY_TO_SWALLOW(), ModelAPI_AttributeBoolean::typeId())); + aCopyAttr->setIsArgument(false); + if (!aCopyAttr->isInitialized()){ + aCopyAttr->setValue(false); + } + + AttributeReferencePtr anOrigRefAttr = std::dynamic_pointer_cast( + aData->addAttribute(ORIGINAL_REFERENCE(), ModelAPI_AttributeReference::typeId())); + anOrigRefAttr->setIsArgument(false); + if (!anOrigRefAttr->isInitialized()){ + anOrigRefAttr->setValue(nullptr); + } + + AttributeIntegerPtr aCopyAmountAttr = std::dynamic_pointer_cast( + aData->addAttribute(COPY_AMOUNT(), ModelAPI_AttributeInteger::typeId())); + aCopyAmountAttr->setIsArgument(false); + + if (!aCopyAmountAttr->isInitialized()){ + aCopyAmountAttr->setValue(0); + } } bool ModelAPI_Result::setDisabled(std::shared_ptr theThis, const bool theFlag) @@ -86,6 +117,23 @@ bool ModelAPI_Result::isDisabled() return myIsDisabled; } +void ModelAPI_Result::setPersistent(const bool theValue) +{ + AttributeBooleanPtr aAttr = data()->boolean(ModelAPI_Result::IS_PERSISTENT_ID()); + if (aAttr.get() != NULL) { + aAttr->setValue(theValue); + } +} + +bool ModelAPI_Result::isPersistent() +{ + AttributeBooleanPtr aAttr = data()->boolean(ModelAPI_Result::IS_PERSISTENT_ID()); + if (aAttr.get() != NULL) { + return aAttr->value(); + } + return false; +} + bool ModelAPI_Result::isConcealed() { return myIsConcealed; @@ -109,6 +157,12 @@ void ModelAPI_Result::setIsConcealed(const bool theValue, const bool /*theForced } } +// std::shared_ptr ModelAPI_Result::createCopyToSwallow() +// { +// return std::shared_ptr(); +// } + + std::shared_ptr ModelAPI_Result::shape() { return std::shared_ptr(); diff --git a/src/ModelAPI/ModelAPI_Result.h b/src/ModelAPI/ModelAPI_Result.h index e82ea2f35..0d189f862 100644 --- a/src/ModelAPI/ModelAPI_Result.h +++ b/src/ModelAPI/ModelAPI_Result.h @@ -35,6 +35,8 @@ typedef std::list > ListOfShape; */ class ModelAPI_Result : public ModelAPI_Object { +// std::shared_ptr myTempCopy; +// bool myIsCreatingCopy; bool myIsConcealed; ///< the result is concealed from the data tree (referenced by other objects) protected: bool myIsDisabled; ///< the result is disabled: removed for the user, but keeps the general info @@ -97,6 +99,39 @@ class ModelAPI_Result : public ModelAPI_Object return MY_BRING_TO_FRONT_ID; } + /// Reference to the IsPersistent flag of the result. + /// Persistent means the result will remain even after generating a new feature from it. + /// The bool value is used. + inline static const std::string& IS_PERSISTENT_ID() + { + static const std::string MY_IS_PERSISTENT_ID("Is_persistent"); + return MY_IS_PERSISTENT_ID; + } + + ///True if the result is a copy of persistent result. + /// The bool value is used. + inline static const std::string& IS_COPY_TO_SWALLOW() + { + static const std::string MY_COPY_TO_SWALLOW("is_copy_to_swallow"); + return MY_COPY_TO_SWALLOW; + } + + /// If this result is a copy to swallow - stores the original result in this attribute + /// The reference value is used. + inline static const std::string& ORIGINAL_REFERENCE() + { + static const std::string MY_ORIGINAL_REFERENCE("original_reference"); + return MY_ORIGINAL_REFERENCE; + } + + /// Reference to the amount of copies of the result. + /// The int value is used. + inline static const std::string& COPY_AMOUNT() + { + static const std::string MY_COPY_AMOUNT("Copy_amount"); + return MY_COPY_AMOUNT; + } + /// Returns true if the result is concealed from the data tree (referenced by other objects) MODELAPI_EXPORT virtual bool isConcealed(); @@ -115,6 +150,16 @@ class ModelAPI_Result : public ModelAPI_Object /// Returns the result is disabled or not. MODELAPI_EXPORT virtual bool isDisabled(); + /// Set whether the result should be persistent or not (persistent means the result will + /// remain even after generating a new feature from it) + MODELAPI_EXPORT virtual void setPersistent(const bool theValue); + + /// Returns the result is persistent or not. + MODELAPI_EXPORT virtual bool isPersistent(); + +// /// For persistent result - creates a copy to be swallowed by the new feature instead of original result +// MODELAPI_EXPORT virtual std::shared_ptr createCopyToSwallow(); + /// Request for initialization of data model of the result: adding all attributes MODELAPI_EXPORT virtual void initAttributes(); diff --git a/src/ModelAPI/ModelAPI_ResultBody.h b/src/ModelAPI/ModelAPI_ResultBody.h index 223c91d55..1d9ca77d0 100644 --- a/src/ModelAPI/ModelAPI_ResultBody.h +++ b/src/ModelAPI/ModelAPI_ResultBody.h @@ -216,6 +216,12 @@ public: MODELAPI_EXPORT virtual const std::vector& findShapeColor( const std::wstring& theShapeName) = 0; + /// For persistent result - creates/return a copy of this result, so the copy can be swallowed + // by the new feature instead of original result + MODELAPI_EXPORT virtual std::shared_ptr getCopyToSwallow() = 0; + + MODELAPI_EXPORT virtual void nullifyTempCopy() = 0; + protected: /// Default constructor accessible only from Model_Objects diff --git a/src/ModelAPI/ModelAPI_Tools.cpp b/src/ModelAPI/ModelAPI_Tools.cpp index 508ca38e8..2ef9b0857 100644 --- a/src/ModelAPI/ModelAPI_Tools.cpp +++ b/src/ModelAPI/ModelAPI_Tools.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -1231,6 +1233,7 @@ void setTransparency(ResultPtr theResult, double theTransparency) } } +//************************************************************** double getTransparency(const std::shared_ptr& theResult) { double aTransparency = -1; @@ -1245,6 +1248,84 @@ double getTransparency(const std::shared_ptr& theResult) return aTransparency; } +//************************************************************** +void increaseCopyAmount(std::shared_ptr theResult) +{ + if (!theResult.get()) + return; + + AttributeIntegerPtr anAttribute = theResult->data()->integer(ModelAPI_Result::COPY_AMOUNT()); + if (anAttribute.get() != NULL) { + int aCurrentValue = anAttribute->value(); + anAttribute->setValue(++aCurrentValue); + } + +} + +//************************************************************** +int getCopyAmount(std::shared_ptr theResult) +{ + int anAmountOfCopies = 0; + if (theResult.get() != NULL && + theResult->data()->attribute(ModelAPI_Result::COPY_AMOUNT()).get() != NULL) { + AttributeIntegerPtr anIntAttr = theResult->data()->integer(ModelAPI_Result::COPY_AMOUNT()); + if (anIntAttr.get() && anIntAttr->isInitialized()) { + anAmountOfCopies = anIntAttr->value(); + } + } + return anAmountOfCopies; +} + +//****************************************************** +void setIsCopyToSwallow(std::shared_ptr theResult, bool theFlag) +{ + if (!theResult.get()) + return; + + AttributeBooleanPtr aAttr = theResult->data()->boolean(ModelAPI_Result::IS_COPY_TO_SWALLOW()); + if (aAttr.get() != NULL) { + aAttr->setValue(theFlag); + } +} + +//****************************************************** +bool isCopyToSwallow(std::shared_ptr theResult) +{ + if (!theResult.get()) + return false; + + AttributeBooleanPtr aAttr = theResult->data()->boolean(ModelAPI_Result::IS_COPY_TO_SWALLOW()); + if (aAttr.get() != NULL) { + return aAttr->value(); + } + return false; +} + +//****************************************************** +void setOriginalObject(std::shared_ptr theCopyResult, std::shared_ptr theOriginalResult) +{ + if (!theCopyResult.get()) + return; + + AttributeReferencePtr aAttr = theCopyResult->data()->reference(ModelAPI_Result::ORIGINAL_REFERENCE()); + if (aAttr.get() != NULL) { + aAttr->setValue(theOriginalResult); + } +} + +//****************************************************** +ObjectPtr getOriginalObject(std::shared_ptr theCopyResult) +{ + if (!theCopyResult.get()) + return; + + AttributeReferencePtr aAttr = theCopyResult->data()->reference(ModelAPI_Result::ORIGINAL_REFERENCE()); + if (aAttr.get() != NULL) { + return aAttr->value(); + } + return nullptr; +} + void copyVisualizationAttrs( std::shared_ptr theSource, std::shared_ptr theDest) { diff --git a/src/ModelAPI/ModelAPI_Tools.h b/src/ModelAPI/ModelAPI_Tools.h index beb380f86..597d38e26 100644 --- a/src/ModelAPI/ModelAPI_Tools.h +++ b/src/ModelAPI/ModelAPI_Tools.h @@ -320,6 +320,28 @@ MODELAPI_EXPORT void bringToFront(std::shared_ptr theResult, bo MODELAPI_EXPORT bool isBringToFront(std::shared_ptr theResult); +/*! Increase copy amount of the result +* \param[in] theResult a result object +*/ +MODELAPI_EXPORT void increaseCopyAmount(std::shared_ptr theResult); + +MODELAPI_EXPORT int getCopyAmount(std::shared_ptr theResult); + +/*! Set result is copy or not +* \param[in] theResult a result object +*/ +MODELAPI_EXPORT void setIsCopyToSwallow(std::shared_ptr theResult, bool theFlag); + +MODELAPI_EXPORT bool isCopyToSwallow(std::shared_ptr theResult); + +/*! Set the original result to a copy result +* \param[in] theCopyResult a copy result object +* \param[in] theOriginalResult an original result object +*/ +MODELAPI_EXPORT void setOriginalObject(std::shared_ptr theCopyResult, std::shared_ptr theOriginalResult); + +MODELAPI_EXPORT ObjectPtr getOriginalObject(std::shared_ptr theCopyResult); + /*! Returns current transparency in the given result * \param theResult a result object * \return a transparency value or -1 if it was not defined diff --git a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp index cc22e0d1c..67018cb57 100644 --- a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp +++ b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp @@ -291,8 +291,18 @@ static void getShapeAndContext(const AttributeSelectionPtr& theAttrSelect, GeomShapePtr& theShape, ResultPtr& theContext) { if (theAttrSelect->isInitialized()) { - theShape = theAttrSelect->value(); - theContext = theAttrSelect->context(); + // if(theAttrSelect->isCopyToSwallow()) + // { + // auto aPairContextShape = theAttrSelect->getOriginalContext(); + // theContext = aPairContextShape.first; + // theShape = aPairContextShape.second; + // } + // else + // { + theShape = theAttrSelect->value(); + theContext = theAttrSelect->context(); + //} + if (!theShape.get()) theShape = theContext->shape(); @@ -736,8 +746,12 @@ bool ModelHighAPI_Dumper::process(const std::shared_ptr& theD dumpFolder(aFolder); else { FeaturePtr aFeature = std::dynamic_pointer_cast(*anObjIt); - if (aFeature) // dump common feature + if (aFeature){ // dump common feature dumpFeature(aFeature); + if(aFeature->lastResult() && aFeature->lastResult()->isPersistent()) + *this << aFeature << ".defaultResult().setPersistent(True)" << std::endl; + } + } } } @@ -863,7 +877,9 @@ void ModelHighAPI_Dumper::dumpPostponed(bool theDumpFolders) else { FeaturePtr aFeature = std::dynamic_pointer_cast(*anIt); if (aFeature) + { dumpFeature(aFeature, true); + } } } myDumpPostponedInProgress = false; @@ -1524,33 +1540,51 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<( GeomShapePtr aShape; std::string aShapeTypeStr; + ResultPtr aContext; bool isAdded = false; + bool isCopy = false; for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) { AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex); - aShape = anAttribute->value(); - if(!aShape.get()) { - ResultPtr aContext = anAttribute->context(); - if (aContext.get()) - aShape = aContext->shape(); + if(anAttribute->isCopyToSwallow()) + { + isCopy = true; + auto aPairContextShape = anAttribute->getOriginalContext(); + aContext = aPairContextShape.first; + aShape = aPairContextShape.second; + theAttrSelList->append(aContext,aShape); + anAttribute = theAttrSelList->value(theAttrSelList->size()-1); } + else + { + aShape = anAttribute->value(); + if(!aShape.get()) { + aContext = anAttribute->context(); + if (aContext.get()) + aShape = aContext->shape(); + } - if(!aShape.get()) { - continue; - } + if(!aShape.get()) { + continue; + } - if(isAdded) { - // print each attribute on separate line with the appropriate shift - if (aNbSpaces > 0) { - std::string aSpaces(aNbSpaces + 1, ' '); - *myDumpStorage << ",\n" << aSpaces; - } else - *myDumpStorage << ", "; - } else { - isAdded = true; + if(isAdded) { + // print each attribute on separate line with the appropriate shift + if (aNbSpaces > 0) { + std::string aSpaces(aNbSpaces + 1, ' '); + *myDumpStorage << ",\n" << aSpaces; + } else + *myDumpStorage << ", "; + } else { + isAdded = true; + } } *this << anAttribute; + if(isCopy) + { + theAttrSelList->removeLast(); + } } // check selection list is obtained by filters diff --git a/src/PartSet/PartSet_IconFactory.cpp b/src/PartSet/PartSet_IconFactory.cpp index b678584b2..f8a64551e 100644 --- a/src/PartSet/PartSet_IconFactory.cpp +++ b/src/PartSet/PartSet_IconFactory.cpp @@ -143,16 +143,16 @@ QIcon PartSet_IconFactory::getIcon(ObjectPtr theObj) } ResultBodyPtr aBody = std::dynamic_pointer_cast(aResult); if (aBody.get() && aBody->isConnectedTopology()) - return QIcon(":pictures/compoundofsolids.png"); - return QIcon(":pictures/compound.png"); + return aResult->isPersistent()? QIcon(":pictures/compoundofsolids_persistent.png") : QIcon(":pictures/compoundofsolids.png"); + return aResult->isPersistent()? QIcon(":pictures/compound_persistent.png") : QIcon(":pictures/compound.png"); } - case GeomAPI_Shape::COMPSOLID: return QIcon(":pictures/compsolid.png"); - case GeomAPI_Shape::SOLID: return QIcon(":pictures/solid.png"); - case GeomAPI_Shape::SHELL: return QIcon(":pictures/shell.png"); - case GeomAPI_Shape::FACE: return QIcon(":pictures/face.png"); - case GeomAPI_Shape::WIRE: return QIcon(":pictures/wire.png"); - case GeomAPI_Shape::EDGE: return QIcon(":pictures/edge.png"); - case GeomAPI_Shape::VERTEX: return QIcon(":pictures/vertex.png"); + case GeomAPI_Shape::COMPSOLID: return aResult->isPersistent()? QIcon(":pictures/compsolid_persistent.png") : QIcon(":pictures/compsolid.png"); + case GeomAPI_Shape::SOLID: return aResult->isPersistent()? QIcon(":pictures/solid_persistent.png") : QIcon(":pictures/solid.png"); + case GeomAPI_Shape::SHELL: return aResult->isPersistent()? QIcon(":pictures/shell_persistent.png") : QIcon(":pictures/shell.png"); + case GeomAPI_Shape::FACE: return aResult->isPersistent()? QIcon(":pictures/face_persistent.png") : QIcon(":pictures/face.png"); + case GeomAPI_Shape::WIRE: return aResult->isPersistent()? QIcon(":pictures/wire_persistent.png") : QIcon(":pictures/wire.png"); + case GeomAPI_Shape::EDGE: return aResult->isPersistent()? QIcon(":pictures/edge_persistent.png") :QIcon(":pictures/edge.png"); + case GeomAPI_Shape::VERTEX: return aResult->isPersistent()? QIcon(":pictures/vertex_persistent.png") :QIcon(":pictures/vertex.png"); default: // [to avoid compilation warning] break; } diff --git a/src/XGUI/XGUI_ContextMenuMgr.cpp b/src/XGUI/XGUI_ContextMenuMgr.cpp index 5b4365658..c764385fe 100644 --- a/src/XGUI/XGUI_ContextMenuMgr.cpp +++ b/src/XGUI/XGUI_ContextMenuMgr.cpp @@ -174,6 +174,10 @@ void XGUI_ContextMenuMgr::createActions() anAction->setCheckable(true); addAction("BRING_TO_FRONT_CMD", anAction); + anAction = ModuleBase_Tools::createAction(QIcon(), tr("Is persistent"), aDesktop); + anAction->setCheckable(true); + addAction("PERSISTENT_CMD", anAction); + mySeparator1 = ModuleBase_Tools::createAction(QIcon(), "", aDesktop); mySeparator1->setSeparator(true); @@ -362,6 +366,9 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu() action("SHOW_ISOLINES_CMD")->setEnabled(true); action("SHOW_ISOLINES_CMD")->setChecked(ModelAPI_Tools::isShownIsoLines(aResult)); action("ISOLINES_CMD")->setEnabled(true); + + action("PERSISTENT_CMD")->setEnabled(true); + action("PERSISTENT_CMD")->setChecked(aResult->isPersistent()); } if (!hasFeature) { bool aHasSubResults = ModelAPI_Tools::hasSubResults(aResult); @@ -419,6 +426,7 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu() action("BRING_TO_FRONT_CMD")->setEnabled(hasGroupsOnly); action("SHOW_ISOLINES_CMD")->setEnabled(true); action("ISOLINES_CMD")->setEnabled(true); + action("PERSISTENT_CMD")->setEnabled(true); } if (hasFeature && myWorkshop->canMoveFeature()) { action("MOVE_CMD")->setEnabled(true); @@ -741,6 +749,7 @@ void XGUI_ContextMenuMgr::buildObjBrowserMenu() aList.append(action("SHOW_ISOLINES_CMD")); aList.append(action("ISOLINES_CMD")); aList.append(action("SHOW_FEATURE_CMD")); + aList.append(action("PERSISTENT_CMD")); aList.append(mySeparator3); aList.append(action("DELETE_CMD")); // Result body menu @@ -848,6 +857,7 @@ void XGUI_ContextMenuMgr::buildViewerMenu() aList.append(action("TRANSPARENCY_CMD")); aList.append(action("SHOW_ISOLINES_CMD")); aList.append(action("ISOLINES_CMD")); + aList.append(action("PERSISTENT_CMD")); aList.append(mySeparator3); aList.append(action("SET_VIEW_NORMAL_CMD")); aList.append(action("SET_VIEW_INVERTEDNORMAL_CMD")); @@ -928,6 +938,7 @@ void XGUI_ContextMenuMgr::addObjBrowserMenu(QMenu* theMenu) const anActions.append(action("SHOW_ISOLINES_CMD")); anActions.append(action("ISOLINES_CMD")); anActions.append(action("CLEAN_HISTORY_CMD")); + anActions.append(action("PERSISTENT_CMD")); anActions.append(action("DELETE_CMD")); } #ifdef _DEBUG diff --git a/src/XGUI/XGUI_Workshop.cpp b/src/XGUI/XGUI_Workshop.cpp index 0df895c85..e0c632329 100644 --- a/src/XGUI/XGUI_Workshop.cpp +++ b/src/XGUI/XGUI_Workshop.cpp @@ -1808,6 +1808,14 @@ void XGUI_Workshop::onContextMenuCommand(const QString& theId, bool isChecked) mySelector->clearSelection(); Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY)); } + else if (theId == "PERSISTENT_CMD") { + foreach(ObjectPtr aObj, anObjects) { + ResultPtr aResult = std::dynamic_pointer_cast(aObj); + if (aResult.get()) + aResult->setPersistent(!aResult->isPersistent()); + } + mySelector->clearSelection(); + } else if (theId == "DEFLECTION_CMD") changeDeflection(anObjects); else if (theId == "TRANSPARENCY_CMD") diff --git a/src/XGUI/XGUI_pictures.qrc b/src/XGUI/XGUI_pictures.qrc index f598306b4..1aadf26c7 100644 --- a/src/XGUI/XGUI_pictures.qrc +++ b/src/XGUI/XGUI_pictures.qrc @@ -58,6 +58,16 @@ pictures/result.png pictures/find_result.png + pictures/compsolid_persistent.png + pictures/compoundofsolids_persistent.png + pictures/solid_persistent.png + pictures/edge_persistent.png + pictures/vertex_persistent.png + pictures/face_persistent.png + pictures/compound_persistent.png + pictures/shell_persistent.png + pictures/wire_persistent.png + pictures/eyeclosed.png pictures/eyemiclosed.png pictures/eyeopen.png diff --git a/src/XGUI/pictures/compound_persistent.png b/src/XGUI/pictures/compound_persistent.png new file mode 100755 index 0000000000000000000000000000000000000000..03347e3794c086e2792bdcaff13fd5636cb01e15 GIT binary patch literal 884 zcmV-)1B?8LP)EX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?ls!2paR5;6}lTAoeQ5462=gphJF_aM0 z!p!LMQ7YtdLwopFe{a2J<*&X523pL3wZBmWaz-TinC*cN1AXWkwL2${-?SKYps z-v9C`T_kxG!9$bhz|gg(=E}p(uge-R32d0kdSNO{ze4IyL7s?D3V0^IbOC6;*3$QGMsjN6K3f#=aj{^fKJFyKAi^cpP>~pwkeRtk&J7=P) ztTNygFbI?Z2f&cwI&bqSN+y$%w+p7S8gu0+0K;`8m~f`1ARqvW8?G}0kO@cGsi>I( z1`O932fEH6S|LEnPRs_LS)dQt$c7>f!8pC)VP9ok?T!8i9rxcvB9U{zm9bF(!kMFx z;W_{nx%&@L9*r)wx3u2TbzMBq`_pp~j!%w@PNu1>Nxi+L_12?!&m}MVU%wQi_;mMb zCGOJGk^m_?@j?IgEX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?lD@jB_R5;6Hl=F$;lN6W8u{1HOV`F1u zVn72Y&z}Bya9SrX0}jB##mRZZNY6lvg@uLj-@kth3=9kmjEsy7KYspT@N#iyc=Y5k z8v`D|Vx(uF=I7zXh%S*+Ud-s_;{Kn30AOOE5U}8r{Pg+r|JQHcT)-DjjP_aO-^`6o zF$`M1ZZ(6but?SGw{K=Bet!NLSQ3`7|C@$KvX{YMV{Wnf@nc>MSwBLf4( z>%V^g`ooAXgikD-#EQ@0Lx&DA;!TM}r2~8^QJM$?C{Br#C*`NlpZ$Y{Ff#b{`xnC( n1>LIGZ{N&#ymjRlTm}FDVE1%-TQ&o300000NkvXXu0mjfDga=X literal 0 HcmV?d00001 diff --git a/src/XGUI/pictures/compsolid_persistent.png b/src/XGUI/pictures/compsolid_persistent.png new file mode 100755 index 0000000000000000000000000000000000000000..ec9ab4511fd7f9cec13330a313ae0cf08743fe6e GIT binary patch literal 771 zcmV+e1N{7nP)EX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?lIY~r8R5;6HU>In?2xG|mM2NGpvNrSb z@FuXZF#knX^X$b7b}4a*ALyE&y?FkSg@J*ALCz<_OMqXX%*Ec(+}_HDnU$558Kx+u zAe(80;VF)=YPFfuay`}>zEv$TL=cK?)r z3=9kmjEs!v1~R1NXZ>RN_y2$O|9}4&XD^2$A2^WL0VRB5ArUMpY6kRy5o#DcP?M%v0HVpiy)CDZe%w-fA@M!DG$t=Hr|NdXs z-uxRaWiv1^FtC%97q9_4JG=k*#7J>&1OUs=T+NZht0({f002ovPDHLkV1mX& BXT$&i literal 0 HcmV?d00001 diff --git a/src/XGUI/pictures/edge_persistent.png b/src/XGUI/pictures/edge_persistent.png new file mode 100755 index 0000000000000000000000000000000000000000..d58fae35f66f401949bda566c43e890a27fc06ba GIT binary patch literal 670 zcmV;P0%84$P)EX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?k)Ja4^R5;6}lFbT&Koo^PC!U@cAjrlX#1z z6mT=enK|leV)XxAP7G4 zU7Ds&FQ5)Ir}XlhIrrIiI+=`cD^0s#J008;^(GuE^C8&_cS1tYIUK9|z>a{vGU07*qoM6N<$ Ef}DaYlmGw# literal 0 HcmV?d00001 diff --git a/src/XGUI/pictures/face_persistent.png b/src/XGUI/pictures/face_persistent.png new file mode 100755 index 0000000000000000000000000000000000000000..847fcc19a6bd76ba091d90d5fa859f85985562d5 GIT binary patch literal 757 zcmVEX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?lD@jB_R5;6HV88>!Tml%yTml&JsbONk z4;UGSKVU=(FfL9G&ZR6Y%mPqxrXN3kDKauK{89SyLIGLRGhz7?3=9lRzkmP!$O04O z;^dmGr>)~@V`0h8h!l34`|2=km3sToLe@3p|KXFz?y@j2Ffd5E1%>i(bNi>n#Iv)q zvZ5&745ce)uVG+dU|?uZ4PjtlU|=|M^4QlOKYsWziMa&Gv9htvDagp?M%N5eY~bJt z7t?p}U|?WiV37axjOW#!4cA$?IJq{2`UmnzNk}sM`}dFGKLZ29|9}5b4Epo;55tci zKN$Z1|Icvy=8gZzVaF)%6LI?2@89|i3=IDn85#dGGBEsSfYM4|UvlA#fkTH5G2%^W nc6N6E2^qjdX*w8q11JUn(lJ=?8T^In00000NkvXXu0mjf?YLjE literal 0 HcmV?d00001 diff --git a/src/XGUI/pictures/shell_persistent.png b/src/XGUI/pictures/shell_persistent.png new file mode 100755 index 0000000000000000000000000000000000000000..da6f908323d71e1249389609d6d2a32e9384a54c GIT binary patch literal 779 zcmV+m1N8ifP)EX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?lK}keGR5;6HU>In?2xG|mM2NGpvNrSb z@FuXZF#knS^Pk~AHqFmoJpahTz`(#D=M&*2z%NkdV((~fZ)L;G%F4=&%f^5I{xLj$ z`sClVd9$A|Gc)(G$ooVzNr+1%BuB?fN{ERuF)=YQ{QdV2?leY5MihF-zTLlfAK16= z>$h)RkGHNoz$oh-{#Q~=oDsu~{~;p(85kJ2?pW7kD=km#7JalSI%DZ?=H7EoWK3%D@JLL5GE#knqamYIC#Rv zm|0j3LFN82FfiaZ02}!G=MOx%p6*!lhlLnT7Y=M^xNu-QlQM*Bk1WSTX-cHn0G8js zfB&y*Z~l#zvKbf{85lSj7}$x<3)q02o!x(Y@`nx`Vx%}X0ss<4Ur@~CkW~Nx002ov JPDHLkV1hE7YC!-1 literal 0 HcmV?d00001 diff --git a/src/XGUI/pictures/solid_persistent.png b/src/XGUI/pictures/solid_persistent.png new file mode 100755 index 0000000000000000000000000000000000000000..fc74b0c239a89c0ac1e5b306ce340bb3f956d85e GIT binary patch literal 738 zcmV<80v-K{P)EX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?l7)eAyR5;6HU>In?2xG|mM2NGpvNrSb z@FuXZF#knS^Pk~AHqFmoJpahZz`(#D=M&*2$S+XlV((~fZ)L;A%F0TV?J4B;4`v-R#BO@aUjSg7;{r_M6|KC5x*^B3+%KV48`ac5$ z1J}KaD7q_WulWZzfRT~$M`J||2QFu}r-m?L6N4GT#7JalSI%DZ?=H7EhBy;GO)%RP z^-SPmMlQZ6a!kb7{{J5$7Fk$XQKFQE7y}+$Im^(V8p5Q^z`$^7`#QL<{}by1T!4j{ zxr`zM9&KGYndSHI-~a2{n}4IFYz78K1_n+B26m$J0ybc0XZIhU{GmgK7%9$;0M(>S U+R*u~X8-^I07*qoM6N<$f+J#BkpKVy literal 0 HcmV?d00001 diff --git a/src/XGUI/pictures/vertex_persistent.png b/src/XGUI/pictures/vertex_persistent.png new file mode 100755 index 0000000000000000000000000000000000000000..3915e06f546781106f4f9ed3d61ebc2bfc286bbf GIT binary patch literal 820 zcmV-41Izr0P)EX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?lYDq*vR5;7Uld)@)Q5400=e@K6b!ssV z4q~At!T%vc$<#qI)y>7xQK^GV7m-e-E{a1pDWy_USKEN%`@gO}ASlh(uh_nm_4NX{EA$+AFqbCXVajT?>N5nAFClHln9mBmHe8o^dZO=7 zPEXk`R*sT`ac_`eoM!3+xQ2+NR5LA|4|VD00000x$iEP)EX>4Tx04R}tkvmAkKpe)uriu?*9PA+CkfA!+!3QEvrHVzcP}&NuI+$Ggf+h_~ zii@M*T5#~OSaoo5*44pP5Ck6}&JIqBE>hzEl0u6Z503ls-F+W--vL6S!c?;>3aFZ8 zq~b9#n_CroUeN~uLCj%7Vy2!*FJ|C5zV6}U>s^Frd7t}p1eLtW0G~)4Wx8PzZxGLH zS~}-_;t(rI3h_DdxIq^re&o9B@*C%(!vfC?8JW~PafnzfcCg&RtYoOf)5Kv#)hJ)c zx~y>C;;dF`taVTR!a!bINpqd%2;x{m0!fIFQ9~IOScuT7kzyi6`!Nsyu;WjXOD0zt zj2sK7LWSh`!T;cQw`O5#(oG7)fbJLD{ul*%c7bNyw!e>UyLkfmpMfi_?XNa~*-z5z zZ7qBR3~U1z*KJMS11@)fp(kB3BuDbo6bc34{fxdT2Mpc<{cCP-t$mz602%6P`35*R z1jdS#z3%bu-p=0sJ=5y%2L!%y;gb_U*Z=?l21!IgR5;7+lD#ejQ5462=bFs+i$%7b z!bkLq%~&yshA^)nvU*S96;|Q_NQej(ja#V|!XwC9i6~@u)*OY)-i%eq78?KRp8P-V zx%V9S$IuYJ7*Em~!cheX?;Jt2-I7 z99s=Cqo*hl&=Gi^NYdSY{zoG{GZIyjjV3_Wdzme14#4B(@p*u<;N&Hg9Yw#rH^|?r zh1UC=s%#q3(hpN={%VXxcz-Yu)><*cE|U54RelFQH%r3fGO_aj0000