From e3f32c9bf323edcb7086ff9613fa32aeb1180644 Mon Sep 17 00:00:00 2001 From: Nicolas RECHATIN Date: Thu, 5 Aug 2021 12:26:08 +0200 Subject: [PATCH] Add new feature AddNode for OPERA --- src/OperaAPI/CMakeLists.txt | 2 + src/OperaAPI/OperaAPI.i | 2 + src/OperaAPI/OperaAPI_AddNode.cpp | 84 +++++++++++ src/OperaAPI/OperaAPI_AddNode.h | 80 +++++++++++ src/OperaAPI/OperaAPI_swig.h | 1 + src/OperaPlugin/CMakeLists.txt | 6 +- src/OperaPlugin/OperaPlugin_AddNode.cpp | 135 ++++++++++++++++++ src/OperaPlugin/OperaPlugin_AddNode.h | 100 +++++++++++++ src/OperaPlugin/OperaPlugin_Plugin.cpp | 5 +- src/OperaPlugin/OperaPlugin_Tools.cpp | 46 ++++++ src/OperaPlugin/OperaPlugin_Tools.h | 37 +++++ src/OperaPlugin/OperaPlugin_Volume.cpp | 20 --- src/OperaPlugin/OperaPlugin_msg_fr.ts | 57 +++++++- src/OperaPlugin/Test/TestAddNode.py | 101 +++++++++++++ src/OperaPlugin/addnode_widget.xml | 16 +++ src/OperaPlugin/doc/OperaPlugin.rst | 1 + src/OperaPlugin/doc/TUI_addnode.rst | 11 ++ src/OperaPlugin/doc/addnodeFeature.rst | 43 ++++++ src/OperaPlugin/doc/examples/addnode.py | 14 ++ src/OperaPlugin/doc/images/AddNode.png | Bin 0 -> 6688 bytes src/OperaPlugin/doc/images/AddNode1.png | Bin 0 -> 7560 bytes src/OperaPlugin/doc/images/AddNode_button.png | Bin 0 -> 660 bytes src/OperaPlugin/doc/volumeFeature.rst | 6 +- src/OperaPlugin/icons/AddNode.png | Bin 0 -> 1049 bytes src/OperaPlugin/plugin-Opera.xml | 8 ++ src/OperaPlugin/tests.set | 1 + src/PythonAPI/model/opera/__init__.py | 1 + 27 files changed, 750 insertions(+), 27 deletions(-) create mode 100644 src/OperaAPI/OperaAPI_AddNode.cpp create mode 100644 src/OperaAPI/OperaAPI_AddNode.h create mode 100644 src/OperaPlugin/OperaPlugin_AddNode.cpp create mode 100644 src/OperaPlugin/OperaPlugin_AddNode.h create mode 100644 src/OperaPlugin/OperaPlugin_Tools.cpp create mode 100644 src/OperaPlugin/OperaPlugin_Tools.h create mode 100644 src/OperaPlugin/Test/TestAddNode.py create mode 100644 src/OperaPlugin/addnode_widget.xml create mode 100644 src/OperaPlugin/doc/TUI_addnode.rst create mode 100644 src/OperaPlugin/doc/addnodeFeature.rst create mode 100644 src/OperaPlugin/doc/examples/addnode.py create mode 100644 src/OperaPlugin/doc/images/AddNode.png create mode 100644 src/OperaPlugin/doc/images/AddNode1.png create mode 100644 src/OperaPlugin/doc/images/AddNode_button.png create mode 100644 src/OperaPlugin/icons/AddNode.png diff --git a/src/OperaAPI/CMakeLists.txt b/src/OperaAPI/CMakeLists.txt index a9da8f181..192634bd9 100644 --- a/src/OperaAPI/CMakeLists.txt +++ b/src/OperaAPI/CMakeLists.txt @@ -22,10 +22,12 @@ INCLUDE(Common) SET(PROJECT_HEADERS OperaAPI.h OperaAPI_Volume.h + OperaAPI_AddNode.h ) SET(PROJECT_SOURCES OperaAPI_Volume.cpp + OperaAPI_AddNode.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/OperaAPI/OperaAPI.i b/src/OperaAPI/OperaAPI.i index 6bbadc525..57223e944 100644 --- a/src/OperaAPI/OperaAPI.i +++ b/src/OperaAPI/OperaAPI.i @@ -39,6 +39,8 @@ // shared pointers %shared_ptr(OperaAPI_Volume) +%shared_ptr(OperaAPI_AddNode) // all supported interfaces %include "OperaAPI_Volume.h" +%include "OperaAPI_AddNode.h" diff --git a/src/OperaAPI/OperaAPI_AddNode.cpp b/src/OperaAPI/OperaAPI_AddNode.cpp new file mode 100644 index 000000000..297858054 --- /dev/null +++ b/src/OperaAPI/OperaAPI_AddNode.cpp @@ -0,0 +1,84 @@ +// 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 "OperaAPI_AddNode.h" + +#include +#include + +//================================================================================================== +OperaAPI_AddNode::OperaAPI_AddNode(const std::shared_ptr& theFeature) +: ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + + +//================================================================================================== +OperaAPI_AddNode::OperaAPI_AddNode(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theMainObject, + const std::list& theToolsList) + : ModelHighAPI_Interface(theFeature) +{ + if(initialize()) { + fillAttribute(theMainObject, mainObject()); + setToolsList(theToolsList); + } +} + +//================================================================================================== +OperaAPI_AddNode::~OperaAPI_AddNode() +{ +} + +//================================================================================================== +void OperaAPI_AddNode::setMainObject(const ModelHighAPI_Selection& theMainObject) +{ + fillAttribute(theMainObject, mainObject()); + execute(); +} + +//================================================================================================== +void OperaAPI_AddNode::setToolsList(const std::list& theToolsList) +{ + fillAttribute(theToolsList, toolsList()); + execute(); +} + +//================================================================================================== +void OperaAPI_AddNode::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + AttributeSelectionPtr anAttrObject = aBase->selection(OperaPlugin_AddNode::MAIN_OBJECT_ID()); + theDumper << aBase << " = model.addAddNode(" << aDocName << ", " << anAttrObject << ", "; + + AttributeSelectionListPtr anAttrList = + aBase->selectionList(OperaPlugin_AddNode::TOOLS_LIST_ID()); + theDumper << anAttrList << ")" << std::endl; +} + +//================================================================================================== +AddNodePtr addAddNode(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theMainObject, + const std::list& theToolsList) +{ + std::shared_ptr aFeature = thePart->addFeature(OperaAPI_AddNode::ID()); + return AddNodePtr(new OperaAPI_AddNode(aFeature, theMainObject, theToolsList)); +} diff --git a/src/OperaAPI/OperaAPI_AddNode.h b/src/OperaAPI/OperaAPI_AddNode.h new file mode 100644 index 000000000..288621627 --- /dev/null +++ b/src/OperaAPI/OperaAPI_AddNode.h @@ -0,0 +1,80 @@ +// 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 OPERAAPI_ADDNODE_H_ +#define OPERAAPI_ADDNODE_H_ + +#include "OperaAPI.h" +#include + +#include +#include + +class ModelHighAPI_Selection; + +/// \class OperaAPI_AddNode +/// \ingroup CPPHighAPI +/// \brief Interface for AddNode feature. +class OperaAPI_AddNode: public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + OPERAAPI_EXPORT + explicit OperaAPI_AddNode(const std::shared_ptr& theFeature); + + /// Constructor with values. + OPERAAPI_EXPORT + explicit OperaAPI_AddNode(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theMainObject, + const std::list& theToolsList); + + /// Destructor. + OPERAAPI_EXPORT + virtual ~OperaAPI_AddNode(); + + INTERFACE_2(OperaPlugin_AddNode::ID(), + mainObject, OperaPlugin_AddNode::MAIN_OBJECT_ID(), + ModelAPI_AttributeSelection, /** Main Object */, + toolsList, OperaPlugin_AddNode::TOOLS_LIST_ID(), + ModelAPI_AttributeSelectionList, /** Tools list*/) + + /// Set main object + OPERAAPI_EXPORT + void setMainObject(const ModelHighAPI_Selection& theMainObject); + + /// Set tools list + OPERAAPI_EXPORT + void setToolsList(const std::list& theToolsList); + + /// Dump wrapped feature + OPERAAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; +}; + +/// Pointer AddNode feature +typedef std::shared_ptr AddNodePtr; + +/// \ingroup CPPHighAPI +/// \brief Create Volume feature. +OPERAAPI_EXPORT +AddNodePtr addAddNode(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theMainObject, + const std::list& theToolsList); + +#endif // OPERAAPI_ADDNODE_H_ diff --git a/src/OperaAPI/OperaAPI_swig.h b/src/OperaAPI/OperaAPI_swig.h index eaa088dc9..661e51618 100644 --- a/src/OperaAPI/OperaAPI_swig.h +++ b/src/OperaAPI/OperaAPI_swig.h @@ -24,5 +24,6 @@ #include "OperaAPI.h" #include "OperaAPI_Volume.h" + #include "OperaAPI_AddNode.h" #endif // OPERAAPI_SWIG_H_ diff --git a/src/OperaPlugin/CMakeLists.txt b/src/OperaPlugin/CMakeLists.txt index a1f7fd600..92f3460c8 100644 --- a/src/OperaPlugin/CMakeLists.txt +++ b/src/OperaPlugin/CMakeLists.txt @@ -20,18 +20,22 @@ SET(PROJECT_HEADERS OperaPlugin.h OperaPlugin_Plugin.h + OperaPlugin_Tools.h OperaPlugin_Volume.h + OperaPlugin_AddNode.h ) SET(PROJECT_SOURCES OperaPlugin_Plugin.cpp + OperaPlugin_Tools.cpp OperaPlugin_Volume.cpp - + OperaPlugin_AddNode.cpp ) SET(XML_RESOURCES plugin-Opera.xml volume_widget.xml + addnode_widget.xml ) SET(TEXT_RESOURCES diff --git a/src/OperaPlugin/OperaPlugin_AddNode.cpp b/src/OperaPlugin/OperaPlugin_AddNode.cpp new file mode 100644 index 000000000..9c67576d2 --- /dev/null +++ b/src/OperaPlugin/OperaPlugin_AddNode.cpp @@ -0,0 +1,135 @@ +// 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 "OperaPlugin_AddNode.h" + +#include + +//================================================================================================= +OperaPlugin_AddNode::OperaPlugin_AddNode() // Nothing to do during instantiation +{ +} + +//================================================================================================= +void OperaPlugin_AddNode::initAttributes() +{ + data()->addAttribute(MAIN_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(TOOLS_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); +} + +//================================================================================================= +bool toolsIntersect(ListOfShape& theToolsList){ + + for(auto it = theToolsList.begin(); it != theToolsList.end(); it++) + for(auto jt = theToolsList.begin(); jt != theToolsList.end(); jt++) + { + GeomShapePtr first = *it; + GeomShapePtr second = *jt; + if (!(first == second)) + if (first->isIntersect(second)) + return true; + } + return false; +} + +//================================================================================================= +void OperaPlugin_AddNode::performAlgo(const GeomAlgoAPI_Tools::BOPType theBooleanType, + const GeomShapePtr& theObject, + const ListOfShape& theTools, + const ListOfShape& thePlanes, + int& theResultIndex) +{ + //Perform algorithm + ListOfShape aListWithObject; + aListWithObject.push_back(theObject); + std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList()); + std::shared_ptr aBoolAlgo; + GeomShapePtr aResShape; + aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aListWithObject, + theTools, + theBooleanType)); + //Check for error + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) + return setError(anError); + + //Naming + handleNaming(aResShape, aBoolAlgo, aMakeShapeList, theTools); +} + +//================================================================================================= +void OperaPlugin_AddNode::handleNaming(GeomShapePtr theResShape, + GeomMakeShapePtr theBoolAlgo, + std::shared_ptr theMakeShapeList, + const ListOfShape& theTools) +{ + //Get result shape + int anIndexToRemove = 0; + theResShape = theBoolAlgo->shape(); + theMakeShapeList->appendAlgo(theBoolAlgo); + ListOfShape aTestList = theTools; + aTestList.push_front(theResShape); + + //Build result compund + GeomShapePtr aCompound = GeomAlgoAPI_CompoundBuilder::compound(aTestList); + if (aCompound) { + ResultBodyPtr aResultBody = document()->createBody(data(), anIndexToRemove++); + aResultBody->storeModified(aTestList, aCompound, theBoolAlgo); + aResultBody->loadModifiedShapes(theMakeShapeList, aCompound, GeomAPI_Shape::VERTEX); + aResultBody->loadModifiedShapes(theMakeShapeList, aCompound, GeomAPI_Shape::EDGE); + aResultBody->loadModifiedShapes(theMakeShapeList, aCompound, GeomAPI_Shape::FACE); + setResult(aResultBody); + } +} + +//================================================================================================= +void OperaPlugin_AddNode::execute() +{ + int aResultIndex = 0; + ListOfShape aPlanes, aToolList; + + //Get Selection and Shapes + AttributeSelectionPtr aMainObjectAttr = selection(MAIN_OBJECT_ID()); + AttributeSelectionListPtr aToolsAttrList = selectionList(TOOLS_LIST_ID()); + + // Get Shapes from selection and test intersection with Main Object + GeomShapePtr aMainObjectShape = shapeOfSelection(aMainObjectAttr); + for (int anObjectsIndex = 0; anObjectsIndex < aToolsAttrList->size(); anObjectsIndex++){ + GeomShapePtr currentToolShape = shapeOfSelection(aToolsAttrList->value(anObjectsIndex)); + if (!aMainObjectShape->isIntersect(currentToolShape) || aMainObjectShape->intersect(currentToolShape)) + return setError("Error: All tools must be fully inside the main object"); + aToolList.push_back(currentToolShape); + } + + // Check tools intersections + if (toolsIntersect(aToolList)) + return setError("Error: Tools must not intersect each others"); + + //Check for error then clean part results + if(aMainObjectShape == nullptr ) + return setError("Error: cannot perform an AddNode with no main object."); + if(aToolList.empty()) + return setError("Error: Tools list cannot be empty."); + aMainObjectAttr->context()->setDisabled(aMainObjectAttr->context(), true); // To avoid activation of the Part result + + //Perform Algorithm + performAlgo(GeomAlgoAPI_Tools::BOOL_CUT, + shapeOfSelection(aMainObjectAttr), aToolList, aPlanes, + aResultIndex); +} diff --git a/src/OperaPlugin/OperaPlugin_AddNode.h b/src/OperaPlugin/OperaPlugin_AddNode.h new file mode 100644 index 000000000..0a41de4ad --- /dev/null +++ b/src/OperaPlugin/OperaPlugin_AddNode.h @@ -0,0 +1,100 @@ +// 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 OperaPlugin_AddNode_H_ +#define OperaPlugin_AddNode_H_ + +#include +#include + +#include "GeomAPI_ShapeHierarchy.h" +#include "GeomAPI_ShapeIterator.h" + +#include +#include +#include +#include "GeomAlgoAPI_Tools.h" + +#include +#include +#include +#include +#include +#include + +/**\class OperaPlugin_AddNode + * \ingroup Plugins + * \brief Feature for creation of a Node using solids. + * + * Node creates Node object - This Node takes selected solids shape + * and transform the result to a Node with a medium for OPERA. + */ +class OperaPlugin_AddNode : public ModelAPI_Feature +{ + public: + /// Node kind + inline static const std::string& ID() + { + static const std::string MY_NODE_ID("AddNode"); + return MY_NODE_ID; + } + /// Attribute name of main object. + inline static const std::string& MAIN_OBJECT_ID() + { + static const std::string MY_MAIN_OBJECT_ID("main_object"); + return MY_MAIN_OBJECT_ID; + } + /// Attribute name of selected tools list + inline static const std::string& TOOLS_LIST_ID() + { + static const std::string MY_TOOLS_LIST_ID("tools_list"); + return MY_TOOLS_LIST_ID; + } + + /// Perform boolean alogrithm according to AddNode cases + void performAlgo(const GeomAlgoAPI_Tools::BOPType theBooleanType, + const GeomShapePtr& theObject, + const ListOfShape& theTools, + const ListOfShape& thePlanes, + int& theResultIndex); + + /// Creates naming for AddNode results + void OperaPlugin_AddNode::handleNaming(GeomShapePtr theResShape, + GeomMakeShapePtr theBoolAlgo, + std::shared_ptr theMakeShapeList, + const ListOfShape& theTools); + + /// Creates a new part document if needed + OPERAPLUGIN_EXPORT virtual void execute(); + + /// Request for initialization of data model of the feature: adding all attributes + OPERAPLUGIN_EXPORT virtual void initAttributes(); + + /// Returns the kind of a feature + OPERAPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = OperaPlugin_AddNode::ID(); + return MY_KIND; + } + + /// Use plugin manager for features creation + OperaPlugin_AddNode(); +}; + +#endif diff --git a/src/OperaPlugin/OperaPlugin_Plugin.cpp b/src/OperaPlugin/OperaPlugin_Plugin.cpp index 894925751..34e927644 100644 --- a/src/OperaPlugin/OperaPlugin_Plugin.cpp +++ b/src/OperaPlugin/OperaPlugin_Plugin.cpp @@ -19,11 +19,10 @@ #include #include +#include #include -#include - // the only created instance of this plugin static OperaPlugin_Plugin* MY_PRIMITIVES_INSTANCE = new OperaPlugin_Plugin(); @@ -37,6 +36,8 @@ FeaturePtr OperaPlugin_Plugin::createFeature(std::string theFeatureID) { if (theFeatureID == OperaPlugin_Volume::ID()) { return FeaturePtr(new OperaPlugin_Volume); + } else if (theFeatureID == OperaPlugin_AddNode::ID()) { + return FeaturePtr(new OperaPlugin_AddNode); } else { return FeaturePtr(); } diff --git a/src/OperaPlugin/OperaPlugin_Tools.cpp b/src/OperaPlugin/OperaPlugin_Tools.cpp new file mode 100644 index 000000000..8ca459431 --- /dev/null +++ b/src/OperaPlugin/OperaPlugin_Tools.cpp @@ -0,0 +1,46 @@ +// 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 "OperaPlugin_Tools.h" + +//================================================================================================= +GeomShapePtr shapeOfSelection(AttributeSelectionPtr theSel) +{ + GeomShapePtr aResult; + FeaturePtr aSelFeature = theSel->contextFeature(); + + //Get result from selection + if (aSelFeature.get()) { + if (aSelFeature->results().empty()) // if selected feature has no results, make nothing + return aResult; + if (aSelFeature->results().size() == 1) { // for one sub-result don't make compound + aResult = aSelFeature->firstResult()->shape(); + } + } + + //Get shape from result + if (!aResult.get()) + aResult = theSel->value(); + if (!aResult.get()) { + if (theSel->context().get()) + aResult = theSel->context()->shape(); + } + + return aResult; +} diff --git a/src/OperaPlugin/OperaPlugin_Tools.h b/src/OperaPlugin/OperaPlugin_Tools.h new file mode 100644 index 000000000..ba8b76e13 --- /dev/null +++ b/src/OperaPlugin/OperaPlugin_Tools.h @@ -0,0 +1,37 @@ +// 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 OperaPlugin_Tools_H_ +#define OperaPlugin_Tools_H_ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +GeomShapePtr shapeOfSelection(AttributeSelectionPtr theSel); + +#endif diff --git a/src/OperaPlugin/OperaPlugin_Volume.cpp b/src/OperaPlugin/OperaPlugin_Volume.cpp index 57393a226..5a2b69613 100644 --- a/src/OperaPlugin/OperaPlugin_Volume.cpp +++ b/src/OperaPlugin/OperaPlugin_Volume.cpp @@ -33,26 +33,6 @@ OperaPlugin_Volume::OperaPlugin_Volume() // Nothing to do during instantiation { } -//================================================================================================= -static GeomShapePtr shapeOfSelection(AttributeSelectionPtr theSel) { - GeomShapePtr aResult; - FeaturePtr aSelFeature = theSel->contextFeature(); - if (aSelFeature.get()) { - if (aSelFeature->results().empty()) // if selected feature has no results, make nothing - return aResult; - if (aSelFeature->results().size() == 1) { // for one sub-result don't make compound - aResult = aSelFeature->firstResult()->shape(); - } - } - if (!aResult.get()) - aResult = theSel->value(); - if (!aResult.get()) { - if (theSel->context().get()) - aResult = theSel->context()->shape(); - } - return aResult; -} - //================================================================================================= void OperaPlugin_Volume::initAttributes() { diff --git a/src/OperaPlugin/OperaPlugin_msg_fr.ts b/src/OperaPlugin/OperaPlugin_msg_fr.ts index aca1a8815..88085affe 100644 --- a/src/OperaPlugin/OperaPlugin_msg_fr.ts +++ b/src/OperaPlugin/OperaPlugin_msg_fr.ts @@ -23,7 +23,19 @@ - Volume:medium + AddNode + + AddNode + AddNode + + + Perform OPERA Add Node + Faire un AddNode OPERA + + + + + AddNode:medium Medium Milieu @@ -54,4 +66,47 @@ + + + + + + AddNode:main_object + + Main object + Objet Principal + + + Select a main object + Veuillez séléctioner un objet principal + + + + + Volume:tools_list + + Tools objects + Outils + + + Select solid objects as tools + Sélectionnez les outils + + + + + Volume:main_object + + Attribute "%1" is not initialized. + Sélectionnez un object principal. + + + + Volume:tools_list + + Attribute "%2" is not initialized. + Sélectionnez les outils. + + + diff --git a/src/OperaPlugin/Test/TestAddNode.py b/src/OperaPlugin/Test/TestAddNode.py new file mode 100644 index 000000000..ca0573892 --- /dev/null +++ b/src/OperaPlugin/Test/TestAddNode.py @@ -0,0 +1,101 @@ +# 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 +# + +""" + TestAddNode.py + Test case of OperaPlugin_AddNode +""" +#========================================================================= +# Initialization of the test +#========================================================================= +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create two box (one inside the other) +Box_1 = model.addBox(Part_1_doc, 0, 0, 0, 20, 20, 20) +Box_2 = model.addBox(Part_1_doc, 0, 0, 0, 10, 10, 10) + +### Create more boxes for error tests +Box_3= model.addBox(Part_1_doc, 0, 0, 0, 10, 10, 10) +Box_4 = model.addBox(Part_1_doc, 0, 0, 0, 20, 20, 20) +Box_5 = model.addBox(Part_1_doc, 0, 0, 0, 20, 20, 20) +Box_6 = model.addBox(Part_1_doc, 50, 50, 50, 10, 10, 10) +Box_7 = model.addBox(Part_1_doc, 0, 0, 0, 20, 20, 20) +Box_8 = model.addBox(Part_1_doc, 20, 0, 0, 10, 10, 10) +Box_9 = model.addBox(Part_1_doc, 0, 0, 0, 20, 20, 20) +Box_10 = model.addBox(Part_1_doc, 0, 0, 0, 10, 10, 10) +Box_11 = model.addBox(Part_1_doc, 5, 5, 5, 10, 10, 10) +Box_12 = model.addBox(Part_1_doc, 0, 0, 0, 20, 20, 20) +Box_13 = model.addBox(Part_1_doc, 0, 0, 0, 10, 10, 10) +Box_14 = model.addBox(Part_1_doc, 0, 0, 0, 5, 5, 5) + + +### Create a volume from the cylinder and the sphere +AddNode_1 = model.addAddNode(Part_1_doc, model.selection("SOLID", "Box_1_1"), [model.selection("SOLID", "Box_2_1")]) + +#Checks +from GeomAPI import GeomAPI_Shape + +model.testNbResults(AddNode_1, 1) +model.testNbSubResults(AddNode_1, [2]) +model.testNbSubShapes(AddNode_1, GeomAPI_Shape.SOLID, [2]) +model.testNbSubShapes(AddNode_1, GeomAPI_Shape.FACE, [18]) +model.testHaveNamingFaces(AddNode_1, model, Part_1_doc) + +### Create a AddNode with no main object +AddNode_2 = model.addAddNode(Part_1_doc, model.selection("SOLID", "None"), [model.selection("SOLID", "Box_3_1")]) +model.testNbResults(AddNode_2, 0) +assert(AddNode_2.feature().error() == "Attribute \"main_object\" is not initialized.") + +### Create a AddNode with no tools +AddNode_3 = model.addAddNode(Part_1_doc, model.selection("SOLID", "Box_4_1"), []) +model.testNbResults(AddNode_3, 0) +assert(AddNode_3.feature().error() == "Attribute \"tools_list\" is not initialized.") + +### Create a AddNode with tools outside the main object +AddNode_4 = model.addAddNode(Part_1_doc, model.selection("SOLID", "Box_5_1"), [model.selection("SOLID", "Box_6_1")]) +model.testNbResults(AddNode_4, 0) +assert(AddNode_4.feature().error() == "Error: All tools must be fully inside the main object") + +### Create a AddNode with tools intersecting the main object +AddNode_5 = model.addAddNode(Part_1_doc, model.selection("SOLID", "Box_7_1"), [model.selection("SOLID", "Box_8_1")]) +model.testNbResults(AddNode_5, 0) +assert(AddNode_5.feature().error() == "Error: All tools must be fully inside the main object") + +### Create a AddNode with intersecting tools +AddNode_6 = model.addAddNode(Part_1_doc, model.selection("SOLID", "Box_9_1"), [model.selection("SOLID", "Box_10_1"), model.selection("SOLID", "Box_11_1")]) +model.testNbResults(AddNode_6, 0) +assert(AddNode_6.feature().error() == "Error: Tools must not intersect each others") + +### Create a AddNode with a tool inside another +AddNode_7 = model.addAddNode(Part_1_doc, model.selection("SOLID", "Box_12_1"), [model.selection("SOLID", "Box_13_1"), model.selection("SOLID", "Box_14_1")]) +model.testNbResults(AddNode_7, 0) +assert(AddNode_7.feature().error() == "Error: Tools must not intersect each others") + + +# TODO : Test with out of bounds tools +# TODO : Test with intersected tools + +#========================================================================= +# End of test +#========================================================================= diff --git a/src/OperaPlugin/addnode_widget.xml b/src/OperaPlugin/addnode_widget.xml new file mode 100644 index 000000000..adf237d15 --- /dev/null +++ b/src/OperaPlugin/addnode_widget.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/OperaPlugin/doc/OperaPlugin.rst b/src/OperaPlugin/doc/OperaPlugin.rst index f61f280bd..0e61c3302 100644 --- a/src/OperaPlugin/doc/OperaPlugin.rst +++ b/src/OperaPlugin/doc/OperaPlugin.rst @@ -11,3 +11,4 @@ The Opera plug-in provides a set of common operations to ROOT geometry. It imple :maxdepth: 1 volumeFeature.rst + addnodeFeature.rst diff --git a/src/OperaPlugin/doc/TUI_addnode.rst b/src/OperaPlugin/doc/TUI_addnode.rst new file mode 100644 index 000000000..fdd30ced3 --- /dev/null +++ b/src/OperaPlugin/doc/TUI_addnode.rst @@ -0,0 +1,11 @@ + + .. _tui_create_addnode: + +Create Add Node +============= + +.. literalinclude:: examples/addnode.py + :linenos: + :language: python + +:download:`Download this script ` diff --git a/src/OperaPlugin/doc/addnodeFeature.rst b/src/OperaPlugin/doc/addnodeFeature.rst new file mode 100644 index 000000000..1b28e4e04 --- /dev/null +++ b/src/OperaPlugin/doc/addnodeFeature.rst @@ -0,0 +1,43 @@ +.. |AddNode_button.icon| image:: images/AddNode_button.png + +AddNode +------ + +AddNode feature perform an AddNode operation used in bulding the ROOT geometrical hierarchy. + +To create a AddNode in the active part: + +#. select in the Main Menu *Opera - > AddNode* item or +#. click |AddNode_button.icon| **AddNode** button in the toolbar: + +AddNode is created by a solid as main object and a list of solids as tools + +.. figure:: images/AddNode.png + :align: center + +Input fields: + +- **MainOject** defines the solid main object; +- **Tools** defines the list of solid tools + +**TUI Command**: + +.. py:function:: + model.addAddNode(Part_doc, model.selection("SOLID", main_object_name), model.selection[("SOLID", ...), ...]) + + :param part: The current part object. + :param object: A main object solid. + :param list: A list of tools solids. + :return: Result compound. + +Result +"""""" + +One compound with the boolean operation result and the tools objects below it, depending on geometry cases + +.. figure:: images/AddNode1.png + :align: center + + AddNode created + +**See Also** a sample TUI Script of :ref:`tui_create_addnode` operation. diff --git a/src/OperaPlugin/doc/examples/addnode.py b/src/OperaPlugin/doc/examples/addnode.py new file mode 100644 index 000000000..8e8aea51c --- /dev/null +++ b/src/OperaPlugin/doc/examples/addnode.py @@ -0,0 +1,14 @@ +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, 0, 0, 0, 20, 20, 20) +Box_2 = model.addBox(Part_1_doc, 0, 0, 0, 10, 10, 10) + +AddNode_1 = model.addAddNode(Part_1_doc, model.selection("SOLID", "Box_1_1"), [model.selection("SOLID", "Box_2_1")]) + +model.do() +model.end() diff --git a/src/OperaPlugin/doc/images/AddNode.png b/src/OperaPlugin/doc/images/AddNode.png new file mode 100644 index 0000000000000000000000000000000000000000..64a17554824dfa3431d3cb8f93983950d8332808 GIT binary patch literal 6688 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 pOoy}$c_4yQ{Gw-1cfy&_*d)10N6?i4B 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 literal 0 HcmV?d00001 diff --git a/src/OperaPlugin/doc/images/AddNode_button.png b/src/OperaPlugin/doc/images/AddNode_button.png new file mode 100644 index 0000000000000000000000000000000000000000..9265afff1c7aa6443362ab0716e08d6d805b1a38 GIT binary patch literal 660 zcmV;F0&D$=P){Ql!70}TKZx&V~`t;}Wl|NlR_7&QQ`+)FESSjTvLNB`{r2N$cGpA!0il+-bY^CB|J}KN zf6q1%t2R&20nfLme|xOBq$scnZV1#!^O#kjGjJ!zIerw#K<^iCUkS~g~OYV zpOTuo^tH5HTzwcB8J&%l&=i#VM5M(hOG`};&)5)2Fs z3=Cht{kVH*>)x~X7#JA7eg9U~Tz_`abYulM-K(V}@8Rkxub|>=tn?|?lab*+$Musx zG|hPz@Awq!Y4Q}ieQ@BD1-|!6wbv1aJrX)fgvf_je&uIZ}E1x2$6;|;7h;% z85kHCKF4~%1$YT0ZjjK07w91HS~B{Jp%*7lV?vS&6?Vj7Vl(ifUf!fe*)pldgeXr znH_L0+|d93{!g1bvoS5s#ncd?8VN9d`uqi5jDdlHfx#`O?Cqz|m<1RFeE^`cI=v+T0000EX>4Tx04R}tkv&MmKpe$i(@LvU9PA+C5U@H~5EXIMDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=H{g6A|?JWDYS_3;J6>}?mh0_0Yan9G%GL;XnNI5 zCE{WxyDA1>5kdfA45M3SmN6$uNqCO0d-(Wz7vovp=l&eMYR+PSPb8jYhFK-vAfDc= z8l3luBdjQ^#OK6gCS8#Dk?V@fZ=4Gb3p_Jorc?985n{2>!Ab|SqNx#25l2-`r+gvf zvdVdjvsSLL);;+P!#RCrnd>x%kia6AAVGwJ8cHamiWsdrDHc++ANTMNI(~^<3b{&P z0{s+Iiweph_Zc;c7biLU2#|RMI1)6o+{yw(t<_X|`2CnqBztR9^K1r{) zwdfJhw+&oew>4!CxZD8-o($QP9m!8q$mfCgGy0}1(0>c`thv3l_Hp_Eq^Yaq4RCM> zj20++-Q(S%&ffk#)9UXB7N~N93U2t%00006VoOIv0RI600RN!9r;`8x010qNS#tmY z4c7nw4c7reD4Tcy000McNliru6j2n%zxU1^ z+g*1BUldwhOtf5GO)?O|hd@|bvL5XB+LS4|L|f8>^&sRwAVkqZ#DWi#Jw&CwP!$WbOAkZz@`Ah~$c_PORgV+^;7!`zuFp-D&Z8 zz3{j_B~Qs_k(4L&s~4|ON{Gb(pz7p>YSZ9v4u)FSV=I4n(CcP1kUuD+GS13R+W}4OTpThn8@!etlqw9cXA2RZRr| zkiC&Js{)~Jfzj<327q&OPv+xm`u^(z06^7LjEs$LFamtL8N0SG)YKg4?x@F_6aj$+ zS9%tDuoFHxg*7RH_C;y{DA}rQIN`kVu+u9R1AyVao&W$;$TJ1W|G{44nyy9s9;M2%m0Kk&6j70Kr z&cnng-3_dsyKKixih}XPgPifljDDYf@{H~V{(O7v-`Fb(#uN8*hH3OIOvx_`b|B9E zggBQfXyAllf!uraQ0EqxGSibU@-?I;a{7lLFh8Z#+W8U + + + diff --git a/src/OperaPlugin/tests.set b/src/OperaPlugin/tests.set index 019d00b2f..cd7e55f0d 100644 --- a/src/OperaPlugin/tests.set +++ b/src/OperaPlugin/tests.set @@ -19,4 +19,5 @@ SET(TEST_NAMES TestVolume.py + TestAddNode.py ) diff --git a/src/PythonAPI/model/opera/__init__.py b/src/PythonAPI/model/opera/__init__.py index a13965476..a1aa77d1d 100644 --- a/src/PythonAPI/model/opera/__init__.py +++ b/src/PythonAPI/model/opera/__init__.py @@ -20,3 +20,4 @@ """ from OperaAPI import addVolume +from OperaAPI import addAddNode -- 2.39.2