From 8f29b823e6a1a407252eea9dbf110ad695c7121c Mon Sep 17 00:00:00 2001 From: cg246364 Date: Tue, 19 Jan 2021 15:46:17 +0100 Subject: [PATCH] CEA : Lot2 - Bounding box --- src/FeaturesAPI/CMakeLists.txt | 2 + src/FeaturesAPI/FeaturesAPI.i | 2 + src/FeaturesAPI/FeaturesAPI_BoundingBox.cpp | 84 ++++ src/FeaturesAPI/FeaturesAPI_BoundingBox.h | 74 ++++ src/FeaturesAPI/FeaturesAPI_swig.h | 1 + src/FeaturesPlugin/CMakeLists.txt | 9 + .../FeaturesPlugin_BoundingBox.cpp | 195 +++++++++ .../FeaturesPlugin_BoundingBox.h | 140 +++++++ .../FeaturesPlugin_CommonBoundingBox.cpp | 115 +++++ .../FeaturesPlugin_CommonBoundingBox.h | 59 +++ .../FeaturesPlugin_CreateBoundingBox.cpp | 158 +++++++ .../FeaturesPlugin_CreateBoundingBox.h | 130 ++++++ src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp | 6 + src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts | 76 +++- src/FeaturesPlugin/Test/TestBoundingBox.py | 81 ++++ src/FeaturesPlugin/bounding_box_widget.xml | 22 + .../create_bounding_box_widget.xml | 21 + src/FeaturesPlugin/doc/FeaturesPlugin.rst | 1 + .../doc/TUI_boundingBoxFeature.rst | 11 + src/FeaturesPlugin/doc/boundingBoxFeature.rst | 51 +++ .../doc/examples/create_bounding_box.py | 13 + .../doc/images/BoundingBoxResult.png | Bin 0 -> 21077 bytes src/FeaturesPlugin/doc/images/bounding.png | Bin 0 -> 1061 bytes .../doc/images/boundingBoxPropertyPanel.png | Bin 0 -> 24120 bytes src/FeaturesPlugin/icons/bounding.png | Bin 0 -> 1061 bytes src/FeaturesPlugin/plugin-Features.xml | 8 + src/GeomAlgoAPI/CMakeLists.txt | 2 + src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.cpp | 395 ++++++++++++++++++ src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.h | 45 ++ src/PythonAPI/model/features/__init__.py | 2 +- 30 files changed, 1698 insertions(+), 5 deletions(-) create mode 100644 src/FeaturesAPI/FeaturesAPI_BoundingBox.cpp create mode 100644 src/FeaturesAPI/FeaturesAPI_BoundingBox.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_BoundingBox.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_BoundingBox.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.h create mode 100644 src/FeaturesPlugin/Test/TestBoundingBox.py create mode 100644 src/FeaturesPlugin/bounding_box_widget.xml create mode 100644 src/FeaturesPlugin/create_bounding_box_widget.xml create mode 100644 src/FeaturesPlugin/doc/TUI_boundingBoxFeature.rst create mode 100644 src/FeaturesPlugin/doc/boundingBoxFeature.rst create mode 100644 src/FeaturesPlugin/doc/examples/create_bounding_box.py create mode 100644 src/FeaturesPlugin/doc/images/BoundingBoxResult.png create mode 100644 src/FeaturesPlugin/doc/images/bounding.png create mode 100644 src/FeaturesPlugin/doc/images/boundingBoxPropertyPanel.png create mode 100644 src/FeaturesPlugin/icons/bounding.png create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.cpp create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.h diff --git a/src/FeaturesAPI/CMakeLists.txt b/src/FeaturesAPI/CMakeLists.txt index 844baeb00..1ebde42d0 100644 --- a/src/FeaturesAPI/CMakeLists.txt +++ b/src/FeaturesAPI/CMakeLists.txt @@ -53,6 +53,7 @@ SET(PROJECT_HEADERS FeaturesAPI_Defeaturing.h FeaturesAPI_PointCoordinates.h FeaturesAPI_GeometryCalculation.h + FeaturesAPI_BoundingBox.h ) SET(PROJECT_SOURCES @@ -88,6 +89,7 @@ SET(PROJECT_SOURCES FeaturesAPI_Defeaturing.cpp FeaturesAPI_PointCoordinates.cpp FeaturesAPI_GeometryCalculation.cpp + FeaturesAPI_BoundingBox.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/FeaturesAPI/FeaturesAPI.i b/src/FeaturesAPI/FeaturesAPI.i index 4a63d049b..6f89d07cf 100644 --- a/src/FeaturesAPI/FeaturesAPI.i +++ b/src/FeaturesAPI/FeaturesAPI.i @@ -92,6 +92,7 @@ %shared_ptr(FeaturesAPI_Copy) %shared_ptr(FeaturesAPI_ImportResult) %shared_ptr(FeaturesAPI_Defeaturing) +%shared_ptr(FeaturesAPI_BoundingBox) %typecheck(SWIG_TYPECHECK_POINTER) std::pair, bool>, const std::pair, bool> & { @@ -231,3 +232,4 @@ %include "FeaturesAPI_ImportResult.h" %include "FeaturesAPI_PointCoordinates.h" %include "FeaturesAPI_GeometryCalculation.h" +%include "FeaturesAPI_BoundingBox.h" diff --git a/src/FeaturesAPI/FeaturesAPI_BoundingBox.cpp b/src/FeaturesAPI/FeaturesAPI_BoundingBox.cpp new file mode 100644 index 000000000..d72009d39 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_BoundingBox.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2018-2020 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_BoundingBox.h" + +#include +#include +#include +#include + +#include +#include +#include + +//================================================================================================= +FeaturesAPI_BoundingBox::FeaturesAPI_BoundingBox( + const std::shared_ptr& theFeature) + : ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +//================================================================================================= +FeaturesAPI_BoundingBox::FeaturesAPI_BoundingBox( + const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theObject) +:ModelHighAPI_Interface(theFeature) +{ + if (initialize()) { + fillAttribute(theObject, myobjectSelected); + execute(); + } +} + +//================================================================================================= +FeaturesAPI_BoundingBox::~FeaturesAPI_BoundingBox() +{ +} + +//================================================================================================= +void FeaturesAPI_BoundingBox::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + AttributeSelectionPtr anAttrObject; + anAttrObject = aBase->selection(FeaturesPlugin_CreateBoundingBox::OBJECT_ID()); + + theDumper << aBase << " = model.getBoundingBox(" << aDocName << ", " << anAttrObject; + + theDumper << ")" << std::endl; +} + +//================================================================================================= +BoundingBoxPtr getBoundingBox(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theObject) +{ + + FeaturePtr aFeature = + thePart->addFeature(FeaturesPlugin_CreateBoundingBox::ID()); + + BoundingBoxPtr aBoundingBox; + + aBoundingBox.reset(new FeaturesAPI_BoundingBox(aFeature, theObject)); + + return aBoundingBox; +} + diff --git a/src/FeaturesAPI/FeaturesAPI_BoundingBox.h b/src/FeaturesAPI/FeaturesAPI_BoundingBox.h new file mode 100644 index 000000000..cc45aa4af --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_BoundingBox.h @@ -0,0 +1,74 @@ +// Copyright (C) 2018-2020 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_BoundingBox_H_ +#define FeaturesAPI_BoundingBox_H_ + +#include "FeaturesAPI.h" + +#include "FeaturesPlugin_CreateBoundingBox.h" + +#include +#include + +#include + +class ModelAPI_Document; +class ModelHighAPI_Selection; + +/// \class FeaturesAPI_NormalToFace +/// \ingroup CPPHighAPI +/// \brief Interface for NormalToface feature. +class FeaturesAPI_BoundingBox: public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_BoundingBox(const std::shared_ptr& theFeature); + + FEATURESAPI_EXPORT + explicit FeaturesAPI_BoundingBox(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theObject); + + /// Destructor. + FEATURESAPI_EXPORT + virtual ~FeaturesAPI_BoundingBox(); + + INTERFACE_1(FeaturesPlugin_CreateBoundingBox::ID(), + objectSelected, FeaturesPlugin_CreateBoundingBox::OBJECT_ID(), + ModelAPI_AttributeSelection, /** object selected*/) + + /// Dump wrapped feature + FEATURESAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; + +}; + +/// Pointer on the NormalToface object. +typedef std::shared_ptr BoundingBoxPtr; + +/// \ingroup CPPHighAPI +/// \brief get the bounding Box +/// \param thePart the part +/// \param theobject the object selected +FEATURESAPI_EXPORT +BoundingBoxPtr getBoundingBox(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theObject); + +#endif // FeaturesAPI_BoundingBox_H_ diff --git a/src/FeaturesAPI/FeaturesAPI_swig.h b/src/FeaturesAPI/FeaturesAPI_swig.h index 1ad19edc1..6bd15c3c0 100644 --- a/src/FeaturesAPI/FeaturesAPI_swig.h +++ b/src/FeaturesAPI/FeaturesAPI_swig.h @@ -55,5 +55,6 @@ #include "FeaturesAPI_ImportResult.h" #include "FeaturesAPI_PointCoordinates.h" #include "FeaturesAPI_GeometryCalculation.h" + #include "FeaturesAPI_BoundingBox.h" #endif // FeaturesAPI_swig_H_ diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index 1e14a6289..7a793e03b 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -69,6 +69,9 @@ SET(PROJECT_HEADERS FeaturesPlugin_VersionedChFi.h FeaturesPlugin_PointCoordinates.h FeaturesPlugin_GeometryCalculation.h + FeaturesPlugin_BoundingBox.h + FeaturesPlugin_CommonBoundingBox.h + FeaturesPlugin_CreateBoundingBox.h ) SET(PROJECT_SOURCES @@ -118,6 +121,9 @@ SET(PROJECT_SOURCES FeaturesPlugin_VersionedChFi.cpp FeaturesPlugin_PointCoordinates.cpp FeaturesPlugin_GeometryCalculation.cpp + FeaturesPlugin_BoundingBox.cpp + FeaturesPlugin_CommonBoundingBox.cpp + FeaturesPlugin_CreateBoundingBox.cpp ) SET(XML_RESOURCES @@ -156,6 +162,8 @@ SET(XML_RESOURCES defeaturing_widget.xml point_coordinates_widget.xml geometry_calculation_widget.xml + bounding_box_widget.xml + create_bounding_box_widget.xml ) SET(TEXT_RESOURCES @@ -698,4 +706,5 @@ ADD_UNIT_TESTS(TestExtrusion.py Test20247.py TestPointCoordinates.py TestGeometryCalculation.py + TestBoundingBox.py ) diff --git a/src/FeaturesPlugin/FeaturesPlugin_BoundingBox.cpp b/src/FeaturesPlugin/FeaturesPlugin_BoundingBox.cpp new file mode 100644 index 000000000..6e30b6f54 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_BoundingBox.cpp @@ -0,0 +1,195 @@ +// Copyright (C) 2018-2020 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_BoundingBox.h" + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//================================================================================================= +FeaturesPlugin_BoundingBox::FeaturesPlugin_BoundingBox() +{ +} + +//================================================================================================= +void FeaturesPlugin_BoundingBox::initAttributes() +{ + // attribute for object selected + data()->addAttribute(OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + + // attributes for result message and values + data()->addAttribute(X_MIN_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Y_MIN_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Z_MIN_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(X_MAX_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Y_MAX_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Z_MAX_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(CREATEBOX_ID(), ModelAPI_AttributeBoolean::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), X_MIN_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Y_MIN_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Z_MIN_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), X_MAX_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Y_MAX_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Z_MAX_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), CREATEBOX_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), RESULT_VALUES_ID()); + + data()->addAttribute(RESULT_VALUES_ID(), ModelAPI_AttributeDoubleArray::typeId()); + + data()->realArray(RESULT_VALUES_ID())->setSize(6); + +} + +//================================================================================================= +void FeaturesPlugin_BoundingBox::execute() +{ + updateValues(); + createBoxByTwoPoints(); + + if (boolean(CREATEBOX_ID())->value()) { + if (!myCreateFeature.get()) + createBox(); + updateBox(); + } else { + if (myCreateFeature.get()) { + myCreateFeature->eraseResults(); + SessionPtr aSession = ModelAPI_Session::get(); + DocumentPtr aDoc = aSession->activeDocument(); + aDoc->removeFeature(myCreateFeature); + myCreateFeature.reset(); + } + } +} + +//================================================================================================= +void FeaturesPlugin_BoundingBox::attributeChanged(const std::string& theID) +{ + if (theID == OBJECT_ID()) { + if (myCreateFeature.get()) + updateBox(); + } +} + +//================================================================================================= +AttributePtr FeaturesPlugin_BoundingBox::attributResultValues() +{ + return attribute(RESULT_VALUES_ID()); +} + +//================================================================================================= +void FeaturesPlugin_BoundingBox::updateValues() +{ + AttributeSelectionPtr aSelection = selection(OBJECT_ID()); + if (aSelection->isInitialized()) { + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + std::stringstream streamxmin; + std::stringstream streamymin; + std::stringstream streamzmin; + std::stringstream streamxmax; + std::stringstream streamymax; + std::stringstream streamzmax; + + GeomShapePtr aShape; + if (aSelection && aSelection->isInitialized()) { + aShape = aSelection->value(); + if (!aShape && aSelection->context()) + aShape = aSelection->context()->shape(); + } + + if (aShape && !aShape->isEqual(myShape)) { + double aXmin, aXmax, aYmin,aYmax,aZmin,aZmax; + std::string aError; + if (!GetBoundingBox(aShape, + true, + aXmin, aXmax, + aYmin,aYmax, + aZmin,aZmax, + aError)) + setError("Error in bounding box calculation :" + aError); + + myShape = aShape; + streamxmin << std::setprecision(14) << aXmin; + aValues->setValue(0, aXmin); + streamxmax << std::setprecision(14) << aXmax; + aValues->setValue(1, aXmax); + streamymin << std::setprecision(14) << aYmin; + aValues->setValue(2, aYmin); + streamymax << std::setprecision(14) << aYmax; + aValues->setValue(3, aYmax); + streamzmin << std::setprecision(14) << aZmin; + aValues->setValue(4, aZmin); + streamzmax << std::setprecision(14) << aZmax; + aValues->setValue(5, aZmax); + string(X_MIN_COORD_ID() )->setValue( "X = " + streamxmin.str() ); + string(Y_MIN_COORD_ID() )->setValue( "Y = " + streamymin.str() ); + string(Z_MIN_COORD_ID() )->setValue( "Z = " + streamzmin.str() ); + string(X_MAX_COORD_ID() )->setValue( "X = " + streamxmax.str() ); + string(Y_MAX_COORD_ID() )->setValue( "Y = " + streamymax.str() ); + string(Z_MAX_COORD_ID() )->setValue( "Z = " + streamzmax.str() ); + } + } +} + +//================================================================================================= +void FeaturesPlugin_BoundingBox::createBox() +{ + SessionPtr aSession = ModelAPI_Session::get(); + + DocumentPtr aDoc = aSession->activeDocument(); + + if (aDoc.get()) { + myCreateFeature = aDoc->addFeature(FeaturesPlugin_CreateBoundingBox::ID()); + } +} + +//================================================================================================= +void FeaturesPlugin_BoundingBox::updateBox() +{ + myCreateFeature->boolean(FeaturesPlugin_CreateBoundingBox::COMPUTE_ID())->setValue(false); + myCreateFeature->selection(FeaturesPlugin_CreateBoundingBox::OBJECT_ID()) + ->setValue(selection(OBJECT_ID())->context(), + selection(OBJECT_ID())->value()); + + AttributeDoubleArrayPtr aValuesFeatures = + std::dynamic_pointer_cast + (myCreateFeature->attribute(RESULT_VALUES_ID())); + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + for (int anI=0; anI < 6; anI++) + aValuesFeatures->setValue(anI,aValues->value(anI)); + + myCreateFeature->execute(); + myCreateFeature->boolean(FeaturesPlugin_CreateBoundingBox::COMPUTE_ID())->setValue(true); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_BoundingBox.h b/src/FeaturesPlugin/FeaturesPlugin_BoundingBox.h new file mode 100644 index 000000000..ef12a579f --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_BoundingBox.h @@ -0,0 +1,140 @@ +// Copyright (C) 2018-2020 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_BoundingBox_H_ +#define FeaturesPlugin_BoundingBox_H_ + +#include + +/// \class FeaturesPlugin_BoundingBox +/// \ingroup Plugins +/// \brief Feature to view the Bounding Box. + +class FeaturesPlugin_BoundingBox : public FeaturesPlugin_CommonBoundingBox +{ +public: + /// Bounding box macro kind. + inline static const std::string& ID() + { + static const std::string MY_ID("BoundingBoxMacro"); + return MY_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 x coodinate. + inline static const std::string& X_MIN_COORD_ID() + { + static const std::string MY_X_MIN_COORD_ID("xmincoordinate"); + return MY_X_MIN_COORD_ID; + } + + /// Attribute name for y coodinate. + inline static const std::string& Y_MIN_COORD_ID() + { + static const std::string MY_Y_MIN_COORD_ID("ymincoordinate"); + return MY_Y_MIN_COORD_ID; + } + + /// Attribute name for z coodinate. + inline static const std::string& Z_MIN_COORD_ID() + { + static const std::string MY_Z_MIN_COORD_ID("zmincoordinate"); + return MY_Z_MIN_COORD_ID; + } + + /// Attribute name for x max coodinate. + inline static const std::string& X_MAX_COORD_ID() + { + static const std::string MY_X_MAX_COORD_ID("xmaxcoordinate"); + return MY_X_MAX_COORD_ID; + } + + /// Attribute name for y max coodinate. + inline static const std::string& Y_MAX_COORD_ID() + { + static const std::string MY_Y_MAX_COORD_ID("ymaxcoordinate"); + return MY_Y_MAX_COORD_ID; + } + + /// Attribute name for z max coodinate. + inline static const std::string& Z_MAX_COORD_ID() + { + static const std::string MY_Z_MAX_COORD_ID("zmaxcoordinate"); + return MY_Z_MAX_COORD_ID; + } + + /// Attribute name for checkbox create box. + inline static const std::string& CREATEBOX_ID() + { + static const std::string MY_CREATEBOX_ID("createbox"); + return MY_CREATEBOX_ID; + } + + /// Attribute name for values of result. + inline static const std::string& RESULT_VALUES_ID() + { + static const std::string MY_RESULT_VALUES_ID("result_values"); + return MY_RESULT_VALUES_ID; + } + + /// \return the kind of a feature. + virtual const std::string& getKind() + { + return 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_BoundingBox(); + +private: + /// Return Attribut values of result. + virtual AttributePtr attributResultValues(); + + /// Update values displayed. + void updateValues(); + /// Create Box + void createBox(); + /// Update Box + void updateBox(); + + /// Feature to create box + FeaturePtr myCreateFeature; + +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.cpp b/src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.cpp new file mode 100644 index 000000000..9a3784aa8 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2018-2020 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_CommonBoundingBox.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + + + +//================================================================================================= +void FeaturesPlugin_CommonBoundingBox::createBoxByTwoPoints() +{ + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attributResultValues()); + + SessionPtr aSession = ModelAPI_Session::get(); + + DocumentPtr aDoc = aSession->activeDocument(); + + GeomVertexPtr vertexFirst = + GeomAlgoAPI_PointBuilder::vertex(aValues->value(0), + aValues->value(2), + aValues->value(4)); + + GeomVertexPtr vertexSecond = + GeomAlgoAPI_PointBuilder::vertex(aValues->value(1), + aValues->value(3), + aValues->value(5)); + + + std::shared_ptr aBoxAlgo; + + + aBoxAlgo = std::shared_ptr( + new GeomAlgoAPI_Box(vertexFirst->point(),vertexSecond->point())); + + + // These checks should be made to the GUI for the feature but + // the corresponding validator does not exist yet. + if (!aBoxAlgo->check()) { + setError(aBoxAlgo->getError()); + return; + } + + // Build the box + aBoxAlgo->build(); + + // Check if the creation of the box + if (!aBoxAlgo->isDone()) { + // The error is not displayed in a popup window. It must be in the message console. + setError(aBoxAlgo->getError()); + return; + } + if (!aBoxAlgo->checkValid("Box builder with two points")) { + // The error is not displayed in a popup window. It must be in the message console. + setError(aBoxAlgo->getError()); + return; + } + + int aResultIndex = 0; + ResultBodyPtr aResultBox = document()->createBody(data(), aResultIndex); + loadNamingDS(aBoxAlgo, aResultBox); + setResult(aResultBox, aResultIndex); +} + +//================================================================================================= +void FeaturesPlugin_CommonBoundingBox::loadNamingDS(std::shared_ptr theBoxAlgo, + std::shared_ptr theResultBox) +{ + // Load the result + theResultBox->store(theBoxAlgo->shape()); + + // Prepare the naming + theBoxAlgo->prepareNamingFaces(); + + // Insert to faces + std::map< std::string, std::shared_ptr > listOfFaces = + theBoxAlgo->getCreatedFaces(); + for (std::map< std::string, std::shared_ptr >::iterator it = listOfFaces.begin(); + it != listOfFaces.end(); + ++it) { + theResultBox->generated((*it).second, (*it).first); + } +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.h b/src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.h new file mode 100644 index 000000000..c28392d47 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_CommonBoundingBox.h @@ -0,0 +1,59 @@ +// Copyright (C) 2018-2020 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_CommonBoundingBox_H_ +#define FeaturesPlugin_CommonBoundingBox_H_ + +#include "FeaturesPlugin.h" +#include + +#include + +#include +#include + +#include + +/// \class FeaturesPlugin_CommonBoundingBox +/// \ingroup Plugins +/// \brief Feature to view the Bounding Box. + +class FeaturesPlugin_CommonBoundingBox : 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 attributResultValues() = 0; + +protected: + FeaturesPlugin_CommonBoundingBox() {} + + /// Create box with two points + void createBoxByTwoPoints(); + + /// Create namming + void loadNamingDS(std::shared_ptr theBoxAlgo, + std::shared_ptr theResultBox); + + GeomShapePtr myShape; +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.cpp b/src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.cpp new file mode 100644 index 000000000..f59d98b94 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.cpp @@ -0,0 +1,158 @@ +// Copyright (C) 2018-2020 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_CreateBoundingBox.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +//================================================================================================= +FeaturesPlugin_CreateBoundingBox::FeaturesPlugin_CreateBoundingBox() +{ +} + +//================================================================================================= +void FeaturesPlugin_CreateBoundingBox::initAttributes() +{ + // attribute for object selected + data()->addAttribute(OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + + // attributes for result message and values + data()->addAttribute(X_MIN_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Y_MIN_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Z_MIN_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(X_MAX_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Y_MAX_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(Z_MAX_COORD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(COMPUTE_ID(), ModelAPI_AttributeBoolean::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), X_MIN_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Y_MIN_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Z_MIN_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), X_MAX_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Y_MAX_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), Z_MAX_COORD_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), COMPUTE_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), OBJECT_ID()); + data()->addAttribute(RESULT_VALUES_ID(), ModelAPI_AttributeDoubleArray::typeId()); + + data()->realArray(RESULT_VALUES_ID())->setSize(6); + data()->boolean(COMPUTE_ID())->setValue(true); +} + +//================================================================================================= +void FeaturesPlugin_CreateBoundingBox::execute() +{ + updateValues(); + createBoxByTwoPoints(); +} + +//================================================================================================= +void FeaturesPlugin_CreateBoundingBox::attributeChanged(const std::string& theID) +{ +} + +//================================================================================================= +void FeaturesPlugin_CreateBoundingBox::updateValues() +{ + AttributeSelectionPtr aSelection = selection(OBJECT_ID()); + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + + if (aSelection->isInitialized()) { + std::stringstream streamxmin; + std::stringstream streamymin; + std::stringstream streamzmin; + std::stringstream streamxmax; + std::stringstream streamymax; + std::stringstream streamzmax; + + GeomShapePtr aShape; + if (aSelection && aSelection->isInitialized()) { + aShape = aSelection->value(); + if (!aShape && aSelection->context()) + aShape = aSelection->context()->shape(); + } + + AttributeBooleanPtr anIsCompute = boolean(COMPUTE_ID()); + if (!anIsCompute->value()) { + myShape = aShape; + anIsCompute->setValue(true); + } + + if (aShape && !aShape->isEqual(myShape)) { + double aXmin, aXmax, aYmin,aYmax,aZmin,aZmax; + std::string aError; + if (!GetBoundingBox(aShape, + true, + aXmin, aXmax, + aYmin,aYmax, + aZmin,aZmax, + aError)) + setError("Error in bounding box calculation :" + aError); + myShape = aShape; + streamxmin << std::setprecision(14) << aXmin; + aValues->setValue(0, aXmin); + streamxmax << std::setprecision(14) << aXmax; + aValues->setValue(1, aXmax); + streamymin << std::setprecision(14) << aYmin; + aValues->setValue(2, aYmin); + streamymax << std::setprecision(14) << aYmax; + aValues->setValue(3, aYmax); + streamzmin << std::setprecision(14) << aZmin; + aValues->setValue(4, aZmin); + streamzmax << std::setprecision(14) << aZmax; + aValues->setValue(5, aZmax); + } else { + streamxmin << std::setprecision(14) << aValues->value(0); + streamxmax << std::setprecision(14) << aValues->value(1); + streamymin << std::setprecision(14) << aValues->value(2); + streamymax << std::setprecision(14) << aValues->value(3); + streamzmin << std::setprecision(14) << aValues->value(4); + streamzmax << std::setprecision(14) << aValues->value(5); + } + + string(X_MIN_COORD_ID() )->setValue( "X = " + streamxmin.str() ); + string(Y_MIN_COORD_ID() )->setValue( "Y = " + streamymin.str() ); + string(Z_MIN_COORD_ID() )->setValue( "Z = " + streamzmin.str() ); + string(X_MAX_COORD_ID() )->setValue( "X = " + streamxmax.str() ); + string(Y_MAX_COORD_ID() )->setValue( "Y = " + streamymax.str() ); + string(Z_MAX_COORD_ID() )->setValue( "Z = " + streamzmax.str() ); + } +} + +//================================================================================================= +AttributePtr FeaturesPlugin_CreateBoundingBox::attributResultValues() +{ + return attribute(RESULT_VALUES_ID()); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.h b/src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.h new file mode 100644 index 000000000..6884ac0db --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_CreateBoundingBox.h @@ -0,0 +1,130 @@ +// Copyright (C) 2018-2020 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_CreateBoundingBox_H_ +#define FeaturesPlugin_CreateBoundingBox_H_ + +#include + +/// \class FeaturesPlugin_BoundingBox +/// \ingroup Plugins +/// \brief Feature to view the Bounding Box. + +class FeaturesPlugin_CreateBoundingBox : public FeaturesPlugin_CommonBoundingBox +{ +public: + /// Bounding box kind. + inline static const std::string& ID() + { + static const std::string MY_ID("BoundingBox"); + return MY_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 x coodinate. + inline static const std::string& X_MIN_COORD_ID() + { + static const std::string MY_X_MIN_COORD_ID("xmincoordinate"); + return MY_X_MIN_COORD_ID; + } + + /// Attribute name for y coodinate. + inline static const std::string& Y_MIN_COORD_ID() + { + static const std::string MY_Y_MIN_COORD_ID("ymincoordinate"); + return MY_Y_MIN_COORD_ID; + } + + /// Attribute name for z coodinate. + inline static const std::string& Z_MIN_COORD_ID() + { + static const std::string MY_Z_MIN_COORD_ID("zmincoordinate"); + return MY_Z_MIN_COORD_ID; + } + + /// Attribute name for x max coodinate. + inline static const std::string& X_MAX_COORD_ID() + { + static const std::string MY_X_MAX_COORD_ID("xmaxcoordinate"); + return MY_X_MAX_COORD_ID; + } + + /// Attribute name for y max coodinate. + inline static const std::string& Y_MAX_COORD_ID() + { + static const std::string MY_Y_MAX_COORD_ID("ymaxcoordinate"); + return MY_Y_MAX_COORD_ID; + } + + /// Attribute name for z max coodinate. + inline static const std::string& Z_MAX_COORD_ID() + { + static const std::string MY_Z_MAX_COORD_ID("zmaxcoordinate"); + return MY_Z_MAX_COORD_ID; + } + + /// Attribute name for values of result. + inline static const std::string& RESULT_VALUES_ID() + { + static const std::string MY_RESULT_VALUES_ID("result_values"); + return MY_RESULT_VALUES_ID; + } + + /// Attribute name for indicate to compute the bounding box. + inline static const std::string& COMPUTE_ID() + { + static const std::string MY_COMPUTE_ID("compute"); + return MY_COMPUTE_ID; + } + + /// \return the kind of a feature. + virtual const std::string& getKind() + { + return 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); + + /// Return Attribut values of result. + FEATURESPLUGIN_EXPORT virtual AttributePtr attributResultValues(); + + /// Use plugin manager for features creation + FeaturesPlugin_CreateBoundingBox(); + +private: + /// Update values displayed. + void updateValues(); + +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index 8419057e2..d3332d14f 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -203,6 +205,10 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new FeaturesPlugin_PointCoordinates); } else if (theFeatureID == FeaturesPlugin_GeometryCalculation::ID()) { return FeaturePtr(new FeaturesPlugin_GeometryCalculation); + } else if (theFeatureID == FeaturesPlugin_BoundingBox::ID()) { + return FeaturePtr(new FeaturesPlugin_BoundingBox); + } else if (theFeatureID == FeaturesPlugin_CreateBoundingBox::ID()) { + return FeaturePtr(new FeaturesPlugin_CreateBoundingBox); } diff --git a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts index ff2645663..878c1f4d5 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts +++ b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts @@ -131,6 +131,10 @@ Geometry calculation Calcul de la géométrie + + Bounding box + Boîte englobante + Placement Placement @@ -149,6 +153,30 @@ + + + BoundingBox + + BoundingBox + Boîte englobante + + + Create box + Créer la boîte + + + + BoundingBoxMacro + + BoundingBox + Boîte englobante + + + Create box + Créer la boîte + + + Chamfer @@ -4462,14 +4490,44 @@ Deuxième direction - + + - PointCoordinates + BoundingBoxMacro - Point coordinates - Coordonnées d'un point + Bounding box + Boîte englobante + + + + BoundingBoxMacro:main_object + + Object + Objet + + BoundingBoxMacro:createbox + + Create box + Créer la boîte + + + + BoundingBox + + Bounding box + Boîte englobante + + + + BoundingBox:main_object + + Object + Objet + + + GeometryCalculation @@ -4500,6 +4558,7 @@ Volume = + Measurement @@ -4771,6 +4830,15 @@ + + + PointCoordinates + + Point coordinates + Coordonnées d'un point + + + Rotation diff --git a/src/FeaturesPlugin/Test/TestBoundingBox.py b/src/FeaturesPlugin/Test/TestBoundingBox.py new file mode 100644 index 000000000..673445e86 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestBoundingBox.py @@ -0,0 +1,81 @@ +# Copyright (C) 2014-2020 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 ... +""" +#========================================================================= +# Initialization of the test +#========================================================================= + + +import os +import math + +from ModelAPI import * +from salome.shaper import model + + +__updated__ = "2020-11-12" + + +#========================================================================= +# test Bounding Box +#========================================================================= +def test_Bounding_Box(): + + model.begin() + file_path = os.path.join(os.getenv("DATA_DIR"),"Shapes","Step","screw.step") + partSet = model.moduleDocument() + Part_1 = model.addPart(partSet) + Part_1_doc = Part_1.document() + Import_1 = model.addImport(Part_1_doc,file_path) + model.do() + ### Create BoundingBox + BoundingBox_1 = model.getBoundingBox(Part_1_doc, model.selection("SOLID", "screw_1")) + model.end() + + myDelta = 1e-6 + Props = model.getGeometryCalculation(Part_1_doc,model.selection("SOLID", "BoundingBox_1_1")) + + print(" Basic Properties:") + print(" Wires length: ", Props[0]) + print(" Surface area: ", Props[1]) + print(" Volume : ", Props[2]) + + aReflength = 0.32855301948678 + aReslength = Props[0] + assert (math.fabs(aReslength - aReflength) < myDelta), "The surface is wrong: expected = {0}, real = {1}".format(aReflength, aReslength) + + aRefSurface = 0.0041640657342782 + aResSurface = Props[1] + assert (math.fabs(aResSurface - aRefSurface) < myDelta), "The surface is wrong: expected = {0}, real = {1}".format(aRefSurface, aResSurface) + + aRefVolume = 1.6785355394103e-05 + aResVolume = Props[2] + assert (math.fabs(aResVolume - aRefVolume) < myDelta), "The volume is wrong: expected = {0}, real = {1}".format(aRefVolume, aResVolume) + + +if __name__ == '__main__': + + test_Bounding_Box() + + #========================================================================= + # End of test + #========================================================================= diff --git a/src/FeaturesPlugin/bounding_box_widget.xml b/src/FeaturesPlugin/bounding_box_widget.xml new file mode 100644 index 000000000..199c2a111 --- /dev/null +++ b/src/FeaturesPlugin/bounding_box_widget.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/src/FeaturesPlugin/create_bounding_box_widget.xml b/src/FeaturesPlugin/create_bounding_box_widget.xml new file mode 100644 index 000000000..39e494f92 --- /dev/null +++ b/src/FeaturesPlugin/create_bounding_box_widget.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/src/FeaturesPlugin/doc/FeaturesPlugin.rst b/src/FeaturesPlugin/doc/FeaturesPlugin.rst index 23cfb1f5c..34e8fea42 100644 --- a/src/FeaturesPlugin/doc/FeaturesPlugin.rst +++ b/src/FeaturesPlugin/doc/FeaturesPlugin.rst @@ -12,6 +12,7 @@ Features plug-in provides a set of common topological operations. It implements booleanOperations.rst angularCopyFeature.rst + boundingBoxFeature.rst chamferFeature.rst copyFeature.rst defeaturingFeature.rst diff --git a/src/FeaturesPlugin/doc/TUI_boundingBoxFeature.rst b/src/FeaturesPlugin/doc/TUI_boundingBoxFeature.rst new file mode 100644 index 000000000..bb73b2234 --- /dev/null +++ b/src/FeaturesPlugin/doc/TUI_boundingBoxFeature.rst @@ -0,0 +1,11 @@ + + .. _tui_create_Bounding_Box: + +Create bounding box +=================== + +.. literalinclude:: examples/create_bounding_box.py + :linenos: + :language: python + +:download:`Download this script ` diff --git a/src/FeaturesPlugin/doc/boundingBoxFeature.rst b/src/FeaturesPlugin/doc/boundingBoxFeature.rst new file mode 100644 index 000000000..9a1222d73 --- /dev/null +++ b/src/FeaturesPlugin/doc/boundingBoxFeature.rst @@ -0,0 +1,51 @@ +.. |boundingBox.icon| image:: images/bounding.png + +Bounding box +============ + +The **Bounding box** feature displays the bounding box of sub-elements of a geometrical object (shape). + +The property panel displays the coordinates of inferior (**Min**) and superior (**Max**) points. +the resulting bounding box can be created via a dedicated check-box **Create Box**. If this last is checked corresponding result and feature would be created. + +If the check-box **Create Box** isn't checked, **Apply** button does not generate any result and has the same effect as **Cancel** for this feature. + +To display the bounding box in the active part: + +#. select in the Main Menu *Inspection - > Bounding box* item or +#. click |boundingBox.icon| **Bounding box** button in the toolbar + +Coordinates of the two points (inferior and superior) of the bounding box can be displayed for a selected object in the property panel : + +.. figure:: images/boundingBoxPropertyPanel.png + :align: center + + Bounding Box + + +Input fields: + +- **Object** contains object selected in 3D OCC viewer or object browser. +- **Create box** check-box allow the creation of the bounding box (result and feature). + +Note, the coordinates of two points displayed can be selected. + +**TUI Command**: + +.. py:function:: model.getBoundingBox(Part_doc, shape) + + :param part: The current part object. + :param object: A shape in format *model.selection("Type", shape)*. + :return: Created bounding box. + +Result +"""""" + +Result of **Bounding box** where **Create box** is checked. + +.. figure:: images/BoundingBoxResult.png + :align: center + + Object selected + +**See Also** a sample TUI Script of :ref:`tui_create_Bounding_Box` operation. diff --git a/src/FeaturesPlugin/doc/examples/create_bounding_box.py b/src/FeaturesPlugin/doc/examples/create_bounding_box.py new file mode 100644 index 000000000..d904006ae --- /dev/null +++ b/src/FeaturesPlugin/doc/examples/create_bounding_box.py @@ -0,0 +1,13 @@ +from salome.shaper import model +import os + +model.begin() +file_path = os.path.join(os.getenv("DATA_DIR"),"Shapes","Step","screw.step") +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Import_1 = model.addImport(Part_1_doc,file_path) +model.do() +### Create BoundingBox +BoundingBox_1 = model.getBoundingBox(Part_1_doc, model.selection("SOLID", "screw_1")) +model.end() diff --git a/src/FeaturesPlugin/doc/images/BoundingBoxResult.png b/src/FeaturesPlugin/doc/images/BoundingBoxResult.png new file mode 100644 index 0000000000000000000000000000000000000000..89eb1278d8ca4be370001b38b16a7805b2bfe6a5 GIT binary patch literal 21077 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 Kr5_4!Km7{}(#ClJ literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/icons/bounding.png b/src/FeaturesPlugin/icons/bounding.png new file mode 100644 index 0000000000000000000000000000000000000000..5a77a4542187d3e1c39e684aaa911f20a81931c0 GIT binary patch literal 1061 zcmV+=1ls$FP)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZVoa5%^ z1~zQhu3eHqih~q`fO_wPXn+7>K{gC%5GRoS4&)kw#Js(|xBdP5mjQ%-|NhPJ>(?)a zpFe*xoIQKi3C*y5K)qf-dK*9hu^<};WJ-Ye&d$!;czAdiv^_Ez{{H*V@aOM8F#h%D zAH&bze}M+q@B8xQ3&Z{U_Z2}d1M1ZTY6JO-0U&@FVcsMc01XoXQoKMc05t17Kmf5o z1wl3_Iyg9NQ?iL-`0?#C12?}g12>;A!yljvfB!+a?#C|>{=@JcNVDEp3ex@th^>J5 zIfw%gK(H`W1ljQ8#}9_@Kn(KUr;i^QzI^@8@Ehvg-@k!|{)HI$6U6@Whd~VCP>}vx zAO=7HF~LNPjg6P-22?Qch{!PrNU8v7Sq4TnE(S(cPKN)?oDBb1I2isgaxna6WMg3B z6k=fI-~;Q^@W=&GAg2m|7ytnTbK#GF|Nb$s^NBJ1{Pv0AA24nHVfe?uCM3+j0Cdei zpjZC_F%!_M?7x08eE;}@;osk1V2%I(|AV)UT|xPkDWKff9N0uB57`#S>@BQwK)CWzLbU*E%wFaQw%0R%Vj`}gk*f0(!!KEHbl z3^!oX0J@BU4QMzptp5G^1vKaf1poTM@Z+*!3_kZtFJ%) zfDQc(^!^_vU`)P!%J3Ix*uURD!G`|(^ApTxW&wsHBP96$0_BhaKmfrb<=wk?UR#_QK|!oPKfW^j`Sl&le|UTqh&ll?2Oxl0kPQC@ zikLq?zk?*T8#mG4T0mOvtLZIP0fON&F z`MC_|mXrZq`wfi$0=);qAaDNt^Mm2e{<#cy56p*Uy;DFu4a&9x2q0LIbQ4 + + + + + + diff --git a/src/GeomAlgoAPI/CMakeLists.txt b/src/GeomAlgoAPI/CMakeLists.txt index 0aaebb219..e3ea735fb 100644 --- a/src/GeomAlgoAPI/CMakeLists.txt +++ b/src/GeomAlgoAPI/CMakeLists.txt @@ -89,6 +89,7 @@ SET(PROJECT_HEADERS GeomAlgoAPI_Chamfer.h GeomAlgoAPI_Defeaturing.h GeomAlgoAPI_GeometryCalculation.h + GeomAlgoAPI_BoundingBox.h ) SET(PROJECT_SOURCES @@ -157,6 +158,7 @@ SET(PROJECT_SOURCES GeomAlgoAPI_Chamfer.cpp GeomAlgoAPI_Defeaturing.cpp GeomAlgoAPI_GeometryCalculation.cpp + GeomAlgoAPI_BoundingBox.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.cpp new file mode 100644 index 000000000..856b2eb4d --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.cpp @@ -0,0 +1,395 @@ +// Copyright (C) 2014-2020 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_BoundingBox.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** +* This function constructs and returns modified shape from the original one +* for singular cases. It is used for the method GetMinDistanceSingular. +* +* \param theShape the original shape +* \param theModifiedShape output parameter. The modified shape. +* \param theAddDist output parameter. The added distance for modified shape. +* \retval true if the shape is modified; false otherwise. +* +* \internal +*/ +Standard_Boolean ModifyShape(const TopoDS_Shape &theShape, + TopoDS_Shape &theModifiedShape, + Standard_Real &theAddDist) +{ + TopExp_Explorer anExp; + int nbf = 0; + + theAddDist = 0.; + theModifiedShape.Nullify(); + + for ( anExp.Init( theShape, TopAbs_FACE ); anExp.More(); anExp.Next() ) { + nbf++; + theModifiedShape = anExp.Current(); + } + if(nbf==1) { + TopoDS_Shape sh = theShape; + while(sh.ShapeType()==TopAbs_COMPOUND) { + TopoDS_Iterator it(sh); + sh = it.Value(); + } + Handle(Geom_Surface) S = BRep_Tool::Surface(TopoDS::Face(theModifiedShape)); + if(S->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) || + S->IsKind(STANDARD_TYPE(Geom_ToroidalSurface)) || + S->IsUPeriodic()) { + const Standard_Boolean isShell = + (sh.ShapeType()==TopAbs_SHELL || sh.ShapeType()==TopAbs_FACE); + + if (!isShell && S->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) { + Handle(Geom_SphericalSurface) SS = Handle(Geom_SphericalSurface)::DownCast(S); + gp_Pnt PC = SS->Location(); + BRep_Builder B; + TopoDS_Vertex V; + B.MakeVertex(V,PC,1.e-7); + theModifiedShape = V; + theAddDist = SS->Radius(); + return Standard_True; + } + if (!isShell && S->IsKind(STANDARD_TYPE(Geom_ToroidalSurface))) { + Handle(Geom_ToroidalSurface) TS = Handle(Geom_ToroidalSurface)::DownCast(S); + gp_Ax3 ax3 = TS->Position(); + Handle(Geom_Circle) C = new Geom_Circle(ax3.Ax2(),TS->MajorRadius()); + BRep_Builder B; + TopoDS_Edge E; + B.MakeEdge(E,C,1.e-7); + theModifiedShape = E; + theAddDist = TS->MinorRadius(); + return Standard_True; + } + + // non solid case or any periodic surface (Mantis 22454). + double U1,U2,V1,V2; + // changes for 0020677: EDF 1219 GEOM: MinDistance gives 0 instead of 20.88 + //S->Bounds(U1,U2,V1,V2); changed by + ShapeAnalysis::GetFaceUVBounds(TopoDS::Face(theModifiedShape),U1,U2,V1,V2); + // end of changes for 020677 (dmv) + Handle(Geom_RectangularTrimmedSurface) TrS1 = + new Geom_RectangularTrimmedSurface(S,U1,(U1+U2)/2.,V1,V2); + Handle(Geom_RectangularTrimmedSurface) TrS2 = + new Geom_RectangularTrimmedSurface(S,(U1+U2)/2.,U2,V1,V2); + TopoDS_Shape aMShape; + + TopoDS_Face F1 = BRepBuilderAPI_MakeFace(TrS1, Precision::Confusion()); + TopoDS_Face F2 = BRepBuilderAPI_MakeFace(TrS2, Precision::Confusion()); + + if (isShell) { + BRep_Builder B; + B.MakeCompound(TopoDS::Compound(aMShape)); + B.Add(aMShape, F1); + B.Add(aMShape, F2); + } else { + // The original shape is a solid. + BRepBuilderAPI_Sewing aSewing (Precision::Confusion()*10.0); + aSewing.Add(F1); + aSewing.Add(F2); + aSewing.Perform(); + aMShape = aSewing.SewedShape(); + BRep_Builder B; + TopoDS_Solid aSolid; + B.MakeSolid(aSolid); + B.Add(aSolid, aMShape); + aMShape = aSolid; + } + + Handle(ShapeFix_Shape) sfs = new ShapeFix_Shape; + sfs->Init(aMShape); + sfs->SetPrecision(1.e-6); + sfs->SetMaxTolerance(1.0); + sfs->Perform(); + theModifiedShape = sfs->Shape(); + return Standard_True; + } + } + + theModifiedShape = theShape; + return Standard_False; + } + +//======================================================================= +// function : GetMinDistanceSingular +//======================================================================= +double GetMinDistanceSingular(const TopoDS_Shape& aSh1, + const TopoDS_Shape& aSh2, + gp_Pnt& Ptmp1, gp_Pnt& Ptmp2) +{ + TopoDS_Shape tmpSh1; + TopoDS_Shape tmpSh2; + Standard_Real AddDist1 = 0.; + Standard_Real AddDist2 = 0.; + Standard_Boolean IsChange1 = ModifyShape(aSh1, tmpSh1, AddDist1); + Standard_Boolean IsChange2 = ModifyShape(aSh2, tmpSh2, AddDist2); + + if( !IsChange1 && !IsChange2 ) + return -2.0; + + BRepExtrema_DistShapeShape dst(tmpSh1,tmpSh2); + if (dst.IsDone()) { + double MinDist = 1.e9; + gp_Pnt PMin1, PMin2, P1, P2; + for (int i = 1; i <= dst.NbSolution(); i++) { + P1 = dst.PointOnShape1(i); + P2 = dst.PointOnShape2(i); + Standard_Real Dist = P1.Distance(P2); + if (MinDist > Dist) { + MinDist = Dist; + PMin1 = P1; + PMin2 = P2; + } + } + if(MinDist<1.e-7) { + Ptmp1 = PMin1; + Ptmp2 = PMin2; + } + else { + gp_Dir aDir(gp_Vec(PMin1,PMin2)); + if( MinDist > (AddDist1+AddDist2) ) { + Ptmp1 = gp_Pnt(PMin1.X() + aDir.X()*AddDist1, + PMin1.Y() + aDir.Y()*AddDist1, + PMin1.Z() + aDir.Z()*AddDist1); + Ptmp2 = gp_Pnt(PMin2.X() - aDir.X()*AddDist2, + PMin2.Y() - aDir.Y()*AddDist2, + PMin2.Z() - aDir.Z()*AddDist2); + return (MinDist - AddDist1 - AddDist2); + } + else { + if( AddDist1 > 0 ) { + Ptmp1 = gp_Pnt(PMin1.X() + aDir.X()*AddDist1, + PMin1.Y() + aDir.Y()*AddDist1, + PMin1.Z() + aDir.Z()*AddDist1); + Ptmp2 = Ptmp1; + } + else { + Ptmp2 = gp_Pnt(PMin2.X() - aDir.X()*AddDist2, + PMin2.Y() - aDir.Y()*AddDist2, + PMin2.Z() - aDir.Z()*AddDist2); + Ptmp1 = Ptmp2; + } + } + } + double res = MinDist - AddDist1 - AddDist2; + if(res<0.) res = 0.0; + return res; + } + return -2.0; +} + +//======================================================================= +// function : GetMinDistance +//======================================================================= +Standard_Real GetMinDistance(const TopoDS_Shape& theShape1, + const TopoDS_Shape& theShape2, + gp_Pnt& thePnt1, gp_Pnt& thePnt2) +{ + Standard_Real aResult = 1.e9; + + // Issue 0020231: A min distance bug with torus and vertex. + // Make GetMinDistance() return zero if a sole VERTEX is inside any of SOLIDs + + // which of shapes consists of only one vertex? + TopExp_Explorer exp1(theShape1,TopAbs_VERTEX), exp2(theShape2,TopAbs_VERTEX); + TopoDS_Shape V1 = exp1.More() ? exp1.Current() : TopoDS_Shape(); + TopoDS_Shape V2 = exp2.More() ? exp2.Current() : TopoDS_Shape(); + exp1.Next(); exp2.Next(); + if ( exp1.More() ) V1.Nullify(); + if ( exp2.More() ) V2.Nullify(); + // vertex and container of solids + TopoDS_Shape V = V1.IsNull() ? V2 : V1; + TopoDS_Shape S = V1.IsNull() ? theShape1 : theShape2; + if ( !V.IsNull() ) { + // classify vertex against solids + gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( V ) ); + for ( exp1.Init( S, TopAbs_SOLID ); exp1.More(); exp1.Next() ) { + BRepClass3d_SolidClassifier classifier( exp1.Current(), p, 1e-6); + if ( classifier.State() == TopAbs_IN ) { + thePnt1 = p; + thePnt2 = p; + return 0.0; + } + } + } + + aResult = GetMinDistanceSingular(theShape1, theShape2, thePnt1, thePnt2); + + + BRepExtrema_DistShapeShape dst (theShape1, theShape2); + if (dst.IsDone()) { + gp_Pnt P1, P2; + + for (int i = 1; i <= dst.NbSolution(); i++) { + P1 = dst.PointOnShape1(i); + P2 = dst.PointOnShape2(i); + + Standard_Real Dist = P1.Distance(P2); + if (aResult < 0 || aResult > Dist) { + aResult = Dist; + thePnt1 = P1; + thePnt2 = P2; + } + } + } + + return aResult; +} + +//======================================================================= +// function : PreciseBoundingBox +//======================================================================= +Standard_Boolean PreciseBoundingBox + (const TopoDS_Shape &theShape, Bnd_Box &theBox) +{ + if (theBox.IsVoid()) BRepBndLib::Add( theShape, theBox ); + if (theBox.IsVoid()) return Standard_False; + + Standard_Real aBound[6]; + theBox.Get(aBound[0], aBound[2], aBound[4], aBound[1], aBound[3], aBound[5]); + + Standard_Integer i; + const gp_Pnt aMid(0.5*(aBound[1] + aBound[0]), // XMid + 0.5*(aBound[3] + aBound[2]), // YMid + 0.5*(aBound[5] + aBound[4])); // ZMid + const gp_XYZ aSize(aBound[1] - aBound[0], // DX + aBound[3] - aBound[2], // DY + aBound[5] - aBound[4]); // DZ + const gp_Pnt aPnt[6] = + { + gp_Pnt(aBound[0] - (aBound[1] - aBound[0]), aMid.Y(), aMid.Z()), // XMin + gp_Pnt(aBound[1] + (aBound[1] - aBound[0]), aMid.Y(), aMid.Z()), // XMax + gp_Pnt(aMid.X(), aBound[2] - (aBound[3] - aBound[2]), aMid.Z()), // YMin + gp_Pnt(aMid.X(), aBound[3] + (aBound[3] - aBound[2]), aMid.Z()), // YMax + gp_Pnt(aMid.X(), aMid.Y(), aBound[4] - (aBound[5] - aBound[4])), // ZMin + gp_Pnt(aMid.X(), aMid.Y(), aBound[5] + (aBound[5] - aBound[4])) // ZMax + }; + const gp_Dir aDir[3] = { gp::DX(), gp::DY(), gp::DZ() }; + const Standard_Real aPlnSize[3] = + { + 0.5*Max(aSize.Y(), aSize.Z()), // XMin, XMax planes + 0.5*Max(aSize.X(), aSize.Z()), // YMin, YMax planes + 0.5*Max(aSize.X(), aSize.Y()) // ZMin, ZMax planes + }; + gp_Pnt aPMin[2]; + + for (i = 0; i < 6; i++) { + const Standard_Integer iHalf = i/2; + const gp_Pln aPln(aPnt[i], aDir[iHalf]); + BRepBuilderAPI_MakeFace aMkFace(aPln, -aPlnSize[iHalf], aPlnSize[iHalf], + -aPlnSize[iHalf], aPlnSize[iHalf]); + + if (!aMkFace.IsDone()) { + return Standard_False; + } + + TopoDS_Shape aFace = aMkFace.Shape(); + + // Get minimal distance between planar face and shape. + Standard_Real aMinDist = GetMinDistance(aFace, theShape, aPMin[0], aPMin[1]); + + if (aMinDist < 0.) { + return Standard_False; + } + + aBound[i] = aPMin[1].Coord(iHalf + 1); + } + + // Update Bounding box with the new values. + theBox.SetVoid(); + theBox.Update(aBound[0], aBound[2], aBound[4], aBound[1], aBound[3], aBound[5]); + + return Standard_True; +} + +//================================================================================================= +bool GetBoundingBox(const std::shared_ptr& theShape, + const bool thePrecise, + Standard_Real& theXmin,Standard_Real& theXmax, + Standard_Real& theYmin,Standard_Real& theYmax, + Standard_Real& theZmin,Standard_Real& theZmax, + std::string& theError) +{ + #ifdef _DEBUG + std::cout << "GetBoundingBox " << std::endl; + #endif + + if (!theShape.get()) { + theError = "GetBoundingBox : An invalid argument"; + return false; + } + + TopoDS_Shape aShape = theShape->impl(); + + //Compute the parameters + Bnd_Box B; + try { + OCC_CATCH_SIGNALS; + BRepBuilderAPI_Copy aCopyTool (aShape); + if (!aCopyTool.IsDone()) { + theError = "GetBoundingBox Error: Bad shape detected"; + return false; + } + + aShape = aCopyTool.Shape(); + + // remove triangulation to obtain more exact boundaries + BRepTools::Clean(aShape); + + BRepBndLib::Add(aShape, B); + + if (thePrecise) { + if (!PreciseBoundingBox(aShape, B)) { + theError = "GetBoundingBox Error: Bounding box cannot be precised"; + return false; + } + } + + B.Get(theXmin, theYmin, theZmin, theXmax, theYmax, theZmax); + } + catch (Standard_Failure& aFail) { + theError = aFail.GetMessageString(); + return false; + } + return true; +} + diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.h b/src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.h new file mode 100644 index 000000000..010c3a30b --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.h @@ -0,0 +1,45 @@ +// Copyright (C) 2014-2020 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_BoundingBox_H_ +#define GeomAlgoAPI_BoundingBox_H_ + +#include +#include +#include + +/// get the boundin box of theshape. + /// \param theShape the shape + /// \param thePrecise precise TRUE for precise computation; FALSE for fast one. + /// \param theXmin X min of the box + /// \param theXmax X max of the box + /// \param theYmin Y min of the box + /// \param theYmax Y max of the box + /// \param theZmin Z min of the box + /// \param theZmax Z max of the box + /// \param theError error +GEOMALGOAPI_EXPORT +bool GetBoundingBox(const std::shared_ptr& theShape, + const bool thePrecise, + Standard_Real& theXmin,Standard_Real& theXmax, + Standard_Real& theYmin,Standard_Real& theYmax, + Standard_Real& theZmin,Standard_Real& theZmax, + std::string& theError); + +#endif diff --git a/src/PythonAPI/model/features/__init__.py b/src/PythonAPI/model/features/__init__.py index 84b7b6562..81ba70cc0 100644 --- a/src/PythonAPI/model/features/__init__.py +++ b/src/PythonAPI/model/features/__init__.py @@ -30,7 +30,7 @@ from FeaturesAPI import addRecover from FeaturesAPI import addFillet, addChamfer from FeaturesAPI import addFusionFaces from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle -from FeaturesAPI import getPointCoordinates, getGeometryCalculation +from FeaturesAPI import getPointCoordinates, getGeometryCalculation, getBoundingBox from FeaturesAPI import addRemoveResults from FeaturesAPI import addCopy, addImportResult from FeaturesAPI import addDefeaturing -- 2.39.2