From 1c1edb80f5d79bfe8cffa3e215c9d3de4c26147d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me?= Date: Wed, 4 Nov 2020 12:25:52 +0100 Subject: [PATCH] Implementation of inspection shared faces --- .../ConnectorPlugin_PublishToStudyFeature.py | 82 ++++---- src/FeaturesAPI/CMakeLists.txt | 2 + src/FeaturesAPI/FeaturesAPI.i | 2 + src/FeaturesAPI/FeaturesAPI_SharedFaces.cpp | 83 ++++++++ src/FeaturesAPI/FeaturesAPI_SharedFaces.h | 78 ++++++++ src/FeaturesAPI/FeaturesAPI_swig.h | 1 + src/FeaturesPlugin/CMakeLists.txt | 37 +++- .../FeaturesPlugin_CommonSharedFaces.cpp | 111 +++++++++++ .../FeaturesPlugin_CommonSharedFaces.h | 70 +++++++ .../FeaturesPlugin_GroupSharedFaces.cpp | 129 ++++++++++++ .../FeaturesPlugin_GroupSharedFaces.h | 114 +++++++++++ src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp | 32 +-- .../FeaturesPlugin_SharedFaces.cpp | 187 ++++++++++++++++++ .../FeaturesPlugin_SharedFaces.h | 133 +++++++++++++ src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts | 122 ++++++++++++ .../Test/TestCheckSharedFaces.py | 83 ++++++++ src/FeaturesPlugin/doc/FeaturesPlugin.rst | 1 + .../doc/TUI_SharedFacesFeature.rst | 11 ++ .../doc/checkSharedFaceFeature.rst | 52 +++++ .../doc/examples/checkSharedFaces.py | 21 ++ .../images/checkSharedFacesPropertyPanel.png | Bin 0 -> 25567 bytes .../doc/images/sharedFacesResult.png | Bin 0 -> 7992 bytes .../doc/images/shared_shapes.png | Bin 0 -> 583 bytes src/FeaturesPlugin/icons/shared_shapes.png | Bin 0 -> 583 bytes src/FeaturesPlugin/plugin-Features.xml | 10 +- .../shared_Faces_macro_widget.xml | 34 ++++ src/FeaturesPlugin/shared_Faces_widget.xml | 31 +++ src/FeaturesPlugin/tests.set | 3 +- src/GeomAlgoAPI/CMakeLists.txt | 2 + src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.cpp | 148 ++++++++++++++ src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.h | 39 ++++ src/ModelAPI/CMakeLists.txt | 6 +- .../ModuleBase_WidgetMultiSelector.h | 4 +- src/PythonAPI/model/features/__init__.py | 1 + 34 files changed, 1564 insertions(+), 65 deletions(-) create mode 100644 src/FeaturesAPI/FeaturesAPI_SharedFaces.cpp create mode 100644 src/FeaturesAPI/FeaturesAPI_SharedFaces.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_SharedFaces.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_SharedFaces.h create mode 100644 src/FeaturesPlugin/Test/TestCheckSharedFaces.py create mode 100644 src/FeaturesPlugin/doc/TUI_SharedFacesFeature.rst create mode 100644 src/FeaturesPlugin/doc/checkSharedFaceFeature.rst create mode 100644 src/FeaturesPlugin/doc/examples/checkSharedFaces.py create mode 100644 src/FeaturesPlugin/doc/images/checkSharedFacesPropertyPanel.png create mode 100644 src/FeaturesPlugin/doc/images/sharedFacesResult.png create mode 100644 src/FeaturesPlugin/doc/images/shared_shapes.png create mode 100644 src/FeaturesPlugin/icons/shared_shapes.png create mode 100644 src/FeaturesPlugin/shared_Faces_macro_widget.xml create mode 100644 src/FeaturesPlugin/shared_Faces_widget.xml create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.cpp create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.h diff --git a/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py b/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py index 8da67a31e..5e2444e23 100644 --- a/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py +++ b/src/ConnectorPlugin/ConnectorPlugin_PublishToStudyFeature.py @@ -163,51 +163,55 @@ class PublishToStudyFeature(ModelAPI.ModelAPI_Feature): # If theFields is true, the same is performed for Fields. def processGroups(self, theRes, theEngine, thePartFeatureId, theStudyShape, theFields): allGroupsProcessed = [] + allRefGroups = [] if theFields: - aRefGroups = ModelAPI.referencedFeatures(theRes, "Field", True) + allRefGroups.append(ModelAPI.referencedFeatures(theRes, "Field", True)) else: - aRefGroups = ModelAPI.referencedFeatures(theRes, "Group", True) - for aRef in aRefGroups: - aGroupIndices = [] - aGroupHasIndex = {} - aResShape = theRes.shape() - if theFields: - aSelList = aRef.selectionList("selected") - else: - aSelList = aRef.selectionList("group_list") - aSelType = GeomAPI_Shape.shapeTypeByStr(aSelList.selectionType()) - for aGroupRes in aRef.results(): - aShape = aGroupRes.shape() - anExplorer = GeomAPI_ShapeExplorer(aShape, aSelType) - while anExplorer.more(): - anId = GeomAlgoAPI.GeomAlgoAPI_CompoundBuilder.id(aResShape, anExplorer.current()) - if anId > 0 and not anId in aGroupHasIndex: - aGroupIndices.append(anId) - aGroupHasIndex[anId] = 0 - anExplorer.next() - if len(aGroupIndices): # create group - aGroupFeatureId = aRef.data().featureId() + allRefGroups.append(ModelAPI.referencedFeatures(theRes, "Group", True)) + allRefGroups.append(ModelAPI.referencedFeatures(theRes, "Shared_faces", True)) + allRefGroups.append(ModelAPI.referencedFeatures(theRes, "Duplicated_faces", True)) + for aRefGroups in allRefGroups: + for aRef in aRefGroups: + aGroupIndices = [] + aGroupHasIndex = {} + aResShape = theRes.shape() if theFields: - aFieldOp = theEngine.GetIFieldOperations() - aGroupEntry = "field" + str(thePartFeatureId) + ":" + str(aGroupFeatureId) - aGroup = aFieldOp.FindField(theStudyShape, aGroupEntry) + aSelList = aRef.selectionList("selected") else: - aGroupOp = theEngine.GetIGroupOperations() - aGroupEntry = "group" + str(thePartFeatureId) + ":" + str(aGroupFeatureId) - aGroup = aGroupOp.FindGroup(theStudyShape, aGroupEntry) - if not aGroup: # create a new + aSelList = aRef.selectionList("group_list") + aSelType = GeomAPI_Shape.shapeTypeByStr(aSelList.selectionType()) + for aGroupRes in aRef.results(): + aShape = aGroupRes.shape() + anExplorer = GeomAPI_ShapeExplorer(aShape, aSelType) + while anExplorer.more(): + anId = GeomAlgoAPI.GeomAlgoAPI_CompoundBuilder.id(aResShape, anExplorer.current()) + if anId > 0 and not anId in aGroupHasIndex: + aGroupIndices.append(anId) + aGroupHasIndex[anId] = 0 + anExplorer.next() + if len(aGroupIndices): # create group + aGroupFeatureId = aRef.data().featureId() if theFields: - aGroup = aFieldOp.CreateFieldByType(theStudyShape, aSelType) + aFieldOp = theEngine.GetIFieldOperations() + aGroupEntry = "field" + str(thePartFeatureId) + ":" + str(aGroupFeatureId) + aGroup = aFieldOp.FindField(theStudyShape, aGroupEntry) else: - aGroup = aGroupOp.CreateGroup(theStudyShape, aSelType) - aGroup.SetEntry(aGroupEntry) - theEngine.AddInStudy(aGroup, aRef.firstResult().data().name(), theStudyShape) - aGroup.SetSelection(aGroupIndices) - if theFields: - self.fillField(aGroup, aRef, theEngine, aGroupIndices) - # a group takes shape from the main result - #aGroup.SetShapeByStream(aRef.firstResult().shape().getShapeStream(False)) # group shape - allGroupsProcessed.append(aGroupEntry) + aGroupOp = theEngine.GetIGroupOperations() + aGroupEntry = "group" + str(thePartFeatureId) + ":" + str(aGroupFeatureId) + aGroup = aGroupOp.FindGroup(theStudyShape, aGroupEntry) + if not aGroup: # create a new + if theFields: + aGroup = aFieldOp.CreateFieldByType(theStudyShape, aSelType) + else: + aGroup = aGroupOp.CreateGroup(theStudyShape, aSelType) + aGroup.SetEntry(aGroupEntry) + theEngine.AddInStudy(aGroup, aRef.firstResult().data().name(), theStudyShape) + aGroup.SetSelection(aGroupIndices) + if theFields: + self.fillField(aGroup, aRef, theEngine, aGroupIndices) + # a group takes shape from the main result + #aGroup.SetShapeByStream(aRef.firstResult().shape().getShapeStream(False)) # group shape + allGroupsProcessed.append(aGroupEntry) # check all existing groups: if some does not processed, remove it from the tree aSOIter = SHAPERSTUDY_utils.getStudy().NewChildIterator(theStudyShape.GetSO()) while aSOIter.More(): diff --git a/src/FeaturesAPI/CMakeLists.txt b/src/FeaturesAPI/CMakeLists.txt index 6cd9ba368..f3c6cc2ba 100644 --- a/src/FeaturesAPI/CMakeLists.txt +++ b/src/FeaturesAPI/CMakeLists.txt @@ -33,6 +33,7 @@ SET(PROJECT_HEADERS FeaturesAPI_Intersection.h FeaturesAPI_Measurement.h FeaturesAPI_NormalToFace.h + FeaturesAPI_SharedFaces.h FeaturesAPI_MultiRotation.h FeaturesAPI_MultiTranslation.h FeaturesAPI_Partition.h @@ -70,6 +71,7 @@ SET(PROJECT_SOURCES FeaturesAPI_Intersection.cpp FeaturesAPI_Measurement.cpp FeaturesAPI_NormalToFace.cpp + FeaturesAPI_SharedFaces.cpp FeaturesAPI_MultiRotation.cpp FeaturesAPI_MultiTranslation.cpp FeaturesAPI_Partition.cpp diff --git a/src/FeaturesAPI/FeaturesAPI.i b/src/FeaturesAPI/FeaturesAPI.i index d9fa7c0b5..634f2f018 100644 --- a/src/FeaturesAPI/FeaturesAPI.i +++ b/src/FeaturesAPI/FeaturesAPI.i @@ -64,6 +64,7 @@ %shared_ptr(FeaturesAPI_BooleanFill) %shared_ptr(FeaturesAPI_Chamfer) %shared_ptr(FeaturesAPI_NormalToFace) +%shared_ptr(FeaturesAPI_SharedFaces) %shared_ptr(FeaturesAPI_Extrusion) %shared_ptr(FeaturesAPI_ExtrusionBoolean) %shared_ptr(FeaturesAPI_ExtrusionCut) @@ -214,6 +215,7 @@ %include "FeaturesAPI_Intersection.h" %include "FeaturesAPI_Measurement.h" %include "FeaturesAPI_NormalToFace.h" +%include "FeaturesAPI_SharedFaces.h" %include "FeaturesAPI_MultiRotation.h" %include "FeaturesAPI_MultiTranslation.h" %include "FeaturesAPI_Partition.h" diff --git a/src/FeaturesAPI/FeaturesAPI_SharedFaces.cpp b/src/FeaturesAPI/FeaturesAPI_SharedFaces.cpp new file mode 100644 index 000000000..06889c647 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_SharedFaces.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2018-2021 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_SharedFaces.h" + +#include + +#include +#include +#include +#include + +//================================================================================================= +FeaturesAPI_SharedFaces:: + FeaturesAPI_SharedFaces(const std::shared_ptr& theFeature) + : ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +//================================================================================================= +FeaturesAPI_SharedFaces::FeaturesAPI_SharedFaces( + const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theobject, + const std::string & theNameGroup) +:ModelHighAPI_Interface(theFeature) +{ + if (initialize()) { + fillAttribute(theobject, myobjectselected); + fillAttribute(theNameGroup, mygroupname); + execute(); + } +} + +//================================================================================================= +FeaturesAPI_SharedFaces::~FeaturesAPI_SharedFaces() +{ +} + +//================================================================================================= +void FeaturesAPI_SharedFaces::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + AttributeSelectionPtr anAttrObject; + anAttrObject = aBase->selection(FeaturesPlugin_GroupSharedFaces::OBJECT_ID()); + + theDumper << aBase << " = model.getSharedFaces(" << aDocName << ", " << anAttrObject; + theDumper << ", " << aBase->string(FeaturesPlugin_GroupSharedFaces::GROUP_NAME_ID()); + theDumper << ")" << std::endl; +} + +//================================================================================================= +SharedFacesPtr getSharedFaces(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theobject, + const std::string & theNameGroup) +{ + FeaturePtr aFeature = thePart->addFeature(FeaturesPlugin_GroupSharedFaces::ID()); + SharedFacesPtr aSharedFaces; + + aSharedFaces.reset(new FeaturesAPI_SharedFaces(aFeature, + theobject, + theNameGroup)); + return aSharedFaces; +} + diff --git a/src/FeaturesAPI/FeaturesAPI_SharedFaces.h b/src/FeaturesAPI/FeaturesAPI_SharedFaces.h new file mode 100644 index 000000000..fc594119f --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_SharedFaces.h @@ -0,0 +1,78 @@ +// Copyright (C) 2018-2021 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_SharedFaces_H_ +#define FeaturesAPI_SharedFaces_H_ + +#include "FeaturesAPI.h" + +#include "FeaturesPlugin_GroupSharedFaces.h" + +#include +#include +#include + +class ModelAPI_Document; +class ModelHighAPI_Selection; + +/// \class FeaturesAPI_SharedFaces +/// \ingroup CPPHighAPI +/// \brief Interface to find shared faces +class FeaturesAPI_SharedFaces: public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_SharedFaces(const std::shared_ptr& theFeature); + + FEATURESAPI_EXPORT + explicit FeaturesAPI_SharedFaces(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theobject, + const std::string & theNameGroup); + + /// Destructor. + FEATURESAPI_EXPORT + virtual ~FeaturesAPI_SharedFaces(); + + INTERFACE_2(FeaturesPlugin_GroupSharedFaces::ID(), + objectselected, FeaturesPlugin_GroupSharedFaces::OBJECT_ID(), + ModelAPI_AttributeSelection, + /** object selected*/, + groupname, FeaturesPlugin_GroupSharedFaces::GROUP_NAME_ID(), + ModelAPI_AttributeString, + /** group name*/) + + /// Dump wrapped feature + FEATURESAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; +}; + +/// Pointer on the SharedFaces object. +typedef std::shared_ptr SharedFacesPtr; + +/// \ingroup CPPHighAPI +/// \brief get the Shared Faces +/// \param thePart the part +/// \param theobject the object selected +FEATURESAPI_EXPORT +SharedFacesPtr getSharedFaces(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theObject, + const std::string & theNameGroup); + +#endif // FeaturesAPI_SharedFaces_H_ diff --git a/src/FeaturesAPI/FeaturesAPI_swig.h b/src/FeaturesAPI/FeaturesAPI_swig.h index ede8f2c11..fa19f357b 100644 --- a/src/FeaturesAPI/FeaturesAPI_swig.h +++ b/src/FeaturesAPI/FeaturesAPI_swig.h @@ -36,6 +36,7 @@ #include "FeaturesAPI_Intersection.h" #include "FeaturesAPI_Measurement.h" #include "FeaturesAPI_NormalToFace.h" + #include "FeaturesAPI_SharedFaces.h" #include "FeaturesAPI_MultiRotation.h" #include "FeaturesAPI_MultiTranslation.h" #include "FeaturesAPI_Partition.h" diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index e3f25e81d..4857c34b7 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -19,6 +19,16 @@ INCLUDE(Common) INCLUDE(UnitTest) +INCLUDE(UseQtExt) + +# additional include directories +INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/src/GeomDataAPI + ${PROJECT_SOURCE_DIR}/src/Locale + ${QT_INCLUDES}) + +# additional preprocessor / compiler flags +ADD_DEFINITIONS(${QT_DEFINITIONS}) + SET(PROJECT_HEADERS @@ -60,6 +70,9 @@ SET(PROJECT_HEADERS FeaturesPlugin_Fillet.h FeaturesPlugin_Fillet1D.h FeaturesPlugin_Measurement.h + FeaturesPlugin_SharedFaces.h + FeaturesPlugin_CommonSharedFaces.h + FeaturesPlugin_GroupSharedFaces.h FeaturesPlugin_FusionFaces.h FeaturesPlugin_RemoveResults.h FeaturesPlugin_Chamfer.h @@ -114,6 +127,9 @@ SET(PROJECT_SOURCES FeaturesPlugin_Fillet.cpp FeaturesPlugin_Fillet1D.cpp FeaturesPlugin_Measurement.cpp + FeaturesPlugin_SharedFaces.cpp + FeaturesPlugin_CommonSharedFaces.cpp + FeaturesPlugin_GroupSharedFaces.cpp FeaturesPlugin_FusionFaces.cpp FeaturesPlugin_RemoveResults.cpp FeaturesPlugin_Chamfer.cpp @@ -159,6 +175,8 @@ SET(XML_RESOURCES fillet_widget.xml fillet1d_widget.xml measurement_widget.xml + shared_Faces_widget.xml + shared_Faces_macro_widget.xml fusion_faces_widget.xml chamfer_widget.xml copy_widget.xml @@ -178,6 +196,12 @@ SET(TEXT_RESOURCES FeaturesPlugin_msg_ru.ts ) +# sources / moc wrappings +QT_WRAP_MOC(PROJECT_AUTOMOC ${PROJECT_MOC_HEADERS}) + +#QT5_ADD_TRANSLATION(QM_RESOURCES ${TEXT_RESOURCES}) + +SOURCE_GROUP ("Generated Files" FILES ${PROJECT_AUTOMOC} ${PROJECT_COMPILED_RESOURCES} ${QM_RESOURCES}) SOURCE_GROUP ("XML Files" FILES ${XML_RESOURCES}) SOURCE_GROUP ("Resource Files" FILES ${TEXT_RESOURCES}) @@ -204,7 +228,12 @@ SET(PROJECT_LIBRARIES ) ADD_DEFINITIONS(-DFEATURESPLUGIN_EXPORTS) -ADD_LIBRARY(FeaturesPlugin MODULE ${PROJECT_SOURCES} ${PROJECT_HEADERS} ${XML_RESOURCES} ${TEXT_RESOURCES}) +ADD_LIBRARY(FeaturesPlugin MODULE + ${PROJECT_SOURCES} + ${PROJECT_HEADERS} + ${XML_RESOURCES} + ${TEXT_RESOURCES} + ${PROJECT_AUTOMOC}) TARGET_LINK_LIBRARIES(FeaturesPlugin ${PROJECT_LIBRARIES}) INSTALL(TARGETS FeaturesPlugin DESTINATION ${SHAPER_INSTALL_PLUGIN_FILES}) @@ -221,16 +250,16 @@ ADD_UNIT_TESTS( IF(${HAVE_SALOME}) enable_testing() set(TEST_INSTALL_DIRECTORY "${SALOME_SHAPER_INSTALL_TESTS}/FeaturesPlugin") - + install(FILES CTestTestfileInstall.cmake DESTINATION ${TEST_INSTALL_DIRECTORY} RENAME CTestTestfile.cmake) install(FILES tests.set DESTINATION ${TEST_INSTALL_DIRECTORY}) - + set(TMP_TESTS_NAMES) foreach(tfile ${TEST_NAMES}) list(APPEND TMP_TESTS_NAMES "Test/${tfile}") endforeach(tfile ${TEST_NAMES}) - + install(FILES ${TMP_TESTS_NAMES} DESTINATION ${TEST_INSTALL_DIRECTORY}) ENDIF(${HAVE_SALOME}) diff --git a/src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.cpp b/src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.cpp new file mode 100644 index 000000000..5e1e5eb4b --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.cpp @@ -0,0 +1,111 @@ +// Copyright (C) 2018-2021 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_CommonSharedFaces.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +//================================================================================================= +void FeaturesPlugin_CommonSharedFaces::updateFaces() +{ + AttributeSelectionPtr ancompSolidAttr = + std::dynamic_pointer_cast(attributObject()); + AttributeSelectionListPtr aFacesListAttr = + std::dynamic_pointer_cast (attributListFaces()); + + GeomShapePtr aShape = ancompSolidAttr->value(); + AttributeBooleanPtr anIsCompute = + std::dynamic_pointer_cast(attributIsCompute()); + if (!anIsCompute->value()) { + myShape = aShape; + anIsCompute->setValue(true); + } + if (aShape.get() && ancompSolidAttr->context().get() && !aShape->isEqual(myShape)) { + if (aFacesListAttr->isInitialized()) + aFacesListAttr->clear(); + aShape = ancompSolidAttr->context()->shape(); + if (aShape) { + std::string anError; + ListOfShape aFaces; + ListOfShape theShapes; + theShapes.push_back(aShape); + + if (!GetSharedFaces(theShapes, aFaces, false, anError)) + setError("Error in GetSharedFaces calculation :" + anError); + myShape = aShape; + aFacesListAttr->setSelectionType("face"); + ListOfShape::const_iterator anIt = aFaces.cbegin(); + for(; anIt != aFaces.cend(); ++anIt) { + GeomShapePtr aFacePtr = *anIt; + if (!aFacePtr.get()) + setError("GetSharedFaces : An invalid face found " + anError); + aFacesListAttr->append( ancompSolidAttr->context(), aFacePtr); + } + std::stringstream alabel; + alabel << aFacesListAttr->size(); + AttributeStringPtr aNumberFacesAttr = + std::dynamic_pointer_cast + (attributNumberFaces()); + aNumberFacesAttr->setValue( alabel.str() ); + } + } +} + +//================================================================================================= +void FeaturesPlugin_CommonSharedFaces::setFacesGroup(const std::wstring& theName ) +{ + std::vector aColor; + ResultGroupPtr aGroup = document()->createGroup(data()); + // clean the result of the operation + aGroup->data()->setName(theName); + aGroup->store(GeomShapePtr()); + + // shapes containing in group + ListOfShape aFaces; + AttributeSelectionListPtr aFacesListAttr = + std::dynamic_pointer_cast (attributListFaces()); + + for (int anI =0; anI< aFacesListAttr->size(); anI++) { + AttributeSelectionPtr aAtt = aFacesListAttr->value(anI); + aFaces.push_back( aAtt->value() ); + } + + GeomShapePtr aCompound = GeomAlgoAPI_CompoundBuilder::compound(aFaces); + aGroup->store(aCompound); + aColor = {255,0,0}; + setResult(aGroup); + ModelAPI_Tools::setColor( lastResult(),aColor); +} + diff --git a/src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.h b/src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.h new file mode 100644 index 000000000..dead172f8 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_CommonSharedFaces.h @@ -0,0 +1,70 @@ +// Copyright (C) 2018-2021 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_CommonSharedFaces_H_ +#define FeaturesPlugin_CommonSharedFaces_H_ + +#include "FeaturesPlugin.h" + +#include +#include +#include + +#include +#include + + +/// \class FeaturesPlugin_CommonSharedFaces +/// \ingroup Plugins +/// \brief Feature to check the shared faces of solid + +class FeaturesPlugin_CommonSharedFaces : public ModelAPI_Feature +{ +public: + + /// Performs the algorithm and stores results it in the data structure. + FEATURESPLUGIN_EXPORT virtual void execute(){}; + + /// Return Attribut values of result. + virtual AttributePtr attributObject() = 0; + + /// Return Attribut values of result. + virtual AttributePtr attributListFaces() = 0; + + /// Return Attribut values of result. + virtual AttributePtr attributNumberFaces() = 0; + + /// Return Attribut values of IsCompute. + virtual AttributePtr attributIsCompute() = 0; + + protected: + FeaturesPlugin_CommonSharedFaces() {} + + //Set group of faces + void setFacesGroup(const std::wstring& theName ); + + // Update the list of faces + void updateFaces(); + + // the shape studied + GeomShapePtr myShape; + +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.cpp b/src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.cpp new file mode 100644 index 000000000..46a7efdb2 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.cpp @@ -0,0 +1,129 @@ +// Copyright (C) 2018-2021 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_GroupSharedFaces.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +//================================================================================================= +FeaturesPlugin_GroupSharedFaces::FeaturesPlugin_GroupSharedFaces() +{ +} + +//================================================================================================= +void FeaturesPlugin_GroupSharedFaces::initAttributes() +{ + // attribute for object selected + data()->addAttribute(OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + AttributeSelectionListPtr aList = std::dynamic_pointer_cast( + data()->addAttribute(LIST_FACES_ID(), ModelAPI_AttributeSelectionList::typeId())); + + data()->addAttribute(NUMBER_FACES_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(TRANSPARENCY_ID(), ModelAPI_AttributeInteger::typeId()); + data()->addAttribute(GROUP_NAME_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(COMPUTE_ID(), ModelAPI_AttributeBoolean::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), TRANSPARENCY_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), COMPUTE_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), NUMBER_FACES_ID()); + data()->boolean(COMPUTE_ID())->setValue(true); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_GroupSharedFaces::attributObject() +{ + return attribute(OBJECT_ID()); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_GroupSharedFaces::attributIsCompute() +{ + return attribute(COMPUTE_ID()); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_GroupSharedFaces::attributListFaces() +{ + return attribute(LIST_FACES_ID()); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_GroupSharedFaces::attributNumberFaces() +{ + return attribute(NUMBER_FACES_ID()); +} + +//================================================================================================= +void FeaturesPlugin_GroupSharedFaces::execute() +{ + if ( selectionList(LIST_FACES_ID())->isInitialized() + && string(GROUP_NAME_ID())->value() != "") { + AttributeStringPtr aNameAtt = string( GROUP_NAME_ID() ) ; + std::wstring aNameFace = aNameAtt->isUValue() ? + Locale::Convert::toWString(aNameAtt->valueU()) : + Locale::Convert::toWString(aNameAtt->value()); + + if (lastResult().get()) + eraseResultFromList(lastResult()); + setFacesGroup(aNameFace); + } else { + if (lastResult().get()) { + eraseResultFromList(lastResult()); + } + } + if (selection(OBJECT_ID())->isInitialized() && integer(TRANSPARENCY_ID())->isInitialized()) { + AttributeSelectionPtr ancompSolidAttr = selection(OBJECT_ID()); + ResultPtr aResult = ancompSolidAttr->context(); + + double aTranparency = integer(TRANSPARENCY_ID())->value() / 100.0; + ModelAPI_Tools::setTransparency(aResult, aTranparency); + + ResultBodyPtr aResultBody = std::dynamic_pointer_cast(aResult); + std::list allRes; + ModelAPI_Tools::allSubs(aResultBody, allRes); + std::list::iterator aRes; + for(aRes = allRes.begin(); aRes != allRes.end(); aRes++) { + ModelAPI_Tools::setTransparency(*aRes, aTranparency); + } + } +} + +//================================================================================================= +void FeaturesPlugin_GroupSharedFaces::attributeChanged(const std::string& theID) +{ + if (theID == OBJECT_ID()) + updateFaces(); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.h b/src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.h new file mode 100644 index 000000000..663dd3fe6 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_GroupSharedFaces.h @@ -0,0 +1,114 @@ +// Copyright (C) 2018-2021 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_GroupSharedFaces_H_ +#define FeaturesPlugin_GroupSharedFaces_H_ + +#include + +/// \class FeaturesPlugin_GroupSharedFaces +/// \ingroup Plugins +/// \brief Feature to check the shared faces of solid + +class FeaturesPlugin_GroupSharedFaces : public FeaturesPlugin_CommonSharedFaces +{ +public: + inline static const std::string& ID() + { + static const std::string MY_ID("Shared_faces"); + return MY_ID; + } + + /// \return the kind of a feature. + virtual const std::string& getKind() + { + return ID(); + } + + /// Attribute name for object selected. + inline static const std::string& OBJECT_ID() + { + static const std::string MY_OBJECT_ID("main_object"); + return MY_OBJECT_ID; + } + + /// Attribute name for number of faces. + inline static const std::string& NUMBER_FACES_ID() + { + static const std::string MY_NUMBER_FACES_ID("number_shared_faces"); + return MY_NUMBER_FACES_ID; + } + + /// Attribute name for z coodinate. + inline static const std::string& LIST_FACES_ID() + { + static const std::string MY_LIST_FACES_ID("group_list"); + return MY_LIST_FACES_ID; + } + + /// Attribute name for transparency. + inline static const std::string& TRANSPARENCY_ID() + { + static const std::string MY_TRANSPARENCY_ID("transparency"); + return MY_TRANSPARENCY_ID; + } + + /// Attribute name for group name. + inline static const std::string& GROUP_NAME_ID() + { + static const std::string MY_GROUP_NAME_ID("group_name"); + return MY_GROUP_NAME_ID; + } + + /// Attribute name for indicate to launch the algo. + inline static const std::string& COMPUTE_ID() + { + static const std::string MY_COMPUTE_ID("compute"); + return MY_COMPUTE_ID; + } + + /// 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(); + + /// Called on change of any argument-attribute of this object + /// \param theID identifier of changed attribute + FEATURESPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + + /// Use plugin manager for features creation + FeaturesPlugin_GroupSharedFaces(); + + private: + + /// Return Attribut values of object. + virtual AttributePtr attributObject(); + + /// Return Attribut values of list of faces. + virtual AttributePtr attributListFaces(); + + /// Return Attribut values of number of faces. + virtual AttributePtr attributNumberFaces(); + + /// Return Attribut values of IsCompute. + virtual AttributePtr attributIsCompute(); +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index 3d00700d6..83b31db6e 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -19,54 +19,52 @@ #include +#include #include +#include #include -#include #include -#include #include #include +#include #include -#include #include #include -#include +#include #include +#include +#include #include +#include +#include #include #include #include #include -#include #include #include #include #include #include #include +#include #include +#include #include -#include #include #include +#include #include #include +#include #include #include #include -#include -#include -#include -#include -#include #include +#include #include -#include - -#include - // the only created instance of this plugin static FeaturesPlugin_Plugin* MY_FEATURES_INSTANCE = new FeaturesPlugin_Plugin(); @@ -193,6 +191,10 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new FeaturesPlugin_Fillet1D); } else if (theFeatureID == FeaturesPlugin_Measurement::ID()) { return FeaturePtr(new FeaturesPlugin_Measurement); + } else if (theFeatureID == FeaturesPlugin_SharedFaces::ID()) { + return FeaturePtr(new FeaturesPlugin_SharedFaces); + } else if (theFeatureID == FeaturesPlugin_GroupSharedFaces::ID()) { + return FeaturePtr(new FeaturesPlugin_GroupSharedFaces); } else if (theFeatureID == FeaturesPlugin_RemoveResults::ID()) { return FeaturePtr(new FeaturesPlugin_RemoveResults); } else if (theFeatureID == FeaturesPlugin_Chamfer::ID()) { diff --git a/src/FeaturesPlugin/FeaturesPlugin_SharedFaces.cpp b/src/FeaturesPlugin/FeaturesPlugin_SharedFaces.cpp new file mode 100644 index 000000000..9e4117556 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_SharedFaces.cpp @@ -0,0 +1,187 @@ +// Copyright (C) 2018-2021 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_SharedFaces.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +//================================================================================================= +FeaturesPlugin_SharedFaces::FeaturesPlugin_SharedFaces() +{ +} + +//================================================================================================= +void FeaturesPlugin_SharedFaces::initAttributes() +{ + // attribute for object selected + data()->addAttribute(OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + AttributeSelectionListPtr aList = std::dynamic_pointer_cast( + data()->addAttribute(LIST_FACES_ID(), ModelAPI_AttributeSelectionList::typeId())); + + data()->addAttribute(NUMBER_FACES_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(TRANSPARENCY_ID(), ModelAPI_AttributeInteger::typeId()); + data()->addAttribute(CREATE_GROUP_ID(), ModelAPI_AttributeBoolean::typeId()); + data()->addAttribute(GROUP_NAME_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(COMPUTE_ID(), ModelAPI_AttributeBoolean::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), GROUP_NAME_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), COMPUTE_ID()); + data()->boolean(COMPUTE_ID())->setValue(true); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_SharedFaces::attributIsCompute() +{ + return attribute(COMPUTE_ID()); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_SharedFaces::attributObject() +{ + return attribute(OBJECT_ID()); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_SharedFaces::attributListFaces() +{ + return attribute(LIST_FACES_ID()); +} + +//================================================================================================= +AttributePtr FeaturesPlugin_SharedFaces::attributNumberFaces() +{ + return attribute(NUMBER_FACES_ID()); +} + +//================================================================================================= +void FeaturesPlugin_SharedFaces::execute() +{ + if (boolean(CREATE_GROUP_ID())->value()) { + + if (string(GROUP_NAME_ID())->value() != "" + && selectionList(LIST_FACES_ID())->isInitialized()) { + + if (lastResult().get()) { + eraseResultFromList(lastResult()); + } + + if (!myCreateGroupFeature.get()) + createGroup(); + updateGroup(); + } + + } else { + if (selectionList(LIST_FACES_ID())->isInitialized()) { + + if (myCreateGroupFeature.get()) { + myCreateGroupFeature->eraseResults(); + SessionPtr aSession = ModelAPI_Session::get(); + DocumentPtr aDoc = aSession->activeDocument(); + aDoc->removeFeature(myCreateGroupFeature); + myCreateGroupFeature.reset(); + } + + if (lastResult().get()) + eraseResultFromList(lastResult()); + setFacesGroup(L"Group_SharedFaces"); + } + } + + if (selection(OBJECT_ID())->isInitialized()) { + AttributeSelectionPtr ancompSolidAttr = selection(OBJECT_ID()); + ResultPtr aResult = ancompSolidAttr->context(); + + double aTranparency = integer(TRANSPARENCY_ID())->value()/100.0; + ModelAPI_Tools::setTransparency(aResult, aTranparency); + + ResultBodyPtr aResultBody = std::dynamic_pointer_cast(aResult); + std::list allRes; + ModelAPI_Tools::allSubs(aResultBody, allRes); + std::list::iterator aRes; + for(aRes = allRes.begin(); aRes != allRes.end(); aRes++) { + ModelAPI_Tools::setTransparency(*aRes, aTranparency); + } + } +} + +//================================================================================================= +void FeaturesPlugin_SharedFaces::attributeChanged(const std::string& theID) +{ + if (theID == OBJECT_ID()) { + + updateFaces(); + if (myCreateGroupFeature.get()) + updateGroup(); + } +} + + +//================================================================================================= +void FeaturesPlugin_SharedFaces::createGroup() +{ + SessionPtr aSession = ModelAPI_Session::get(); + + DocumentPtr aDoc = aSession->activeDocument(); + + if (aDoc.get()) { + myCreateGroupFeature = aDoc->addFeature(FeaturesPlugin_GroupSharedFaces::ID()); + } +} + +//================================================================================================= +void FeaturesPlugin_SharedFaces::updateGroup() +{ + myCreateGroupFeature->boolean(FeaturesPlugin_GroupSharedFaces::COMPUTE_ID())->setValue(false); + myCreateGroupFeature->string(FeaturesPlugin_GroupSharedFaces::GROUP_NAME_ID()) + ->setValue( string(GROUP_NAME_ID())->value()); + + myCreateGroupFeature->selection(FeaturesPlugin_GroupSharedFaces::OBJECT_ID()) + ->setValue( selection(OBJECT_ID())->context() , + selection(OBJECT_ID())->value() ); + AttributeSelectionListPtr aFacesFeatures = + std::dynamic_pointer_cast + (myCreateGroupFeature->attribute(LIST_FACES_ID())); + + AttributeSelectionListPtr aFaces = + std::dynamic_pointer_cast(attribute(LIST_FACES_ID())); + aFaces->copyTo(aFacesFeatures); + + myCreateGroupFeature->integer(FeaturesPlugin_GroupSharedFaces::TRANSPARENCY_ID()) + ->setValue( integer(TRANSPARENCY_ID())->value()); + myCreateGroupFeature->execute(); + myCreateGroupFeature->boolean(FeaturesPlugin_GroupSharedFaces::COMPUTE_ID())->setValue(true); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_SharedFaces.h b/src/FeaturesPlugin/FeaturesPlugin_SharedFaces.h new file mode 100644 index 000000000..d78e9b3e6 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_SharedFaces.h @@ -0,0 +1,133 @@ +// Copyright (C) 2018-2021 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_SharedFaces_H_ +#define FeaturesPlugin_SharedFaces_H_ + +#include + +/// \class FeaturesPlugin_SharedFaces +/// \ingroup Plugins +/// \brief Feature to check the shared faces of solid + +class FeaturesPlugin_SharedFaces : public FeaturesPlugin_CommonSharedFaces +{ +public: + inline static const std::string& ID() + { + static const std::string MY_ID("Shared_faces_macro"); + return MY_ID; + } + + /// \return the kind of a feature. + virtual const std::string& getKind() + { + return ID(); + } + + /// Attribute name for object selected. + inline static const std::string& OBJECT_ID() + { + static const std::string MY_OBJECT_ID("main_object"); + return MY_OBJECT_ID; + } + + /// Attribute name for number of faces. + inline static const std::string& NUMBER_FACES_ID() + { + static const std::string MY_NUMBER_FACES_ID("number_shared_faces"); + return MY_NUMBER_FACES_ID; + } + + /// Attribute name for z coodinate. + inline static const std::string& LIST_FACES_ID() + { + static const std::string MY_LIST_FACES_ID("group_list"); + return MY_LIST_FACES_ID; + } + + /// Attribute name for transparency. + inline static const std::string& TRANSPARENCY_ID() + { + static const std::string MY_TRANSPARENCY_ID("transparency"); + return MY_TRANSPARENCY_ID; + } + + /// Attribute name for checkbox create group. + inline static const std::string& CREATE_GROUP_ID() + { + static const std::string MY_CREATE_GROUP_ID("create_group"); + return MY_CREATE_GROUP_ID; + } + + /// Attribute name for indicate to launch the algo. + inline static const std::string& COMPUTE_ID() + { + static const std::string MY_COMPUTE_ID("compute"); + return MY_COMPUTE_ID; + } + + /// Attribute name for group name. + inline static const std::string& GROUP_NAME_ID() + { + static const std::string MY_GROUP_NAME_ID("group_name"); + return MY_GROUP_NAME_ID; + } + + /// 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(); + + /// Called on change of any argument-attribute of this object + /// \param theID identifier of changed attribute + FEATURESPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + + /// Reimplemented from ModelAPI_Feature::isMacro(). Returns true. + FEATURESPLUGIN_EXPORT virtual bool isMacro() const { return true; } + + /// Use plugin manager for features creation + FeaturesPlugin_SharedFaces(); + + private: + + /// Return Attribut values of object. + virtual AttributePtr attributObject(); + + /// Return Attribut values of list of faces. + virtual AttributePtr attributListFaces(); + + /// Return Attribut values of number of faces. + virtual AttributePtr attributNumberFaces(); + + /// Return Attribut values of IsCompute. + virtual AttributePtr attributIsCompute(); + + /// Create group + void createGroup(); + + /// Update group + void updateGroup(); + + /// Feature to create group + FeaturePtr myCreateGroupFeature; +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts index 878c1f4d5..acf1254f2 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts +++ b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts @@ -143,6 +143,10 @@ Rotation Rotation + + Check shared faces + Vérifier les faces partagées + Symmetry Symétrie @@ -4966,6 +4970,124 @@ + + + Shared_faces_macro + + Check shared faces + Vérifier les faces partagées + + + Shared faces + Faces partagées + + + Number of shared faces : + Nombre de faces partagées : + + + + Shared_faces_macro:create_group + + Create group + Créer un groupe + + + + Shared_faces_macro:group_name + + Group name + Nom du groupe + + + + Shared_faces_macro:main_object + + Object + Objet + + + Shared faces + Faces partagées + + + + Shared_faces_macro:group_list + + Shared faces + Faces partagées + + + List of faces : + Liste des faces : + + + + Shared_faces_macro:transparency + + Transparency + Transparence + + + + Shared_faces + + Check shared faces + Vérifier les faces partagées + + + Shared faces + Faces partagées + + + Number of shared faces : + Nombre de faces partagées : + + + + Shared_faces:create_group + + Create group + Créer un groupe + + + + Shared_faces:group_name + + Group name + Nom du groupe + + + + Shared_faces:main_object + + Object + Objet + + + Shared faces + Faces partagées + + + + Shared_faces:group_list + + Shared faces + Faces partagées + + + List of faces : + Liste des faces : + + + + Shared_faces:transparency + + Transparency + Transparence + + + Symmetry diff --git a/src/FeaturesPlugin/Test/TestCheckSharedFaces.py b/src/FeaturesPlugin/Test/TestCheckSharedFaces.py new file mode 100644 index 000000000..d6a3ee357 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestCheckSharedFaces.py @@ -0,0 +1,83 @@ +# Copyright (C) 2014-2021 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 +# + +""" + Unit test of Check shared faces +""" +#========================================================================= +# Initialization of the test +#========================================================================= + + +import os +import math + +from ModelAPI import * +from GeomAPI import * +from salome.shaper import model + + +__updated__ = "2020-11-12" + + +if __name__ == '__main__': + + model.begin() + partSet = model.moduleDocument() + Part_1 = model.addPart(partSet) + Part_1_doc = Part_1.document() + ### Create Box + Box_1 = model.addBox(Part_1_doc, 10, 10, 10) + ### Create Point + Point_2 = model.addPoint(Part_1_doc, 20, 10, 10) + ### Create Box + Box_2 = model.addBox(Part_1_doc, model.selection("VERTEX", "[Box_1_1/Front][Box_1_1/Left][Box_1_1/Bottom]"), model.selection("VERTEX", "Point_1")) + ### Create CompSolid + CompSolid_1 = model.addCompSolid(Part_1_doc, [model.selection("SOLID", "Box_1_1"), model.selection("SOLID", "Box_2_1")]) + + ### Create Shared_faces + Shared_faces_1 = model.getSharedFaces(Part_1_doc, model.selection("COMPSOLID", "CompSolid_1_1"), "mygroup") + + model.do() + # Check results + Shared_faces_1_Feature = Shared_faces_1.feature() + assert Shared_faces_1_Feature.error() == '' + assert Shared_faces_1_Feature.name() == "Shared_faces_1" + + aSelectionList = Shared_faces_1_Feature.selectionList("group_list") + assert aSelectionList.size() == 1 + + assert(Part_1_doc.size("Groups") == 1) + + #assert Part_1_doc.object("Groups", 0).name() == "mygroup" + resShape = modelAPI_Result(Part_1_doc.object("Groups", 0)).shape() + assert(not resShape.isNull()) + + # the group result is a face, check that this is one face + aShapeExplorer = GeomAPI_ShapeExplorer(resShape, GeomAPI_Shape.FACE) + assert(aShapeExplorer.more()) + assert(aShapeExplorer.current().isFace()) + aShapeExplorer.next() + assert(not aShapeExplorer.more()) + + model.end() + + #========================================================================= + # End of test + #========================================================================= diff --git a/src/FeaturesPlugin/doc/FeaturesPlugin.rst b/src/FeaturesPlugin/doc/FeaturesPlugin.rst index 6fc8177bd..900ffb734 100644 --- a/src/FeaturesPlugin/doc/FeaturesPlugin.rst +++ b/src/FeaturesPlugin/doc/FeaturesPlugin.rst @@ -27,6 +27,7 @@ Features plug-in provides a set of common topological operations. It implements linearCopyFeature.rst measurementFeature.rst normalToFaceFeature.rst + checkSharedFaceFeature.rst pipeFeature.rst placementFeature.rst pointCoordinatesFeature.rst diff --git a/src/FeaturesPlugin/doc/TUI_SharedFacesFeature.rst b/src/FeaturesPlugin/doc/TUI_SharedFacesFeature.rst new file mode 100644 index 000000000..f170d48e9 --- /dev/null +++ b/src/FeaturesPlugin/doc/TUI_SharedFacesFeature.rst @@ -0,0 +1,11 @@ + + .. _tui_shared_faces: + +Check shared faces +================== + +.. literalinclude:: examples/checkSharedFaces.py + :linenos: + :language: python + +:download:`Download this script ` diff --git a/src/FeaturesPlugin/doc/checkSharedFaceFeature.rst b/src/FeaturesPlugin/doc/checkSharedFaceFeature.rst new file mode 100644 index 000000000..f8a83276b --- /dev/null +++ b/src/FeaturesPlugin/doc/checkSharedFaceFeature.rst @@ -0,0 +1,52 @@ +.. |shared_shapes.icon| image:: images/shared_shapes.png + +Check shared faces +================== + +The **Check shared faces** feature find the shared faces within a composolid or compound. + +The result is a list of faces and a group that can be created with a specified name. + +To check shared faces in the active part: + +#. Select in the Main Menu *Inspection - > Check shared faces* item or +#. Click |shared_shapes.icon| **Check shared faces** button in the toolbar + +The property panel is shown below. + +.. figure:: images/checkSharedFacesPropertyPanel.png + :align: center + + Check shared faces + + +Input fields: + +- **Object** contains composolid or compound selected in 3D OCC viewer or object browser. +- **Number of shared faces** indicate the number of found faces. +- **List of faces** the list of found faces. +- **Transparency** set the transparency of selected object. +- **Create group** check-box that allows the creation of the found faces group. +- **Group name** specifies the name of the group created. + + +**TUI Command**: + +.. py:function:: model.getSharedFaces(Part_doc, shape, nameGroup) + + :param part: The current part object. + :param object: A composolid or compound in format *model.selection("Type", shape)*. + :param string: name of group created. + :return: Created group. + +Result +"""""" + +Result of **Check shared faces** where **Create group** is checked. + +.. figure:: images/sharedFacesResult.png + :align: center + + Shared faces + +**See Also** a sample TUI Script of :ref:`tui_shared_faces` operation. diff --git a/src/FeaturesPlugin/doc/examples/checkSharedFaces.py b/src/FeaturesPlugin/doc/examples/checkSharedFaces.py new file mode 100644 index 000000000..198027331 --- /dev/null +++ b/src/FeaturesPlugin/doc/examples/checkSharedFaces.py @@ -0,0 +1,21 @@ +from salome.shaper import model +import os + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +### Create Box +Box_1 = model.addBox(Part_1_doc, 10, 10, 10) +### Create Point +Point_2 = model.addPoint(Part_1_doc, 20, 10, 10) +### Create Box +Box_2 = model.addBox(Part_1_doc, model.selection("VERTEX", "[Box_1_1/Front][Box_1_1/Left][Box_1_1/Bottom]"), model.selection("VERTEX", "Point_1")) +### Create CompSolid +CompSolid_1 = model.addCompSolid(Part_1_doc, [model.selection("SOLID", "Box_1_1"), model.selection("SOLID", "Box_2_1")]) + +### Create Shared_faces +Shared_faces_1 = model.getSharedFaces(Part_1_doc, model.selection("COMPSOLID", "CompSolid_1_1"), "mygroup") + +model.do() +model.end() diff --git a/src/FeaturesPlugin/doc/images/checkSharedFacesPropertyPanel.png b/src/FeaturesPlugin/doc/images/checkSharedFacesPropertyPanel.png new file mode 100644 index 0000000000000000000000000000000000000000..6e1ac8a86cc13856d99bc2118b792fb9b1b937b3 GIT binary patch literal 25567 zcmc$_V{{~M*Y6)sG_jpbY}>ZYiEZ1~#I|i~reh}^Ol;f6$?tyFbJo4iy3Zf$yf`mv z_3mBO)z`JF`s%&E`%{sM@)F-+abQ6}K)y>!iYkMEfI@t2WzbMx9;E;(>8}?U2T3hw z5DTt*8U)-gC$@{2ri+TbnTxxjlPQR~oxQCoowKo%si~c_g}uuqcppCq2oZ>s zsF12h_Sw3-2ioB0@9W2uyA2{@;vQtL%N|TfA#Pw+5Ny<4El~ssED3cFWdsSZMABZl zICMi8IuHpszQ6MJA?c(HKo)*d_Ph1U<&}0TJ3Dx2VrlYbi5lo?0Fz226?`tFA(8@) zi$Fn0CCbiEr}CzV z3R$$bIwIsv>hP=(7)c6ny z+VDB~8>(bZJO6f9p&{7DOw%Dql1dOJE7J zSISfGFR)84>+^H99R`)F-rqm>_{NBxC}OWj_GJeGSp}1+M8)(?*QVa_q12}eF@vv; zESD?K-1kks)TLUg-hgpiF0A3Dl%C=ncw~5AnZcYi1Ox=CY7Ocx$57$qq`)n8)v#-+ zwkRckUk3$JRVEIr++LJ7`g6E*eeHcq0WA60Os200@q{AWGvJZ^i~(%Oy~ z)L|Mzfnf$?caSyE@c=o|x%RyJ$kb=16oE`w_zIy`cK0z2DI@p&x0tsreG84tL01L1 z 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 Z_+MI5r#St7{+t6vNlyJ^wTyY#{{ZFg8ZH0; literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/doc/images/sharedFacesResult.png b/src/FeaturesPlugin/doc/images/sharedFacesResult.png new file mode 100644 index 0000000000000000000000000000000000000000..114ac342149b334af9c2478682effc4d250ff046 GIT binary patch literal 7992 zcmZu$cRbbK|G$~pNvPz8Lawg8x<+Oyv$)x$T$_Z;wUUsKkqFmL*?WeoL`GaA>)IoG zlyN1pe&_1*`F_8D{5&4smwV3poYy|j=j(*(YO6EQo~MOCAPl!|s@{b_PSk;4hm%y` zo0|swqTqwVP5GAoN$}%)(&`2H|D3y;p}XEAYj;meR}{p?`H>R}?)K0Xg>rVYedNAI z{z(A>fwtdLRno^K{}^9#V)HkYcrmI3lN1rlg9(rm- zFBAF}6KeCUvF#0E=%izLV6b6O5nGVrkm%VLEUg-E+VLrgu+Gboe5qJieW!)XLa;NP z(U{=)T(W|O|8-*Q7(IrD&9X$n=X1|N}8-@pIV3p^2EsM59B`6GsdUI z?H?+YRNpOg1mN~UDqQ3t>&$VSkOrS1PU}1bWS!NL-Hn3tsSMZZgO};#r`S<)m{X6E z{UMJPQS5q5e&W#<`>F59Pca3V(KEY4zCSM^hhU;g!q0(sAAjM{e_+)tP42bu8^=qj zxF7p?Av}b2l6E#_@^RUTFDy=)kf*;-)}2kyBR}OCl(KLV0l6O-M!5GxFuK~m_4ye{ zdfD-6M18}76A;W+gNEQI&bHA{b6?05U$iqqrsSn^Fj;>_15E$GlkP(9OTlELf|RHs zJ93R(EEJrO*&9PAAh=d%O(uPR{%DOf7oHPb>{2;0Mf0)&FZ~k`tnsBGyO!l;b2~Hh;USVT(KcCjNWp)HiaC z{&8sAP)Kvy%aPHV-P|+2d@<$^{#bc zTrzdSU&OAvxe)cTHw0N@R+ppidq&Y8@%tC)^22%jngoV>3`c~kuOt(l@WV-!bG-jcVCTN!6w zrk9(P<*S_k^nC(Nx%Z198@G77rZK#Pu7(Xmk;2i~aA_<3d;mLVc2m*MZABrov_E4< z^Tt!3g{E=NMOFsXNNAW?b?`Oz0QQKoiW&HfW61R?><$8pBqKysWk8xjl$9wmOg=YMn3&!#dEvFn z(zz9pgl@aQ6c!xCCvj&^sq|w;Y`@-goa3vF5Abcm`_qR98xG&7ChlkD1=eH35(p@j zDeiv5MqoLS_2spb_(^|GGwpjGFjZCzh1vcoOb@rOM8=_Z9`3;xsa4f2YN!&s1qIZd zdp&Gsj4JMrEcv-l_r`ESNh0g#B^^zf=?-y*biznDz6OYWKrDT05g{lz`$hNmDB=1m z8o9pf*&2d_ab~vrBe5?nl4AIRkrO>J&USC}!t1RJLL~I2l>%9SaaDrjr5l~ukOAsF zmQ^C_C*^Z)OwC1=B&nc-sb7p?Bmx=&?J3NkJbPtPL#!>4^rRGff*c3-mF(z1<-8z- z-x;WRZ`AkK;8xKFFTL^w#EZj6TCz|)Jgz?pU1PnM=K85vL)U%GaK@Nm^&akv`k0>S zFS_=%?RDW1g2QZ1c&{_3F%+D%N>fVlKqR{l;Z^gdLE~g$x$94Tfn9WoBI+weux zMT$Z35qBrN&4!#ET*nNMCM|M_d z1^0To%G!Nc6%uM`z(dAV3v}j<3}C1K_-It-7ZLlBy)8kFSo;bDW;E(I%545y&}R)t zp^}scV(o6l>QBqQusO$6LZ^)$QS4mU*A`Y-9oZ}`g85ZxzMj~PIBEu90gbi$x3LfI zPQ%3x8_R;<@XpiK4j&5x&7dBzChOj+Hz&R=>wDS|$S8+}n0Zs{^}1J2vZfAL65Ojf ziM77@r1`QnQpFw58_L7;iM1f?h>}|chc7O2oMCb2{wp>qgqNI;6!_Yf+q)|6J`+-% zQep=~TZ~!nhRbQYM@k&3-Ua891=vtuG)`~>$=qDsQ0^(6m2hx)2~>;e;<{MB`qJN7 zCzt28@Qo^xA^p0qEraWTHw294+YRhCc}vmo57kr0#e%({qBnc z>q#>Gw?Xmi5GxYQAtY(uBFg_XzzjO;ZWx`#WF5Vnsq$r~G6$oDSBeGtT2D253$kFc zqB3Jy=}oKG(3bGu6k^+{iC!s1c|u9GdmX&j6wZD99acApI_zAT?<=cQr@Cuuf~f4* zZ!@{|`{K1LUoznxmv=Q*crFQ7h}wjTEvjY-;@FI_JzT>B+=UgA8AB#tV=Km3ZiWXs zX*CL6T27}su3&+Tk9hUOGZs!ac99(?d3v}*0DE%1H347rYeKQhBGkj&^Jn;Y*}VVT z;^lecgNChE3I>r@J$onoJbD;-{Z%=BJ=U%;H+qJA1521It3f9M9bh-g!u(J+8a<&e zl0S*@E);9t5=27JXn3I8jA3%NF1}p6g}+BErkfF~jH6*gX-;0} zdjZnEiaHf($y@H`95Yg=D*q!+nwBOTs zb!>hR<+_dCO$Z&pcg>}-@1=uX7+Nq%k9u{1g=NHhK7OQW(b@E7}k0lV|o;TE;Bi8nE zUc7lLDTB5V4_#AY$Aa3q@GXmymlSuq6_!eg>qfY$y|+FR zpZD5@gl+(fuE3tqg?N80Vl>*5EQ05F0;4*0Wah3v>sw3o>vA4t%$N?K^o|t03XHK8BAG1lReW%~ z?UKD6c05AB{?o)Fd)@mduJOBOCTxY@IudC?(u7)Zi-|jF^)1MSIkazyezE10l{K3# zG%51P&iW>ulOtjLiUu)ewY3>rP*^D9#~|yeWoZdiwcD$mB)lD0 zXEDxUkTBf*WpPfxE+5rJ_AADWv+U=Jsfq3yY5bE#=LKOIbTc0Bd~gu(()^xyab{kJ z#X(?S6yKIbf`sqo<-$D#5%JpC<9A!4|F4x=`~1S_Oq)bPVio#_*tG+5yE}8fDD@sz z>#fZ*NN7cRW1tWWCYRk5nJx$opyoWrC&sw}HjaT|hMSV#aa|y{xb_pc$e8ki0}Sv7 zLNZ?0{v?h!F;Tb{eeoLK4RbcqLS}!#Qg^)fckJD=jI}c3B_cQ~fm_7=-H9Xi`T5bA z2ly4oib|)%7cY`)WBh%u4Yyog>_>Qd-5y!qfnQh#_yoK~VCzSWq{U<&2zc!T?2lr# zpL6zK0tXGq23i_iTT@X#8y_-7p@<3!*W3eA@lG~``v!)^uCJvQFTSb$YStp{v4guI z7O?PO@w2lKrMZb&@P#Fvb4Pn^?T~7KLgg{DpA~ORz|&kzBa+KRH~R_&HRd2I&D;A?Fhd4kOnzfZ@ARS?s28O zM*L>a@FjXVJ=gJldl*|s-Ck<=j&1sFq)+xs9W0&Hj(|y#k;U+5AwK^Zs;3XssyN(K z0*Ht_M4g0woC&+TKH9mwG%quFscJrsW~oE0n5A^<8#zGs>31ulpl3Gg9ie@hi}E&g@{N7O)k0->fLuU=2+u(`+~Z$7m-2KNQmg@g~#T-$K|X@M-DktJ;!E(u7+ zQ=yS2p)@Z}WJv@guwLEG@Z6m``+_aXw40Y&TB(OMf=h zio|LZZ?UzBS$jYrYjzBET1=E;$q&udkv5-h%%gSjgI?nhBEqKf9{;=F>~z&vBJ@1~ zGvT^DBAqk)=MR10c!Sq7ds97Kc$x!jv;F6H_;^`Z9wX!ww^N zkoLLn=sIh`%4(5fHzQzY)1Cez2m68gaMb-|uataahtr#bvb({CR&ks_CXrxuCctO< z%9K#VWKxKl*jkdgsC_(_RbO^#5h79?XkREvb1iiNr>*KX1PCI8L!=yVy5g&a7~5(T ziYstU^XpE0-TV1&Uu=K_)*!ZnvSS{N#;+vb8<-Gyn0DBi=cDwaK78&fT+^no@K#%C zHC^4?3VjdDo)eJ!+k8KeTDY&MAWbxH_ z(GKGTNc!!smeqxb=u7ce7SaKBM{DQXuf-DIbgnG_G`k9tJ9*&V>*!3id97QP>Odw_ z-AyXix4^P-55%(<+kciWD>B;T)jAEFAR&*jlW85d3BFUw=As87xJo*p)uhFIKeuRMF_-&;o=54WZm4{ zGCm(<1r5aI^Rqh9>SEg29<`Bp1$!eSp@#-Pu!RMOKL1&*46J7I(eRHjhzF#JlQN@V zTe_EY&Z;46+vo5lFJkmrca8<7e-kJzjM$0?#JoP)lKmdK#?gYc?KmupuErDJREn(F zSt1HSp4@;tSQeRoH0=aD1s*47Qd1{@@FLI!dbmZ*W}-q zo|I**&_H@_sdwoPgO)?Q=a9TPdz;Zby34o&#~AjlG01+!fu+nsg-$OBG%ik&2g`dm zG;}#7U(d0gkwndia{FVuER;C(KRh36*$uS+Cq%PX_U8s#_79g8|Fk9&YVr!ROuOoB z2#)WkFaA?;ZXAw+^z1wcpX)zML>S1M#z#wcuk0a%6aV&bKqH6lIVW7RdU&Xj;%N@+ zJ{4=aK7M7a1;?0oJXxY^MRlh?5Hoc_<=+a|=X;ny(n9*t&_Q~tS4zi-ef!)PYu$(z z!_iH;J`}K;*rtVtnVtjG@M?BfU!(g#Q$EXPOU_|g^1oe~NFv>UBQDEPj&D{tjHr$R zUNG+(Pq=Oh%#L!BoD+L6j4OKOmC~0tB8wUVE@&bTpSuhyn0y(6yErX8ODOwq?*|l% zM-vP%dcPAs@;k;h6_e}bOH~<8A9QNqc|`VA&RdgP^&Op zI6F~l;fP!O z0U&lzn&bI!n?C!!jST$ygmN5pC4lD2Q_UtNHnv%OS8l66E_olQ9FRE z0+7Rrk>GwZ?-zKR+d+qK{p!%dsk%86IyWJ*?hAc{s;JlfpaD!+N&0aGHI|>#CFV?Q(^u4<` zu1+XRfwMS2u;tPXRr-4#<#(o)s8gE*h4e8$OL|ApaRVOvgmERI>-byg4JKDkhB@?kB&~`{}IaW0ITci zer@OF3aCrJ>^?IqbPF>x8{}x8-dLW9w&Zuc^tZ(Xx&qzK_nxP;xle)K8TGM`x67QZ z;~Cs=90`P6;*gUZma7q`9rB@j-7$#}dKU1_jFmXeV4FWOj)uAm>*}xdy?S^G)-@-E zH7Bw)FE)*_E|H?AnEu_IfHAi3s?0@&!lsL-F6RSY~s9ALVSk)E$ihH=;l>A6^v;kFb z7Sjx0S8&Qr966IWm~kpXhB7rGc_=-`sXmzo_aQiS>H2S(0u>6VM5xA8;-PG^(>0^v zU~}kAkPmCABliSC>@08aF$Sp9^}YN+k?NGpSf^`B#`KMry!&^L%9bw!PMzcQW?3*1 z!5jht+qT~KO+%n}h(hv9wYZSQ-Se2NaxTK=+p>TA2&MjP!pfh+54JxE73g*sFwC+> znG0`A5D^ZJpT?u2Xz~F1@x!kaY&J1y%zbDoK07)V9Ub2_!ohEj^vH0Z^g8^g0v>I^SjUbLy{4v(*qPoE=0W~&b~2sDmQ8;A zNMkn{9X-?JOp*G-=sOzZ8~a}0=GMasiHr%Q5p2(Wi^(8IXU(?x8zb2FVaTD-53CrS z>%exlmWV)5WcU8?0ZP;yZgxE468Q}q zbvw5k0_z+Z?E5YJ+Uf!}G z>Jd8}3VbKMS7k-Hlaf~R)*WvQbpqxd68Pz#gF)CoX#B$8BA{{>6Ev~^%-Pz*=)m2K zOPB*YeGuf8LeRtm>o%+GwY+z|`u5tyo2($YwKkk~P`gm}Lif8leo`R?&1=#gCOPY6 zJgf7};DiC?gH-NO)onXXOmB)8bh77m`>68d=$?4InLw|TlQDU#vSHZv<=Z_e$FmcL z=T@>;esym=DXYKB_)EwB09w)!Df{aQ@MrB#uVm9`Z*25}P8};}=@XE9`LcS`OZ(6V zpS!J068hv%JmlPMa!&b|vG4(rThMI*L!f((^VGT+?dbcINIi;|C^g z4tmP9oDP;@y2Gpy$8CD0_>%VUOCAs3l+@~IQInL7rAnprH!$2TsIR95{lK)$o@T1J z(*(Ed-pw%qG!k^{Lr~o#I>FRhw;AO6{H4Q;K9}<+4(Imnx@@@NNc*3dN4I_7;JK)k z1Ck^)AHNbk8YRUl)igQ+G}o>5;>Zd%y{^{UZPVGXy zgHovey0U3vf@UormmZ#&B6YpP?RhJXG1%KTz-EI4n3#x&GZ_#2?p}*QMOGIIaCajR z#h}YYUbr&+I+6BK#*wft7ff!kw@;RP6nlDjO^pF4!*F1IKc!0>3@3&IB z+$9yo=PSPjV6x<8l+aBgCdoqDZ2-!26v)s0Z4M)O!iVAroUKfn!}Z)EekZ$@mS$@5_7pKCa97YCr=ze7R)!P9cykpIZ>|7~T2<%B^X z-FHY3TAg$O`QJzD|47fSL=J{X!1%A2|1)%aoj6XSw#SRKQn3PA{<%0jzCHZ+A*fNV zLJ66=_Fsul{=Gs3^D%+60Q~>e=$IxmlUfJIk5VP@(*LN5*@FE0|NpD;Y6}Ip)MWr# gt$ic?xps6yjICw+<2WxMSrEuAHEq=*<@?Y64`L2SSO5S3 literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/doc/images/shared_shapes.png b/src/FeaturesPlugin/doc/images/shared_shapes.png new file mode 100644 index 0000000000000000000000000000000000000000..23f804802251319855cadf3afb9bdabb5182e900 GIT binary patch literal 583 zcmV-N0=WH&P)bgGWIS@MM7DVsg zT;{Y$DPd*+G)+U(G}Lv?dcEE@O|yle1kO1`q-skICn*>%Fqoz*B@Fc)W9X*W+AKNZWNCW(f&_Xpg5lBUL3vMMO}M17Qe(?qQFJ zKuXk2^K8;nXaPRFT`wFbH-CcQJrV+{Qf4+2UIQP;JLBg2&%e}7LseCzlt^Y+8tSX6 z?C<~Ky(4%}kT4_M1HX<0+-Ovi3dwBv&QxcF!FxmkDuSxa2zS6&=8L|S5QwUXN=YrR zV2p8SV$?E1Q^E(}_o7C5EXK4%LV0LrsK}r=C93N1S)8l=bgGWIS@MM7DVsg zT;{Y$DPd*+G)+U(G}Lv?dcEE@O|yle1kO1`q-skICn*>%Fqoz*B@Fc)W9X*W+AKNZWNCW(f&_Xpg5lBUL3vMMO}M17Qe(?qQFJ zKuXk2^K8;nXaPRFT`wFbH-CcQJrV+{Qf4+2UIQP;JLBg2&%e}7LseCzlt^Y+8tSX6 z?C<~Ky(4%}kT4_M1HX<0+-Ovi3dwBv&QxcF!FxmkDuSxa2zS6&=8L|S5QwUXN=YrR zV2p8SV$?E1Q^E(}_o7C5EXK4%LV0LrsK}r=C93N1S)8l= + icon="icons/Features/fusion_faces.png" auto_preview="true" helpfile="FeaturesPlugin/fusionFacesFeature.html"> + + + + + + diff --git a/src/FeaturesPlugin/shared_Faces_macro_widget.xml b/src/FeaturesPlugin/shared_Faces_macro_widget.xml new file mode 100644 index 000000000..b9031cff2 --- /dev/null +++ b/src/FeaturesPlugin/shared_Faces_macro_widget.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + diff --git a/src/FeaturesPlugin/shared_Faces_widget.xml b/src/FeaturesPlugin/shared_Faces_widget.xml new file mode 100644 index 000000000..76fb12a02 --- /dev/null +++ b/src/FeaturesPlugin/shared_Faces_widget.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/src/FeaturesPlugin/tests.set b/src/FeaturesPlugin/tests.set index 7e5e58612..63adae8c3 100644 --- a/src/FeaturesPlugin/tests.set +++ b/src/FeaturesPlugin/tests.set @@ -177,7 +177,7 @@ SET(TEST_NAMES TestMeasurementAngle3Points.py TestMeasurementPresentation.py TestFusionFaces.py - TestFusionFaces2697.py + TestFusionFaces2697.py Test1379.py Test1922.py Test1942.py @@ -522,4 +522,5 @@ SET(TEST_NAMES TestBoundingBox.py Test23885.py TestNormalToFace.py + TestCheckSharedFaces.py ) diff --git a/src/GeomAlgoAPI/CMakeLists.txt b/src/GeomAlgoAPI/CMakeLists.txt index 2fa736470..b98a0a61e 100644 --- a/src/GeomAlgoAPI/CMakeLists.txt +++ b/src/GeomAlgoAPI/CMakeLists.txt @@ -33,6 +33,7 @@ SET(PROJECT_HEADERS GeomAlgoAPI_Prism.h GeomAlgoAPI_Revolution.h GeomAlgoAPI_Boolean.h + GeomAlgoAPI_SharedFaces.h GeomAlgoAPI_ThroughAll.h GeomAlgoAPI_Rotation.h GeomAlgoAPI_Translation.h @@ -104,6 +105,7 @@ SET(PROJECT_SOURCES GeomAlgoAPI_Prism.cpp GeomAlgoAPI_Revolution.cpp GeomAlgoAPI_Boolean.cpp + GeomAlgoAPI_SharedFaces.cpp GeomAlgoAPI_ThroughAll.cpp GeomAlgoAPI_Rotation.cpp GeomAlgoAPI_Translation.cpp diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.cpp new file mode 100644 index 000000000..e57dec2f3 --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.cpp @@ -0,0 +1,148 @@ +// Copyright (C) 2014-2021 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 "GeomAlgoAPI_SharedFaces.h" + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +//======================================================================= +//function : GetSharedShapes +//purpose : +// +// NOTE on the implementation +// +// 1) Resulting sub-shapes are published as a children of the 1st input shape +// from theShapes list. Due to this reason only direct sub-shapes of the 1st +// shape can be contained in the result of the operation (i.e. shares between +// 2nd/3rd, etc couples cannot be retrieved. +// 2) An exception from above case is when a single compound is specified as an +// input. In this case we search shares between its top-level content, so we +// are able to search shares between all possible couples of shapes. +// 3) Parameter theMultiShare controls what types of shares to search: +// - True: get sub-shapes that are shared between ALL input shapes; +// - False: get shares between couples of input sub-shapes (see points 1 and 2). +// +// Thus, we have the following cases: +// [1] theShapes = N shapes (N>1), theMultiShare = True +// Result: sub-shapes that are shared by all theShapes +// [2] theShapes = N shapes (N>1), theMultiShare = False +// Result: sub-shapes of 1st shape from theShapes that are shared with any shape +// from theShapes +// [3] theShapes = 1 shape, theMultiShare = True +// Result: sub-shapes that are shared by all top-level sub-objects of theShapes[0] +// [4] theShapes = 1 shape, theMultiShare = False +// Result: sub-shapes of all possible couples of all top-level sub-objects of +// theShapes[0]. +//================================================================================================= +bool GetSharedFaces(const ListOfShape& theShapes, + ListOfShape & theFaces, + const bool theMultiShare, + std::string& theError) +{ + + #ifdef _DEBUG + std::cout << "GetSharedFaces " << std::endl; + #endif + int aLen = theShapes.size(); + if (aLen < 1) return NULL; + + TopAbs_ShapeEnum aShapeType = TopAbs_FACE; + + TopoDS_Shape aShape; + TopTools_SequenceOfShape aShapesSeq; + + ListOfShape::const_iterator anIt = theShapes.cbegin(); + + for(; anIt != theShapes.cend(); ++anIt) { + + GeomShapePtr aShapePtr = *anIt; + + if (!aShapePtr.get()) { + theError = "GetSharedFaces : An invalid argument"; + return false; + } + aShape = aShapePtr->impl(); + aShapesSeq.Append( aShape ); + } + + // if only single shape is specified as input + // collect all ites top-level sub-shapes for processing + if (aShapesSeq.Length() == 1) { + aShape = aShapesSeq.First(); + aShapesSeq.Clear(); + for (TopoDS_Iterator it( aShape ); it.More(); it.Next()) + aShapesSeq.Append( it.Value() ); + } + + TopTools_MapOfShape mapShape; + + // find shared shapes + + // number of iterations + int nbIters = theMultiShare || theShapes.size() > 1 ? 1 : aShapesSeq.Length()-1; + // numShares factor to search (i.e. by what nb of shapes each found sub-shape should be shared) + int nbShares = theMultiShare ? aShapesSeq.Length()-1 : 1; + + for (int iter = 1; iter <= nbIters; iter++) { + for (int ind = iter + 1; ind <= aShapesSeq.Length(); ind++) { + if (ind - 1 + nbShares > aShapesSeq.Length()) break; + TopoDS_Compound aCurrSelection; + TopoDS_Shape aShape1 = aShapesSeq.Value( iter ); + TopTools_IndexedMapOfShape mapSelected; + TopExp::MapShapes(aShape1, aShapeType, mapSelected); + for (int s = 0; s < nbShares; s++) { + BRep_Builder B; + TopoDS_Compound aCompound; + B.MakeCompound(aCompound); + const TopoDS_Shape& aShape2 = aShapesSeq.Value( ind+s ); + TopTools_MapOfShape mapShape2; + TopExp_Explorer exp (aShape2, aShapeType); + for (; exp.More(); exp.Next()) { + const TopoDS_Shape& aSS = exp.Current(); + if (mapShape2.Add(aSS) && mapSelected.Contains(aSS)) { + B.Add(aCompound, aSS); + } + } + mapSelected.Clear(); + aCurrSelection = aCompound; + TopExp::MapShapes(aCurrSelection, aShapeType, mapSelected); + } + TopoDS_Iterator itSel(aCurrSelection, Standard_True, Standard_True); + for (; itSel.More(); itSel.Next()) { + const TopoDS_Shape& aSS = itSel.Value(); + GeomShapePtr aS(new GeomAPI_Shape); + aS->setImpl(new TopoDS_Shape(aSS)); + if (mapShape.Add(aSS) ) + theFaces.push_back(aS); + } + } + } + return true; +} + diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.h b/src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.h new file mode 100644 index 000000000..5c3ba1fdc --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_SharedFaces.h @@ -0,0 +1,39 @@ +// Copyright (C) 2014-2021 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 GEOMALGOAPI_SHAREDFACES_H_ +#define GEOMALGOAPI_SHAREDFACES_H_ + +#include +#include + +#include + +/// get the boundin box of theshape. +/// \param theShape the shape +/// \param theTolerance precise TRUE for precise computation; FALSE for fast one. +/// \param theFaces the faces shared +/// \param theError error +GEOMALGOAPI_EXPORT +bool GetSharedFaces(const ListOfShape& theShapes, + ListOfShape & theFaces, + const bool theMultiShare, + std::string& theError); + +#endif //GEOMALGOAPI_SHAREDFACES_H_ diff --git a/src/ModelAPI/CMakeLists.txt b/src/ModelAPI/CMakeLists.txt index c6a298461..aaed42c91 100644 --- a/src/ModelAPI/CMakeLists.txt +++ b/src/ModelAPI/CMakeLists.txt @@ -172,16 +172,16 @@ ADD_UNIT_TESTS(${TEST_NAMES}) if(${HAVE_SALOME}) enable_testing() set(TEST_INSTALL_DIRECTORY "${SALOME_SHAPER_INSTALL_TESTS}/ModelAPI") - + install(FILES CTestTestfileInstall.cmake DESTINATION ${TEST_INSTALL_DIRECTORY} RENAME CTestTestfile.cmake) install(FILES tests.set DESTINATION ${TEST_INSTALL_DIRECTORY}) - + set(TMP_TESTS_NAMES) foreach(tfile ${TEST_NAMES}) list(APPEND TMP_TESTS_NAMES "Test/${tfile}") endforeach(tfile ${TEST_NAMES}) - + install(FILES ${TMP_TESTS_NAMES} DESTINATION ${TEST_INSTALL_DIRECTORY}) endif(${HAVE_SALOME}) diff --git a/src/ModuleBase/ModuleBase_WidgetMultiSelector.h b/src/ModuleBase/ModuleBase_WidgetMultiSelector.h index db4b8cd92..54c659ccb 100644 --- a/src/ModuleBase/ModuleBase_WidgetMultiSelector.h +++ b/src/ModuleBase/ModuleBase_WidgetMultiSelector.h @@ -49,8 +49,8 @@ class ModuleBase_FilterStarter; * Implementation of widget for shapes selection. This widget provides selection of several shapes. * It can be defined in XML file as following: * \code -* * \endcode * It uses following parameters: diff --git a/src/PythonAPI/model/features/__init__.py b/src/PythonAPI/model/features/__init__.py index 62a0ca1a8..836387529 100644 --- a/src/PythonAPI/model/features/__init__.py +++ b/src/PythonAPI/model/features/__init__.py @@ -32,6 +32,7 @@ from FeaturesAPI import addFusionFaces from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle from FeaturesAPI import getPointCoordinates, getGeometryCalculation, getBoundingBox from FeaturesAPI import getNormal +from FeaturesAPI import getSharedFaces from FeaturesAPI import addRemoveResults from FeaturesAPI import addCopy, addImportResult from FeaturesAPI import addDefeaturing -- 2.39.2