]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Implementation of the task #3109 : Feature Copy
authormpv <mpv@opencascade.com>
Mon, 9 Dec 2019 11:35:06 +0000 (14:35 +0300)
committermpv <mpv@opencascade.com>
Mon, 9 Dec 2019 11:35:06 +0000 (14:35 +0300)
29 files changed:
src/FeaturesAPI/CMakeLists.txt
src/FeaturesAPI/FeaturesAPI.i
src/FeaturesAPI/FeaturesAPI_Copy.cpp [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_Copy.h [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_swig.h
src/FeaturesPlugin/CMakeLists.txt
src/FeaturesPlugin/FeaturesPlugin_Copy.cpp [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Copy.h [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp
src/FeaturesPlugin/Test/TestCopyFeature.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyFeatureMoveGroupOfFeature.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyMoveResult.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyMoveSubShapes.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyNames.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopySubShapes.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyWholeFeature.py [new file with mode: 0644]
src/FeaturesPlugin/copy_widget.xml [new file with mode: 0644]
src/FeaturesPlugin/icons/copy.png [new file with mode: 0644]
src/FeaturesPlugin/plugin-Features.xml
src/Model/Model_AttributeSelection.cpp
src/Model/Model_AttributeSelection.h
src/Model/Model_Document.cpp
src/Model/Model_ResultBody.cpp
src/Model/Model_ResultBody.h
src/ModelAPI/ModelAPI_AttributeSelectionList.h
src/ModelAPI/ModelAPI_Feature.h
src/ModelAPI/ModelAPI_ResultBody.cpp
src/ModelAPI/ModelAPI_ResultBody.h
src/PythonAPI/model/features/__init__.py

index acd6222f5e1cc4c674c701072acaf6b93b2f4f21..dbdb6bc5b388d7926537c898e7f2ac04651da234 100644 (file)
@@ -48,6 +48,7 @@ SET(PROJECT_HEADERS
   FeaturesAPI_Translation.h
   FeaturesAPI_Union.h
   FeaturesAPI_FusionFaces.h
   FeaturesAPI_Translation.h
   FeaturesAPI_Union.h
   FeaturesAPI_FusionFaces.h
+  FeaturesAPI_Copy.h
 )
 
 SET(PROJECT_SOURCES
 )
 
 SET(PROJECT_SOURCES
@@ -78,6 +79,7 @@ SET(PROJECT_SOURCES
   FeaturesAPI_Translation.cpp
   FeaturesAPI_Union.cpp
   FeaturesAPI_FusionFaces.cpp
   FeaturesAPI_Translation.cpp
   FeaturesAPI_Union.cpp
   FeaturesAPI_FusionFaces.cpp
+  FeaturesAPI_Copy.cpp
 )
 
 SET(PROJECT_LIBRARIES
 )
 
 SET(PROJECT_LIBRARIES
index c0a747b2f65bf52d060c472d250209cf053f792a..9ed6db0d36611bac85dad93aa0d4e611d6535ef5 100644 (file)
@@ -77,6 +77,7 @@
 %shared_ptr(FeaturesAPI_Union)
 %shared_ptr(FeaturesAPI_FusionFaces)
 %shared_ptr(FeaturesAPI_RemoveResults)
 %shared_ptr(FeaturesAPI_Union)
 %shared_ptr(FeaturesAPI_FusionFaces)
 %shared_ptr(FeaturesAPI_RemoveResults)
+%shared_ptr(FeaturesAPI_Copy)
 
 
 %typecheck(SWIG_TYPECHECK_POINTER) std::pair<std::list<ModelHighAPI_Selection>, bool>, const std::pair<std::list<ModelHighAPI_Selection>, bool> & {
 
 
 %typecheck(SWIG_TYPECHECK_POINTER) std::pair<std::list<ModelHighAPI_Selection>, bool>, const std::pair<std::list<ModelHighAPI_Selection>, bool> & {
 %include "FeaturesAPI_Union.h"
 %include "FeaturesAPI_FusionFaces.h"
 %include "FeaturesAPI_RemoveResults.h"
 %include "FeaturesAPI_Union.h"
 %include "FeaturesAPI_FusionFaces.h"
 %include "FeaturesAPI_RemoveResults.h"
+%include "FeaturesAPI_Copy.h"
diff --git a/src/FeaturesAPI/FeaturesAPI_Copy.cpp b/src/FeaturesAPI/FeaturesAPI_Copy.cpp
new file mode 100644 (file)
index 0000000..5ec81ab
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright (C) 2014-2019  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
+//
+
+#include "FeaturesAPI_Copy.h"
+
+#include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Tools.h>
+
+//================================================================================================
+FeaturesAPI_Copy::FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature)
+: ModelHighAPI_Interface(theFeature)
+{
+  initialize();
+}
+
+//================================================================================================
+FeaturesAPI_Copy::FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                     const std::list<ModelHighAPI_Selection>& theObjects,
+                                     const int theNumber)
+: ModelHighAPI_Interface(theFeature)
+{
+  if(initialize()) {
+    setNumber(theNumber);
+    setObjects(theObjects);
+  }
+}
+
+//================================================================================================
+FeaturesAPI_Copy::~FeaturesAPI_Copy() {}
+
+//=================================================================================================
+void FeaturesAPI_Copy::setObjects(const std::list<ModelHighAPI_Selection>& theObjects)
+{
+  fillAttribute(theObjects, myobjects);
+  execute();
+}
+//=================================================================================================
+void FeaturesAPI_Copy::setNumber(const int theNumber)
+{
+  fillAttribute(theNumber, mynumber);
+  execute();
+}
+
+//=================================================================================================
+void FeaturesAPI_Copy::dump(ModelHighAPI_Dumper& theDumper) const
+{
+  FeaturePtr aBase = feature();
+  const std::string& aDocName = theDumper.name(aBase->document());
+
+  AttributeSelectionListPtr anObjects = aBase->selectionList(FeaturesPlugin_Copy::OBJECTS());
+  AttributeIntegerPtr aNumber = aBase->integer(FeaturesPlugin_Copy::NUMBER());
+
+  theDumper << aBase << " = model.addCopy("
+            << aDocName << ", " << anObjects << ", " << aNumber << ")" << std::endl;
+}
+
+//=================================================================================================
+CopyPtr addCopy(const std::shared_ptr<ModelAPI_Document>& thePart,
+                const std::list<ModelHighAPI_Selection>& theObjects,
+                const int theNumber)
+{
+  std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(FeaturesAPI_Copy::ID());
+  return CopyPtr(new FeaturesAPI_Copy(aFeature, theObjects, theNumber));
+}
diff --git a/src/FeaturesAPI/FeaturesAPI_Copy.h b/src/FeaturesAPI/FeaturesAPI_Copy.h
new file mode 100644 (file)
index 0000000..92174f9
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright (C) 2014-2019  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
+//
+
+#ifndef FeaturesAPI_Copy_H_
+#define FeaturesAPI_Copy_H_
+
+#include "FeaturesAPI.h"
+
+#include <FeaturesPlugin_Copy.h>
+
+#include <ModelHighAPI_Interface.h>
+#include <ModelHighAPI_Macro.h>
+
+class ModelHighAPI_Dumper;
+class ModelHighAPI_Selection;
+
+/// \class FeaturesAPI_Copy
+/// \ingroup CPPHighAPI
+/// \brief Interface for Copy feature.
+class FeaturesAPI_Copy: public ModelHighAPI_Interface
+{
+public:
+  /// Constructor without values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature);
+
+  /// Constructor with values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                            const std::list<ModelHighAPI_Selection>& theBaseObjects,
+                            const int theVersion = 0);
+
+  /// Destructor.
+  FEATURESAPI_EXPORT virtual ~FeaturesAPI_Copy();
+
+  INTERFACE_2(FeaturesPlugin_Copy::ID(),
+              objects, FeaturesPlugin_Copy::OBJECTS(),
+              ModelAPI_AttributeSelectionList, /** Source objects */,
+              number, FeaturesPlugin_Copy::NUMBER(),
+              ModelAPI_AttributeInteger, /** Number of copies */)
+
+  /// Modify objects attribute of the feature.
+  FEATURESAPI_EXPORT void setObjects(const std::list<ModelHighAPI_Selection>& theBaseObjects);
+
+  /// Modify number of copies attribute of the feature.
+  FEATURESAPI_EXPORT void setNumber(const int theNumber);
+
+  /// Dump wrapped feature
+  FEATURESAPI_EXPORT virtual void dump(ModelHighAPI_Dumper& theDumper) const;
+};
+
+/// Pointer on Copy object.
+typedef std::shared_ptr<FeaturesAPI_Copy> CopyPtr;
+
+/// \ingroup CPPHighAPI
+/// \brief Create Copy feature.
+FEATURESAPI_EXPORT
+CopyPtr addCopy(const std::shared_ptr<ModelAPI_Document>& thePart,
+                const std::list<ModelHighAPI_Selection>& theObjects,
+                const int theNumber);
+
+#endif // FeaturesAPI_Copy_H_
index cd71216fae72dacf4ad6f159760cf424ba686105..767e0966fda6e81afa2a17b84e0e484c8a62d266 100644 (file)
@@ -50,5 +50,6 @@
   #include "FeaturesAPI_Union.h"
   #include "FeaturesAPI_FusionFaces.h"
   #include "FeaturesAPI_RemoveResults.h"
   #include "FeaturesAPI_Union.h"
   #include "FeaturesAPI_FusionFaces.h"
   #include "FeaturesAPI_RemoveResults.h"
+  #include "FeaturesAPI_Copy.h"
 
 #endif // FeaturesAPI_swig_H_
 
 #endif // FeaturesAPI_swig_H_
index ea14d9ea6191b21c6dcb092626702e2aecae0a7a..913a83aff1bd71a27cb4b307822a6d3f49f7fe82 100644 (file)
@@ -61,6 +61,7 @@ SET(PROJECT_HEADERS
     FeaturesPlugin_FusionFaces.h
     FeaturesPlugin_RemoveResults.h
     FeaturesPlugin_Chamfer.h
     FeaturesPlugin_FusionFaces.h
     FeaturesPlugin_RemoveResults.h
     FeaturesPlugin_Chamfer.h
+    FeaturesPlugin_Copy.h
 )
 
 SET(PROJECT_SOURCES
 )
 
 SET(PROJECT_SOURCES
@@ -103,6 +104,7 @@ SET(PROJECT_SOURCES
     FeaturesPlugin_FusionFaces.cpp
     FeaturesPlugin_RemoveResults.cpp
     FeaturesPlugin_Chamfer.cpp
     FeaturesPlugin_FusionFaces.cpp
     FeaturesPlugin_RemoveResults.cpp
     FeaturesPlugin_Chamfer.cpp
+    FeaturesPlugin_Copy.cpp
 )
 
 SET(XML_RESOURCES
 )
 
 SET(XML_RESOURCES
@@ -135,6 +137,7 @@ SET(XML_RESOURCES
   measurement_widget.xml
   fusion_faces_widget.xml
   chamfer_widget.xml
   measurement_widget.xml
   fusion_faces_widget.xml
   chamfer_widget.xml
+  copy_widget.xml
 )
 
 SET(TEXT_RESOURCES
 )
 
 SET(TEXT_RESOURCES
@@ -541,4 +544,11 @@ ADD_UNIT_TESTS(TestExtrusion.py
                Test3033.py
                Test3076.py
                Test17909.py
                Test3033.py
                Test3076.py
                Test17909.py
+               TestCopyFeature.py
+               TestCopyFeatureMoveGroupOfFeature.py
+               TestCopyMoveResult.py
+               TestCopyMoveSubShapes.py
+               TestCopyNames.py
+               TestCopySubShapes.py
+               TestCopyWholeFeature.py
 )
 )
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp b/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp
new file mode 100644 (file)
index 0000000..76db7fa
--- /dev/null
@@ -0,0 +1,155 @@
+// Copyright (C) 2017-2019  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
+//
+
+#include "FeaturesPlugin_Copy.h"
+
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_Tools.h>
+
+#include <GeomAlgoAPI_Copy.h>
+#include <GeomAlgoAPI_Tools.h>
+#include <GeomAPI_ShapeExplorer.h>
+
+#include <sstream>
+
+void FeaturesPlugin_Copy::initAttributes()
+{
+  data()->addAttribute(OBJECTS(), ModelAPI_AttributeSelectionList::typeId());
+  data()->addAttribute(NUMBER(), ModelAPI_AttributeInteger::typeId());
+}
+
+static GeomShapePtr shapeOfSelection(AttributeSelectionPtr theSel) {
+  GeomShapePtr aResult;
+  FeaturePtr aSelFeature = theSel->contextFeature();
+  if (aSelFeature.get()) {
+    if (aSelFeature->results().empty()) // if selected feature has no results, make nothing
+      return aResult;
+    if (aSelFeature->results().size() == 1) { // for one sub-result don't make compound
+      aResult = aSelFeature->firstResult()->shape();
+    }
+  }
+  if (!aResult.get())
+    aResult = theSel->value();
+  if (!aResult.get()) {
+    if (theSel->context().get())
+      aResult = theSel->context()->shape();
+  }
+  return aResult;
+}
+
+void FeaturesPlugin_Copy::execute()
+{
+  int aCopiesNum = integer(NUMBER())->value();
+  AttributeSelectionListPtr aList = selectionList(OBJECTS());
+  int aResultIndex = 0;
+  std::set<std::string> anExistingNames; // to avoid names duplication
+  for(int aCopy = 0; aCopy < aCopiesNum; aCopy++) {
+    for (int aSelIndex = 0; aSelIndex < aList->size(); aSelIndex++) {
+      AttributeSelectionPtr aSel = aList->value(aSelIndex);
+      GeomShapePtr aShape = shapeOfSelection(aSel);
+      if (!aShape.get())
+        continue;
+      std::shared_ptr<GeomAlgoAPI_Copy> aCopyBuilder(new GeomAlgoAPI_Copy(aShape, false, false));
+      std::string anError;
+      if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aCopyBuilder, getKind(), anError)) {
+        setError(anError);
+        return;
+      }
+      GeomShapePtr aResult = aCopyBuilder->shape();
+
+      std::string aBaseName = aSel->context() ? aSel->context()->data()->name() :
+        aSel->contextFeature()->firstResult()->data()->name();
+      std::string aName;
+      int anInd = 0;
+      do {
+        anInd++;
+        std::ostringstream aNameStr;
+        aNameStr << aBaseName << "_" << (aCopy + anInd);
+        aName = aNameStr.str();
+      } while (anExistingNames.count(aName));
+      anExistingNames.insert(aName);
+
+      std::shared_ptr<ModelAPI_ResultBody> aResultBody =
+        document()->createBody(data(), aResultIndex);
+      aResultBody->data()->setName(aName);
+      // to make sub-results also names with a similar name temporarily rename the feature
+      std::string anOrigName = name();
+      data()->setName(aBaseName);
+      aResultBody->store(aResult);
+      data()->setName(anOrigName);
+      aResultBody->loadFirstLevel(aResult, "Copy");
+      setResult(aResultBody, aResultIndex++);
+    }
+  }
+  removeResults(aResultIndex);
+}
+
+void FeaturesPlugin_Copy::getCopies(
+  ObjectPtr theContext, std::shared_ptr<GeomAPI_Shape> theValue,
+  std::list<ObjectPtr>& theCopyContext, std::list<std::shared_ptr<GeomAPI_Shape> >& theCopyVals)
+{
+  ResultPtr aContextRes = std::dynamic_pointer_cast<ModelAPI_Result>(theContext);
+  GeomShapePtr aGroupValue = theValue.get() ? theValue : aContextRes->shape();
+
+  AttributeSelectionListPtr aList = selectionList(OBJECTS());
+  std::list<ResultPtr>::const_iterator aResIter = results().cbegin();
+  while(aResIter != results().cend()) { // do as long as many iterations
+    for (int aSelIndex = 0; aSelIndex < aList->size(); aSelIndex++) {
+      if (aResIter == results().cend()) // no more results corresponding to the selection
+        return;
+      AttributeSelectionPtr aSel = aList->value(aSelIndex);
+      GeomShapePtr aShape = shapeOfSelection(aSel);
+      if (!aShape.get())
+        continue;
+
+      if (aShape->isSubShape(aGroupValue, false)) { // group value is subshape of copied => copy
+        // search the same result in the copy by the same index of sub-shape in the shape
+        GeomAPI_ShapeExplorer anOrigExp(aShape, aGroupValue->shapeType());
+        GeomAPI_ShapeExplorer aCopyShape((*aResIter)->shape(), aGroupValue->shapeType());
+        for(; anOrigExp.more(); anOrigExp.next(), aCopyShape.next()) {
+          if (anOrigExp.current()->isSame(aGroupValue)) {
+            // searching for sub-result if it is composite result, but context-not
+            ResultPtr aResContext = *aResIter;
+            if (aContextRes->shape()->shapeType() > (*aResIter)->shape()->shapeType()) {
+              ResultBodyPtr aResBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aResContext);
+              if (aResBody.get()) {
+                std::list<ResultPtr> aSubs;
+                ModelAPI_Tools::allSubs(aResBody, aSubs, true);
+                std::list<ResultPtr>::iterator aSubIter = aSubs.begin();
+                for(; aSubIter != aSubs.end(); aSubIter++) {
+                  GeomShapePtr aSubShape = (*aSubIter)->shape();
+                  if (aSubShape.get() && aSubShape->isSubShape(aCopyShape.current(), false)) {
+                    aResContext = *aSubIter;
+                    break;
+                  }
+                }
+              }
+            }
+            theCopyContext.push_back(aResContext);
+            theCopyVals.push_back(aResContext->shape()->isSame(
+              aCopyShape.current()) ? GeomShapePtr() : aCopyShape.current());
+            break;
+          }
+        }
+      }
+      aResIter++;
+    }
+  }
+}
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Copy.h b/src/FeaturesPlugin/FeaturesPlugin_Copy.h
new file mode 100644 (file)
index 0000000..b500b51
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright (C) 2017-2019  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
+//
+
+#ifndef FeaturesPlugin_Copy_H_
+#define FeaturesPlugin_Copy_H_
+
+#include "FeaturesPlugin.h"
+
+#include <ModelAPI_Feature.h>
+
+/// \class FeaturesPlugin_Copy
+/// \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 \94Recover\94 feature is that not concealed results may
+///        be selected and in the history behavior: the \93Move to the End\94 of groups will move to
+///        all copy-results.
+
+class FeaturesPlugin_Copy : public ModelAPI_Feature, public ModelAPI_FeatureCopyInterface
+{
+public:
+  /// Feature kind.
+  inline static const std::string& ID()
+  {
+    static const std::string MY_ID("Copy");
+    return MY_ID;
+  }
+
+  /// \return the kind of a feature.
+  FEATURESPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = FeaturesPlugin_Copy::ID();
+    return MY_KIND;
+  }
+
+  /// Selection list attribute that contains all copied shapes selection.
+  inline static const std::string& OBJECTS()
+  {
+    static const std::string MY_OBJECTS("objects");
+    return MY_OBJECTS;
+  }
+  /// Integer attribute that contains the number of resulting copies needed
+  inline static const std::string NUMBER()
+  {
+    static std::string MY_NUMBER("number");
+    return MY_NUMBER;
+  }
+
+  /// Performs the algorithm and stores results it in the data structure.
+  FEATURESPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes.
+  FEATURESPLUGIN_EXPORT virtual void initAttributes();
+
+  /// To update the group feature which is moved over this copy feature (to add copies to selection)
+  FEATURESPLUGIN_EXPORT virtual void getCopies(
+    ObjectPtr theContext, std::shared_ptr<GeomAPI_Shape> theValue,
+    std::list<ObjectPtr>& theCopyContext, std::list<std::shared_ptr<GeomAPI_Shape> >& theCopyVals);
+
+  /// Use plugin manager for features creation.
+  FeaturesPlugin_Copy() {}
+};
+
+#endif
index 7973434462db3f8af12183e9c78748163ed714b5..5a2ff7c5c99ab3c69b6a166de124e61d46966ac6 100644 (file)
@@ -48,6 +48,7 @@
 #include <FeaturesPlugin_Union.h>
 #include <FeaturesPlugin_FusionFaces.h>
 #include <FeaturesPlugin_RemoveResults.h>
 #include <FeaturesPlugin_Union.h>
 #include <FeaturesPlugin_FusionFaces.h>
 #include <FeaturesPlugin_RemoveResults.h>
+#include <FeaturesPlugin_Copy.h>
 #include <FeaturesPlugin_ValidatorTransform.h>
 #include <FeaturesPlugin_Validators.h>
 
 #include <FeaturesPlugin_ValidatorTransform.h>
 #include <FeaturesPlugin_Validators.h>
 
@@ -179,8 +180,11 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID)
     return FeaturePtr(new FeaturesPlugin_RemoveResults);
   } else if (theFeatureID == FeaturesPlugin_Chamfer::ID()) {
     return FeaturePtr(new FeaturesPlugin_Chamfer);
     return FeaturePtr(new FeaturesPlugin_RemoveResults);
   } else if (theFeatureID == FeaturesPlugin_Chamfer::ID()) {
     return FeaturePtr(new FeaturesPlugin_Chamfer);
+  } else if (theFeatureID == FeaturesPlugin_Copy::ID()) {
+    return FeaturePtr(new FeaturesPlugin_Copy);
   }
 
   }
 
+
   // feature of such kind is not found
   return FeaturePtr();
 }
   // feature of such kind is not found
   return FeaturePtr();
 }
diff --git a/src/FeaturesPlugin/Test/TestCopyFeature.py b/src/FeaturesPlugin/Test/TestCopyFeature.py
new file mode 100644 (file)
index 0000000..17f99d1
--- /dev/null
@@ -0,0 +1,48 @@
+# Copyright (C) 2014-2019  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
+#
+
+# Checks selection of the whole featurte and move to the end of the group created on results of this feature..
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(11.02869497636673, 9.8764247475525, 3.312248077480665)
+SketchCircle_2 = Sketch_1.addCircle(4.278198729238611, 4.677840612715367, 1.794922837237287)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1")], model.selection(), 10, 0)
+Group_1 = model.addGroup(Part_1_doc, "Faces", [model.selection("SOLID", "Extrusion_1_1"), model.selection("SOLID", "Extrusion_1_2")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPOUND", "all-in-Extrusion_1")], 1)
+model.do()
+# move the group feature to the end - through copy of the whole feature
+Part_1_doc.moveFeature(Group_1.feature(), Copy_1.feature())
+model.end()
+
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 4) # two original solids plus two copies
+assert(selectionList.value(3).namingName() == "Extrusion_1_1_1_1")
+assert(selectionList.value(2).namingName() == "Extrusion_1_1_1_2")
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyFeatureMoveGroupOfFeature.py b/src/FeaturesPlugin/Test/TestCopyFeatureMoveGroupOfFeature.py
new file mode 100644 (file)
index 0000000..0d8eb2d
--- /dev/null
@@ -0,0 +1,48 @@
+# Copyright (C) 2014-2019  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
+#
+
+# Checks selection of the whole featurte and move to the end of the group created on this feature..
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(11.02869497636673, 9.8764247475525, 3.312248077480665)
+SketchCircle_2 = Sketch_1.addCircle(4.278198729238611, 4.677840612715367, 1.794922837237287)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1")], model.selection(), 10, 0)
+Group_1 = model.addGroup(Part_1_doc, "Faces", [model.selection("COMPOUND", "all-in-Extrusion_1")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPOUND", "all-in-Extrusion_1")], 1)
+model.do()
+# move the group feature to the end - through copy of the whole feature
+Part_1_doc.moveFeature(Group_1.feature(), Copy_1.feature())
+model.end()
+
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 2) # two: original feature and the copy-feature
+assert(selectionList.value(0).namingName() == "all-in-Extrusion_1")
+assert(selectionList.value(1).namingName() == "all-in-Copy_1")
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyMoveResult.py b/src/FeaturesPlugin/Test/TestCopyMoveResult.py
new file mode 100644 (file)
index 0000000..bde1d85
--- /dev/null
@@ -0,0 +1,66 @@
+# Copyright (C) 2014-2019  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
+#
+
+# Checks selection of the whole shape and move to the end of the simple copy wihtout the
+# next modifications applied.
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(38.31034482758622, 31.49775664633739, 7.836206896551726, 31.49775664633739)
+SketchLine_2 = Sketch_1.addLine(7.836206896551726, 31.49775664633739, 7.836206896551726, 8.984209848307833)
+SketchLine_3 = Sketch_1.addLine(7.836206896551726, 8.984209848307833, 38.31034482758622, 8.984209848307833)
+SketchLine_4 = Sketch_1.addLine(38.31034482758622, 8.984209848307833, 38.31034482758622, 31.49775664633739)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchLine_5 = Sketch_1.addLine(21.64285714285715, 31.4977566463374, 25.0012315270936, 8.984209848307833)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.startPoint(), SketchLine_1.result())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_3.result())
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_5r"), model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_5f-SketchLine_3f-SketchLine_4f")], model.selection(), 7, 0)
+Extrusion_1.result().setName("Origin")
+Group_1 = model.addGroup(Part_1_doc, "Solids", [model.selection("SOLID", "Extrusion_1_1_1")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPSOLID", "Origin")], 1)
+Copy_1.result().setName("Origin_1")
+Fillet_1 = model.addFillet(Part_1_doc, [model.selection("EDGE", "[Origin_1_1/Copy_3][Origin_1_1/Copy_7]"), model.selection("EDGE", "[Origin_1_1/Copy_12][Origin_1_1/Copy_14]")], 2)
+Fillet_1.result().setName("CopyCompound")
+model.do()
+# move the group feature to the end - through copy and fillet on this copy (to distinguish the origin and the copy)
+Part_1_doc.moveFeature(Group_1.feature(), Fillet_1.feature())
+model.end()
+
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 1) # still the same solid
+
+assert(selectionList.value(0).namingName() == "Fillet_1_1_1")
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyMoveSubShapes.py b/src/FeaturesPlugin/Test/TestCopyMoveSubShapes.py
new file mode 100644 (file)
index 0000000..f2d78e5
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright (C) 2014-2019  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
+#
+
+# Checks selection of the sub-shapes move to the end with combination of 2 Copy features in history
+
+from salome.shaper import model
+from ModelAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Group_1 = model.addGroup(Part_1_doc, "Edges", [model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("FACE", "Box_1_1/Front")], 2)
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), 0, 5, [model.selection("SOLID", "Box_1_1")])
+Sketch_1 = model.addSketch(Part_1_doc, model.selection("FACE", "Box_1_1/Front"))
+SketchCircle_1 = Sketch_1.addCircle(9.650212071680357, 9.344990586582618, 1.565166813054581)
+ExtrusionCut_1.setNestedSketch(Sketch_1)
+ExtrusionCut_1.result().setColor(225, 0, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Box_1_1_1"))
+SketchCircle_2 = Sketch_2.addCircle(0.7603686814133139, 9.06793355634084, 1.630854194501576)
+model.do()
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchCircle_2_2r")])
+Cut_1 = model.addCut(Part_1_doc, [model.selection("FACE", "Box_1_1_1")], [model.selection("FACE", "Face_1_1")], keepSubResults = True)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("FACE", "Box_1_1_2")], model.selection("EDGE", "PartSet/OX"), 5)
+Copy_2 = model.addCopy(Part_1_doc, [model.selection("FACE", "Box_1_1_2")], 2)
+Rotation_1 = model.addRotation(Part_1_doc, [model.selection("FACE", "Box_1_1_2_1")], model.selection("EDGE", "PartSet/OZ"), 10)
+Rotation_2 = model.addRotation(Part_1_doc, [model.selection("FACE", "Box_1_1_2_2")], model.selection("EDGE", "PartSet/OZ"), 20)
+model.do()
+# move the group feature to the end - through 2 copies and many modifications of the selected edge
+Part_1_doc.moveFeature(Group_1.feature(), Rotation_2.feature())
+model.end()
+
+# result is 5 edges: cut, translation, 2 rotations, extrusion cut
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 5)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyNames.py b/src/FeaturesPlugin/Test/TestCopyNames.py
new file mode 100644 (file)
index 0000000..eac569d
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (C) 2014-2019  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
+#
+
+# Checks that the Copy feature produces correct names, same as in the description #3109
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(-11.9408866995074, 14.67733990147784, -29.35467980295567, 14.67733990147784)
+SketchLine_2 = Sketch_1.addLine(-29.35467980295567, 14.67733990147784, -29.35467980295567, -7.960591133004924)
+SketchLine_3 = Sketch_1.addLine(-29.35467980295567, -7.960591133004924, -11.9408866995074, -7.960591133004924)
+SketchLine_4 = Sketch_1.addLine(-11.9408866995074, -7.960591133004924, -11.9408866995074, 14.67733990147784)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchCircle_1 = Sketch_1.addCircle(14.92610837438425, 16.04556650246306, 6.602917012013241)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f")], model.selection(), 10, 0)
+Extrusion_1.result().setName("Box")
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection(), 10, 0)
+Extrusion_2.result().setName("Cylinder")
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("SOLID", "Box"), model.selection("SOLID", "Cylinder")], 3)
+model.end()
+
+assert(Copy_1.feature().results().size() == 6)
+
+assert(Copy_1.feature().results()[0].data().name() == "Box_1")
+assert(Copy_1.feature().results()[1].data().name() == "Cylinder_1")
+assert(Copy_1.feature().results()[2].data().name() == "Box_2")
+assert(Copy_1.feature().results()[3].data().name() == "Cylinder_2")
+assert(Copy_1.feature().results()[4].data().name() == "Box_3")
+assert(Copy_1.feature().results()[5].data().name() == "Cylinder_3")
+
+model.begin()
+model.testHaveNamingSubshapes(Copy_1, model, Part_1_doc)
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopySubShapes.py b/src/FeaturesPlugin/Test/TestCopySubShapes.py
new file mode 100644 (file)
index 0000000..f8aedf2
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright (C) 2014-2019  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
+#
+
+# Checks that the Copy feature produces correct results and names if sub-shapes of the same shape are selected
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(-25.99630541871921, 19.52832512315271, -16.66748768472907, 29.72783251231528)
+SketchLine_2 = Sketch_1.addLine(-16.66748768472907, 29.72783251231528, -12.4384236453202, 17.66256157635469)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(-12.4384236453202, 17.66256157635469, -25.99630541871921, 19.52832512315271)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 10, 0)
+Extrusion_1.result().setName("Prism")
+Copy_1_objects = [model.selection("FACE", "Prism/Generated_Face&Sketch_1/SketchLine_3"), model.selection("FACE", "Prism/Generated_Face&Sketch_1/SketchLine_2"), model.selection("EDGE", "[Prism/Generated_Face&Sketch_1/SketchLine_1][Prism/To_Face]")]
+Copy_1 = model.addCopy(Part_1_doc, Copy_1_objects, 2)
+Copy_1.result().setName("Prism_1")
+model.end()
+
+assert(Copy_1.feature().results().size() == 6)
+
+for index in range(6):
+  # name is just incremented
+  assert(Copy_1.feature().results()[index].data().name() == "Prism_" + str(index + 1))
+  # type of the shape corresponds to selection: 2 faces then edge
+  if index%3 == 2:
+    assert(Copy_1.feature().results()[index].shape().shapeTypeStr() == "EDGE")
+  else:
+    assert(Copy_1.feature().results()[index].shape().shapeTypeStr() == "FACE")
+
+model.begin()
+model.testHaveNamingSubshapes(Copy_1, model, Part_1_doc)
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyWholeFeature.py b/src/FeaturesPlugin/Test/TestCopyWholeFeature.py
new file mode 100644 (file)
index 0000000..0a9a884
--- /dev/null
@@ -0,0 +1,73 @@
+# Copyright (C) 2014-2019  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
+#
+
+# Checks that the Copy feature produces correct results and names if the whole result and feature are copied.
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(-19.47177806477657, -9.723714972297476, -32.14322074518937, 18.42485654704614)
+SketchLine_2 = Sketch_1.addLine(-32.14322074518937, 18.42485654704614, -13.96909823036938, 5.527027954588926)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(-13.96909823036938, 5.527027954588926, -19.47177806477657, -9.723714972297476)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchLine_4 = Sketch_1.addLine(-19.47177806477657, -9.723714972297476, -38.20802856635906, 4.439747463237282)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(-38.20802856635906, 4.439747463237282, -32.14322074518937, 18.42485654704614)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.endPoint())
+SketchCircle_1 = Sketch_1.addCircle(15.45187411494798, 10.57784256870842, 6.036432809751617)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_5r-SketchLine_4r"), model.selection("FACE", "Sketch_1/Face-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 10, 0)
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("WIRE", "Sketch_1/Face-SketchCircle_1_2f_wire")], model.selection(), 10, 0)
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "all-in-Extrusion_2")], 1)
+model.end()
+
+assert(Copy_1.feature().results().size() == 2)
+
+from ModelAPI import *
+assert(Copy_1.feature().results()[0].data().name() == "Extrusion_1_1_1")
+assert(modelAPI_ResultBody(Copy_1.feature().results()[0]).subResult(0).data().name() == "Extrusion_1_1_1_1")
+assert(modelAPI_ResultBody(Copy_1.feature().results()[0]).subResult(1).data().name() == "Extrusion_1_1_1_2")
+assert(Copy_1.feature().results()[1].data().name() == "Extrusion_2_1_1")
+
+# Check that copy of the whole feature that contains several results produce compound of all results
+model.begin()
+Extrusion_3 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1")], model.selection(), 10, 0)
+Copy_2 = model.addCopy(Part_1_doc, [model.selection("COMPOUND", "all-in-Extrusion_3")], 1)
+model.end()
+
+assert(Copy_2.feature().results()[0].data().name() == "Extrusion_3_1_1")
+assert(Copy_2.feature().results()[0].shape().shapeTypeStr() == "COMPOUND")
+aSub1 = modelAPI_ResultBody(Copy_2.feature().results()[0]).subResult(0)
+assert(aSub1.shape().shapeTypeStr() == "COMPSOLID")
+aSub2 = modelAPI_ResultBody(Copy_2.feature().results()[0]).subResult(1)
+assert(aSub2.shape().shapeTypeStr() == "SOLID")
+
+model.begin()
+model.testHaveNamingSubshapes(Copy_1, model, Part_1_doc)
+model.testHaveNamingSubshapes(Copy_2, model, Part_1_doc)
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/copy_widget.xml b/src/FeaturesPlugin/copy_widget.xml
new file mode 100644 (file)
index 0000000..6f79ca1
--- /dev/null
@@ -0,0 +1,16 @@
+<source>
+  <multi_selector id="objects"
+                  label="Sources:"
+                  tooltip="Select copied objects"
+                  shape_types="vertices edges wires faces shells compsolids objects"
+                  use_choice="false"
+                  concealment="false">
+  </multi_selector>
+  <integervalue id="number"
+                label="Nb copies"
+                step="1"
+                default="1"
+                min="1"
+                tooltip="Number of copies">
+  </integervalue>
+</source>
diff --git a/src/FeaturesPlugin/icons/copy.png b/src/FeaturesPlugin/icons/copy.png
new file mode 100644 (file)
index 0000000..7e11163
Binary files /dev/null and b/src/FeaturesPlugin/icons/copy.png differ
index 3197488a7e3870b1b58ad4d5ed13060f249a736d..428f0031445938fe0354cc5f09dc6f0b3a0ebcf2 100644 (file)
         helpfile="recoverFeature.html">
         <source path="recover_widget.xml"/>
       </feature>
         helpfile="recoverFeature.html">
         <source path="recover_widget.xml"/>
       </feature>
+      <feature id="Copy"
+        title="Copy"
+        tooltip="Copies results or sub-results"
+        icon="icons/Features/copy.png"
+        helpfile="copyFeature.html">
+        <source path="copy_widget.xml"/>
+      </feature>
       <feature id="RemoveResults" title="Remove results" tooltip="Internal feature for results removal" internal="1">
         <multi_selector id="results" concealment="true"/>
       </feature>
       <feature id="RemoveResults" title="Remove results" tooltip="Internal feature for results removal" internal="1">
         <multi_selector id="results" concealment="true"/>
       </feature>
index 8ff6d81f8a87b93a8b4a402de30c454f7893cd8b..468b60f7ab1d0c87c5644336614ece5d804be168 100644 (file)
@@ -1292,8 +1292,8 @@ void Model_AttributeSelection::computeValues(
 
 
 void Model_AttributeSelection::concealedFeature(
 
 
 void Model_AttributeSelection::concealedFeature(
-  const FeaturePtr theFeature, const FeaturePtr theStop, std::list<FeaturePtr>& theConcealers,
-  const ResultPtr theResultOfFeature)
+  const FeaturePtr theFeature, const FeaturePtr theStop, const bool theCheckCopy,
+  std::list<FeaturePtr>& theConcealers, const ResultPtr theResultOfFeature)
 {
   std::set<FeaturePtr> alreadyProcessed;
   alreadyProcessed.insert(theFeature);
 {
   std::set<FeaturePtr> alreadyProcessed;
   alreadyProcessed.insert(theFeature);
@@ -1328,7 +1328,9 @@ void Model_AttributeSelection::concealedFeature(
         if (alreadyProcessed.find(aRefFeat) != alreadyProcessed.end()) // optimization
           continue;
         alreadyProcessed.insert(aRefFeat);
         if (alreadyProcessed.find(aRefFeat) != alreadyProcessed.end()) // optimization
           continue;
         alreadyProcessed.insert(aRefFeat);
-        if (ModelAPI_Session::get()->validators()->isConcealed(aRefFeat->getKind(), (*aRef)->id()))
+        if (ModelAPI_Session::get()->validators()->isConcealed(aRefFeat->getKind(), (*aRef)->id())
+          || (theCheckCopy &&
+              std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(aRefFeat).get()))
         {
           // for extrusion cut in python script the nested sketch reference may be concealed before
           // it is nested, so, check this composite feature is valid
         {
           // for extrusion cut in python script the nested sketch reference may be concealed before
           // it is nested, so, check this composite feature is valid
@@ -1410,12 +1412,13 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
     } else aResIter++;
   }
 
     } else aResIter++;
   }
 
+  bool aStaySame = false;
   if (aResults.empty()) {
     // check the context become concealed by operation which is earlier than this selection
     FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
     FeaturePtr aContextOwner = theDoc->feature(theContext);
     std::list<FeaturePtr> aConcealers;
   if (aResults.empty()) {
     // check the context become concealed by operation which is earlier than this selection
     FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
     FeaturePtr aContextOwner = theDoc->feature(theContext);
     std::list<FeaturePtr> aConcealers;
-    concealedFeature(aContextOwner, aThisFeature, aConcealers, theContext);
+    concealedFeature(aContextOwner, aThisFeature, false, aConcealers, theContext);
     std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
     for(; aConcealer != aConcealers.end(); aConcealer++) {
       std::list<ResultPtr> aRefResults;
     std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
     for(; aConcealer != aConcealers.end(); aConcealer++) {
       std::list<ResultPtr> aRefResults;
@@ -1436,26 +1439,87 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
       if (aResults.empty())
         return true; // feature conceals result, return true, so the context will be removed
     }
       if (aResults.empty())
         return true; // feature conceals result, return true, so the context will be removed
     }
-    if (aResults.empty())
-      return false; // no modifications found, must stay the same
+    aStaySame = aResults.empty();
+  }
+  if (myParent && myParent->isMakeCopy()) {
+    // check there are copies before the new results, so, make a copy
+    std::set<ResultPtr>::iterator aResIter = aResults.begin();
+    std::list<ObjectPtr> aCopyContext;
+    std::list<GeomShapePtr> aCopyVals;
+    // features between the new and the old: check the "Move" interface to get a copy
+    FeaturePtr aRootOwner = theDoc->feature(theContext);
+    FeaturePtr anOwner = ModelAPI_Tools::compositeOwner(aRootOwner);
+    for(; anOwner.get(); anOwner = ModelAPI_Tools::compositeOwner(anOwner))
+      aRootOwner = anOwner;
+    FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
+    // iterate all results to find a "Copy" features between the new and one and to add the
+    // copy-results also to results if this attribute refers to the copied shape
+    int anIndex = kUNDEFINED_FEATURE_INDEX;
+    for(FeaturePtr aFeat = theDoc->objects()->nextFeature(aRootOwner, anIndex); aFeat.get() &&
+        aFeat != aThisFeature; aFeat = theDoc->objects()->nextFeature(aFeat, anIndex)) {
+      std::shared_ptr<ModelAPI_FeatureCopyInterface> aCopier =
+        std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(aFeat);
+      if (aCopier.get()) {
+        GeomShapePtr aValShape(new GeomAPI_Shape);
+        aValShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(
+          theValShape.IsNull() ? theContShape : theValShape));
+        aCopier->getCopies(theContext, aValShape, aCopyContext, aCopyVals);
+      }
+    }
+    // check for the further modifications of the copy contexts and values
+    std::list<ObjectPtr>::iterator aCopyContIter = aCopyContext.begin();
+    std::list<GeomShapePtr>::iterator aCopyValIter = aCopyVals.begin();
+    for(; aCopyContIter != aCopyContext.end(); aCopyContIter++, aCopyValIter++) {
+      ResultPtr aNewCont = std::dynamic_pointer_cast<ModelAPI_Result>(*aCopyContIter);
+      TopoDS_Shape aNewContShape = aNewCont->shape()->impl<TopoDS_Shape>();
+      GeomShapePtr aNewVal = *aCopyValIter;
+      TopoDS_Shape aNewValShape;
+      if (aNewVal.get() && !aNewVal->isNull())
+        aNewValShape = aNewVal->impl<TopoDS_Shape>();
+      std::list<ResultPtr> aNewRes;
+      TopTools_ListOfShape aNewUpdatedVal;
+      if (searchNewContext(theDoc, aNewContShape, aNewCont, aNewValShape,
+          theAccessLabel, aNewRes, aNewUpdatedVal)) {
+        // append new results instead of the current ones
+        std::list<ResultPtr>::iterator aNewIter = aNewRes.begin();
+        TopTools_ListIteratorOfListOfShape aNewUpdVal(aNewUpdatedVal);
+        for(; aNewIter != aNewRes.end(); aNewIter++, aNewUpdVal.Next()) {
+          theResults.push_back(*aNewIter);
+          theValShapes.Append(aNewUpdVal.Value());
+        }
+      } else { // the current result is good
+        theResults.push_back(aNewCont);
+        theValShapes.Append(aNewValShape);
+      }
+    }
+    if (aStaySame && !theResults.empty()) { // no changes except copy, so, keep the origin as first
+      theResults.push_front(theContext);
+      theValShapes.Prepend(theValShape);
+      return true;
+    }
   }
   }
+  if (aStaySame)
+    return false;
+
   // iterate all results to find further modifications
   std::set<ResultPtr>::iterator aResIter = aResults.begin();
   // iterate all results to find further modifications
   std::set<ResultPtr>::iterator aResIter = aResults.begin();
-  for(; aResIter != aResults.end(); aResIter++) {
+  for(aResIter = aResults.begin(); aResIter != aResults.end(); aResIter++) {
     if (aResIter->get() != NULL) {
     if (aResIter->get() != NULL) {
+      ResultPtr aNewResObj = *aResIter;
       // compute new values by two contexts: the old and the new
       TopTools_ListOfShape aValShapes;
       // compute new values by two contexts: the old and the new
       TopTools_ListOfShape aValShapes;
-      computeValues(theContext, *aResIter, theValShape, aValShapes);
+      computeValues(theContext, aNewResObj, theValShape, aValShapes);
 
       TopTools_ListIteratorOfListOfShape aNewVal(aValShapes);
       for(; aNewVal.More(); aNewVal.Next()) {
         std::list<ResultPtr> aNewRes;
         TopTools_ListOfShape aNewUpdatedVal;
         TopoDS_Shape aNewValSh = aNewVal.Value();
 
       TopTools_ListIteratorOfListOfShape aNewVal(aValShapes);
       for(; aNewVal.More(); aNewVal.Next()) {
         std::list<ResultPtr> aNewRes;
         TopTools_ListOfShape aNewUpdatedVal;
         TopoDS_Shape aNewValSh = aNewVal.Value();
-        TopoDS_Shape aNewContShape = (*aResIter)->shape()->impl<TopoDS_Shape>();
+        TopoDS_Shape aNewContShape = aNewResObj->shape()->impl<TopoDS_Shape>();
+
         if (theValShape.IsNull() && aNewContShape.IsSame(aNewValSh))
           aNewValSh.Nullify();
         if (theValShape.IsNull() && aNewContShape.IsSame(aNewValSh))
           aNewValSh.Nullify();
-        if (searchNewContext(theDoc, aNewContShape, *aResIter, aNewValSh,
+        if (searchNewContext(theDoc, aNewContShape, aNewResObj, aNewValSh,
                              theAccessLabel, aNewRes, aNewUpdatedVal))
         {
           // append new results instead of the current ones
                              theAccessLabel, aNewRes, aNewUpdatedVal))
         {
           // append new results instead of the current ones
@@ -1466,7 +1530,7 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
             theValShapes.Append(aNewUpdVal.Value());
           }
         } else { // the current result is good
             theValShapes.Append(aNewUpdVal.Value());
           }
         } else { // the current result is good
-          theResults.push_back(*aResIter);
+          theResults.push_back(aNewResObj);
           theValShapes.Append(aNewValSh);
         }
       }
           theValShapes.Append(aNewValSh);
         }
       }
@@ -1488,25 +1552,40 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
       if (aFeature.get()) {
         FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
         std::list<FeaturePtr> aConcealers;
       if (aFeature.get()) {
         FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
         std::list<FeaturePtr> aConcealers;
-        concealedFeature(aFeature, aThisFeature, aConcealers, ResultPtr());
+        bool aCopyPossible = myParent && myParent->isMakeCopy();
+        concealedFeature(aFeature, aThisFeature, aCopyPossible, aConcealers, ResultPtr());
         if (aConcealers.empty())
           return;
         if (aConcealers.empty())
           return;
+        // if there are copies, but no direct modification, keep the original
+        bool aKeepOrigin = false;
+        if (aCopyPossible) {
+          std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
+          for(aKeepOrigin = true; aConcealer != aConcealers.end(); aConcealer++)
+            if (!std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(*aConcealer).get()) {
+              aKeepOrigin = false;
+              break;
+            }
+          if (aKeepOrigin) {
+            aConcealers.push_front(aFeature);
+          }
+        }
         bool aChanged = false;
         std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
         for(; aConcealer != aConcealers.end(); aConcealer++)
         bool aChanged = false;
         std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
         for(; aConcealer != aConcealers.end(); aConcealer++)
-          if (!myParent->isInList(*aConcealer, anEmptyShape)) {// avoid addition of duplicates
-            setValue(*aConcealer, anEmptyShape);
-            aChanged = true;
-          }
-        if (aConcealer == aConcealers.end()) {
-          if (!aChanged) // remove this
-            theRemove = true;
-        } else { // append new
-          for(aConcealer++; aConcealer != aConcealers.end(); aConcealer++)
-            if (!myParent->isInList(*aConcealer, anEmptyShape)) // avoid addition of duplicates
+          if (aChanged) {
+            if (aKeepOrigin || !myParent->isInList(*aConcealer, anEmptyShape))
               myParent->append(*aConcealer, anEmptyShape);
               myParent->append(*aConcealer, anEmptyShape);
-        }
-        if (aChanged) // searching for the further modifications
+          } else {
+            if (!myParent->isInList(*aConcealer, anEmptyShape)) {// avoid addition of duplicates
+              setValue(*aConcealer, anEmptyShape);
+              aChanged = true;
+            } else if (aCopyPossible && *aConcealer == aFeature) { // keep the origin in case of copy
+              aChanged = true;
+            }
+          }
+        if (!aChanged) // remove this
+          theRemove = true;
+        else if (!aKeepOrigin) // searching further modifications only if current changed
           updateInHistory(theRemove);
       }
     }
           updateInHistory(theRemove);
       }
     }
@@ -1704,6 +1783,9 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
         if (!myParent || !myParent->isInList(aNewContext, aValueShape)) { // avoid duplicates
           setValue(aNewContext, aValueShape);
           aFirst = false;
         if (!myParent || !myParent->isInList(aNewContext, aValueShape)) { // avoid duplicates
           setValue(aNewContext, aValueShape);
           aFirst = false;
+        } else if (aNewContext == aContext && myParent && myParent->isMakeCopy()) {
+          // this may be exactly the old one, not modified in case of copy
+          aFirst = false;
         }
       } else if (myParent) {
         if (!myParent->isInList(aNewContext, aValueShape)) // avoid addition of duplicates
         }
       } else if (myParent) {
         if (!myParent->isInList(aNewContext, aValueShape)) // avoid addition of duplicates
index 50a7ddbb91645726c340b095ce6db0ac97e3acd8..8e70cb28b3f595ea9c10f01c6b2fed92dc9fc296 100644 (file)
@@ -208,8 +208,8 @@ protected:
   /// Returns features that conceals theFeature and located in history before theStop
   /// theResultOfFeature if not null defines exact referenced result of a feature
   void concealedFeature(
   /// Returns features that conceals theFeature and located in history before theStop
   /// theResultOfFeature if not null defines exact referenced result of a feature
   void concealedFeature(
-    const FeaturePtr theFeature, const FeaturePtr theStop, std::list<FeaturePtr>& theConcealers,
-    const ResultPtr theResultOfFeature);
+    const FeaturePtr theFeature, const FeaturePtr theStop, const bool theCheckCopy,
+    std::list<FeaturePtr>& theConcealers, const ResultPtr theResultOfFeature);
 
   friend class Model_Data;
   friend class Model_AttributeSelectionList;
 
   friend class Model_Data;
   friend class Model_AttributeSelectionList;
index 79162a77945b2302e00d7ddc8818358b7cd60457..1efd395afc2519c15626f4bd9d77bdc561af615c 100644 (file)
@@ -1263,6 +1263,12 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis, c
     } while (aSub.get());
   }
 
     } while (aSub.get());
   }
 
+  AttributeSelectionListPtr aMovedList;
+  if (theMoved->getKind() == "Group") {
+    aMovedList = theMoved->selectionList("group_list");
+    if (aMovedList.get())
+      aMovedList->setMakeCopy(true);
+  }
   myObjs->moveFeature(theMoved, anAfterThisSub);
 
   if (theSplit) { // split the group into sub-features
   myObjs->moveFeature(theMoved, anAfterThisSub);
 
   if (theSplit) { // split the group into sub-features
@@ -1275,10 +1281,8 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis, c
     // must be after move to make enabled all features which are before theMoved
     setCurrentFeature(theMoved, true);
   }
     // must be after move to make enabled all features which are before theMoved
     setCurrentFeature(theMoved, true);
   }
-
-  if (theSplit) { // split the group into sub-features
-    theMoved->customAction("split");
-  }
+  if (aMovedList.get())
+    aMovedList->setMakeCopy(false);
 }
 
 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
 }
 
 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
index c5bedb6bae96d0c57bbaea334ff4e774b974df0c..4d19917385d6b25111e39640d112c97475bee7fe 100644 (file)
@@ -113,6 +113,21 @@ void Model_ResultBody::loadModifiedShapes(const std::shared_ptr<GeomAlgoAPI_Make
   }
 }
 
   }
 }
 
+void Model_ResultBody::loadFirstLevel(GeomShapePtr theShape, const std::string& theName)
+{
+  if (mySubs.size()) { // consists of subs
+    for (std::vector<ResultBodyPtr>::const_iterator aSubIter = mySubs.cbegin();
+      aSubIter != mySubs.cend();
+      ++aSubIter)
+    {
+      const ResultBodyPtr& aSub = *aSubIter;
+      aSub->loadFirstLevel(theShape, theName);
+    }
+  } else { // do for this directly
+    myBuilder->loadFirstLevel(theShape, theName);
+  }
+}
+
 int Model_ResultBody::numberOfSubs(bool forTree) const
 {
   return int(mySubs.size());
 int Model_ResultBody::numberOfSubs(bool forTree) const
 {
   return int(mySubs.size());
index 663bf8cecf416c54e02ed6f4e8ace29f3f719672..d0a1f908e68c946dbf38b6859de94cb8326ef33a 100644 (file)
@@ -78,6 +78,8 @@ public:
                                   const GeomAPI_Shape::ShapeType theShapeTypeToExplore,
                                   const std::string& theName = "") override;
 
                                   const GeomAPI_Shape::ShapeType theShapeTypeToExplore,
                                   const std::string& theName = "") override;
 
+  /// load shapes of the first level (to be used during shape import)
+  MODEL_EXPORT virtual void loadFirstLevel(GeomShapePtr theShape, const std::string& theName);
 
   /// Returns the number of sub-elements
   MODEL_EXPORT virtual int numberOfSubs(bool forTree = false) const;
 
   /// Returns the number of sub-elements
   MODEL_EXPORT virtual int numberOfSubs(bool forTree = false) const;
index a88017e3412403701017112c1a31277e6e17f0f8..60b9becfad0f08d09dc1acf4e2b56597a343095e 100644 (file)
@@ -39,7 +39,10 @@ class ModelAPI_AttributeSelectionList : public ModelAPI_Attribute
   /// may be sub-objects, so, it is the same as all sub-shapes are selected (#3005). It is "false"
   /// by default.
   bool myIsWholeResultAllowed;
   /// may be sub-objects, so, it is the same as all sub-shapes are selected (#3005). It is "false"
   /// by default.
   bool myIsWholeResultAllowed;
- public:
+  /// Flag that indicates that update in history must check the copy-features
+  /// and make a copy of selection for them.
+  bool myMakeCopy;
+public:
   /// Adds the new reference to the end of the list
   /// \param theContext object where the sub-shape was selected
   /// \param theSubShape selected sub-shape (if null, the whole context is selected)
   /// Adds the new reference to the end of the list
   /// \param theContext object where the sub-shape was selected
   /// \param theSubShape selected sub-shape (if null, the whole context is selected)
@@ -135,10 +138,20 @@ class ModelAPI_AttributeSelectionList : public ModelAPI_Attribute
     myIsWholeResultAllowed = theFlag;
   }
 
     myIsWholeResultAllowed = theFlag;
   }
 
+  /// Returns true if a copy features must be used in update in history.
+  MODELAPI_EXPORT virtual const bool isMakeCopy() const {
+    return myMakeCopy;
+  }
+
+  /// Sets true if a copy features must be used in update in history.
+  MODELAPI_EXPORT virtual void setMakeCopy(const bool theFlag)  {
+    myMakeCopy = theFlag;
+  }
+
 protected:
   /// Default constructor
   MODELAPI_EXPORT ModelAPI_AttributeSelectionList() : ModelAPI_Attribute()
 protected:
   /// Default constructor
   MODELAPI_EXPORT ModelAPI_AttributeSelectionList() : ModelAPI_Attribute()
-  {myIsWholeResultAllowed = false;}
+  {myIsWholeResultAllowed = false; myMakeCopy = false;}
 
 };
 
 
 };
 
index fe7cc35e872465e157331fc81353a5416ecfb5ad..8c231608656b13658eb1334ce373d715f557adbc 100644 (file)
@@ -250,5 +250,17 @@ class ModelAPI_Feature : public ModelAPI_Object
 //! Pointer on feature object
 typedef std::shared_ptr<ModelAPI_Feature> FeaturePtr;
 
 //! Pointer on feature object
 typedef std::shared_ptr<ModelAPI_Feature> FeaturePtr;
 
-#endif
+//! An interface for performing special copy actions. To give feature which is moved (a group)
+//! over this feature.
+class ModelAPI_FeatureCopyInterface {
+public:
+  /// An algorithm to update the moved feature by the separate Copy feature
+  /// \param theContext the original context object
+  /// \param theValue the original shape
+  /// \param theCopies resulting copy-context will be appended here
+  virtual void getCopies(ObjectPtr theContext, std::shared_ptr<GeomAPI_Shape> theValue,
+                         std::list<ObjectPtr>& theCopyContext,
+                         std::list<std::shared_ptr<GeomAPI_Shape> >& theCopyVals) = 0;
+};
 
 
+#endif
index 603aaac4df93d98a740c804eefbafa7a4928d40d..0a291ae486bd13ed486ae86ba8558e60faeec8dd 100644 (file)
@@ -139,12 +139,6 @@ void ModelAPI_ResultBody::loadDeletedShapes(const GeomMakeShapePtr& theAlgo,
   myBuilder->loadDeletedShapes(theAlgo, theOldShape, theShapeTypeToExplore, theShapesToExclude);
 }
 
   myBuilder->loadDeletedShapes(theAlgo, theOldShape, theShapeTypeToExplore, theShapesToExclude);
 }
 
-void ModelAPI_ResultBody::loadFirstLevel(GeomShapePtr theShape,
-                                         const std::string& theName)
-{
-  myBuilder->loadFirstLevel(theShape, theName);
-}
-
 // LCOV_EXCL_START
 bool ModelAPI_ResultBody::isConnectedTopology()
 {
 // LCOV_EXCL_START
 bool ModelAPI_ResultBody::isConnectedTopology()
 {
index c585280cc4c4c0e9ca30c32921aad5d5af7c6c74..7b9b3177871ffff939626c90df9b938758e107bf 100644 (file)
@@ -162,7 +162,7 @@ public:
 
   /// load shapes of the first level (to be used during shape import)
   MODELAPI_EXPORT virtual void loadFirstLevel(GeomShapePtr theShape,
 
   /// load shapes of the first level (to be used during shape import)
   MODELAPI_EXPORT virtual void loadFirstLevel(GeomShapePtr theShape,
-                                              const std::string& theName);
+                                              const std::string& theName) = 0;
 
   /// Returns true is the topology is connected.
   MODELAPI_EXPORT virtual bool isConnectedTopology() = 0;
 
   /// Returns true is the topology is connected.
   MODELAPI_EXPORT virtual bool isConnectedTopology() = 0;
index 475fdbae834ef5d578a50c2407ce91628d8e4fa5..b5afae0d06a957c5ac9253c7661b55dae2132c0f 100644 (file)
@@ -31,3 +31,4 @@ from FeaturesAPI import addFillet, addChamfer
 from FeaturesAPI import addFusionFaces
 from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle
 from FeaturesAPI import addRemoveResults
 from FeaturesAPI import addFusionFaces
 from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle
 from FeaturesAPI import addRemoveResults
+from FeaturesAPI import addCopy