From: mpv Date: Mon, 9 Dec 2019 11:35:06 +0000 (+0300) Subject: Implementation of the task #3109 : Feature Copy X-Git-Tag: V9_5_0a1~116 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=64f61525459c3d1912057046a8757943ec12ff3c;p=modules%2Fshaper.git Implementation of the task #3109 : Feature Copy --- diff --git a/src/FeaturesAPI/CMakeLists.txt b/src/FeaturesAPI/CMakeLists.txt index acd6222f5..dbdb6bc5b 100644 --- a/src/FeaturesAPI/CMakeLists.txt +++ b/src/FeaturesAPI/CMakeLists.txt @@ -48,6 +48,7 @@ SET(PROJECT_HEADERS FeaturesAPI_Translation.h FeaturesAPI_Union.h FeaturesAPI_FusionFaces.h + FeaturesAPI_Copy.h ) SET(PROJECT_SOURCES @@ -78,6 +79,7 @@ SET(PROJECT_SOURCES FeaturesAPI_Translation.cpp FeaturesAPI_Union.cpp FeaturesAPI_FusionFaces.cpp + FeaturesAPI_Copy.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/FeaturesAPI/FeaturesAPI.i b/src/FeaturesAPI/FeaturesAPI.i index c0a747b2f..9ed6db0d3 100644 --- a/src/FeaturesAPI/FeaturesAPI.i +++ b/src/FeaturesAPI/FeaturesAPI.i @@ -77,6 +77,7 @@ %shared_ptr(FeaturesAPI_Union) %shared_ptr(FeaturesAPI_FusionFaces) %shared_ptr(FeaturesAPI_RemoveResults) +%shared_ptr(FeaturesAPI_Copy) %typecheck(SWIG_TYPECHECK_POINTER) std::pair, bool>, const std::pair, bool> & { @@ -168,3 +169,4 @@ %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 index 000000000..5ec81aba0 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Copy.cpp @@ -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 +#include + +//================================================================================================ +FeaturesAPI_Copy::FeaturesAPI_Copy(const std::shared_ptr& theFeature) +: ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +//================================================================================================ +FeaturesAPI_Copy::FeaturesAPI_Copy(const std::shared_ptr& theFeature, + const std::list& theObjects, + const int theNumber) +: ModelHighAPI_Interface(theFeature) +{ + if(initialize()) { + setNumber(theNumber); + setObjects(theObjects); + } +} + +//================================================================================================ +FeaturesAPI_Copy::~FeaturesAPI_Copy() {} + +//================================================================================================= +void FeaturesAPI_Copy::setObjects(const std::list& 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& thePart, + const std::list& theObjects, + const int theNumber) +{ + std::shared_ptr 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 index 000000000..92174f9f4 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Copy.h @@ -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 + +#include +#include + +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& theFeature); + + /// Constructor with values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Copy(const std::shared_ptr& theFeature, + const std::list& 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& 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 CopyPtr; + +/// \ingroup CPPHighAPI +/// \brief Create Copy feature. +FEATURESAPI_EXPORT +CopyPtr addCopy(const std::shared_ptr& thePart, + const std::list& theObjects, + const int theNumber); + +#endif // FeaturesAPI_Copy_H_ diff --git a/src/FeaturesAPI/FeaturesAPI_swig.h b/src/FeaturesAPI/FeaturesAPI_swig.h index cd71216fa..767e0966f 100644 --- a/src/FeaturesAPI/FeaturesAPI_swig.h +++ b/src/FeaturesAPI/FeaturesAPI_swig.h @@ -50,5 +50,6 @@ #include "FeaturesAPI_Union.h" #include "FeaturesAPI_FusionFaces.h" #include "FeaturesAPI_RemoveResults.h" + #include "FeaturesAPI_Copy.h" #endif // FeaturesAPI_swig_H_ diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index ea14d9ea6..913a83aff 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -61,6 +61,7 @@ SET(PROJECT_HEADERS FeaturesPlugin_FusionFaces.h FeaturesPlugin_RemoveResults.h FeaturesPlugin_Chamfer.h + FeaturesPlugin_Copy.h ) SET(PROJECT_SOURCES @@ -103,6 +104,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_FusionFaces.cpp FeaturesPlugin_RemoveResults.cpp FeaturesPlugin_Chamfer.cpp + FeaturesPlugin_Copy.cpp ) SET(XML_RESOURCES @@ -135,6 +137,7 @@ SET(XML_RESOURCES measurement_widget.xml fusion_faces_widget.xml chamfer_widget.xml + copy_widget.xml ) SET(TEXT_RESOURCES @@ -541,4 +544,11 @@ ADD_UNIT_TESTS(TestExtrusion.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 index 000000000..76db7fa04 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp @@ -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 +#include +#include + +#include +#include +#include + +#include + +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 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 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 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 theValue, + std::list& theCopyContext, std::list >& theCopyVals) +{ + ResultPtr aContextRes = std::dynamic_pointer_cast(theContext); + GeomShapePtr aGroupValue = theValue.get() ? theValue : aContextRes->shape(); + + AttributeSelectionListPtr aList = selectionList(OBJECTS()); + std::list::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(aResContext); + if (aResBody.get()) { + std::list aSubs; + ModelAPI_Tools::allSubs(aResBody, aSubs, true); + std::list::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 index 000000000..b500b51f1 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Copy.h @@ -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 + +/// \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 ”Recover” feature is that not concealed results may +/// be selected and in the history behavior: the “Move to the End” of groups will move to +/// all copy-results. + +class FeaturesPlugin_Copy : public ModelAPI_Feature, public ModelAPI_FeatureCopyInterface +{ +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 theValue, + std::list& theCopyContext, std::list >& theCopyVals); + + /// Use plugin manager for features creation. + FeaturesPlugin_Copy() {} +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index 797343446..5a2ff7c5c 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -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); + } else if (theFeatureID == FeaturesPlugin_Copy::ID()) { + return FeaturePtr(new FeaturesPlugin_Copy); } + // 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 index 000000000..17f99d13a --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCopyFeature.py @@ -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 index 000000000..0d8eb2d6a --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCopyFeatureMoveGroupOfFeature.py @@ -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 index 000000000..bde1d85c1 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCopyMoveResult.py @@ -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 index 000000000..f2d78e575 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCopyMoveSubShapes.py @@ -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 index 000000000..eac569db8 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCopyNames.py @@ -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 index 000000000..f8aedf2e4 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCopySubShapes.py @@ -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 index 000000000..0a9a88481 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCopyWholeFeature.py @@ -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 index 000000000..6f79ca138 --- /dev/null +++ b/src/FeaturesPlugin/copy_widget.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/FeaturesPlugin/icons/copy.png b/src/FeaturesPlugin/icons/copy.png new file mode 100644 index 000000000..7e1116367 Binary files /dev/null and b/src/FeaturesPlugin/icons/copy.png differ diff --git a/src/FeaturesPlugin/plugin-Features.xml b/src/FeaturesPlugin/plugin-Features.xml index 3197488a7..428f00314 100644 --- a/src/FeaturesPlugin/plugin-Features.xml +++ b/src/FeaturesPlugin/plugin-Features.xml @@ -97,6 +97,13 @@ helpfile="recoverFeature.html"> + + + diff --git a/src/Model/Model_AttributeSelection.cpp b/src/Model/Model_AttributeSelection.cpp index 8ff6d81f8..468b60f7a 100644 --- a/src/Model/Model_AttributeSelection.cpp +++ b/src/Model/Model_AttributeSelection.cpp @@ -1292,8 +1292,8 @@ void Model_AttributeSelection::computeValues( void Model_AttributeSelection::concealedFeature( - const FeaturePtr theFeature, const FeaturePtr theStop, std::list& theConcealers, - const ResultPtr theResultOfFeature) + const FeaturePtr theFeature, const FeaturePtr theStop, const bool theCheckCopy, + std::list& theConcealers, const ResultPtr theResultOfFeature) { std::set alreadyProcessed; alreadyProcessed.insert(theFeature); @@ -1328,7 +1328,9 @@ void Model_AttributeSelection::concealedFeature( 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(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 @@ -1410,12 +1412,13 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr } 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(owner()); FeaturePtr aContextOwner = theDoc->feature(theContext); std::list aConcealers; - concealedFeature(aContextOwner, aThisFeature, aConcealers, theContext); + concealedFeature(aContextOwner, aThisFeature, false, aConcealers, theContext); std::list::iterator aConcealer = aConcealers.begin(); for(; aConcealer != aConcealers.end(); aConcealer++) { std::list aRefResults; @@ -1436,26 +1439,87 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr 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::iterator aResIter = aResults.begin(); + std::list aCopyContext; + std::list 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(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 aCopier = + std::dynamic_pointer_cast(aFeat); + if (aCopier.get()) { + GeomShapePtr aValShape(new GeomAPI_Shape); + aValShape->setImpl(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::iterator aCopyContIter = aCopyContext.begin(); + std::list::iterator aCopyValIter = aCopyVals.begin(); + for(; aCopyContIter != aCopyContext.end(); aCopyContIter++, aCopyValIter++) { + ResultPtr aNewCont = std::dynamic_pointer_cast(*aCopyContIter); + TopoDS_Shape aNewContShape = aNewCont->shape()->impl(); + GeomShapePtr aNewVal = *aCopyValIter; + TopoDS_Shape aNewValShape; + if (aNewVal.get() && !aNewVal->isNull()) + aNewValShape = aNewVal->impl(); + std::list aNewRes; + TopTools_ListOfShape aNewUpdatedVal; + if (searchNewContext(theDoc, aNewContShape, aNewCont, aNewValShape, + theAccessLabel, aNewRes, aNewUpdatedVal)) { + // append new results instead of the current ones + std::list::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::iterator aResIter = aResults.begin(); - for(; aResIter != aResults.end(); aResIter++) { + for(aResIter = aResults.begin(); aResIter != aResults.end(); aResIter++) { if (aResIter->get() != NULL) { + ResultPtr aNewResObj = *aResIter; // 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 aNewRes; TopTools_ListOfShape aNewUpdatedVal; TopoDS_Shape aNewValSh = aNewVal.Value(); - TopoDS_Shape aNewContShape = (*aResIter)->shape()->impl(); + TopoDS_Shape aNewContShape = aNewResObj->shape()->impl(); + 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 @@ -1466,7 +1530,7 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr theValShapes.Append(aNewUpdVal.Value()); } } else { // the current result is good - theResults.push_back(*aResIter); + theResults.push_back(aNewResObj); theValShapes.Append(aNewValSh); } } @@ -1488,25 +1552,40 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove) if (aFeature.get()) { FeaturePtr aThisFeature = std::dynamic_pointer_cast(owner()); std::list aConcealers; - concealedFeature(aFeature, aThisFeature, aConcealers, ResultPtr()); + bool aCopyPossible = myParent && myParent->isMakeCopy(); + concealedFeature(aFeature, aThisFeature, aCopyPossible, aConcealers, ResultPtr()); if (aConcealers.empty()) return; + // if there are copies, but no direct modification, keep the original + bool aKeepOrigin = false; + if (aCopyPossible) { + std::list::iterator aConcealer = aConcealers.begin(); + for(aKeepOrigin = true; aConcealer != aConcealers.end(); aConcealer++) + if (!std::dynamic_pointer_cast(*aConcealer).get()) { + aKeepOrigin = false; + break; + } + if (aKeepOrigin) { + aConcealers.push_front(aFeature); + } + } bool aChanged = false; std::list::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); - } - 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); } } @@ -1704,6 +1783,9 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove) 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 diff --git a/src/Model/Model_AttributeSelection.h b/src/Model/Model_AttributeSelection.h index 50a7ddbb9..8e70cb28b 100644 --- a/src/Model/Model_AttributeSelection.h +++ b/src/Model/Model_AttributeSelection.h @@ -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( - const FeaturePtr theFeature, const FeaturePtr theStop, std::list& theConcealers, - const ResultPtr theResultOfFeature); + const FeaturePtr theFeature, const FeaturePtr theStop, const bool theCheckCopy, + std::list& theConcealers, const ResultPtr theResultOfFeature); friend class Model_Data; friend class Model_AttributeSelectionList; diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index 79162a779..1efd395af 100644 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -1263,6 +1263,12 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis, c } 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 @@ -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); } - - 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 theObject) diff --git a/src/Model/Model_ResultBody.cpp b/src/Model/Model_ResultBody.cpp index c5bedb6ba..4d1991738 100644 --- a/src/Model/Model_ResultBody.cpp +++ b/src/Model/Model_ResultBody.cpp @@ -113,6 +113,21 @@ void Model_ResultBody::loadModifiedShapes(const std::shared_ptr::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()); diff --git a/src/Model/Model_ResultBody.h b/src/Model/Model_ResultBody.h index 663bf8cec..d0a1f908e 100644 --- a/src/Model/Model_ResultBody.h +++ b/src/Model/Model_ResultBody.h @@ -78,6 +78,8 @@ public: 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; diff --git a/src/ModelAPI/ModelAPI_AttributeSelectionList.h b/src/ModelAPI/ModelAPI_AttributeSelectionList.h index a88017e34..60b9becfa 100644 --- a/src/ModelAPI/ModelAPI_AttributeSelectionList.h +++ b/src/ModelAPI/ModelAPI_AttributeSelectionList.h @@ -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; - 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) @@ -135,10 +138,20 @@ class ModelAPI_AttributeSelectionList : public ModelAPI_Attribute 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() - {myIsWholeResultAllowed = false;} + {myIsWholeResultAllowed = false; myMakeCopy = false;} }; diff --git a/src/ModelAPI/ModelAPI_Feature.h b/src/ModelAPI/ModelAPI_Feature.h index fe7cc35e8..8c2316086 100644 --- a/src/ModelAPI/ModelAPI_Feature.h +++ b/src/ModelAPI/ModelAPI_Feature.h @@ -250,5 +250,17 @@ class ModelAPI_Feature : public ModelAPI_Object //! Pointer on feature object typedef std::shared_ptr 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 theValue, + std::list& theCopyContext, + std::list >& theCopyVals) = 0; +}; +#endif diff --git a/src/ModelAPI/ModelAPI_ResultBody.cpp b/src/ModelAPI/ModelAPI_ResultBody.cpp index 603aaac4d..0a291ae48 100644 --- a/src/ModelAPI/ModelAPI_ResultBody.cpp +++ b/src/ModelAPI/ModelAPI_ResultBody.cpp @@ -139,12 +139,6 @@ void ModelAPI_ResultBody::loadDeletedShapes(const GeomMakeShapePtr& theAlgo, 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() { diff --git a/src/ModelAPI/ModelAPI_ResultBody.h b/src/ModelAPI/ModelAPI_ResultBody.h index c585280cc..7b9b31778 100644 --- a/src/ModelAPI/ModelAPI_ResultBody.h +++ b/src/ModelAPI/ModelAPI_ResultBody.h @@ -162,7 +162,7 @@ public: /// 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; diff --git a/src/PythonAPI/model/features/__init__.py b/src/PythonAPI/model/features/__init__.py index 475fdbae8..b5afae0d0 100644 --- a/src/PythonAPI/model/features/__init__.py +++ b/src/PythonAPI/model/features/__init__.py @@ -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 addCopy