From 72b9423caaa48805589d6ab87d366f79ecde5bfe Mon Sep 17 00:00:00 2001 From: azv Date: Thu, 23 Nov 2017 15:18:00 +0300 Subject: [PATCH] Moving features to a folder has been implemented (Task 2.3. Ability to put consecutive Features in a folder) --- src/Model/Model_Document.cpp | 27 ++++ src/Model/Model_Document.h | 20 +++ src/Model/Model_Objects.cpp | 180 +++++++++++++++++++++++++ src/Model/Model_Objects.h | 18 +++ src/ModelAPI/CMakeLists.txt | 1 + src/ModelAPI/ModelAPI_Document.h | 19 +++ src/ModelAPI/ModelAPI_Folder.h | 5 + src/ModelAPI/Test/TestFolder_Update.py | 165 +++++++++++++++++++++++ 8 files changed, 435 insertions(+) create mode 100644 src/ModelAPI/Test/TestFolder_Update.py diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index e8e9a5887..f61edd015 100755 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -1265,6 +1265,33 @@ std::shared_ptr Model_Document::addFolder( void Model_Document::removeFolder(std::shared_ptr theFolder) { + if (theFolder) + myObjs->removeFolder(theFolder); +} + +std::shared_ptr Model_Document::findFolderAbove( + const std::list >& theFeatures) +{ + return myObjs->findFolder(theFeatures, false); +} + +std::shared_ptr Model_Document::findFolderBelow( + const std::list >& theFeatures) +{ + return myObjs->findFolder(theFeatures, true); +} + +bool Model_Document::moveToFolder( + const std::list >& theFeatures, + const std::shared_ptr& theFolder) +{ + return myObjs->moveToFolder(theFeatures, theFolder); +} + +bool Model_Document::removeFromFolder( + const std::list >& theFeatures) +{ + return myObjs->removeFromFolder(theFeatures); } std::shared_ptr Model_Document::feature( diff --git a/src/Model/Model_Document.h b/src/Model/Model_Document.h index c16238c89..c0d7a7ad4 100644 --- a/src/Model/Model_Document.h +++ b/src/Model/Model_Document.h @@ -209,6 +209,26 @@ class Model_Document : public ModelAPI_Document std::shared_ptr theAddBefore = std::shared_ptr()); //! Removes the folder from the document (all features in the folder will be kept). MODEL_EXPORT virtual void removeFolder(std::shared_ptr theFolder); + //! Search a folder above the list of features applicable to store them + //! (it means the list of features stored in the folder should be consequential) + //! \return Empty pointer if there is no applicable folder + MODEL_EXPORT virtual std::shared_ptr findFolderAbove( + const std::list >& theFeatures); + //! Search a folder below the list of features applicable to store them + //! (it means the list of features stored in the folder should be consequential) + //! \return Empty pointer if there is no applicable folder + MODEL_EXPORT virtual std::shared_ptr findFolderBelow( + const std::list >& theFeatures); + //! Add a list of features to the folder. The correctness of the adding is not performed + //! (such checks have been done in corresponding find.. method). + //! \return \c true if the movement is successfull + MODEL_EXPORT virtual bool moveToFolder( + const std::list >& theFeatures, + const std::shared_ptr& theFolder); + //! Remove features from the folder + //! \return \c true if the features have been moved out + MODEL_EXPORT virtual bool removeFromFolder( + const std::list >& theFeatures); ///! Returns true if parametric updater need to execute feature on recomputartion ///! On abort, undo or redo it is not necessary: results in document are updated automatically diff --git a/src/Model/Model_Objects.cpp b/src/Model/Model_Objects.cpp index a0c1640f3..104ec2b1d 100644 --- a/src/Model/Model_Objects.cpp +++ b/src/Model/Model_Objects.cpp @@ -1252,6 +1252,186 @@ std::shared_ptr Model_Objects::createFolder( return aFolder; } +void Model_Objects::removeFolder(std::shared_ptr theFolder) +{ + /// \todo +} + +static FeaturePtr limitingFeature(std::list& theFeatures, const bool isLast) +{ + FeaturePtr aFeature; + if (isLast) { + aFeature = theFeatures.back(); + theFeatures.pop_back(); + } else { + aFeature = theFeatures.front(); + theFeatures.pop_front(); + } + return aFeature; +} + +std::shared_ptr Model_Objects::findFolder( + const std::list >& theFeatures, + const bool theBelow) +{ + if (theFeatures.empty()) + return FolderPtr(); // nothing to move + + TDF_Label aFeaturesLab = featuresLabel(); + Handle(TDataStd_ReferenceArray) aRefs; + if (!aFeaturesLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) + return FolderPtr(); // no reference array (something is wrong) + + std::list > aFeatures = theFeatures; + std::shared_ptr aLimitingFeature = limitingFeature(aFeatures, theBelow); + + std::shared_ptr aData = + std::static_pointer_cast(aLimitingFeature->data()); + if (!aData || !aData->isValid()) + return FolderPtr(); // invalid feature + + // label of the first feature in the list for fast searching + TDF_Label aFirstFeatureLabel = aData->label().Father(); + + // find a folder above the features and + // check the given features represent a sequential list of objects following the folder + FolderPtr aFoundFolder; + TDF_Label aLastFeatureInFolder; + int aRefIndex = aRefs->Lower(); + for(; aRefIndex <= aRefs->Upper(); ++aRefIndex) { // iterate all existing features + TDF_Label aCurLabel = aRefs->Value(aRefIndex); + if (IsEqual(aCurLabel, aFirstFeatureLabel)) + break; // no need to continue searching + + // searching the folder below, just continue to search last feature from the list + if (theBelow) + continue; + + if (!aLastFeatureInFolder.IsNull()) { + if (IsEqual(aCurLabel, aLastFeatureInFolder)) + aLastFeatureInFolder.Nullify(); // the last feature in the folder is achived + continue; + } + + aFoundFolder = std::dynamic_pointer_cast(folder(aCurLabel)); + if (aFoundFolder) { + AttributeReferencePtr aLastFeatAttr = + aFoundFolder->reference(ModelAPI_Folder::LAST_FEATURE_ID()); + if (aLastFeatAttr && aLastFeatAttr->isInitialized()) { + // setup iterating inside a folder to find last feature + ObjectPtr aLastFeature = aLastFeatAttr->value(); + if (aLastFeature) { + aData = std::static_pointer_cast(aLastFeature->data()); + if (aData && aData->isValid()) + aLastFeatureInFolder = aData->label().Father(); + } + } + } + } + + if (theBelow && aRefIndex < aRefs->Upper()) { + // check the next object is a folder + TDF_Label aLabel = aRefs->Value(aRefIndex + 1); + aFoundFolder = std::dynamic_pointer_cast(folder(aLabel)); + } + + if (!aLastFeatureInFolder.IsNull() || // the last feature of the folder above is not found + !aFoundFolder) + return FolderPtr(); + + // check the given features are sequential list + int aStep = theBelow ? -1 : 1; + for (aRefIndex += aStep; + !aFeatures.empty() && aRefIndex >= aRefs->Lower() && aRefIndex <= aRefs->Upper(); + aRefIndex += aStep) { + TDF_Label aCurLabel = aRefs->Value(aRefIndex); + TDF_Label aFeatureLabel; + + aLimitingFeature = limitingFeature(aFeatures, theBelow); + aData = std::static_pointer_cast(aLimitingFeature->data()); + if (aData && aData->isValid()) + aFeatureLabel = aData->label().Father(); + + if (!IsEqual(aCurLabel, aFeatureLabel)) + return FolderPtr(); // not a sequential list + } + + return aFoundFolder; +} + +bool Model_Objects::moveToFolder( + const std::list >& theFeatures, + const std::shared_ptr& theFolder) +{ + if (theFeatures.empty() || !theFolder) + return false; + + // labels for the folder and last feature in the list + TDF_Label aFolderLabel, aLastFeatureLabel; + std::shared_ptr aData = + std::static_pointer_cast(theFolder->data()); + if (aData && aData->isValid()) + aFolderLabel = aData->label().Father(); + aData = std::static_pointer_cast(theFeatures.back()->data()); + if (aData && aData->isValid()) + aLastFeatureLabel = aData->label().Father(); + + if (aFolderLabel.IsNull() || aLastFeatureLabel.IsNull()) + return false; + + // check the folder is below the list of features + bool isFolderBelow = false; + TDF_Label aFeaturesLab = featuresLabel(); + Handle(TDataStd_ReferenceArray) aRefs; + if (!aFeaturesLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) + return false; // no reference array (something is wrong) + for (int aRefIndex = aRefs->Lower(); aRefIndex <= aRefs->Upper(); ++aRefIndex) { + TDF_Label aCurLabel = aRefs->Value(aRefIndex); + if (aCurLabel == aFolderLabel) + break; // folder is above the features + else if (aCurLabel == aLastFeatureLabel) { + isFolderBelow = true; + break; + } + } + + if (isFolderBelow) { + aData = std::static_pointer_cast(theFeatures.front()->data()); + if (!aData || !aData->isValid()) + return false; + TDF_Label aPrevFeatureLabel = aData->label().Father(); + // label of the feature before the first feature in the list + for (int aRefIndex = aRefs->Lower(); aRefIndex <= aRefs->Upper(); ++aRefIndex) + if (aPrevFeatureLabel == aRefs->Value(aRefIndex)) { + if (aRefIndex == aRefs->Lower()) + aPrevFeatureLabel.Nullify(); + else + aPrevFeatureLabel = aRefs->Value(aRefIndex - 1); + break; + } + + // move the folder in the list of references before the first feature + RemoveFromRefArray(aFeaturesLab, aFolderLabel); + AddToRefArray(aFeaturesLab, aFolderLabel, aPrevFeatureLabel); + } else { + // update last feature of the folder + AttributeReferencePtr aLastFeatAttr = + theFolder->reference(ModelAPI_Folder::LAST_FEATURE_ID()); + aLastFeatAttr->setValue(theFeatures.back()); + } + + updateHistory(ModelAPI_Feature::group()); + return true; +} + +bool Model_Objects::removeFromFolder( + const std::list >& theFeatures) +{ + /// \todo + return false; +} + + std::shared_ptr Model_Objects::feature( const std::shared_ptr& theResult) { diff --git a/src/Model/Model_Objects.h b/src/Model/Model_Objects.h index 532b161ad..8df42019c 100644 --- a/src/Model/Model_Objects.h +++ b/src/Model/Model_Objects.h @@ -150,6 +150,24 @@ class Model_Objects /// Creates a folder (group of the features in the object browser) std::shared_ptr createFolder( const std::shared_ptr& theBeforeThis); + //! Removes the folder from the document (all features in the folder will be kept). + void removeFolder(std::shared_ptr theFolder); + //! Search a folder applicable for the list of features + //! (it means the list of features stored in the folder should be consequential) + //! \param theFeatures list of features to be added to folder + //! \param theBelow search the folder below the features (if \c false, search above) + //! \return Empty pointer if there is no applicable folder + std::shared_ptr findFolder( + const std::list >& theFeatures, + const bool theBelow); + //! Add a list of features to the folder. The correctness of the adding is not performed + //! (such checks have been done in corresponding find.. method). + //! \return \c true if the movement is successfull + bool moveToFolder(const std::list >& theFeatures, + const std::shared_ptr& theFolder); + //! Remove features from the folder + //! \return \c true if the features have been moved out + bool removeFromFolder(const std::list >& theFeatures); //! Sets the owner of this manager void setOwner(DocumentPtr theDoc); diff --git a/src/ModelAPI/CMakeLists.txt b/src/ModelAPI/CMakeLists.txt index dc2038c44..a5a864cf4 100644 --- a/src/ModelAPI/CMakeLists.txt +++ b/src/ModelAPI/CMakeLists.txt @@ -190,4 +190,5 @@ ADD_UNIT_TESTS(TestConstants.py TestCustomName_RotateGroup.py TestCustomName_Translation.py TestFolder_Create.py + TestFolder_Update.py ) diff --git a/src/ModelAPI/ModelAPI_Document.h b/src/ModelAPI/ModelAPI_Document.h index 069bb4bfd..5e3cc05bc 100644 --- a/src/ModelAPI/ModelAPI_Document.h +++ b/src/ModelAPI/ModelAPI_Document.h @@ -184,6 +184,25 @@ public: std::shared_ptr theAddBefore) = 0; //! Removes the folder from the document (all features in the folder will be kept). virtual void removeFolder(std::shared_ptr theFolder) = 0; + //! Search a folder above the list of features applicable to store them + //! (it means the list of features stored in the folder should be consequential) + //! \return Empty pointer if there is no applicable folder + virtual std::shared_ptr findFolderAbove( + const std::list >& theFeatures) = 0; + //! Search a folder below the list of features applicable to store them + //! (it means the list of features stored in the folder should be consequential) + //! \return Empty pointer if there is no applicable folder + virtual std::shared_ptr findFolderBelow( + const std::list >& theFeatures) = 0; + //! Add a list of features to the folder. The correctness of the adding is not performed + //! (such checks have been done in corresponding find.. method). + //! \return \c true if the movement is successfull + virtual bool moveToFolder(const std::list >& theFeatures, + const std::shared_ptr& theFolder) = 0; + //! Remove features from the folder + //! \return \c true if the features have been moved out + virtual bool removeFromFolder( + const std::list >& theFeatures) = 0; //! Informs the document that it becomes active and some actions must be performed virtual void setActive(const bool theFlag) = 0; diff --git a/src/ModelAPI/ModelAPI_Folder.h b/src/ModelAPI/ModelAPI_Folder.h index 1752a90ea..b89fd8704 100644 --- a/src/ModelAPI/ModelAPI_Folder.h +++ b/src/ModelAPI/ModelAPI_Folder.h @@ -87,6 +87,11 @@ public: { return data()->name(); } + /// Returns the reference attribute by the identifier + inline std::shared_ptr reference(const std::string& theID) + { + return data()->reference(theID); + } protected: /// This method is called just after creation of the object: it must initialize diff --git a/src/ModelAPI/Test/TestFolder_Update.py b/src/ModelAPI/Test/TestFolder_Update.py new file mode 100644 index 000000000..fa336ec06 --- /dev/null +++ b/src/ModelAPI/Test/TestFolder_Update.py @@ -0,0 +1,165 @@ +## 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 +## + +#========================================================================= +# Initialization of the test +#========================================================================= +from ModelAPI import * + +__updated__ = "2017-11-23" + +aSession = ModelAPI_Session.get() + + +def newPoint(theDocument, theX, theY, theZ): + aSession.startOperation() + aPoint = theDocument.addFeature("Point") + aPointData = aPoint.data() + assert(aPointData is not None) + aPointData.real("x").setValue(theX) + aPointData.real("y").setValue(theY) + aPointData.real("z").setValue(theZ) + aPointData.string("creation_method").setValue("by_xyz") + aSession.finishOperation() + return aPoint + + +#========================================================================= +# Test 1. Add a point into a folder above +#========================================================================= +aSession.startOperation() +aPart = aSession.moduleDocument().addFeature("Part") +aSession.finishOperation() + +# add point and a folder before it +aPartDoc = aSession.activeDocument() +aPoint1 = newPoint(aPartDoc, 0., 0., 0.) + +aSession.startOperation() +aFolder1 = aPartDoc.addFolder(aPoint1) +aSession.finishOperation() + +NB_FEATURES_FULL = 2 +NB_FEATURES_OUT = 2 + +assert(aPartDoc.size("Folders") == 1), "Wrong number of folders: {}".format(aPartDoc.size("Folders")) +assert(aPartDoc.size("Features") == 2), "Wrong number of features: {}".format(aPartDoc.size("Features")) +FOLDER_NAME_EXPECTED = "Folder_1" +assert(aFolder1.name() == FOLDER_NAME_EXPECTED), "Actual name '{}', expected '{}'".format(aFolder1.name(), FOLDER_NAME_EXPECTED) + +toFolder = FeatureList() +toFolder.append(aPoint1) + +# move point to the folder +aSession.startOperation() +aFolder = aPartDoc.findFolderAbove(toFolder) +assert(aFolder is not None) +isAdded = aPartDoc.moveToFolder(toFolder, aFolder) +aSession.finishOperation() +assert(isAdded) +NB_FEATURES_OUT -= 1 +# full number of features +assert(aPartDoc.size("Features") == NB_FEATURES_FULL), "Wrong number of features: {}, expected: {}".format(aPartDoc.size("Features"), NB_FEATURES_FULL) +# number of features outside the folder +assert(aPartDoc.size("Features", True) == NB_FEATURES_OUT), "Wrong number of features outside a folder: {}, expected: {}".format(aPartDoc.size("Features", True), NB_FEATURES_OUT) + +#========================================================================= +# Test 2. Add a point into a folder below +#========================================================================= +aPoint2 = newPoint(aPartDoc, 10., 0., 0.) +aPoint3 = newPoint(aPartDoc, 10., 10., 0.) + +NB_FEATURES_FULL += 2 +NB_FEATURES_OUT += 2 + +assert(aPartDoc.size("Features") == NB_FEATURES_FULL), "Wrong number of features: {}, expected: {}".format(aPartDoc.size("Features"), NB_FEATURES_FULL) +assert(aPartDoc.size("Features", True) == NB_FEATURES_OUT), "Wrong number of features outside a folder: {}, expected: {}".format(aPartDoc.size("Features", True), NB_FEATURES_OUT) + +# add a folder +aSession.startOperation() +aFolder2 = aPartDoc.addFolder(aPoint3) +aSession.finishOperation() + +NB_FEATURES_FULL += 1 +NB_FEATURES_OUT += 1 + +assert(aPartDoc.size("Folders") == 2), "Wrong number of folders: {}".format(aPartDoc.size("Folders")) +assert(aPartDoc.size("Features") == NB_FEATURES_FULL), "Wrong number of features: {}, expected: {}".format(aPartDoc.size("Features"), NB_FEATURES_FULL) +assert(aPartDoc.size("Features", True) == NB_FEATURES_OUT), "Wrong number of features outside a folder: {}, expected: {}".format(aPartDoc.size("Features", True), NB_FEATURES_OUT) + +toFolder = FeatureList() +toFolder.append(aPoint2) + +# move point to the folder +aSession.startOperation() +aFolder = aPartDoc.findFolderAbove(toFolder) +assert(aFolder is not None) +isAdded = aPartDoc.moveToFolder(toFolder, aFolder) +aSession.finishOperation() +assert(isAdded) + +NB_FEATURES_OUT -= 1 + +assert(aPartDoc.size("Features") == NB_FEATURES_FULL), "Wrong number of features: {}, expected: {}".format(aPartDoc.size("Features"), NB_FEATURES_FULL) +assert(aPartDoc.size("Features", True) == NB_FEATURES_OUT), "Wrong number of features outside a folder: {}, expected: {}".format(aPartDoc.size("Features", True), NB_FEATURES_OUT) + +#========================================================================= +# Test 3. Add several points into a folder +#========================================================================= +aPoint4 = newPoint(aPartDoc, 0., 10., 0.) + +NB_FEATURES_FULL += 1 +NB_FEATURES_OUT += 1 + +assert(aPartDoc.size("Features") == NB_FEATURES_FULL), "Wrong number of features: {}, expected: {}".format(aPartDoc.size("Features"), NB_FEATURES_FULL) +assert(aPartDoc.size("Features", True) == NB_FEATURES_OUT), "Wrong number of features outside a folder: {}, expected: {}".format(aPartDoc.size("Features", True), NB_FEATURES_OUT) + +# add a folder +aSession.startOperation() +aFolder2 = aPartDoc.addFolder(aPoint3) +aSession.finishOperation() + +NB_FEATURES_FULL += 1 +NB_FEATURES_OUT += 1 + +assert(aPartDoc.size("Folders") == 3), "Wrong number of folders: {}".format(aPartDoc.size("Folders")) +assert(aPartDoc.size("Features") == NB_FEATURES_FULL), "Wrong number of features: {}, expected: {}".format(aPartDoc.size("Features"), NB_FEATURES_FULL) +assert(aPartDoc.size("Features", True) == NB_FEATURES_OUT), "Wrong number of features outside a folder: {}, expected: {}".format(aPartDoc.size("Features", True), NB_FEATURES_OUT) + +toFolder = FeatureList() +toFolder.append(aPoint3) +toFolder.append(aPoint4) + +# move point to the folder +aSession.startOperation() +aFolder = aPartDoc.findFolderAbove(toFolder) +assert(aFolder is not None) +isAdded = aPartDoc.moveToFolder(toFolder, aFolder) +aSession.finishOperation() +assert(isAdded) + +NB_FEATURES_OUT -= 2 + +assert(aPartDoc.size("Features") == NB_FEATURES_FULL), "Wrong number of features: {}, expected: {}".format(aPartDoc.size("Features"), NB_FEATURES_FULL) +assert(aPartDoc.size("Features", True) == NB_FEATURES_OUT), "Wrong number of features outside a folder: {}, expected: {}".format(aPartDoc.size("Features", True), NB_FEATURES_OUT) + + +from salome.shaper import model +assert(model.checkPythonDump()) -- 2.39.2