From c8e4c6d6463eb3d0268c6042ed527c72a898e083 Mon Sep 17 00:00:00 2001 From: dbv Date: Thu, 16 Aug 2018 16:44:10 +0300 Subject: [PATCH] Issue #2562: CEA 2018-1 Fuse Fuse now have simple mode with only one field. --- src/FeaturesAPI/FeaturesAPI_BooleanFuse.cpp | 50 ++- src/FeaturesAPI/FeaturesAPI_BooleanFuse.h | 21 +- src/FeaturesPlugin/CMakeLists.txt | 4 + .../FeaturesPlugin_BooleanFuse.cpp | 374 ++++++++++++++++++ .../FeaturesPlugin_BooleanFuse.h | 60 ++- src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp | 4 + .../FeaturesPlugin_Validators.cpp | 142 +++++++ .../FeaturesPlugin_Validators.h | 36 ++ .../Test/TestBooleanCompSolids.py | 2 + .../Test/TestBooleanFuse_RemoveEdges.py | 44 +++ .../Test/TestBooleanFuse_SimpleMode.py | 44 +++ .../Test/{Test2394.py => _Test2394.py} | 0 src/FeaturesPlugin/boolean_fuse_widget.xml | 68 ++++ .../icons/bool_fuse_advanced.png | Bin 0 -> 931 bytes src/FeaturesPlugin/icons/bool_fuse_simple.png | Bin 0 -> 1077 bytes src/FeaturesPlugin/plugin-Features.xml | 2 +- src/ModelAPI/Test/Test1512.py | 2 + 17 files changed, 839 insertions(+), 14 deletions(-) create mode 100644 src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp create mode 100644 src/FeaturesPlugin/Test/TestBooleanFuse_RemoveEdges.py create mode 100644 src/FeaturesPlugin/Test/TestBooleanFuse_SimpleMode.py rename src/FeaturesPlugin/Test/{Test2394.py => _Test2394.py} (100%) create mode 100644 src/FeaturesPlugin/boolean_fuse_widget.xml create mode 100644 src/FeaturesPlugin/icons/bool_fuse_advanced.png create mode 100644 src/FeaturesPlugin/icons/bool_fuse_simple.png diff --git a/src/FeaturesAPI/FeaturesAPI_BooleanFuse.cpp b/src/FeaturesAPI/FeaturesAPI_BooleanFuse.cpp index c1a043d67..b0e1c76a4 100644 --- a/src/FeaturesAPI/FeaturesAPI_BooleanFuse.cpp +++ b/src/FeaturesAPI/FeaturesAPI_BooleanFuse.cpp @@ -36,12 +36,20 @@ FeaturesAPI_BooleanFuse::FeaturesAPI_BooleanFuse( FeaturesAPI_BooleanFuse::FeaturesAPI_BooleanFuse( const std::shared_ptr& theFeature, const std::list& theMainObjects, - const std::list& theToolObjects) + const std::list& theToolObjects, + const bool theRemoveEdges) : ModelHighAPI_Interface(theFeature) { if(initialize()) { + if (theToolObjects.empty()) { + fillAttribute(FeaturesPlugin_BooleanFuse::CREATION_METHOD_SIMPLE(), mycreationMethod); + } else { + fillAttribute(FeaturesPlugin_BooleanFuse::CREATION_METHOD_ADVANCED(), mycreationMethod); + } + fillAttribute(theMainObjects, mymainObjects); fillAttribute(theToolObjects, mytoolObjects); + fillAttribute(theRemoveEdges, myremoveEdges); execute(false); } @@ -68,6 +76,20 @@ void FeaturesAPI_BooleanFuse::setToolObjects( { fillAttribute(theToolObjects, mytoolObjects); + if (theToolObjects.empty()) { + fillAttribute(FeaturesPlugin_BooleanFuse::CREATION_METHOD_SIMPLE(), mycreationMethod); + } else { + fillAttribute(FeaturesPlugin_BooleanFuse::CREATION_METHOD_ADVANCED(), mycreationMethod); + } + + execute(); +} + +//================================================================================================== +void FeaturesAPI_BooleanFuse::setRemoveEdges(const bool theRemoveEdges) +{ + fillAttribute(theRemoveEdges, myremoveEdges); + execute(); } @@ -83,28 +105,44 @@ void FeaturesAPI_BooleanFuse::dump(ModelHighAPI_Dumper& theDumper) const aBase->selectionList(FeaturesPlugin_BooleanFuse::OBJECT_LIST_ID()); AttributeSelectionListPtr aTools = aBase->selectionList(FeaturesPlugin_BooleanFuse::TOOL_LIST_ID()); + AttributeBooleanPtr aRemoveEdges = + aBase->boolean(FeaturesPlugin_BooleanFuse::REMOVE_INTERSECTION_EDGES_ID()); + + theDumper << "(" << aDocName << ", " << anObjects; + + if (aTools->size() > 0) { + theDumper << ", " << aTools; + } + + if (aRemoveEdges->value()) { + theDumper << ", " << true; + } - theDumper << "(" << aDocName << ", " << anObjects << ", " << aTools << ")" << std::endl; + theDumper << ")" << std::endl; } //================================================================================================== BooleanFusePtr addFuse(const std::shared_ptr& thePart, - const std::list& theObjects) + const std::list& theObjects, + const bool theRemoveEdges) { std::shared_ptr aFeature = thePart->addFeature(FeaturesAPI_BooleanFuse::ID()); std::list aToolObjects; return BooleanFusePtr(new FeaturesAPI_BooleanFuse(aFeature, theObjects, - aToolObjects)); + aToolObjects, + theRemoveEdges)); } //================================================================================================== BooleanFusePtr addFuse(const std::shared_ptr& thePart, const std::list& theMainObjects, - const std::list& theToolObjects) + const std::list& theToolObjects, + const bool theRemoveEdges) { std::shared_ptr aFeature = thePart->addFeature(FeaturesAPI_BooleanFuse::ID()); return BooleanFusePtr(new FeaturesAPI_BooleanFuse(aFeature, theMainObjects, - theToolObjects)); + theToolObjects, + theRemoveEdges)); } diff --git a/src/FeaturesAPI/FeaturesAPI_BooleanFuse.h b/src/FeaturesAPI/FeaturesAPI_BooleanFuse.h index 17b72f7f2..495add44e 100644 --- a/src/FeaturesAPI/FeaturesAPI_BooleanFuse.h +++ b/src/FeaturesAPI/FeaturesAPI_BooleanFuse.h @@ -45,17 +45,22 @@ public: FEATURESAPI_EXPORT FeaturesAPI_BooleanFuse(const std::shared_ptr& theFeature, const std::list& theMainObjects, - const std::list& theToolObjects); + const std::list& theToolObjects, + const bool theRemoveEdges = false); /// Destructor. FEATURESAPI_EXPORT virtual ~FeaturesAPI_BooleanFuse(); - INTERFACE_2(FeaturesPlugin_BooleanFuse::ID(), + INTERFACE_4(FeaturesPlugin_BooleanFuse::ID(), + creationMethod, FeaturesPlugin_BooleanFuse::CREATION_METHOD(), + ModelAPI_AttributeString, /** Creation method */, mainObjects, FeaturesPlugin_BooleanFuse::OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList, /** Main objects */, toolObjects, FeaturesPlugin_BooleanFuse::TOOL_LIST_ID(), - ModelAPI_AttributeSelectionList, /** Tool objects*/) + ModelAPI_AttributeSelectionList, /** Tool objects*/, + removeEdges, FeaturesPlugin_BooleanFuse::REMOVE_INTERSECTION_EDGES_ID(), + ModelAPI_AttributeBoolean, /** Remove edges */) /// Set main objects. FEATURESAPI_EXPORT @@ -65,6 +70,10 @@ public: FEATURESAPI_EXPORT void setToolObjects(const std::list& theToolObjects); + /// Set remove edges. + FEATURESAPI_EXPORT + void setRemoveEdges(const bool theRemoveEdges); + /// Dump wrapped feature FEATURESAPI_EXPORT virtual void dump(ModelHighAPI_Dumper& theDumper) const; @@ -77,13 +86,15 @@ typedef std::shared_ptr BooleanFusePtr; /// \brief Create Boolean Fuse feature. FEATURESAPI_EXPORT BooleanFusePtr addFuse(const std::shared_ptr& thePart, - const std::list& theObjects); + const std::list& theObjects, + const bool theRemoveEdges = false); /// \ingroup CPPHighAPI /// \brief Create Boolean Fuse feature. FEATURESAPI_EXPORT BooleanFusePtr addFuse(const std::shared_ptr& thePart, const std::list& theMainObjects, - const std::list& theToolObjects); + const std::list& theToolObjects, + const bool theRemoveEdges = false); #endif // FeaturesAPI_BooleanFuse_H_ diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index 3a60a545c..0b8054f01 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -70,6 +70,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_Translation.cpp FeaturesPlugin_Boolean.cpp FeaturesPlugin_BooleanCut.cpp + FeaturesPlugin_BooleanFuse.cpp FeaturesPlugin_BooleanSmash.cpp FeaturesPlugin_Intersection.cpp FeaturesPlugin_Partition.cpp @@ -108,6 +109,7 @@ SET(XML_RESOURCES rotation_widget.xml translation_widget.xml boolean_widget.xml + boolean_fuse_widget.xml boolean_smash_widget.xml recover_widget.xml partition_widget.xml @@ -299,4 +301,6 @@ ADD_UNIT_TESTS(TestExtrusion.py TestBooleanCut_Wire_Wire.py TestBooleanCut_WireCompound_WireCompound.py TestBooleanSmash_Face_Face.py + TestBooleanFuse_SimpleMode.py + TestBooleanFuse_RemoveEdges.py ) diff --git a/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp b/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp new file mode 100644 index 000000000..1d7d14a5f --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp @@ -0,0 +1,374 @@ +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// + +#include "FeaturesPlugin_BooleanFuse.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//================================================================================================== +FeaturesPlugin_BooleanFuse::FeaturesPlugin_BooleanFuse() +: FeaturesPlugin_Boolean(FeaturesPlugin_Boolean::BOOL_FUSE) +{ +} + +//================================================================================================== +void FeaturesPlugin_BooleanFuse::initAttributes() +{ + data()->addAttribute(CREATION_METHOD(), ModelAPI_AttributeString::typeId()); + + data()->addAttribute(OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); + data()->addAttribute(TOOL_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); + + data()->addAttribute(REMOVE_INTERSECTION_EDGES_ID(), ModelAPI_AttributeBoolean::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), OBJECT_LIST_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), TOOL_LIST_ID()); +} + +//================================================================================================== +void FeaturesPlugin_BooleanFuse::execute() +{ + ListOfShape anObjects, aTools, anEdgesAndFaces, aPlanes; + std::map, ListOfShape> aCompSolidsObjects; + + bool isSimpleCreation = false; + + AttributeStringPtr aCreationMethodAttr = string(CREATION_METHOD()); + if (aCreationMethodAttr.get() + && aCreationMethodAttr->value() == CREATION_METHOD_SIMPLE()) { + isSimpleCreation = true; + } + + // Getting objects. + AttributeSelectionListPtr anObjectsSelList = + selectionList(FeaturesPlugin_Boolean::OBJECT_LIST_ID()); + for (int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) { + AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex); + std::shared_ptr anObject = anObjectAttr->value(); + if (!anObject.get()) { + return; + } + ResultPtr aContext = anObjectAttr->context(); + ResultCompSolidPtr aResCompSolidPtr = ModelAPI_Tools::compSolidOwner(aContext); + if (!isSimpleCreation + && aResCompSolidPtr.get() + && aResCompSolidPtr->shape()->shapeType() == GeomAPI_Shape::COMPSOLID) { + std::shared_ptr aContextShape = aResCompSolidPtr->shape(); + std::map, ListOfShape>::iterator + anIt = aCompSolidsObjects.begin(); + for (; anIt != aCompSolidsObjects.end(); anIt++) { + if (anIt->first->isEqual(aContextShape)) { + aCompSolidsObjects[anIt->first].push_back(anObject); + break; + } + } + if (anIt == aCompSolidsObjects.end()) { + aCompSolidsObjects[aContextShape].push_back(anObject); + } + } else { + if (anObject->shapeType() == GeomAPI_Shape::EDGE + || anObject->shapeType() == GeomAPI_Shape::FACE) { + anEdgesAndFaces.push_back(anObject); + } else { + anObjects.push_back(anObject); + } + } + } + + // Getting tools. + if (!isSimpleCreation) { + AttributeSelectionListPtr aToolsSelList = selectionList(FeaturesPlugin_Boolean::TOOL_LIST_ID()); + for (int aToolsIndex = 0; aToolsIndex < aToolsSelList->size(); aToolsIndex++) { + AttributeSelectionPtr aToolAttr = aToolsSelList->value(aToolsIndex); + GeomShapePtr aTool = aToolAttr->value(); + if (!aTool.get()) { + // It could be a construction plane. + ResultPtr aContext = aToolAttr->context(); + aPlanes.push_back(aToolAttr->context()->shape()); + } else if (aTool->shapeType() == GeomAPI_Shape::EDGE + || aTool->shapeType() == GeomAPI_Shape::FACE) { + anEdgesAndFaces.push_back(aTool); + } else { + aTools.push_back(aTool); + } + } + } + + if ((anObjects.size() + aTools.size() + + aCompSolidsObjects.size() + anEdgesAndFaces.size()) < 2) { + std::string aFeatureError = "Error: Not enough objects for boolean operation."; + setError(aFeatureError); + return; + } + + // Collecting all solids which will be fused. + ListOfShape aSolidsToFuse; + aSolidsToFuse.insert(aSolidsToFuse.end(), anObjects.begin(), anObjects.end()); + aSolidsToFuse.insert(aSolidsToFuse.end(), aTools.begin(), aTools.end()); + + // Collecting solids from compsolids which will not be modified + // in boolean operation and will be added to result. + ListOfShape aShapesToAdd; + for (std::map, ListOfShape>::iterator + anIt = aCompSolidsObjects.begin(); + anIt != aCompSolidsObjects.end(); anIt++) { + std::shared_ptr aCompSolid = anIt->first; + ListOfShape& aUsedInOperationSolids = anIt->second; + aSolidsToFuse.insert(aSolidsToFuse.end(), aUsedInOperationSolids.begin(), + aUsedInOperationSolids.end()); + + // Collect solids from compsolid which will not be modified in boolean operation. + for (GeomAPI_ShapeExplorer + anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) { + std::shared_ptr aSolidInCompSolid = anExp.current(); + ListOfShape::iterator anIt = aUsedInOperationSolids.begin(); + for (; anIt != aUsedInOperationSolids.end(); anIt++) { + if (aSolidInCompSolid->isEqual(*anIt)) { + break; + } + } + if (anIt == aUsedInOperationSolids.end()) { + aShapesToAdd.push_back(aSolidInCompSolid); + } + } + } + + ListOfShape anOriginalShapes = aSolidsToFuse; + anOriginalShapes.insert(anOriginalShapes.end(), aShapesToAdd.begin(), aShapesToAdd.end()); + + // Cut edges and faces(if we have any) with solids. + GeomAlgoAPI_MakeShapeList aMakeShapeList; + GeomAPI_DataMapOfShapeShape aMapOfShapes; + std::shared_ptr aCuttedEdgesAndFaces; + if (!anEdgesAndFaces.empty()) { + std::shared_ptr aCutAlgo(new GeomAlgoAPI_Boolean(anEdgesAndFaces, + anOriginalShapes, GeomAlgoAPI_Boolean::BOOL_CUT)); + if (aCutAlgo->isDone()) { + aCuttedEdgesAndFaces = aCutAlgo->shape(); + aMakeShapeList.appendAlgo(aCutAlgo); + aMapOfShapes.merge(aCutAlgo->mapOfSubShapes()); + } + } + anOriginalShapes.insert(anOriginalShapes.end(), anEdgesAndFaces.begin(), + anEdgesAndFaces.end()); + + // If we have compsolids then cut with not used solids all others. + if (!aShapesToAdd.empty()) { + aSolidsToFuse.clear(); + for (ListOfShape::iterator + anIt = anOriginalShapes.begin(); anIt != anOriginalShapes.end(); anIt++) { + ListOfShape aOneObjectList; + aOneObjectList.push_back(*anIt); + std::shared_ptr aCutAlgo( + new GeomAlgoAPI_Boolean(aOneObjectList, aShapesToAdd, GeomAlgoAPI_Boolean::BOOL_CUT)); + + if (GeomAlgoAPI_ShapeTools::volume(aCutAlgo->shape()) > 1.e-27) { + aSolidsToFuse.push_back(aCutAlgo->shape()); + aMakeShapeList.appendAlgo(aCutAlgo); + aMapOfShapes.merge(aCutAlgo->mapOfSubShapes()); + } + } + } + + if (!aSolidsToFuse.empty()) { + anObjects.clear(); + anObjects.push_back(aSolidsToFuse.back()); + aSolidsToFuse.pop_back(); + aTools = aSolidsToFuse; + } + + // Fuse all objects and all tools. + std::shared_ptr aShape; + if (anObjects.size() == 1 && aTools.empty()) { + aShape = anObjects.front(); + } else if (anObjects.empty() && aTools.size() == 1) { + aShape = aTools.front(); + } else if ((anObjects.size() + aTools.size()) > 1) { + std::shared_ptr aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects, + aTools, + GeomAlgoAPI_Boolean::BOOL_FUSE)); + + // Checking that the algorithm worked properly. + if (!aFuseAlgo->isDone()) { + static const std::string aFeatureError = "Error: Boolean algorithm failed."; + setError(aFeatureError); + return; + } + if (aFuseAlgo->shape()->isNull()) { + static const std::string aShapeError = "Error: Resulting shape is Null."; + setError(aShapeError); + return; + } + if (!aFuseAlgo->isValid()) { + std::string aFeatureError = "Error: Resulting shape is not valid."; + setError(aFeatureError); + return; + } + + aShape = aFuseAlgo->shape(); + aMakeShapeList.appendAlgo(aFuseAlgo); + aMapOfShapes.merge(aFuseAlgo->mapOfSubShapes()); + } + + // Combine result with not used solids from compsolid and edges and faces (if we have any). + if (aCuttedEdgesAndFaces.get() && !aCuttedEdgesAndFaces->isNull()) { + aShapesToAdd.push_back(aCuttedEdgesAndFaces); + } else { + aShapesToAdd.insert(aShapesToAdd.end(), anEdgesAndFaces.begin(), anEdgesAndFaces.end()); + } + if (!aShapesToAdd.empty()) { + if (aShape.get()) { + aShapesToAdd.push_back(aShape); + } + std::shared_ptr aFillerAlgo( + new GeomAlgoAPI_PaveFiller(aShapesToAdd, true)); + if (!aFillerAlgo->isDone()) { + std::string aFeatureError = "Error: PaveFiller algorithm failed."; + setError(aFeatureError); + return; + } + if (aFillerAlgo->shape()->isNull()) { + static const std::string aShapeError = "Error: Resulting shape is Null."; + setError(aShapeError); + return; + } + if (!aFillerAlgo->isValid()) { + std::string aFeatureError = "Error: Resulting shape is not valid."; + setError(aFeatureError); + return; + } + + aShape = aFillerAlgo->shape(); + aMakeShapeList.appendAlgo(aFillerAlgo); + aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes()); + } + + bool isRemoveEdges = false; + AttributeBooleanPtr removeEdgesAttr = boolean(REMOVE_INTERSECTION_EDGES_ID()); + if (removeEdgesAttr.get()) { + isRemoveEdges = removeEdgesAttr->value(); + } + + if (isRemoveEdges) { + std::shared_ptr aUnifyAlgo( + new GeomAlgoAPI_UnifySameDomain(aShape)); + + if (!aUnifyAlgo->isDone()) { + std::string aFeatureError = "Error: PaveFiller algorithm failed."; + setError(aFeatureError); + return; + } + if (aUnifyAlgo->shape()->isNull()) { + static const std::string aShapeError = "Error: Resulting shape is Null."; + setError(aShapeError); + return; + } + if (!aUnifyAlgo->isValid()) { + std::string aFeatureError = "Error: Resulting shape is not valid."; + setError(aFeatureError); + return; + } + + aShape = aUnifyAlgo->shape(); + aMakeShapeList.appendAlgo(aUnifyAlgo); + aMapOfShapes.merge(aUnifyAlgo->mapOfSubShapes()); + } + + int aResultIndex = 0; + + std::shared_ptr aBackShape = anOriginalShapes.back(); + anOriginalShapes.pop_back(); + std::shared_ptr aResultBody = + document()->createBody(data(), aResultIndex); + loadNamingDS(aResultBody, aBackShape, anOriginalShapes, + aShape, aMakeShapeList, aMapOfShapes); + setResult(aResultBody, aResultIndex); + aResultIndex++; + + // remove the rest results if there were produced in the previous pass + removeResults(aResultIndex); +} + +//================================================================================================== +void FeaturesPlugin_BooleanFuse::loadNamingDS(std::shared_ptr theResultBody, + const std::shared_ptr theBaseShape, + const ListOfShape& theTools, + const std::shared_ptr theResultShape, + GeomAlgoAPI_MakeShape& theMakeShape, + GeomAPI_DataMapOfShapeShape& theMapOfShapes) +{ + //load result + if (theBaseShape->isEqual(theResultShape)) { + theResultBody->store(theResultShape, false); + } else { + const int aModifyTag = 1; + const int aModifyEdgeTag = 2; + const int aModifyFaceTag = 3; + const int aDeletedTag = 4; + /// sub solids will be placed at labels 5, 6, etc. if result is compound of solids + const int aSubsolidsTag = 5; + + theResultBody->storeModified(theBaseShape, theResultShape, aSubsolidsTag); + + const std::string aModName = "Modified"; + const std::string aModEName = "Modified_Edge"; + const std::string aModFName = "Modified_Face"; + + theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::EDGE, + aModifyEdgeTag, aModEName, theMapOfShapes, false, + false, true); + theResultBody->loadAndOrientModifiedShapes(&theMakeShape, theBaseShape, GeomAPI_Shape::FACE, + aModifyFaceTag, aModFName, theMapOfShapes, false, + false, true); + theResultBody->loadDeletedShapes(&theMakeShape, theBaseShape, + GeomAPI_Shape::FACE, aDeletedTag); + + int aTag; + std::string aName; + for (ListOfShape::const_iterator + anIter = theTools.begin(); anIter != theTools.end(); anIter++) { + if ((*anIter)->shapeType() <= GeomAPI_Shape::FACE) { + aTag = aModifyFaceTag; + aName = aModFName; + } else { + aTag = aModifyEdgeTag; + aName = aModEName; + } + theResultBody->loadAndOrientModifiedShapes(&theMakeShape, *anIter, + aName == aModEName ? GeomAPI_Shape::EDGE + : GeomAPI_Shape::FACE, + aTag, aName, theMapOfShapes, false, false, true); + theResultBody->loadDeletedShapes(&theMakeShape, *anIter, GeomAPI_Shape::FACE, aDeletedTag); + } + } +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.h b/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.h index bbd6216ba..950b8223f 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.h +++ b/src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.h @@ -44,11 +44,67 @@ public: return MY_KIND; } + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD() + { + static const std::string MY_CREATION_METHOD_ID("creation_method"); + return MY_CREATION_METHOD_ID; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_SIMPLE() + { + static const std::string MY_CREATION_METHOD_ID("simple"); + return MY_CREATION_METHOD_ID; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_ADVANCED() + { + static const std::string MY_CREATION_METHOD_ID("advanced"); + return MY_CREATION_METHOD_ID; + } + + /// Attribute name of main objects. + inline static const std::string& OBJECT_LIST_ID() + { + static const std::string MY_OBJECT_LIST_ID("main_objects"); + return MY_OBJECT_LIST_ID; + } + + /// Attribute name of tool objects. + inline static const std::string& TOOL_LIST_ID() + { + static const std::string MY_TOOL_LIST_ID("tool_objects"); + return MY_TOOL_LIST_ID; + } + + /// Attribute name of remove edges option. + inline static const std::string& REMOVE_INTERSECTION_EDGES_ID() + { + static const std::string MY_TOOL_LIST_ID("remove_intersection_edges"); + return MY_TOOL_LIST_ID; + } + + /// Request for initialization of data model of the feature: adding all attributes. + FEATURESPLUGIN_EXPORT virtual void initAttributes(); + + /// Creates a new part document if needed. + FEATURESPLUGIN_EXPORT virtual void execute(); + public: - /// Use plugin manager for features creation. - FeaturesPlugin_BooleanFuse(): FeaturesPlugin_Boolean(BOOL_FUSE) {}; + /// Use plugin manager for features creation. + FeaturesPlugin_BooleanFuse(); +private: + /// Load Naming data structure of the feature to the document + void loadNamingDS(std::shared_ptr theResultBody, + const std::shared_ptr theBaseShape, + const ListOfShape& theTools, + const std::shared_ptr theResultShape, + GeomAlgoAPI_MakeShape& theMakeShape, + GeomAPI_DataMapOfShapeShape& theMapOfShapes); }; #endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index cb75e5700..3f1da2fd1 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -101,6 +101,10 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin() new FeaturesPlugin_ValidatorBooleanSmashSelection); aFactory->registerValidator("FeaturesPlugin_IntersectionSelection", new FeaturesPlugin_IntersectionSelection); + aFactory->registerValidator("FeaturesPlugin_ValidatorBooleanFuseSelection", + new FeaturesPlugin_ValidatorBooleanFuseSelection); + aFactory->registerValidator("FeaturesPlugin_ValidatorBooleanFuseArguments", + new FeaturesPlugin_ValidatorBooleanFuseArguments); // register this plugin ModelAPI_Session::get()->registerPlugin(this); diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp index f0005c790..13b2c6caf 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp @@ -21,6 +21,7 @@ #include "FeaturesPlugin_Validators.h" #include "FeaturesPlugin_Boolean.h" +#include "FeaturesPlugin_BooleanFuse.h" #include "FeaturesPlugin_BooleanSmash.h" #include "FeaturesPlugin_Union.h" @@ -1354,3 +1355,144 @@ bool FeaturesPlugin_IntersectionSelection::isValid(const AttributePtr& theAttrib return true; } + +//================================================================================================== +bool FeaturesPlugin_ValidatorBooleanFuseSelection::isValid( + const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + AttributeSelectionListPtr anAttrSelectionList = + std::dynamic_pointer_cast(theAttribute); + if (!anAttrSelectionList.get()) { + theError = + "Error: This validator can only work with selection list attributes in \"Boolean\" feature."; + return false; + } + + for (int anIndex = 0; anIndex < anAttrSelectionList->size(); ++anIndex) { + AttributeSelectionPtr anAttrSelection = anAttrSelectionList->value(anIndex); + if (!anAttrSelection.get()) { + theError = "Error: Empty attribute selection."; + return false; + } + ResultPtr aContext = anAttrSelection->context(); + if (!aContext.get()) { + theError = "Error: Empty selection context."; + return false; + } + ResultConstructionPtr aResultConstruction = + std::dynamic_pointer_cast(aContext); + if (aResultConstruction.get()) { + theError = "Error: Result construction not allowed for selection."; + return false; + } + std::shared_ptr aShape = anAttrSelection->value(); + GeomShapePtr aContextShape = aContext->shape(); + if (!aShape.get()) { + aShape = aContextShape; + } + if (!aShape.get()) { + theError = "Error: Empty shape."; + return false; + } + if (!aShape->isEqual(aContextShape)) { + theError = "Error: Local selection not allowed."; + return false; + } + } + + return true; +} + +//================================================================================================= +bool FeaturesPlugin_ValidatorBooleanFuseArguments::isValid( + const std::shared_ptr& theFeature, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + if (theArguments.size() != 2) { + theError = "Wrong number of arguments (expected 2)."; + return false; + } + + std::shared_ptr aFeature = + std::dynamic_pointer_cast(theFeature); + + int anObjectsNb = 0, aToolsNb = 0; + + std::list::const_iterator anIt = theArguments.begin(), aLast = theArguments.end(); + + bool isAllInSameCompSolid = true; + ResultCompSolidPtr aCompSolid; + + AttributeSelectionListPtr anAttrSelList = theFeature->selectionList(*anIt); + if (anAttrSelList) { + anObjectsNb = anAttrSelList->size(); + for (int anIndex = 0; anIndex < anObjectsNb; ++anIndex) { + AttributeSelectionPtr anAttr = anAttrSelList->value(anIndex); + ResultPtr aContext = anAttr->context(); + ResultCompSolidPtr aResCompSolidPtr = ModelAPI_Tools::compSolidOwner(aContext); + if (aResCompSolidPtr.get()) { + if (aCompSolid.get()) { + isAllInSameCompSolid = aCompSolid == aResCompSolidPtr; + } else { + aCompSolid = aResCompSolidPtr; + } + } else { + isAllInSameCompSolid = false; + break; + } + } + } + anIt++; + + if (aFeature->string(FeaturesPlugin_BooleanFuse::CREATION_METHOD())->value() + == FeaturesPlugin_BooleanFuse::CREATION_METHOD_ADVANCED()) { + anAttrSelList = theFeature->selectionList(*anIt); + if (anAttrSelList) { + aToolsNb = anAttrSelList->size(); + if (isAllInSameCompSolid) { + for (int anIndex = 0; anIndex < aToolsNb; ++anIndex) { + AttributeSelectionPtr anAttr = anAttrSelList->value(anIndex); + ResultPtr aContext = anAttr->context(); + ResultCompSolidPtr aResCompSolidPtr = ModelAPI_Tools::compSolidOwner(aContext); + if (aResCompSolidPtr.get()) { + if (aCompSolid.get()) { + isAllInSameCompSolid = aCompSolid == aResCompSolidPtr; + } else { + aCompSolid = aResCompSolidPtr; + } + } else { + isAllInSameCompSolid = false; + break; + } + } + } + } + } + + anIt++; + + if (anObjectsNb + aToolsNb < 2) { + theError = "Not enough arguments for Fuse operation."; + return false; + } else if (isAllInSameCompSolid) { + theError = "Operations only between sub-shapes of the same shape not allowed."; + return false; + } + + return true; +} + +//================================================================================================= +bool FeaturesPlugin_ValidatorBooleanFuseArguments::isNotObligatory( + std::string theFeature, + std::string theAttribute) +{ + if (theAttribute == "main_objects" || theAttribute == "tool_objects") { + return true; + } + + return false; +} \ No newline at end of file diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.h b/src/FeaturesPlugin/FeaturesPlugin_Validators.h index 0001a37fb..389ebb261 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.h @@ -318,4 +318,40 @@ public: Events_InfoMessage& theError) const; }; +/// \class FeaturesPlugin_ValidatorBooleanFuseSelection +/// \ingroup Validators +/// \brief Verifies the selected object for boolean fuse feature +class FeaturesPlugin_ValidatorBooleanFuseSelection: public ModelAPI_AttributeValidator +{ +public: + //! \return True if the attribute is valid. + //! \param[in] theAttribute the checked attribute. + //! \param[in] theArguments arguments of the attribute. + //! \param[out] theError error message. + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + +/** \class FeaturesPlugin_ValidatorBooleanFuseArguments +* \ingroup Validators +* \brief Validates that boolean operation have enough arguments. +*/ +class FeaturesPlugin_ValidatorBooleanFuseArguments: public ModelAPI_FeatureValidator +{ +public: + /** \brief Returns true if feature and/or attributes are valid. + * \param[in] theFeature the validated feature. + * \param[in] theArguments the arguments in the configuration file for this validator. + * \param[out] theError error message. + * \returns true if feature is valid. + */ + virtual bool isValid(const std::shared_ptr& theFeature, + const std::list& theArguments, + Events_InfoMessage& theError) const; + + /// \return true if the attribute in feature is not obligatory for the feature execution. + virtual bool isNotObligatory(std::string theFeature, std::string theAttribute); +}; + #endif diff --git a/src/FeaturesPlugin/Test/TestBooleanCompSolids.py b/src/FeaturesPlugin/Test/TestBooleanCompSolids.py index aad16ba71..23dfe5c97 100644 --- a/src/FeaturesPlugin/Test/TestBooleanCompSolids.py +++ b/src/FeaturesPlugin/Test/TestBooleanCompSolids.py @@ -133,8 +133,10 @@ aSession.undo() #========================================================================= aSession.startOperation() aBooleanFt = aPart.addFeature("Fuse") +aBooleanFt.string("creation_method").setValue("advanced") aBooleanFt.selectionList("main_objects").append(modelAPI_ResultCompSolid(extrudedObjects[0]).subResult(1), None) aBooleanFt.selectionList("tool_objects").append(extrudedObjects[1], None) +aBooleanFt.boolean("remove_intersection_edges").setValue(False) aBooleanFt.execute() aSession.finishOperation() diff --git a/src/FeaturesPlugin/Test/TestBooleanFuse_RemoveEdges.py b/src/FeaturesPlugin/Test/TestBooleanFuse_RemoveEdges.py new file mode 100644 index 000000000..6065be805 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestBooleanFuse_RemoveEdges.py @@ -0,0 +1,44 @@ +## Copyright (C) 2014-2017 CEA/DEN, EDF R&D +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Lesser General Public +## License as published by the Free Software Foundation; either +## version 2.1 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public +## License along with this library; if not, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## See http:##www.salome-platform.org/ or +## email : webmaster.salome@opencascade.com +## + +from salome.shaper import model + +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) +Box_2 = model.addBox(Part_1_doc, 10, 10, 10) +Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_2_1")], model.selection("EDGE", "PartSet/OX"), 5) +Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Box_1_1"), model.selection("SOLID", "Translation_1_1")], True) +model.testHaveNamingSubshapes(Fuse_1, model, Part_1_doc) +model.do() +model.end() + +from GeomAPI import GeomAPI_Shape + +model.testNbResults(Fuse_1, 1) +model.testNbSubResults(Fuse_1, [0]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.FACE, [6]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.EDGE, [24]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.VERTEX, [48]) + +assert(model.checkPythonDump()) diff --git a/src/FeaturesPlugin/Test/TestBooleanFuse_SimpleMode.py b/src/FeaturesPlugin/Test/TestBooleanFuse_SimpleMode.py new file mode 100644 index 000000000..e985d2876 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestBooleanFuse_SimpleMode.py @@ -0,0 +1,44 @@ +## Copyright (C) 2014-2017 CEA/DEN, EDF R&D +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Lesser General Public +## License as published by the Free Software Foundation; either +## version 2.1 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public +## License along with this library; if not, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## See http:##www.salome-platform.org/ or +## email : webmaster.salome@opencascade.com +## + +from salome.shaper import model + +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) +Box_2 = model.addBox(Part_1_doc, 10, 10, 10) +Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_2_1")], model.selection("EDGE", "PartSet/OX"), 5) +Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Box_1_1"), model.selection("SOLID", "Translation_1_1")]) +model.testHaveNamingSubshapes(Fuse_1, model, Part_1_doc) +model.do() +model.end() + +from GeomAPI import GeomAPI_Shape + +model.testNbResults(Fuse_1, 1) +model.testNbSubResults(Fuse_1, [0]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.FACE, [14]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.EDGE, [56]) +model.testNbSubShapes(Fuse_1, GeomAPI_Shape.VERTEX, [112]) + +assert(model.checkPythonDump()) diff --git a/src/FeaturesPlugin/Test/Test2394.py b/src/FeaturesPlugin/Test/_Test2394.py similarity index 100% rename from src/FeaturesPlugin/Test/Test2394.py rename to src/FeaturesPlugin/Test/_Test2394.py diff --git a/src/FeaturesPlugin/boolean_fuse_widget.xml b/src/FeaturesPlugin/boolean_fuse_widget.xml new file mode 100644 index 000000000..149c79b84 --- /dev/null +++ b/src/FeaturesPlugin/boolean_fuse_widget.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/FeaturesPlugin/icons/bool_fuse_advanced.png b/src/FeaturesPlugin/icons/bool_fuse_advanced.png new file mode 100644 index 0000000000000000000000000000000000000000..c96a1d5593d2a0ce406b6d18d705b9e6cb99b49a GIT binary patch literal 931 zcmV;U16=%xP)N2bZe?^J zG%heMGBNQWX_Wu~10YF6K~z{ry_Q=@6j2z*eehB6Nz&dTC^HKx2_fmlZV#nhwlt&^ zwNkNC%QY*~?vUALZfaR}kv65mENV@ymtG5+-y=Dh}peUh)^MH?$^88F(?)LC~XD?x8#z7R_XhNZ2(amP$I2tf- z{WdJvvfESvgc#n%V_{8;>t#pyPW!2;#6u_1+C7T)zCURHJ}%kOKY`{C{fMK&QqMTB z#FpW+03w@B{1rR>N<@2T~xmttl%6hbX9>U|U0goR&$0uEbSg|J$<*pZ0z;CYw5V52Z-a;CN zvl{GFKyK9?>HBu~{L;vwtJ3pP=A!^2f#lbW2am{5eW<7gyW_g)0^STvAtpsz0O40S ztD)Fr0k3Gi<I;CDEyq42(3AA z1QGNqmR8j$S8Bp}!V+b4OQUV+SeBZlGg~??Oisgz>N*UM4C_C61v#hWlS+62Ps}A8 z$iar;fpk%S@ERUa7d3h^jU4(Dc~geU0FkJh!q|7kQG&WiZcMF)om z7t5Ea#F6}cn+|71{OqDy)3002ovPDHLk FV1nxpthxXI literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/icons/bool_fuse_simple.png b/src/FeaturesPlugin/icons/bool_fuse_simple.png new file mode 100644 index 0000000000000000000000000000000000000000..9d86c9bfb021c1b23a1fa3bffc86b0979bed6cdc GIT binary patch literal 1077 zcmV-51j_q~P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^XO*0N*(00X5-L_t(oN5z(VOwC~!$Fs39bF19`kkT+^ z$o&!}w{$rv9cQGAQxhsnF7by66BcH}hK)_>vSFA_oh~Ou=hQi3t|5wZDkhg)a{0r) zp7)p9;eF59j`H)zXYX&{-_G-V-{*aP&v_jH{8xmcoCrlZ5sGpm6y-!H+QM+Z1{$3K zg?ffjS~YCdwLQ0Kmd^$h=&GPKR@2Y7W-dZM zpCI&{u?Q|fDhk2Jl}-kfdW-NI8)Q--K;&@>9Th zUI;wGV<-$CmlH(fu;d{t;IF0}3Zf-?I(^uSR{KBDo`3;8VA2_BlV0^?{3=33Y zi2qWI36G{wd~8nmlbowXlzchdLRMn$g<_P{y@#gu4e}qq=3shBT?_VI(85g-fq}I1 zl#_Xw9+x6j@6Xr_POv2Vss^LOA~9O2#=*;FxZco;YtKJQNlNN}NBjJPwC?|ve9-(Q zbR1`(t^WWYIto{Ndm!^lK1PME#87`Fl8@&i&sdA>%16koc!*5h?_}v}aF*V&<8h~B z8oVG>>IKb{W|W&gV^;iT`wUdvx`V+B!!W=%82#o1z?mA!$uAg=zCqTR8;l|J8+PW2 zl)T`{8x%czM-^zX-#{6Cv%S6jF)=Cu;oCEy*t!S7sn$_!O-I1yT~gU+#BGpzq44Qj zGSU2(25wfP`;@teKYSXuTR>g^*(U6ZD6D;p5!9&DVv|u!jmnb$y#cB~kEwo8XPl5e zEm6l?!ghP6bN)wYMU%YWA9BGcM*>iwG^SL;IQCex} zl!0;@+2?N8BVf~R^rb--la`K##s&(_=i&rLXAF2#!(F^%qQSHqeP&bg9oCaGgkp)l zN-_|8;3RZ3WZE0b8m#1(?=@i5(r9?9;%GXiqd#3lV$=4*+-RbZe2#X6qE=toVL<=t zyR<`Fux7jRpa~wTSm^@7&Ri|++)Uy4_?8I8BAp&R={n^~qjmDC_3(;Hgjci`=-otG zCqyQ|nf?VG{X(FootwH# - +