]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Moving features to a folder has been implemented (Task 2.3. Ability to put consecutiv...
authorazv <azv@opencascade.com>
Thu, 23 Nov 2017 12:18:00 +0000 (15:18 +0300)
committerazv <azv@opencascade.com>
Thu, 23 Nov 2017 12:18:16 +0000 (15:18 +0300)
src/Model/Model_Document.cpp
src/Model/Model_Document.h
src/Model/Model_Objects.cpp
src/Model/Model_Objects.h
src/ModelAPI/CMakeLists.txt
src/ModelAPI/ModelAPI_Document.h
src/ModelAPI/ModelAPI_Folder.h
src/ModelAPI/Test/TestFolder_Update.py [new file with mode: 0644]

index e8e9a588752204858e1880dde91e7180780f11b7..f61edd015f2185fbc9351c3be3533ab2ccf27a79 100755 (executable)
@@ -1265,6 +1265,33 @@ std::shared_ptr<ModelAPI_Folder> Model_Document::addFolder(
 
 void Model_Document::removeFolder(std::shared_ptr<ModelAPI_Folder> theFolder)
 {
+  if (theFolder)
+    myObjs->removeFolder(theFolder);
+}
+
+std::shared_ptr<ModelAPI_Folder> Model_Document::findFolderAbove(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures)
+{
+  return myObjs->findFolder(theFeatures, false);
+}
+
+std::shared_ptr<ModelAPI_Folder> Model_Document::findFolderBelow(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures)
+{
+  return myObjs->findFolder(theFeatures, true);
+}
+
+bool Model_Document::moveToFolder(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
+      const std::shared_ptr<ModelAPI_Folder>& theFolder)
+{
+  return myObjs->moveToFolder(theFeatures, theFolder);
+}
+
+bool Model_Document::removeFromFolder(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures)
+{
+  return myObjs->removeFromFolder(theFeatures);
 }
 
 std::shared_ptr<ModelAPI_Feature> Model_Document::feature(
index c16238c8927658305f07417de530e0c6b1b19544..c0d7a7ad491db28af4cc5dea9da26046de513dbe 100644 (file)
@@ -209,6 +209,26 @@ class Model_Document : public ModelAPI_Document
       std::shared_ptr<ModelAPI_Feature> theAddBefore = std::shared_ptr<ModelAPI_Feature>());
   //! Removes the folder from the document (all features in the folder will be kept).
   MODEL_EXPORT virtual void removeFolder(std::shared_ptr<ModelAPI_Folder> 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<ModelAPI_Folder> findFolderAbove(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& 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<ModelAPI_Folder> findFolderBelow(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& 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<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
+      const std::shared_ptr<ModelAPI_Folder>& theFolder);
+  //! Remove features from the folder
+  //! \return \c true if the features have been moved out
+  MODEL_EXPORT virtual bool removeFromFolder(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& 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
index a0c1640f3589644c21f0575ac3444f412691fae7..104ec2b1d2408dc4fdad55865a5ba4b1e60406ba 100644 (file)
@@ -1252,6 +1252,186 @@ std::shared_ptr<ModelAPI_Folder> Model_Objects::createFolder(
   return aFolder;
 }
 
+void Model_Objects::removeFolder(std::shared_ptr<ModelAPI_Folder> theFolder)
+{
+  /// \todo
+}
+
+static FeaturePtr limitingFeature(std::list<FeaturePtr>& 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<ModelAPI_Folder> Model_Objects::findFolder(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& 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<std::shared_ptr<ModelAPI_Feature> > aFeatures = theFeatures;
+  std::shared_ptr<ModelAPI_Feature> aLimitingFeature = limitingFeature(aFeatures, theBelow);
+
+  std::shared_ptr<Model_Data> aData =
+      std::static_pointer_cast<Model_Data>(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<ModelAPI_Folder>(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<Model_Data>(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<ModelAPI_Folder>(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<Model_Data>(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<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
+      const std::shared_ptr<ModelAPI_Folder>& theFolder)
+{
+  if (theFeatures.empty() || !theFolder)
+    return false;
+
+  // labels for the folder and last feature in the list
+  TDF_Label aFolderLabel, aLastFeatureLabel;
+  std::shared_ptr<Model_Data> aData =
+      std::static_pointer_cast<Model_Data>(theFolder->data());
+  if (aData && aData->isValid())
+    aFolderLabel = aData->label().Father();
+  aData = std::static_pointer_cast<Model_Data>(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<Model_Data>(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<std::shared_ptr<ModelAPI_Feature> >& theFeatures)
+{
+  /// \todo
+  return false;
+}
+
+
 std::shared_ptr<ModelAPI_Feature> Model_Objects::feature(
     const std::shared_ptr<ModelAPI_Result>& theResult)
 {
index 532b161ad1cf34a2b1299ff6587880423aeeb66b..8df42019c96e4e556d1e18133f4c8cbe9108e11d 100644 (file)
@@ -150,6 +150,24 @@ class Model_Objects
   /// Creates a folder (group of the features in the object browser)
   std::shared_ptr<ModelAPI_Folder> createFolder(
       const std::shared_ptr<ModelAPI_Feature>& theBeforeThis);
+  //! Removes the folder from the document (all features in the folder will be kept).
+  void removeFolder(std::shared_ptr<ModelAPI_Folder> 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<ModelAPI_Folder> findFolder(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& 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<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
+                    const std::shared_ptr<ModelAPI_Folder>& theFolder);
+  //! Remove features from the folder
+  //! \return \c true if the features have been moved out
+  bool removeFromFolder(const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures);
 
   //! Sets the owner of this manager
   void setOwner(DocumentPtr theDoc);
index dc2038c446b47275a58ef64c12246c7146750d6a..a5a864cf40a8b750016fbc49072fc895a7376056 100644 (file)
@@ -190,4 +190,5 @@ ADD_UNIT_TESTS(TestConstants.py
                TestCustomName_RotateGroup.py
                TestCustomName_Translation.py
                TestFolder_Create.py
+               TestFolder_Update.py
 )
index 069bb4bfd29286c710e8384776d112b81692daab..5e3cc05bc7c7cad9caad585bf52fef3c03e751bc 100644 (file)
@@ -184,6 +184,25 @@ public:
       std::shared_ptr<ModelAPI_Feature> theAddBefore) = 0;
   //! Removes the folder from the document (all features in the folder will be kept).
   virtual void removeFolder(std::shared_ptr<ModelAPI_Folder> 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<ModelAPI_Folder> findFolderAbove(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& 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<ModelAPI_Folder> findFolderBelow(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& 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<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
+                            const std::shared_ptr<ModelAPI_Folder>& theFolder) = 0;
+  //! Remove features from the folder
+  //! \return \c true if the features have been moved out
+  virtual bool removeFromFolder(
+      const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures) = 0;
 
   //! Informs the document that it becomes active and some actions must be performed
   virtual void setActive(const bool theFlag) = 0;
index 1752a90eafcc45f28b3389ce3f14857cf82b115b..b89fd8704bfc75e6a01f6c13c84468ec63618246 100644 (file)
@@ -87,6 +87,11 @@ public:
   {
     return data()->name();
   }
+  /// Returns the reference attribute by the identifier
+  inline std::shared_ptr<ModelAPI_AttributeReference> 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 (file)
index 0000000..fa336ec
--- /dev/null
@@ -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<mailto: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())