From: jfa Date: Sat, 31 Aug 2024 00:47:48 +0000 (+0100) Subject: [bos #40618] [CEA] Offset/Thickness Feature X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=refs%2Ftlpr%2F73%2Fhead;p=modules%2Fshaper.git [bos #40618] [CEA] Offset/Thickness Feature --- diff --git a/src/FeaturesAPI/CMakeLists.txt b/src/FeaturesAPI/CMakeLists.txt index 4652651c9..7c4224928 100644 --- a/src/FeaturesAPI/CMakeLists.txt +++ b/src/FeaturesAPI/CMakeLists.txt @@ -36,9 +36,11 @@ SET(PROJECT_HEADERS FeaturesAPI_NormalToFace.h FeaturesAPI_MultiRotation.h FeaturesAPI_MultiTranslation.h + FeaturesAPI_Offset.h FeaturesAPI_Partition.h FeaturesAPI_Pipe.h FeaturesAPI_Loft.h + FeaturesAPI_Thickness.h FeaturesAPI_Placement.h FeaturesAPI_PointCloudOnFace.h FeaturesAPI_Recover.h @@ -79,9 +81,11 @@ SET(PROJECT_SOURCES FeaturesAPI_NormalToFace.cpp FeaturesAPI_MultiRotation.cpp FeaturesAPI_MultiTranslation.cpp + FeaturesAPI_Offset.cpp FeaturesAPI_Partition.cpp FeaturesAPI_Pipe.cpp FeaturesAPI_Loft.cpp + FeaturesAPI_Thickness.cpp FeaturesAPI_Placement.cpp FeaturesAPI_PointCloudOnFace.cpp FeaturesAPI_Recover.cpp diff --git a/src/FeaturesAPI/FeaturesAPI.i b/src/FeaturesAPI/FeaturesAPI.i index d482e844e..9e0c4e8f2 100644 --- a/src/FeaturesAPI/FeaturesAPI.i +++ b/src/FeaturesAPI/FeaturesAPI.i @@ -85,6 +85,7 @@ %shared_ptr(FeaturesAPI_MultiRotation) %shared_ptr(FeaturesAPI_MultiTranslation) %shared_ptr(FeaturesAPI_NormalToFace) +%shared_ptr(FeaturesAPI_Offset) %shared_ptr(FeaturesAPI_Partition) %shared_ptr(FeaturesAPI_Pipe) %shared_ptr(FeaturesAPI_Placement) @@ -101,6 +102,7 @@ %shared_ptr(FeaturesAPI_Sewing) %shared_ptr(FeaturesAPI_SharedFaces) %shared_ptr(FeaturesAPI_Symmetry) +%shared_ptr(FeaturesAPI_Thickness) %shared_ptr(FeaturesAPI_Translation) %shared_ptr(FeaturesAPI_Union) @@ -233,6 +235,7 @@ %include "FeaturesAPI_NormalToFace.h" %include "FeaturesAPI_MultiRotation.h" %include "FeaturesAPI_MultiTranslation.h" +%include "FeaturesAPI_Offset.h" %include "FeaturesAPI_Partition.h" %include "FeaturesAPI_Pipe.h" %include "FeaturesAPI_Placement.h" @@ -248,5 +251,6 @@ %include "FeaturesAPI_Sewing.h" %include "FeaturesAPI_SharedFaces.h" %include "FeaturesAPI_Symmetry.h" +%include "FeaturesAPI_Thickness.h" %include "FeaturesAPI_Translation.h" %include "FeaturesAPI_Union.h" diff --git a/src/FeaturesAPI/FeaturesAPI_Offset.cpp b/src/FeaturesAPI/FeaturesAPI_Offset.cpp new file mode 100644 index 000000000..2d7446ae3 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Offset.cpp @@ -0,0 +1,121 @@ +// Copyright (C) 2017-2024 CEA, EDF +// +// 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_Offset.h" + +#include +#include +#include +#include + +//================================================================================================== + +FeaturesAPI_Offset::FeaturesAPI_Offset(const std::shared_ptr& theFeature) + : ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +FeaturesAPI_Offset::FeaturesAPI_Offset(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const bool isPipeJoint) + : FeaturesAPI_Offset(theFeature) +{ + if (initialize()) { + fillAttribute(FeaturesPlugin_Offset::CREATION_METHOD_EQUAL(), mycreationMethod); + fillAttribute(theBaseObject, mybaseObject); + fillAttribute(theOffset, myoffsetValue); + fillAttribute(isPipeJoint, myisPipeJoint); + + execute(); + } +} + +FeaturesAPI_Offset::FeaturesAPI_Offset(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const std::list& theFaces) + : FeaturesAPI_Offset(theFeature) +{ + if (initialize()) { + fillAttribute(FeaturesPlugin_Offset::CREATION_METHOD_PARTIAL(), mycreationMethod); + fillAttribute(theBaseObject, mybaseObject); + fillAttribute(theOffset, myoffsetValue); + fillAttribute(theFaces, myfaces); + + execute(); + } +} + +FeaturesAPI_Offset::~FeaturesAPI_Offset() +{ +} + +void FeaturesAPI_Offset::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + AttributeSelectionPtr anAttrShape = + aBase->selection(FeaturesPlugin_Offset::BASE_SHAPE_ID()); + + AttributeDoublePtr anAttrOffset = aBase->real(FeaturesPlugin_Offset::OFFSET_VALUE_ID()); + + std::string aCreationMethod = + aBase->string(FeaturesPlugin_Offset::CREATION_METHOD_ID())->value(); + + if (aCreationMethod == FeaturesPlugin_Offset::CREATION_METHOD_EQUAL()) { + AttributeBooleanPtr anAttrIsPipe = aBase->boolean(FeaturesPlugin_Offset::PIPE_JOINT_ID()); + + theDumper << aBase << " = model.addOffset(" << aDocName << ", " << anAttrShape; + theDumper << ", " << anAttrOffset << ", " << anAttrIsPipe << ")" << std::endl; + } + else if (aCreationMethod == FeaturesPlugin_Offset::CREATION_METHOD_PARTIAL()) { + AttributeSelectionListPtr anAttrFaces = + aBase->selectionList(FeaturesPlugin_Offset::FACES_ID()); + + theDumper << aBase << " = model.addOffsetPartial(" << aDocName << ", " << anAttrShape; + theDumper << ", " << anAttrOffset << ", " << anAttrFaces << ")" << std::endl; + } +} + +//================================================================================================== + +OffsetPtr addOffset(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const bool isPipeJoint) +{ + FeaturePtr aFeature = thePart->addFeature(FeaturesAPI_Offset::ID()); + + OffsetPtr aOffset (new FeaturesAPI_Offset(aFeature, theBaseObject, theOffset, isPipeJoint)); + return aOffset; +} + +OffsetPtr addOffsetPartial(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const std::list& theFaces) +{ + FeaturePtr aFeature = thePart->addFeature(FeaturesAPI_Offset::ID()); + + OffsetPtr aOffset (new FeaturesAPI_Offset(aFeature, theBaseObject, theOffset, theFaces)); + return aOffset; +} diff --git a/src/FeaturesAPI/FeaturesAPI_Offset.h b/src/FeaturesAPI/FeaturesAPI_Offset.h new file mode 100644 index 000000000..64727089e --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Offset.h @@ -0,0 +1,102 @@ +// Copyright (C) 2017-2024 CEA, EDF +// +// 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_Offset_H_ +#define FeaturesAPI_Offset_H_ + +#include "FeaturesAPI.h" + +#include + +#include +#include +#include + +class ModelHighAPI_Selection; + +/// \class FeaturesAPI_Offset +/// \ingroup CPPHighAPI +/// \brief Interface for Offset feature. +class FeaturesAPI_Offset: public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Offset(const std::shared_ptr& theFeature); + + /// Constructor with values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Offset(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const bool isPipeJoint); + + /// Constructor with values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Offset(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const std::list& theFaces); + + /// Destructor. + FEATURESAPI_EXPORT + virtual ~FeaturesAPI_Offset(); + + INTERFACE_5(FeaturesPlugin_Offset::ID(), + + creationMethod, FeaturesPlugin_Offset::CREATION_METHOD_ID(), + ModelAPI_AttributeString, /** Creation method */, + + baseObject, FeaturesPlugin_Offset::BASE_SHAPE_ID(), + ModelAPI_AttributeSelection, /** Base object */, + + offsetValue, FeaturesPlugin_Offset::OFFSET_VALUE_ID(), + ModelAPI_AttributeDouble, /** Value of the offset */, + + isPipeJoint, FeaturesPlugin_Offset::PIPE_JOINT_ID(), + ModelAPI_AttributeBoolean, /** Is pipe or intersection joint */, + + faces, FeaturesPlugin_Offset::FACES_ID(), + ModelAPI_AttributeSelectionList, /** List of faces for partial offset */) + + /// Dump wrapped feature + FEATURESAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; +}; + +/// Pointer on the offset object. +typedef std::shared_ptr OffsetPtr; + +/// \ingroup CPPHighAPI +/// \brief Create Offset feature. +FEATURESAPI_EXPORT +OffsetPtr addOffset(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const bool isPipeJoint = true); + +/// \ingroup CPPHighAPI +/// \brief Create Offset feature. +FEATURESAPI_EXPORT +OffsetPtr addOffsetPartial(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theOffset, + const std::list& theFaces); + +#endif // FeaturesAPI_Offset_H_ diff --git a/src/FeaturesAPI/FeaturesAPI_Thickness.cpp b/src/FeaturesAPI/FeaturesAPI_Thickness.cpp new file mode 100644 index 000000000..0a0a6c5f3 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Thickness.cpp @@ -0,0 +1,129 @@ +// Copyright (C) 2017-2024 CEA, EDF +// +// 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_Thickness.h" + +#include +#include +#include +#include + +//================================================================================================== + +FeaturesAPI_Thickness::FeaturesAPI_Thickness(const std::shared_ptr& theFeature) + : ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +FeaturesAPI_Thickness::FeaturesAPI_Thickness(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const bool isInside) + : FeaturesAPI_Thickness(theFeature) +{ + if (initialize()) { + fillAttribute(FeaturesPlugin_Thickness::CREATION_METHOD_THICK(), mycreationMethod); + fillAttribute(theBaseObject, mybaseObject); + fillAttribute(theThickness, mythicknessValue); + fillAttribute(isInside, myisInside); + + execute(); + } +} + +FeaturesAPI_Thickness::FeaturesAPI_Thickness(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const std::list& theFaces, + const bool isInside) + : FeaturesAPI_Thickness(theFeature) +{ + if (initialize()) { + fillAttribute(FeaturesPlugin_Thickness::CREATION_METHOD_HOLLOWED(), mycreationMethod); + fillAttribute(theBaseObject, mybaseObject); + fillAttribute(theThickness, mythicknessValue); + fillAttribute(theFaces, myfaces); + fillAttribute(isInside, myisInside); + + execute(); + } +} + +FeaturesAPI_Thickness::~FeaturesAPI_Thickness() +{ +} + +void FeaturesAPI_Thickness::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + AttributeSelectionPtr anAttrShape = + aBase->selection(FeaturesPlugin_Thickness::BASE_SHAPE_ID()); + + AttributeDoublePtr anAttrThickness = + aBase->real(FeaturesPlugin_Thickness::THICKNESS_VALUE_ID()); + + AttributeBooleanPtr anAttrIsInside = + aBase->boolean(FeaturesPlugin_Thickness::INSIDE_ID()); + + std::string aCreationMethod = + aBase->string(FeaturesPlugin_Thickness::CREATION_METHOD_ID())->value(); + + if (aCreationMethod == FeaturesPlugin_Thickness::CREATION_METHOD_THICK()) { + theDumper << aBase << " = model.addThickness(" << aDocName; + theDumper << ", " << anAttrShape << ", " << anAttrThickness; + theDumper << ", " << anAttrIsInside << ")" << std::endl; + } + else if (aCreationMethod == FeaturesPlugin_Thickness::CREATION_METHOD_HOLLOWED()) { + AttributeSelectionListPtr anAttrFaces = + aBase->selectionList(FeaturesPlugin_Thickness::FACES_ID()); + + theDumper << aBase << " = model.addHollowedSolid(" << aDocName; + theDumper << ", " << anAttrShape << ", " << anAttrThickness; + theDumper << ", " << anAttrFaces; + theDumper << ", " << anAttrIsInside << ")" << std::endl; + } +} + +//================================================================================================== + +ThicknessPtr addThickness(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const bool isInside) +{ + FeaturePtr aFeature = thePart->addFeature(FeaturesAPI_Thickness::ID()); + + ThicknessPtr aThickness (new FeaturesAPI_Thickness(aFeature, theBaseObject, theThickness, isInside)); + return aThickness; +} + +ThicknessPtr addHollowedSolid(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const std::list& theFaces, + const bool isInside) +{ + FeaturePtr aFeature = thePart->addFeature(FeaturesAPI_Thickness::ID()); + + ThicknessPtr aThickness (new FeaturesAPI_Thickness(aFeature, theBaseObject, theThickness, theFaces, isInside)); + return aThickness; +} diff --git a/src/FeaturesAPI/FeaturesAPI_Thickness.h b/src/FeaturesAPI/FeaturesAPI_Thickness.h new file mode 100644 index 000000000..386413190 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Thickness.h @@ -0,0 +1,104 @@ +// Copyright (C) 2017-2024 CEA, EDF +// +// 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_Thickness_H_ +#define FeaturesAPI_Thickness_H_ + +#include "FeaturesAPI.h" + +#include + +#include +#include +#include + +class ModelHighAPI_Selection; + +/// \class FeaturesAPI_Thickness +/// \ingroup CPPHighAPI +/// \brief Interface for Thickness feature. +class FeaturesAPI_Thickness: public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Thickness(const std::shared_ptr& theFeature); + + /// Constructor with values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Thickness(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const bool isInside); + + /// Constructor with values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Thickness(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const std::list& theFaces, + const bool isInside); + + /// Destructor. + FEATURESAPI_EXPORT + virtual ~FeaturesAPI_Thickness(); + + INTERFACE_5(FeaturesPlugin_Thickness::ID(), + + creationMethod, FeaturesPlugin_Thickness::CREATION_METHOD_ID(), + ModelAPI_AttributeString, /** Creation method */, + + baseObject, FeaturesPlugin_Thickness::BASE_SHAPE_ID(), + ModelAPI_AttributeSelection, /** Base object */, + + thicknessValue, FeaturesPlugin_Thickness::THICKNESS_VALUE_ID(), + ModelAPI_AttributeDouble, /** Value of the thickness */, + + faces, FeaturesPlugin_Thickness::FACES_ID(), + ModelAPI_AttributeSelectionList, /** List of faces to remove in hollowed solid mode */, + + isInside, FeaturesPlugin_Thickness::INSIDE_ID(), + ModelAPI_AttributeBoolean, /** Do thicken towards inside */) + + /// Dump wrapped feature + FEATURESAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; +}; + +/// Pointer on the thickness object. +typedef std::shared_ptr ThicknessPtr; + +/// \ingroup CPPHighAPI +/// \brief Create Thickness feature. +FEATURESAPI_EXPORT +ThicknessPtr addThickness(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const bool isInside = true); + +/// \ingroup CPPHighAPI +/// \brief Create Thickness feature. +FEATURESAPI_EXPORT +ThicknessPtr addHollowedSolid(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theBaseObject, + const ModelHighAPI_Double& theThickness, + const std::list& theFaces, + const bool isInside = true); + +#endif // FeaturesAPI_Thickness_H_ diff --git a/src/FeaturesAPI/FeaturesAPI_swig.h b/src/FeaturesAPI/FeaturesAPI_swig.h index 4dbce1c75..1620196f8 100644 --- a/src/FeaturesAPI/FeaturesAPI_swig.h +++ b/src/FeaturesAPI/FeaturesAPI_swig.h @@ -38,9 +38,11 @@ #include "FeaturesAPI_NormalToFace.h" #include "FeaturesAPI_MultiRotation.h" #include "FeaturesAPI_MultiTranslation.h" + #include "FeaturesAPI_Offset.h" #include "FeaturesAPI_Partition.h" #include "FeaturesAPI_Pipe.h" #include "FeaturesAPI_Loft.h" + #include "FeaturesAPI_Thickness.h" #include "FeaturesAPI_Placement.h" #include "FeaturesAPI_PointCloudOnFace.h" #include "FeaturesAPI_Recover.h" diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index 4946d5434..01cfeea11 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -40,6 +40,7 @@ SET(PROJECT_HEADERS FeaturesPlugin_Partition.h FeaturesPlugin_Pipe.h FeaturesPlugin_Loft.h + FeaturesPlugin_Thickness.h FeaturesPlugin_Placement.h FeaturesPlugin_PointCloudOnFace.h FeaturesPlugin_CompositeBoolean.h @@ -53,6 +54,7 @@ SET(PROJECT_HEADERS FeaturesPlugin_Union.h FeaturesPlugin_ValidatorTransform.h FeaturesPlugin_Validators.h + FeaturesPlugin_Offset.h FeaturesPlugin_RemoveSubShapes.h FeaturesPlugin_Tools.h FeaturesPlugin_Symmetry.h @@ -102,6 +104,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_Partition.cpp FeaturesPlugin_Pipe.cpp FeaturesPlugin_Loft.cpp + FeaturesPlugin_Thickness.cpp FeaturesPlugin_Placement.cpp FeaturesPlugin_PointCloudOnFace.cpp FeaturesPlugin_CompositeBoolean.cpp @@ -115,6 +118,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_Union.cpp FeaturesPlugin_ValidatorTransform.cpp FeaturesPlugin_Validators.cpp + FeaturesPlugin_Offset.cpp FeaturesPlugin_RemoveSubShapes.cpp FeaturesPlugin_Tools.cpp FeaturesPlugin_Symmetry.cpp @@ -167,6 +171,8 @@ SET(XML_RESOURCES intersection_widget.xml pipe_widget.xml loft_widget.xml + thickness_widget.xml + offset_widget.xml remove_subshapes_widget.xml union_widget.xml symmetry_widget.xml diff --git a/src/FeaturesPlugin/FeaturesPlugin_Offset.cpp b/src/FeaturesPlugin/FeaturesPlugin_Offset.cpp new file mode 100644 index 000000000..f46b82a8c --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Offset.cpp @@ -0,0 +1,120 @@ +// Copyright (C) 2014-2024 CEA, EDF +// +// 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_Offset.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//================================================================================================== +FeaturesPlugin_Offset::FeaturesPlugin_Offset() +{ +} + +//================================================================================================== +void FeaturesPlugin_Offset::initAttributes() +{ + data()->addAttribute(CREATION_METHOD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(BASE_SHAPE_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(OFFSET_VALUE_ID(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(PIPE_JOINT_ID(), ModelAPI_AttributeBoolean::typeId()); + data()->addAttribute(FACES_ID(), ModelAPI_AttributeSelectionList::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PIPE_JOINT_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), FACES_ID()); +} + +//================================================================================================== +void FeaturesPlugin_Offset::execute() +{ + // Get base shape and sub-shapes list. + AttributeSelectionPtr aShapeAttrSelection = selection(BASE_SHAPE_ID()); + if (!aShapeAttrSelection.get()) { + return; + } + + // Get base shape. + GeomShapePtr aBaseShape = aShapeAttrSelection->value(); + if (!aBaseShape.get()) { + return; + } + + // Get offset value. + double anOffset = real(OFFSET_VALUE_ID())->value(); + + // Getting creation method. + std::string aCreationMethod = string(CREATION_METHOD_ID())->value(); + + // Perform total or partial offset + std::shared_ptr anOffsetAlgo; + + if (aCreationMethod == CREATION_METHOD_EQUAL()) { + // total offset can have pipe or intersection joints + bool isPipeJoint = boolean(PIPE_JOINT_ID())->value(); + + anOffsetAlgo = std::shared_ptr + (new GeomAlgoAPI_Offset (aBaseShape, anOffset, isPipeJoint)); + } + else { + // partial offset has faces argument + AttributeSelectionListPtr aFacesAttrList = selectionList(FACES_ID()); + if (!aShapeAttrSelection.get() || !aFacesAttrList.get()) { + return; + } + + ListOfShape aFaces; + for (int anIndex = 0; anIndex < aFacesAttrList->size(); ++anIndex) { + AttributeSelectionPtr aFaceAttrInList = aFacesAttrList->value(anIndex); + GeomShapePtr aFace = aFaceAttrInList->value(); + if (!aFace.get()) { + return; + } + aFaces.push_back(aFace); + } + + anOffsetAlgo = std::shared_ptr + (new GeomAlgoAPI_Offset (aBaseShape, aFaces, anOffset)); + } + + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(anOffsetAlgo, getKind(), anError)) { + setError(anError); + return; + } + + GeomShapePtr aResult = anOffsetAlgo->shape(); + + // Store result. + int anIndex = 0; + ResultBodyPtr aResultBody = document()->createBody(data(), anIndex); + aResultBody->storeModified(aBaseShape, aResult); + aResultBody->loadModifiedShapes(anOffsetAlgo, aBaseShape, GeomAPI_Shape::FACE); + aResultBody->loadGeneratedShapes(anOffsetAlgo, aBaseShape, GeomAPI_Shape::FACE); + setResult(aResultBody, anIndex); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Offset.h b/src/FeaturesPlugin/FeaturesPlugin_Offset.h new file mode 100644 index 000000000..e320efe23 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Offset.h @@ -0,0 +1,107 @@ +// Copyright (C) 2014-2024 CEA, EDF +// +// 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_Offset_H_ +#define FeaturesPlugin_Offset_H_ + +#include "FeaturesPlugin.h" + +#include + +/// \class FeaturesPlugin_Offset +/// \ingroup Plugins +/// \brief Feature for offset. +class FeaturesPlugin_Offset: public ModelAPI_Feature +{ +public: + /// Use plugin manager for features creation + FeaturesPlugin_Offset(); + + /// Feature kind. + inline static const std::string& ID() + { + static const std::string MY_ID("Offset3d"); + return MY_ID; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_ID() + { + static const std::string MY_CREATION_METHOD_ID("creation_method"); + return MY_CREATION_METHOD_ID; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_EQUAL() + { + static const std::string MY_CREATION_METHOD_EQUAL("offset_equal"); + return MY_CREATION_METHOD_EQUAL; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_PARTIAL() + { + static const std::string MY_CREATION_METHOD_PARTIAL("offset_partial"); + return MY_CREATION_METHOD_PARTIAL; + } + + /// Attribute name of base shape. + inline static const std::string& BASE_SHAPE_ID() + { + static const std::string MY_BASE_SHAPE_ID("base_shape"); + return MY_BASE_SHAPE_ID; + } + + /// Attribute name of offset value. + inline static const std::string& OFFSET_VALUE_ID() + { + static const std::string MY_OFFSET_VALUE_ID("offset_value"); + return MY_OFFSET_VALUE_ID; + } + + /// Attribute name of pipe/intersection joint bool flag. + inline static const std::string& PIPE_JOINT_ID() + { + static const std::string MY_PIPE_JOINT_ID("pipe_joint"); + return MY_PIPE_JOINT_ID; + } + + /// Attribute name of sub-faces to offset (for partial offset). + inline static const std::string& FACES_ID() + { + static const std::string MY_FACES_ID("faces_to_offset"); + return MY_FACES_ID; + } + + + /// \return the kind of a feature. + FEATURESPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = FeaturesPlugin_Offset::ID(); + return MY_KIND; + } + + /// Request for initialization of data model of the feature: adding all attributes. + FEATURESPLUGIN_EXPORT virtual void initAttributes(); + + /// Performs the algorithm and stores results it in the data structure. + FEATURESPLUGIN_EXPORT virtual void execute(); +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index 53669c8f2..4d0c56a74 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include #include @@ -101,6 +103,10 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin() new FeaturesPlugin_ValidatorExtrusionBoundaryFace); aFactory->registerValidator("FeaturesPlugin_ValidatorBooleanSelection", new FeaturesPlugin_ValidatorBooleanSelection); + aFactory->registerValidator("FeaturesPlugin_ValidatorOffsetFacesSelection", + new FeaturesPlugin_ValidatorOffsetFacesSelection); + aFactory->registerValidator("FeaturesPlugin_ValidatorThicknessSelection", + new FeaturesPlugin_ValidatorThicknessSelection); aFactory->registerValidator("FeaturesPlugin_ValidatorPartitionSelection", new FeaturesPlugin_ValidatorPartitionSelection); aFactory->registerValidator("FeaturesPlugin_ValidatorRemoveSubShapesSelection", @@ -176,6 +182,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new FeaturesPlugin_Pipe); } else if (theFeatureID == FeaturesPlugin_Loft::ID()) { return FeaturePtr(new FeaturesPlugin_Loft); + } else if (theFeatureID == FeaturesPlugin_Thickness::ID()) { + return FeaturePtr(new FeaturesPlugin_Thickness); } else if (theFeatureID == FeaturesPlugin_Placement::ID()) { return FeaturePtr(new FeaturesPlugin_Placement); } else if (theFeatureID == FeaturesPlugin_Recover::ID()) { @@ -198,6 +206,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new FeaturesPlugin_Symmetry); } else if (theFeatureID == FeaturesPlugin_Scale::ID()) { return FeaturePtr(new FeaturesPlugin_Scale); + } else if (theFeatureID == FeaturesPlugin_Offset::ID()) { + return FeaturePtr(new FeaturesPlugin_Offset); } else if (theFeatureID == FeaturesPlugin_Sewing::ID()) { return FeaturePtr(new FeaturesPlugin_Sewing); } else if (theFeatureID == FeaturesPlugin_MultiTranslation::ID()) { diff --git a/src/FeaturesPlugin/FeaturesPlugin_Thickness.cpp b/src/FeaturesPlugin/FeaturesPlugin_Thickness.cpp new file mode 100644 index 000000000..db3970280 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Thickness.cpp @@ -0,0 +1,140 @@ +// Copyright (C) 2014-2024 CEA, EDF +// +// 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_Thickness.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//================================================================================================== +FeaturesPlugin_Thickness::FeaturesPlugin_Thickness() +{ +} + +//================================================================================================== +void FeaturesPlugin_Thickness::initAttributes() +{ + data()->addAttribute(CREATION_METHOD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(BASE_SHAPE_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(FACES_ID(), ModelAPI_AttributeSelectionList::typeId()); + data()->addAttribute(THICKNESS_VALUE_ID(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(INSIDE_ID(), ModelAPI_AttributeBoolean::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), FACES_ID()); +} + +//================================================================================================== +void FeaturesPlugin_Thickness::execute() +{ + // Getting creation method. + std::string aCreationMethod = string(CREATION_METHOD_ID())->value(); + + // Get base shape + AttributeSelectionPtr aShapeAttrSelection = selection(BASE_SHAPE_ID()); + if (!aShapeAttrSelection.get()) { + return; + } + GeomShapePtr aBaseShape = aShapeAttrSelection->value(); + if (!aBaseShape.get()) { + return; + } + + // Get thickness value. + double aThickness = real(THICKNESS_VALUE_ID())->value(); + + // Get thickening direction + bool isInside = boolean(INSIDE_ID())->value(); + + // Perform thickness + std::shared_ptr aThicknessAlgo; + + if (aCreationMethod == CREATION_METHOD_THICK()) { + aThicknessAlgo = std::shared_ptr + (new GeomAlgoAPI_Thickness (aBaseShape, aThickness, isInside)); + } + else { + // hollowed solid algorithm has faces argument + AttributeSelectionListPtr aFacesAttrList = selectionList(FACES_ID()); + if (!aShapeAttrSelection.get() || !aFacesAttrList.get()) { + return; + } + + ListOfShape aFaces; + for (int anIndex = 0; anIndex < aFacesAttrList->size(); ++anIndex) { + AttributeSelectionPtr aFaceAttrInList = aFacesAttrList->value(anIndex); + GeomShapePtr aFace = aFaceAttrInList->value(); + if (!aFace.get()) { + return; + } + aFaces.push_back(aFace); + } + + aThicknessAlgo = std::shared_ptr + (new GeomAlgoAPI_Thickness (aBaseShape, aFaces, aThickness, isInside)); + } + + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aThicknessAlgo, getKind(), anError)) { + setError(anError); + return; + } + + GeomShapePtr aResult = aThicknessAlgo->shape(); + + // Store result. + int anIndex = 0; + ResultBodyPtr aResultBody = document()->createBody(data(), anIndex); + if (aCreationMethod == CREATION_METHOD_THICK()) { + aResultBody->storeGenerated(aBaseShape, aResult); + } + else { + aResultBody->storeModified(aBaseShape, aResult); + } + aResultBody->loadModifiedShapes(aThicknessAlgo, aBaseShape, GeomAPI_Shape::FACE); + aResultBody->loadGeneratedShapes(aThicknessAlgo, aBaseShape, GeomAPI_Shape::FACE); + aResultBody->loadGeneratedShapes(aThicknessAlgo, aBaseShape, GeomAPI_Shape::EDGE); + aResultBody->loadGeneratedShapes(aThicknessAlgo, aBaseShape, GeomAPI_Shape::VERTEX); + setResult(aResultBody, anIndex); +} + +//================================================================================================== +void FeaturesPlugin_Thickness::attributeChanged(const std::string& theID) +{ + if (theID == CREATION_METHOD_ID()) { + // clean base shape, as it has different type in different modes, + // so, it cannot be the same + AttributeSelectionPtr aBaseAttr = selection(BASE_SHAPE_ID()); + AttributeSelectionListPtr aFaceAttr = selectionList(FACES_ID()); + data()->blockSendAttributeUpdated(true, false); + aBaseAttr->reset(); + aFaceAttr->clear(); // reset doesn't work for a list + data()->blockSendAttributeUpdated(false, false); + } + ModelAPI_Feature::attributeChanged(theID); +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Thickness.h b/src/FeaturesPlugin/FeaturesPlugin_Thickness.h new file mode 100644 index 000000000..a070bb1db --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Thickness.h @@ -0,0 +1,111 @@ +// Copyright (C) 2014-2024 CEA, EDF +// +// 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_Thickness_H_ +#define FeaturesPlugin_Thickness_H_ + +#include "FeaturesPlugin.h" + +#include + +/// \class FeaturesPlugin_Thickness +/// \ingroup Plugins +/// \brief Feature for thickness. +class FeaturesPlugin_Thickness: public ModelAPI_Feature +{ +public: + /// Use plugin manager for features creation + FeaturesPlugin_Thickness(); + + /// Feature kind. + inline static const std::string& ID() + { + static const std::string MY_ID("Thickness"); + return MY_ID; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_ID() + { + static const std::string MY_CREATION_METHOD_ID("creation_method"); + return MY_CREATION_METHOD_ID; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_THICK() + { + static const std::string MY_CREATION_METHOD_THICK("thickness"); + return MY_CREATION_METHOD_THICK; + } + + /// Attribute name for creation method. + inline static const std::string& CREATION_METHOD_HOLLOWED() + { + static const std::string MY_CREATION_METHOD_HOLLOWED("hollowed_solid"); + return MY_CREATION_METHOD_HOLLOWED; + } + + /// Attribute name of base shape. + inline static const std::string& BASE_SHAPE_ID() + { + static const std::string MY_BASE_SHAPE_ID("base_shape"); + return MY_BASE_SHAPE_ID; + } + + /// Attribute name of sub-faces to remove (for hollowed solid mode). + inline static const std::string& FACES_ID() + { + static const std::string MY_FACES_ID("faces_to_remove"); + return MY_FACES_ID; + } + + /// Attribute name of thickness value. + inline static const std::string& THICKNESS_VALUE_ID() + { + static const std::string MY_THICKNESS_VALUE_ID("thickness_value"); + return MY_THICKNESS_VALUE_ID; + } + + /// Attribute name of pipe/intersection joint bool flag. + inline static const std::string& INSIDE_ID() + { + static const std::string MY_INSIDE_ID("is_inside"); + return MY_INSIDE_ID; + } + + + /// \return the kind of a feature. + FEATURESPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = FeaturesPlugin_Thickness::ID(); + return MY_KIND; + } + + /// Request for initialization of data model of the feature: adding all attributes. + FEATURESPLUGIN_EXPORT virtual void initAttributes(); + + /// Performs the algorithm and stores results it in the data structure. + FEATURESPLUGIN_EXPORT virtual void execute(); + + /// Called on change of any argument-attribute of this object. + /// \param[in] theID identifier of changed attribute. + FEATURESPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp index 3b9784fc1..56054e8e0 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include #include @@ -1115,6 +1116,160 @@ bool FeaturesPlugin_ValidatorFillet1DSelection::isValid(const AttributePtr& theA return true; } +//============================================================================================= +bool FeaturesPlugin_ValidatorOffsetFacesSelection::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + AttributeSelectionListPtr aSubShapesAttrList = + std::dynamic_pointer_cast(theAttribute); + if (!aSubShapesAttrList.get()) { + theError = "Error: This validator can only work with selection list in \"Offset\" feature."; + return false; + } + + static const std::string aBaseShapeID = "base_shape"; + FeaturePtr aFeature = std::dynamic_pointer_cast(theAttribute->owner()); + AttributeSelectionPtr aShapeAttrSelection = aFeature->selection(aBaseShapeID); + + if (!aShapeAttrSelection.get()) { + theError = "Error: Could not get \"%1\" attribute."; + theError.arg(aBaseShapeID); + return false; + } + + GeomShapePtr aBaseShape = aShapeAttrSelection->value(); + ResultPtr aContext = aShapeAttrSelection->context(); + if (!aContext.get()) { + theError = "Error: Empty context."; + return false; + } + if (!aBaseShape.get()) { + aBaseShape = aContext->shape(); + } + if (!aBaseShape.get()) { + theError = "Error: Empty base shape."; + return false; + } + + if (!aSubShapesAttrList->size()) { + theError = "Error: No faces selected."; + return false; + } + + GeomAPI_IndexedMapOfShape aSubShapesMap (aBaseShape); + for (int anIndex = 0; anIndex < aSubShapesAttrList->size(); ++anIndex) { + AttributeSelectionPtr anAttrSelectionInList = aSubShapesAttrList->value(anIndex); + GeomShapePtr aShapeToAdd = anAttrSelectionInList->value(); + if (!aShapeToAdd.get()) { + theError = "Error: Wrong shape selected."; + return false; + } + if (!aSubShapesMap.FindIndex(aShapeToAdd)) { + theError = "Error: Only sub-shapes of selected shape are allowed for selection."; + return false; + } + } + + return true; +} + +//============================================================================================= +bool FeaturesPlugin_ValidatorThicknessSelection::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + static const std::string aBaseShapeID = "base_shape"; + static const std::string aCreationMethodID = "creation_method"; + static const std::string aCreationMethodTH = "thickness"; + + FeaturePtr aFeature = std::dynamic_pointer_cast(theAttribute->owner()); + + AttributeSelectionPtr aShapeAttrSelection = + std::dynamic_pointer_cast(theAttribute); + if (aShapeAttrSelection.get()) { + // 1. Check main objects type (depends on creation method) + GeomShapePtr aBaseShape = aShapeAttrSelection->value(); + ResultPtr aContext = aShapeAttrSelection->context(); + if (!aContext.get()) { + theError = "Error: Empty context."; + return false; + } + if (!aBaseShape.get()) { + aBaseShape = aContext->shape(); + } + if (!aBaseShape.get()) { + theError = "Error: Empty base shape."; + return false; + } + + AttributeStringPtr aCreationMethodAttr = aFeature->string(aCreationMethodID); + if (aCreationMethodAttr->value() == aCreationMethodTH) { + // should be a face or a shell + return aBaseShape->isShell() || aBaseShape->isFace(); + } + + // shoud be a solid + return aBaseShape->isSolid(); + } + + // 2. Check faces selection + AttributeSelectionListPtr aSubShapesAttrList = + std::dynamic_pointer_cast(theAttribute); + if (!aSubShapesAttrList.get()) { + theError = "Error: This validator can only work with selection in \"Thickness\" feature."; + return false; + } + + // base shape + aShapeAttrSelection = aFeature->selection(aBaseShapeID); + + if (!aShapeAttrSelection.get()) { + theError = "Error: Could not get \"%1\" attribute."; + theError.arg(aBaseShapeID); + return false; + } + + GeomShapePtr aBaseShape = aShapeAttrSelection->value(); + ResultPtr aContext = aShapeAttrSelection->context(); + if (!aContext.get()) { + theError = "Error: Empty context."; + return false; + } + if (!aBaseShape.get()) { + aBaseShape = aContext->shape(); + } + if (!aBaseShape.get()) { + theError = "Error: Empty base shape."; + return false; + } + + if (!aSubShapesAttrList->size()) { + theError = "Error: No faces selected."; + return false; + } + + GeomAPI_IndexedMapOfShape aSubShapesMap (aBaseShape); + for (int anIndex = 0; anIndex < aSubShapesAttrList->size(); ++anIndex) { + AttributeSelectionPtr anAttrSelectionInList = aSubShapesAttrList->value(anIndex); + GeomShapePtr aShapeToAdd = anAttrSelectionInList->value(); + if (!aShapeToAdd.get()) { + theError = "Error: Wrong shape selected."; + return false; + } + if (!aSubShapesMap.FindIndex(aShapeToAdd)) { + theError = "Error: Only sub-shapes of selected shape are allowed for selection."; + return false; + } + if (!aShapeToAdd->isFace()) { + theError = "Error: Only faces are allowed for selection."; + return false; + } + } + + return true; +} + //================================================================================================== bool FeaturesPlugin_ValidatorPartitionSelection::isValid(const AttributePtr& theAttribute, const std::list& theArguments, diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.h b/src/FeaturesPlugin/FeaturesPlugin_Validators.h index 7839f8ee7..a0c684a46 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.h @@ -219,6 +219,38 @@ public: Events_InfoMessage& theError) const; }; +/// \class FeaturesPlugin_ValidatorOffsetFacesSelection +/// \ingroup Validators +/// \brief Validates selection for "Offset" feature. +class FeaturesPlugin_ValidatorOffsetFacesSelection: public ModelAPI_AttributeValidator +{ +public: + /// \return True if the attribute is valid. It checks whether the selection + /// is acceptable for operation. + /// \param[in] theAttribute an attribute to check. + /// \param[in] theArguments a filter parameters. + /// \param[out] theError error message. + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + +/// \class FeaturesPlugin_ValidatorThicknessSelection +/// \ingroup Validators +/// \brief Validates selection for "Thickness" feature. +class FeaturesPlugin_ValidatorThicknessSelection: public ModelAPI_AttributeValidator +{ +public: + /// \return True if the attribute is valid. It checks whether the selection + /// is acceptable for operation. + /// \param[in] theAttribute an attribute to check. + /// \param[in] theArguments a filter parameters. + /// \param[out] theError error message. + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + /// \class FeaturesPlugin_ValidatorPartitionSelection /// \ingroup Validators /// \brief Validates selection for partition. diff --git a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts index a01ffc9bc..ebc521b62 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts +++ b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts @@ -61,6 +61,14 @@ Normal to a face Normale d'une face + + Offset + Décalage + + + Thickness + Épaisseur + Partition Partition @@ -1343,6 +1351,182 @@ + + + Offset3d + + Offset + Décalage + + + Perform offset + Effectuer des décalage + + + + Offset3d:base_shape + + Shape: + Forme: + + + Select a shape to offset. + Sélectionnez une forme à décaler. + + + Attribute "%1" is not initialized. + Sélectionnez un objet + + + + Offset3d:base_shape:GeomValidators_ShapeType + + The object is empty + L'objet est vide + + + + Offset3d:offset_value + + Offset + Décalage + + + + Offset3d:creation_method + + Offset + Décalage + + + Partial offset + Décalage partiel + + + + Offset3d:pipe_joint + + Join by pipes + Rejoindre par des tuyaux + + + + Offset3d:faces_to_offset + + Faces to offset: + Faces à décaler : + + + Select faces of the main shape. + Sélectionnez les faces de la forme principale. + + + + Offset3d:faces_to_offset:FeaturesPlugin_ValidatorOffsetFacesSelection + + Error: Empty context. + Erreur : contexte vide. + + + Error: No faces selected. + Erreur: Aucun face sélectionné. + + + + + + Thickness + + Thickness + Épaisseur + + + Make a thickness or a hollowed solid + Réaliser une épaisseur ou un solide creux + + + + Thickness:creation_method + + Thickness + Épaisseur + + + Hollowed Solid + Solide évidé + + + + Thickness:base_shape + + Face or Shell: + Face ou Coque: + + + Select a face or a shell. + Sélectionnez une face ou une coque. + + + Solid: + Solide: + + + Select a solid. + Sélectionnez un solide. + + + Attribute "%1" is not initialized. + Sélectionnez un objet + + + + Thickness:base_shape:FeaturesPlugin_ValidatorThicknessSelection + + Error: Empty context. + Erreur : contexte vide. + + + + Thickness:faces_to_remove + + Faces to remove: + Faces à supprimer: + + + Select faces of the main shape. + Sélectionnez les faces de la forme principale. + + + + Thickness:faces_to_remove:FeaturesPlugin_ValidatorThicknessSelection + + Error: Empty context. + Erreur : contexte vide. + + + Error: No faces selected. + Erreur: Aucun face sélectionné. + + + + Thickness:thickness_value + + Thickness + Épaisseur + + + Thickness value + Valeur d'épaisseur + + + + Thickness:is_inside + + Thicken towards the inside + Épaissir vers l'intérieur + + + Partition diff --git a/src/FeaturesPlugin/Test/TestOffset.py b/src/FeaturesPlugin/Test/TestOffset.py new file mode 100644 index 000000000..8c2f511e5 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestOffset.py @@ -0,0 +1,82 @@ +# Copyright (C) 2018-2024 CEA, EDF +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 200, 200, 200) +Box_2 = model.addBox(Part_1_doc, 200, 200, 200) +Box_3 = model.addBox(Part_1_doc, 200, 200, 200) + +### Create Shell +Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Left")]) + +# Global offset (pipe joints) +Offset_1 = model.addOffset(Part_1_doc, model.selection("SHELL", "Shell_1_1"), 70., True) + +# Global offset (intersection joints) +Offset_2 = model.addOffset(Part_1_doc, model.selection("SOLID", "Box_2_1"), 60., False) + +# Partial offset +Offset_3 = model.addOffsetPartial(Part_1_doc, + model.selection("SOLID", "Box_3_1"), + 50., + [model.selection("FACE", "Box_3_1/Top"), + model.selection("FACE", "Box_3_1/Back"), + model.selection("FACE", "Box_3_1/Right")]) + +model.end() + +from GeomAPI import GeomAPI_Shape + +#test Offset_1 +model.testNbResults(Offset_1, 1) +model.testNbSubResults(Offset_1, [0]) +model.testNbSubShapes(Offset_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Offset_1, GeomAPI_Shape.FACE, [7]) +model.testNbSubShapes(Offset_1, GeomAPI_Shape.EDGE, [27]) +model.testNbSubShapes(Offset_1, GeomAPI_Shape.VERTEX, [54]) +model.testResultsAreas(Offset_1, [193670.3477]) + +#test Offset_2 +model.testNbResults(Offset_1, 1) +model.testNbSubResults(Offset_2, [0]) +model.testNbSubShapes(Offset_2, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Offset_2, GeomAPI_Shape.FACE, [6]) +model.testNbSubShapes(Offset_2, GeomAPI_Shape.EDGE, [24]) +model.testNbSubShapes(Offset_2, GeomAPI_Shape.VERTEX, [48]) +model.testResultsVolumes(Offset_2, [32768000]) + +#test Offset_3 +model.testNbResults(Offset_3, 1) +model.testNbSubResults(Offset_3, [0]) +model.testNbSubShapes(Offset_3, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Offset_3, GeomAPI_Shape.FACE, [6]) +model.testNbSubShapes(Offset_3, GeomAPI_Shape.EDGE, [24]) +model.testNbSubShapes(Offset_3, GeomAPI_Shape.VERTEX, [48]) +model.testResultsVolumes(Offset_3, [15625000]) diff --git a/src/FeaturesPlugin/Test/TestThickness.py b/src/FeaturesPlugin/Test/TestThickness.py new file mode 100644 index 000000000..f39a7c1fb --- /dev/null +++ b/src/FeaturesPlugin/Test/TestThickness.py @@ -0,0 +1,83 @@ +# Copyright (C) 2018-2024 CEA, EDF +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 200, 200, 200) +Box_2 = model.addBox(Part_1_doc, 200, 200, 200) +Box_3 = model.addBox(Part_1_doc, 200, 200, 200) + +### Create Shell +Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Left")]) +Shell_2 = model.addShell(Part_1_doc, [model.selection("FACE", "Box_2_1/Top"), + model.selection("FACE", "Box_2_1/Front"), + model.selection("FACE", "Box_2_1/Left")]) + +# Thickness mode 1: thicken a shell/face +Thickness_1 = model.addThickness(Part_1_doc, model.selection("SHELL", "Shell_1_1"), 30, False) +Thickness_2 = model.addThickness(Part_1_doc, model.selection("SHELL", "Shell_2_1"), 30, True) + +# Thickness mode 2: hollowed solid +Thickness_3 = model.addHollowedSolid(Part_1_doc, + model.selection("SOLID", "Box_3_1"), + 40, + [model.selection("FACE", "Box_3_1/Top"), + model.selection("FACE", "Box_3_1/Left")], + False) + +model.end() + +from GeomAPI import GeomAPI_Shape + +#test Thickness_1 +model.testNbResults(Thickness_1, 1) +model.testNbSubResults(Thickness_1, [0]) +model.testNbSubShapes(Thickness_1, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Thickness_1, GeomAPI_Shape.FACE, [12]) +model.testNbSubShapes(Thickness_1, GeomAPI_Shape.EDGE, [48]) +model.testNbSubShapes(Thickness_1, GeomAPI_Shape.VERTEX, [96]) +model.testResultsVolumes(Thickness_1, [4167000]) + +#test Thickness_2 +model.testNbResults(Thickness_1, 1) +model.testNbSubResults(Thickness_2, [0]) +model.testNbSubShapes(Thickness_2, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Thickness_2, GeomAPI_Shape.FACE, [12]) +model.testNbSubShapes(Thickness_2, GeomAPI_Shape.EDGE, [48]) +model.testNbSubShapes(Thickness_2, GeomAPI_Shape.VERTEX, [96]) +model.testResultsVolumes(Thickness_2, [3087000]) + +#test Thickness_3 +model.testNbResults(Thickness_3, 1) +model.testNbSubResults(Thickness_3, [0]) +model.testNbSubShapes(Thickness_3, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Thickness_3, GeomAPI_Shape.FACE, [10]) +model.testNbSubShapes(Thickness_3, GeomAPI_Shape.EDGE, [48]) +model.testNbSubShapes(Thickness_3, GeomAPI_Shape.VERTEX, [96]) +model.testResultsVolumes(Thickness_3, [8128000]) diff --git a/src/FeaturesPlugin/doc/FeaturesPlugin.rst b/src/FeaturesPlugin/doc/FeaturesPlugin.rst index 43d13da81..a89beaf80 100644 --- a/src/FeaturesPlugin/doc/FeaturesPlugin.rst +++ b/src/FeaturesPlugin/doc/FeaturesPlugin.rst @@ -31,6 +31,7 @@ Features plug-in provides a set of common topological operations. It implements loftFeature.rst measurementFeature.rst normalToFaceFeature.rst + offsetFeature.rst pipeFeature.rst placementFeature.rst pointCoordinatesFeature.rst @@ -42,5 +43,6 @@ Features plug-in provides a set of common topological operations. It implements rotationFeature.rst sewingFeature.rst symmetryFeature.rst + thicknessFeature.rst transformationFeature.rst translationFeature.rst diff --git a/src/FeaturesPlugin/doc/TUI_offsetFeature.rst b/src/FeaturesPlugin/doc/TUI_offsetFeature.rst new file mode 100644 index 000000000..d53f635ff --- /dev/null +++ b/src/FeaturesPlugin/doc/TUI_offsetFeature.rst @@ -0,0 +1,11 @@ + + .. _tui_offset: + +offset +====== + +.. literalinclude:: examples/offset.py + :linenos: + :language: python + +:download:`Download this script ` diff --git a/src/FeaturesPlugin/doc/TUI_thicknessFeature.rst b/src/FeaturesPlugin/doc/TUI_thicknessFeature.rst new file mode 100644 index 000000000..ded4f9217 --- /dev/null +++ b/src/FeaturesPlugin/doc/TUI_thicknessFeature.rst @@ -0,0 +1,11 @@ + + .. _tui_thickness: + +thickness +========= + +.. literalinclude:: examples/thickness.py + :linenos: + :language: python + +:download:`Download this script ` diff --git a/src/FeaturesPlugin/doc/examples/offset.py b/src/FeaturesPlugin/doc/examples/offset.py new file mode 100644 index 000000000..2e1423829 --- /dev/null +++ b/src/FeaturesPlugin/doc/examples/offset.py @@ -0,0 +1,30 @@ +from SketchAPI import * + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 200, 200, 200) +Box_2 = model.addBox(Part_1_doc, 200, 200, 200) + +### Create Shell +Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Left")]) + +# Offset of the whole shape, pipe joints (isPipeJoint = True) +Offset_1 = model.addOffset(Part_1_doc, model.selection("SHELL", "Shell_1_1"), 70., True) + +# Partial offset. Negative offset value means offset in direction, opposite to normale. +Offset_2 = model.addOffsetPartial(Part_1_doc, + model.selection("SOLID", "Box_2_1"), + -50., + [model.selection("FACE", "Box_2_1/Top")]) + +model.end() diff --git a/src/FeaturesPlugin/doc/examples/thickness.py b/src/FeaturesPlugin/doc/examples/thickness.py new file mode 100644 index 000000000..364d13e29 --- /dev/null +++ b/src/FeaturesPlugin/doc/examples/thickness.py @@ -0,0 +1,34 @@ +from SketchAPI import * + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Box +Box_1 = model.addBox(Part_1_doc, 200, 200, 200) +Box_2 = model.addBox(Part_1_doc, 200, 200, 200) + +### Create Shell +Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Front"), + model.selection("FACE", "Box_1_1/Left")]) + +# Thickness mode 1: thicken a shell/face +# isInside = False, so thicken towards outside +Thickness_1 = model.addThickness(Part_1_doc, model.selection("SHELL", "Shell_1_1"), 30, False) + +# Thickness mode 2: hollowed solid +# isInside = True, so thicken towards inside +Thickness_2 = model.addHollowedSolid(Part_1_doc, + model.selection("SOLID", "Box_2_1"), + 40, + [model.selection("FACE", "Box_2_1/Top"), + model.selection("FACE", "Box_2_1/Left")], + True) + +model.end() diff --git a/src/FeaturesPlugin/doc/images/offset3D.png b/src/FeaturesPlugin/doc/images/offset3D.png new file mode 100644 index 000000000..47e03cc92 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/offset3D.png differ diff --git a/src/FeaturesPlugin/doc/images/offset3D_partial.png b/src/FeaturesPlugin/doc/images/offset3D_partial.png new file mode 100644 index 000000000..ced1d32df Binary files /dev/null and b/src/FeaturesPlugin/doc/images/offset3D_partial.png differ diff --git a/src/FeaturesPlugin/doc/images/offsetPartialPropertyPanel.png b/src/FeaturesPlugin/doc/images/offsetPartialPropertyPanel.png new file mode 100644 index 000000000..15f368409 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/offsetPartialPropertyPanel.png differ diff --git a/src/FeaturesPlugin/doc/images/offsetPropertyPanel.png b/src/FeaturesPlugin/doc/images/offsetPropertyPanel.png new file mode 100644 index 000000000..272372c56 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/offsetPropertyPanel.png differ diff --git a/src/FeaturesPlugin/doc/images/offset_partial_result.png b/src/FeaturesPlugin/doc/images/offset_partial_result.png new file mode 100644 index 000000000..a39dba131 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/offset_partial_result.png differ diff --git a/src/FeaturesPlugin/doc/images/offset_result.png b/src/FeaturesPlugin/doc/images/offset_result.png new file mode 100644 index 000000000..6404310e0 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/offset_result.png differ diff --git a/src/FeaturesPlugin/doc/images/thickness.png b/src/FeaturesPlugin/doc/images/thickness.png new file mode 100644 index 000000000..bee29286b Binary files /dev/null and b/src/FeaturesPlugin/doc/images/thickness.png differ diff --git a/src/FeaturesPlugin/doc/images/thickness2.png b/src/FeaturesPlugin/doc/images/thickness2.png new file mode 100644 index 000000000..31b9e5677 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/thickness2.png differ diff --git a/src/FeaturesPlugin/doc/images/thicknessPropertyPanel.png b/src/FeaturesPlugin/doc/images/thicknessPropertyPanel.png new file mode 100644 index 000000000..79cd361d1 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/thicknessPropertyPanel.png differ diff --git a/src/FeaturesPlugin/doc/images/thicknessPropertyPanel2.png b/src/FeaturesPlugin/doc/images/thicknessPropertyPanel2.png new file mode 100644 index 000000000..382f0e250 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/thicknessPropertyPanel2.png differ diff --git a/src/FeaturesPlugin/doc/images/thickness_result.png b/src/FeaturesPlugin/doc/images/thickness_result.png new file mode 100644 index 000000000..47ec5a002 Binary files /dev/null and b/src/FeaturesPlugin/doc/images/thickness_result.png differ diff --git a/src/FeaturesPlugin/doc/images/thickness_result2.png b/src/FeaturesPlugin/doc/images/thickness_result2.png new file mode 100644 index 000000000..6a6087a4c Binary files /dev/null and b/src/FeaturesPlugin/doc/images/thickness_result2.png differ diff --git a/src/FeaturesPlugin/doc/offsetFeature.rst b/src/FeaturesPlugin/doc/offsetFeature.rst new file mode 100644 index 000000000..c32ea011d --- /dev/null +++ b/src/FeaturesPlugin/doc/offsetFeature.rst @@ -0,0 +1,108 @@ +.. |offset.icon| image:: images/offset3D.png + +Offset +====== + +**Offset** feature translates each point of the **Shape** +along a local normal by the given **Offset distance** +(signed number, negative value means inner offset). + +To create an Offset in the active part: + +#. select in the Main Menu *Features - > Offset* item or +#. click |offset.icon| **Offset** button in the toolbar + +Two Offset algorithms are: + +.. figure:: images/offset3D.png + :align: left + :height: 24px + +offset the whole shape by the same value + +.. figure:: images/offset3D_partial.png + :align: left + :height: 24px + +offset selected faces by the given value, other faces by zero. + +-------------------------------------------------------------------------------- + +Offset the whole shape by the same value +---------------------------------------- + +.. figure:: images/offsetPropertyPanel.png + :align: center + + Offset by the same value property panel + +Input fields: + +- **Shape** defines the base shape (solid, shell or face) selected in 3D OCC viewer or object browser; +- **Distance** defines the offset value. Negative value meaning inner offset; +- **Join by pipes** check box defines the mode of filling the gaps between translated + adjacent surfaces: + + - if Join by pipes is activated, they are filled with pipes; + - else the surfaces are extended and intersected, so that sharp edges are preserved; + +**TUI Command**: + +.. py:function:: model.addOffset(Part_doc, shape, dist, isPipeJoint) + + :param part: The current part object. + :param object: A shape in format *model.selection(TYPE, shape)*. + :param real: Offset distance value. + :param boolean: Join by pipes/intersection flag. + :return: Created object. + +Result +"""""" + +Result of offset of a box. Join by pipes activated. + +.. figure:: images/offset_result.png + :align: center + + Offset of a box + +**See Also** a sample TUI Script of :ref:`tui_offset` operation. + + +Offset selected faces by the given value, other faces by zero +------------------------------------------------------------- + +.. figure:: images/offsetPartialPropertyPanel.png + :align: center + + Partial Offset property panel + +Input fields: + +- **Shape** defines the base shape (solid, shell or face) selected in 3D OCC viewer or object browser; +- **Distance** defines the offset value. Negative value meaning inner offset; +- **Faces to offset** defines the faces of the base shape, which should be offset; + +*Note*: In Partial Offset mode gaps are allways filled by intersection. + +**TUI Command**: + +.. py:function:: model.addOffsetPartial(Part_doc, shape, dist, faces) + + :param part: The current part object. + :param object: A shape in format *model.selection(TYPE, shape)*. + :param real: Offset distance value. + :param objects: Faces of the shape in format *[model.selection(TYPE, shape), ...]*. + :return: Created object. + +Result +"""""" + +Result of partial offset of a box. Top and front faces selected. + +.. figure:: images/offset_partial_result.png + :align: center + + Partial offset of a box + +**See Also** a sample TUI Script of :ref:`tui_offset` operation. diff --git a/src/FeaturesPlugin/doc/thicknessFeature.rst b/src/FeaturesPlugin/doc/thicknessFeature.rst new file mode 100644 index 000000000..fb8eba53f --- /dev/null +++ b/src/FeaturesPlugin/doc/thicknessFeature.rst @@ -0,0 +1,100 @@ +.. |thickness.icon| image:: images/thickness.png + +Thickness +========= + +To create a **Thickness** or a **Hollowed Solid** in the active part: + +#. select in the Main Menu *Features - > Thickness* item or +#. click |thickness.icon| **Thickness** button in the toolbar + +Two Thickness algorithms are: + +.. figure:: images/thickness.png + :align: left + :height: 24px + +make a thickness of a shell or a face + +.. figure:: images/thickness2.png + :align: left + :height: 24px + +make a hollowed solid + +-------------------------------------------------------------------------------- + +Thickness of a shell or a face +------------------------------ + +.. figure:: images/thicknessPropertyPanel.png + :align: center + + Thickness property panel + +Input fields: + +- **Shape** defines the base shape (shell or face) selected in 3D OCC viewer or object browser; +- **Distance** defines the thickness value. Negative values are not allowed; +- **Thicken towards the inside** check box defines the thickening direction; + +**TUI Command**: + +.. py:function:: model.addThickness(Part_doc, shape, dist, isInside) + + :param part: The current part object. + :param object: A shape in format *model.selection(TYPE, shape)*. + :param real: Thickness value. + :param boolean: Inside/outside direction flag. + :return: Created object. + +Result +"""""" + +Result of thickness of a shell. + +.. figure:: images/thickness_result.png + :align: center + + Thickness of a shell + +**See Also** a sample TUI Script of :ref:`tui_thickness` operation. + + +Hollowed solid +-------------- + +.. figure:: images/thicknessPropertyPanel2.png + :align: center + + Hollowed Solid property panel + +Input fields: + +- **Shape** defines the base shape (solid) selected in 3D OCC viewer or object browser; +- **Distance** defines the thickness value. Negative values are not allowed; +- **Faces to remove** defines the faces of the base solid, where the hole is done; +- **Thicken towards the inside** check box defines the thickening direction; + +**TUI Command**: + +.. py:function:: model.addHollowedSolid(Part_doc, shape, dist, faces, isInside) + + :param part: The current part object. + :param object: A shape in format *model.selection("SOLID", shape)*. + :param real: Thickness value. + :param objects: Faces of the solid in format *[model.selection("FACE", shape), ...]*. + :param boolean: Inside/outside direction flag. + :return: Created object. + +Result +"""""" + +Result of hollowed solid of a box. Left and front faces selected. + +.. figure:: images/thickness_result2.png + :align: center + + Hollowed solid of a box + +**See Also** a sample TUI Script of :ref:`tui_thickness` operation. diff --git a/src/FeaturesPlugin/icons/offset3D.png b/src/FeaturesPlugin/icons/offset3D.png new file mode 100644 index 000000000..47e03cc92 Binary files /dev/null and b/src/FeaturesPlugin/icons/offset3D.png differ diff --git a/src/FeaturesPlugin/icons/offset3D_partial.png b/src/FeaturesPlugin/icons/offset3D_partial.png new file mode 100644 index 000000000..ced1d32df Binary files /dev/null and b/src/FeaturesPlugin/icons/offset3D_partial.png differ diff --git a/src/FeaturesPlugin/icons/thickness.png b/src/FeaturesPlugin/icons/thickness.png new file mode 100644 index 000000000..bee29286b Binary files /dev/null and b/src/FeaturesPlugin/icons/thickness.png differ diff --git a/src/FeaturesPlugin/icons/thickness2.png b/src/FeaturesPlugin/icons/thickness2.png new file mode 100644 index 000000000..31b9e5677 Binary files /dev/null and b/src/FeaturesPlugin/icons/thickness2.png differ diff --git a/src/FeaturesPlugin/offset_widget.xml b/src/FeaturesPlugin/offset_widget.xml new file mode 100644 index 000000000..8418dda7e --- /dev/null +++ b/src/FeaturesPlugin/offset_widget.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/src/FeaturesPlugin/plugin-Features.xml b/src/FeaturesPlugin/plugin-Features.xml index 3bebe12a5..113c0ab4c 100644 --- a/src/FeaturesPlugin/plugin-Features.xml +++ b/src/FeaturesPlugin/plugin-Features.xml @@ -5,6 +5,10 @@ icon="icons/Features/scale.png" helpfile="transformationFeature.html"> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/GeomAlgoAPI/CMakeLists.txt b/src/GeomAlgoAPI/CMakeLists.txt index e01dbf2c7..65dffd544 100644 --- a/src/GeomAlgoAPI/CMakeLists.txt +++ b/src/GeomAlgoAPI/CMakeLists.txt @@ -88,6 +88,7 @@ SET(PROJECT_HEADERS GeomAlgoAPI_CurveBuilder.h GeomAlgoAPI_NExplode.h GeomAlgoAPI_Offset.h + GeomAlgoAPI_Thickness.h GeomAlgoAPI_SolidClassifier.h GeomAlgoAPI_MapShapesAndAncestors.h GeomAlgoAPI_Projection.h @@ -97,7 +98,7 @@ SET(PROJECT_HEADERS GeomAlgoAPI_NormalToFace.h GeomAlgoAPI_Tube.h GeomAlgoAPI_ShapeInfo.h - GeomAlgoAPI_CanonicalRecognition.h + GeomAlgoAPI_CanonicalRecognition.h GeomAlgoAPI_GlueFaces.h GeomAlgoAPI_LimitTolerance.h GeomAlgoAPI_Utils.h @@ -169,6 +170,7 @@ SET(PROJECT_SOURCES GeomAlgoAPI_CurveBuilder.cpp GeomAlgoAPI_NExplode.cpp GeomAlgoAPI_Offset.cpp + GeomAlgoAPI_Thickness.cpp GeomAlgoAPI_SolidClassifier.cpp GeomAlgoAPI_MapShapesAndAncestors.cpp GeomAlgoAPI_Projection.cpp @@ -178,9 +180,8 @@ SET(PROJECT_SOURCES GeomAlgoAPI_NormalToFace.cpp GeomAlgoAPI_Tube.cpp GeomAlgoAPI_ShapeInfo.cpp - GeomAlgoAPI_CanonicalRecognition.cpp + GeomAlgoAPI_CanonicalRecognition.cpp GeomAlgoAPI_GlueFaces.cpp - GeomAlgoAPI_CanonicalRecognition.cpp GeomAlgoAPI_LimitTolerance.cpp GeomAlgoAPI_Utils.cpp GeomAlgoAPI_NonPlanarFace.cpp diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.cpp index 1206dd3ee..1e0b54dbc 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,9 @@ void GeomAlgoAPI_MakeShape::generated(const GeomShapePtr theOldShape, } else if(myBuilderType == OCCT_BOPAlgo_Builder) { BOPAlgo_Builder* aBOPBuilder = implPtr(); aList = aBOPBuilder->Generated(theOldShape->impl()); + } else if(myBuilderType == OCCT_BRepOffset_MakeOffset) { + BRepOffset_MakeOffset* aMakeOffset = implPtr(); + aList = aMakeOffset->Generated(theOldShape->impl()); } for(TopTools_ListIteratorOfListOfShape anIt(aList); anIt.More(); anIt.Next()) { GeomShapePtr aShape(new GeomAPI_Shape()); @@ -126,6 +130,12 @@ void GeomAlgoAPI_MakeShape::modified(const GeomShapePtr theOldShape, } else if(myBuilderType == OCCT_BOPAlgo_Builder) { BOPAlgo_Builder* aBOPBuilder = implPtr(); aList = aBOPBuilder->Modified(theOldShape->impl()); + } else if(myBuilderType == OCCT_BRepOffset_MakeOffset) { + BRepOffset_MakeOffset* aMakeOffset = implPtr(); + try { + aList = aMakeOffset->Modified(theOldShape->impl()); + } catch(Standard_NoSuchObject const&) { + } } for(TopTools_ListIteratorOfListOfShape anIt(aList); anIt.More(); anIt.Next()) { GeomShapePtr aShape(new GeomAPI_Shape()); @@ -163,6 +173,9 @@ bool GeomAlgoAPI_MakeShape::isDeleted(const GeomShapePtr theOldShape) } else if(myBuilderType == OCCT_BOPAlgo_Builder) { BOPAlgo_Builder* aBOPBuilder = implPtr(); isDeleted = aBOPBuilder->IsDeleted(theOldShape->impl()) == Standard_True; + } else if(myBuilderType == OCCT_BRepOffset_MakeOffset) { + BRepOffset_MakeOffset* aMakeOffset = implPtr(); + isDeleted = aMakeOffset->IsDeleted(theOldShape->impl()) == Standard_True; } return isDeleted; @@ -257,6 +270,12 @@ void GeomAlgoAPI_MakeShape::initialize() myShape->setImpl(new TopoDS_Shape(implPtr()->Shape())); break; } + case OCCT_BRepOffset_MakeOffset: { + myDone = implPtr()->IsDone() == Standard_True; + myShape.reset(new GeomAPI_Shape()); + myShape->setImpl(new TopoDS_Shape(implPtr()->Shape())); + break; + } default: break; } diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.h b/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.h index 7acd2291a..29abccfb7 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_MakeShape.h @@ -38,7 +38,8 @@ public: enum BuilderType { Unknown, OCCT_BRepBuilderAPI_MakeShape, - OCCT_BOPAlgo_Builder + OCCT_BOPAlgo_Builder, + OCCT_BRepOffset_MakeOffset }; public: diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp index 590b37bbc..2d74029e6 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp @@ -27,16 +27,51 @@ #include #include +#include #include #include GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomShapePtr& theShape, - const double theOffsetValue) + const double theOffsetValue) { - build(theShape, theOffsetValue); + buildSimple(theShape, theOffsetValue); } -void GeomAlgoAPI_Offset::build(const GeomShapePtr& theShape, const double theOffsetValue) +GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomShapePtr& theShape, + const double theOffsetValue, + const bool isPipeJoint) +{ + buildByJoin(theShape, theOffsetValue, isPipeJoint); +} + +GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theOffsetValue) +{ + buildPartial(theShape, theFaces, theOffsetValue); +} + +GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomPlanePtr& thePlane, + const GeomShapePtr& theEdgeOrWire, + const double theOffsetValue, + const GeomAlgoAPI_OffsetJoint theJoint, + const bool theIsApprox) +{ + build2d(thePlane, theEdgeOrWire, theOffsetValue, theJoint, theIsApprox); +} + +void GeomAlgoAPI_Offset::generated(const GeomShapePtr theOldShape, + ListOfShape& theNewShapes) +{ + try { + GeomAlgoAPI_MakeShape::generated(theOldShape, theNewShapes); + } catch(...) { + // nothing is generated + } +} + +void GeomAlgoAPI_Offset::buildSimple(const GeomShapePtr& theShape, + const double theOffsetValue) { BRepOffsetAPI_MakeOffsetShape* anOffsetAlgo = new BRepOffsetAPI_MakeOffsetShape; anOffsetAlgo->PerformBySimple(theShape->impl(), theOffsetValue); @@ -52,21 +87,96 @@ void GeomAlgoAPI_Offset::build(const GeomShapePtr& theShape, const double theOff } } -void GeomAlgoAPI_Offset::generated(const GeomShapePtr theOldShape, - ListOfShape& theNewShapes) +void GeomAlgoAPI_Offset::buildByJoin(const GeomShapePtr& theShape, + const double theOffsetValue, + const bool isPipeJoint) { - try { - GeomAlgoAPI_MakeShape::generated(theOldShape, theNewShapes); - } catch(...) { - // nothing is generated + Standard_Real aTol = Precision::Confusion(); + BRepOffset_Mode aMode = BRepOffset_Skin; + Standard_Boolean anIntersection = Standard_False; + Standard_Boolean aSelfInter = Standard_False; + + BRepOffsetAPI_MakeOffsetShape* anOffsetAlgo = new BRepOffsetAPI_MakeOffsetShape; + anOffsetAlgo->PerformByJoin(theShape->impl(), + theOffsetValue, + aTol, + aMode, + anIntersection, + aSelfInter, + isPipeJoint ? GeomAbs_Arc : GeomAbs_Intersection); + setImpl(anOffsetAlgo); + setBuilderType(OCCT_BRepBuilderAPI_MakeShape); + + if (anOffsetAlgo->IsDone()) { + const TopoDS_Shape& aResult = anOffsetAlgo->Shape(); + std::shared_ptr aShape(new GeomAPI_Shape()); + aShape->setImpl(new TopoDS_Shape(aResult)); + setShape(aShape); + setDone(true); } } -GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomPlanePtr& thePlane, - const GeomShapePtr& theEdgeOrWire, - const double theOffsetValue, - const GeomAlgoAPI_OffsetJoint theJoint, - const bool theIsApprox) +void GeomAlgoAPI_Offset::buildPartial(const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theOffsetValue) +{ + if (theFaces.empty()) + return; + + TopoDS_Shape aShapeBase = theShape->impl(); + + Standard_Real aTol = Precision::Confusion(); + BRepOffset_Mode aMode = BRepOffset_Skin; + Standard_Boolean anIntersection = Standard_False; + Standard_Boolean aSelfInter = Standard_False; + + BRepOffset_MakeOffset* aMakeOffset = new BRepOffset_MakeOffset; + aMakeOffset->Initialize(aShapeBase, + theOffsetValue, // set offset on all faces to anOffset + aTol, + aMode, + anIntersection, + aSelfInter, + GeomAbs_Intersection, + Standard_False); + + // put selected faces into a map + TopTools_MapOfShape aMapFaces; + for (ListOfShape::const_iterator anIt = theFaces.begin(); + anIt != theFaces.end(); ++anIt) { + if ((*anIt)->isFace()) + aMapFaces.Add((*anIt)->impl()); + } + + // set offset on non-selected faces to zero + TopExp_Explorer anExp (aShapeBase, TopAbs_FACE); + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &aFace = anExp.Current(); + if (!aMapFaces.Contains(aFace)) { + aMakeOffset->SetOffsetOnFace(TopoDS::Face(aFace), 0.0); + } + } + + // perform offset operation + aMakeOffset->MakeOffsetShape(); + + setImpl(aMakeOffset); + setBuilderType(OCCT_BRepOffset_MakeOffset); + + if (aMakeOffset->IsDone()) { + const TopoDS_Shape& aResult = aMakeOffset->Shape(); + std::shared_ptr aShape(new GeomAPI_Shape()); + aShape->setImpl(new TopoDS_Shape(aResult)); + setShape(aShape); + setDone(true); + } +} + +void GeomAlgoAPI_Offset::build2d(const GeomPlanePtr& thePlane, + const GeomShapePtr& theEdgeOrWire, + const double theOffsetValue, + const GeomAlgoAPI_OffsetJoint theJoint, + const bool theIsApprox) { // 1. Make wire from edge, if need TopoDS_Wire aWire; diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h index 7c58f8e70..5bc01a6b6 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h @@ -41,11 +41,31 @@ enum class GeomAlgoAPI_OffsetJoint { KeepDistance, Arcs, Lines }; class GeomAlgoAPI_Offset : public GeomAlgoAPI_MakeShape { public: - /// \brief Construct offset. + /// \brief Perform simple offset. + /// \param[in] theShape base shape + /// \param[in] theOffsetValue offset distance, it can be negative GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset(const GeomShapePtr& theShape, const double theOffsetValue); - /// \brief Perform the offset algorithm on the plane + /// \brief Perform 3d offset algorithm + /// \param[in] theShape base shape + /// \param[in] theOffsetValue offset distance, it can be negative + /// \param[in] isPipeJoint type of joint of faces (pipes or intersections) + GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset + (const GeomShapePtr& theShape, + const double theOffsetValue, + const bool isPipeJoint); + + /// \brief Perform partial 3d offset algorithm + /// \param[in] theShape base shape + /// \param[in] theFaces list of faces to be offset + /// \param[in] theOffsetValue offset distance for selected faces, it can be negative + GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset + (const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theOffsetValue); + + /// \brief Perform 2d offset algorithm on the plane /// \param[in] thePlane base plane for all offsets /// \param[in] theEdgesOrWire base shapes /// \param[in] theOffsetValue offset distance, it can be negative @@ -63,10 +83,27 @@ public: GEOMALGOAPI_EXPORT virtual void generated(const GeomShapePtr theOldShape, ListOfShape& theNewShapes); - private: - /// \brief Perform offset operation - void build(const GeomShapePtr& theShape, const double theOffsetValue); + /// \brief Perform simple offset operation + void buildSimple(const GeomShapePtr& theShape, + const double theOffsetValue); + + /// \brief Perform 3d offset algorithm by join + void buildByJoin(const GeomShapePtr& theShape, + const double theOffsetValue, + const bool isPipeJoint); + + /// \brief Perform partial 3d offset algorithm + void buildPartial(const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theOffsetValue); + + /// \brief Perform 2d offset algorithm on the plane + void build2d(const std::shared_ptr& thePlane, + const GeomShapePtr& theEdgeOrWire, + const double theOffsetValue, + const GeomAlgoAPI_OffsetJoint theJoint = GeomAlgoAPI_OffsetJoint::KeepDistance, + const bool theIsApprox = false); }; #endif diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Thickness.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Thickness.cpp new file mode 100644 index 000000000..179a5b8e9 --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Thickness.cpp @@ -0,0 +1,273 @@ +// Copyright (C) 2019-2024 CEA, EDF +// +// 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_Thickness.h" + +#include + +#include +#include +#include + +#include +#include +#include + +static GeomShapePtr convert(const TopoDS_Shape& theShape) +{ + GeomShapePtr aNewShape(new GeomAPI_Shape); + aNewShape->setImpl(new TopoDS_Shape(theShape)); + return aNewShape; +} + +GeomAlgoAPI_Thickness::GeomAlgoAPI_Thickness(const GeomShapePtr& theShape, + const double theThickness, + const bool isInside) +{ + buildThickening(theShape, theThickness, isInside); +} + +GeomAlgoAPI_Thickness::GeomAlgoAPI_Thickness(const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theThickness, + const bool isInside) +{ + buildHollowedSolid(theShape, theFaces, theThickness, isInside); +} + +void GeomAlgoAPI_Thickness::generated(const GeomShapePtr theOldShape, + ListOfShape& theNewShapes) +{ + GeomAlgoAPI_MakeShape::generated(theOldShape, theNewShapes); + + MapModified::iterator aFound = myGenerated.find(theOldShape); + if (aFound != myGenerated.end()) + theNewShapes.insert(theNewShapes.end(), + aFound->second.begin(), aFound->second.end()); +} + +void GeomAlgoAPI_Thickness::buildThickening(const GeomShapePtr& theShape, + const double theThickness, + const bool isInside) +{ + Standard_Real aTol = Precision::Confusion(); + if (theThickness < aTol) { + myError = "The thickness value is Too small or negative"; + return; + } + + Standard_Real anOffset = theThickness; + if (isInside) + anOffset = -anOffset; + + const TopoDS_Shape& aShapeBase = theShape->impl(); + const TopAbs_ShapeEnum aType = aShapeBase.ShapeType(); + + if (aType != TopAbs_FACE && aType != TopAbs_SHELL) { + myError = "The base shape for thickening should be a face or a shell"; + return; + } + + BRepClass3d_SolidClassifier aClassifier(aShapeBase); + aClassifier.PerformInfinitePoint(Precision::Confusion()); + if (aClassifier.State()==TopAbs_IN) { + // If the generated pipe faces normals are oriented towards the inside, + // the offset is negative, so that the thickening is still towards outside + anOffset = -anOffset; + } + + Standard_Boolean anIntersection = Standard_False; + Standard_Boolean aSelfInter = Standard_False; + Standard_Boolean isThickening = Standard_True; + + BRepOffset_MakeOffset* aMakeOffset = + new BRepOffset_MakeOffset(aShapeBase, + anOffset, + aTol, + BRepOffset_Skin, + anIntersection, + aSelfInter, + GeomAbs_Intersection, + isThickening); + + setImpl(aMakeOffset); + setBuilderType(OCCT_BRepOffset_MakeOffset); + + if (aMakeOffset->IsDone()) { + TopoDS_Shape aResult = aMakeOffset->Shape(); + + // Control the solid orientation. This is mostly done to fix a bug in case + // of extrusion of a circle. The built solid is then badly oriented. + BRepClass3d_SolidClassifier anotherClassifier(aResult); + anotherClassifier.PerformInfinitePoint(Precision::Confusion()); + if (anotherClassifier.State() == TopAbs_IN) { + aResult.Reverse(); + } + + std::shared_ptr aShape (new GeomAPI_Shape()); + aShape->setImpl(new TopoDS_Shape(aResult)); + setShape(aShape); + setDone(true); + + // History for ThickShell is incomplete (OCCT bug 33844) + // Here we try to add generated walls (FACE from EDGE or VERTEX) + + // 1. Gather all generated/modified/initial faces in a map + // (for that faces we don't need to adjust history). + // Collect also edges and vertices of initial shape in a separate map. + TopTools_MapOfShape aFacesMap; + TopTools_MapOfShape anInitialMap; + TopTools_ListOfShape aList, aListTmp; + { + TopExp_Explorer anExp (aShapeBase, TopAbs_FACE); + for (; anExp.More(); anExp.Next()) { + TopoDS_Shape aFace = anExp.Current(); + aList.Append(aFace); + + aListTmp = aMakeOffset->Modified(aFace); + aList.Append(aListTmp); + aListTmp = aMakeOffset->Generated(aFace); + aList.Append(aListTmp); + } + } + { + TopExp_Explorer anExp (aShapeBase, TopAbs_EDGE); + for (; anExp.More(); anExp.Next()) { + TopoDS_Shape anEdge = anExp.Current(); + anInitialMap.Add(anEdge); + + aListTmp = aMakeOffset->Modified(anEdge); + aList.Append(aListTmp); + aListTmp = aMakeOffset->Generated(anEdge); + aList.Append(aListTmp); + } + } + { + TopExp_Explorer anExp (aShapeBase, TopAbs_VERTEX); + for (; anExp.More(); anExp.Next()) { + TopoDS_Shape aVertex = anExp.Current(); + anInitialMap.Add(aVertex); + + aListTmp = aMakeOffset->Modified(aVertex); + aList.Append(aListTmp); + aListTmp = aMakeOffset->Generated(aVertex); + aList.Append(aListTmp); + } + } + TopTools_ListIteratorOfListOfShape anIt (aList); + for (; anIt.More(); anIt.Next()) { + TopoDS_Shape aSh = anIt.Value(); + if (aSh.ShapeType() == TopAbs_FACE) { + aFacesMap.Add(aSh); + } + } + + // 2. Explode result shape into faces, try to construct new + // history for the faces, that are not in the map. + TopExp_Explorer anExp (aResult, TopAbs_FACE); + for (; anExp.More(); anExp.Next()) { + TopoDS_Shape aFace = anExp.Current(); + if (!aFacesMap.Contains(aFace)) { + bool isFound = false; + // a. Try to find initial edge in this face + TopExp_Explorer anExpE (aFace, TopAbs_EDGE); + for (; anExpE.More() && !isFound; anExpE.Next()) { + TopoDS_Shape anEdge = anExpE.Current(); + if (anInitialMap.Contains(anEdge)) { + myGenerated[convert(anEdge)].push_back(convert(aFace)); + isFound = true; + } + } + if (!isFound) { + // b. Try to find initial vertex in this face + TopExp_Explorer anExpV (aFace, TopAbs_VERTEX); + for (; anExpV.More() && !isFound; anExpV.Next()) { + TopoDS_Shape aVertex = anExpV.Current(); + if (anInitialMap.Contains(aVertex)) { + myGenerated[convert(aVertex)].push_back(convert(aFace)); + isFound = true; + } + } + } + } + } + // END: history adjustment + } +} + +void GeomAlgoAPI_Thickness::buildHollowedSolid(const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theThickness, + const bool isInside) +{ + if (theFaces.empty()) + return; + + Standard_Real aTol = Precision::Confusion(); + if (theThickness < aTol) { + myError = "The thickness value is Too small or negative"; + return; + } + + Standard_Real anOffset = theThickness; + if (isInside) + anOffset = -anOffset; + + //TopoDS_Shape aShapeBase = theShape->impl(); + const TopoDS_Shape& aShapeBase = theShape->impl(); + const TopAbs_ShapeEnum aType = aShapeBase.ShapeType(); + + if (aType != TopAbs_SOLID) { + myError = "The base shape for hollowed solid creation should be a solid"; + return; + } + + // put selected faces into a list + TopTools_ListOfShape aFacesToRm; + for (ListOfShape::const_iterator anIt = theFaces.begin(); + anIt != theFaces.end(); ++anIt) { + if ((*anIt)->isFace()) + aFacesToRm.Append((*anIt)->impl()); + } + + Standard_Boolean anIntersection = Standard_False; + Standard_Boolean aSelfInter = Standard_False; + + // Create a hollowed solid. + BRepOffsetAPI_MakeThickSolid* aMkSolid = new BRepOffsetAPI_MakeThickSolid(); + aMkSolid->MakeThickSolidByJoin(aShapeBase, + aFacesToRm, + anOffset, + aTol, + BRepOffset_Skin, + anIntersection, + aSelfInter, + GeomAbs_Intersection); + + setImpl(aMkSolid); + setBuilderType(OCCT_BRepBuilderAPI_MakeShape); + + if (aMkSolid->IsDone()) { + const TopoDS_Shape& aResult = aMkSolid->Shape(); + std::shared_ptr aShape(new GeomAPI_Shape()); + aShape->setImpl(new TopoDS_Shape(aResult)); + setShape(aShape); + setDone(true); + } +} diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Thickness.h b/src/GeomAlgoAPI/GeomAlgoAPI_Thickness.h new file mode 100644 index 000000000..dcd304ebc --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Thickness.h @@ -0,0 +1,76 @@ +// Copyright (C) 2019-2024 CEA, EDF +// +// 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_Thickness_H_ +#define GeomAlgoAPI_Thickness_H_ + +#include +#include + +/// \class GeomAlgoAPI_Thickness +/// \ingroup DataAlgo +/// \brief Perform Thickness or Hollowed Solid algorithm for the shape +class GeomAlgoAPI_Thickness : public GeomAlgoAPI_MakeShape +{ + typedef std::map MapModified; + +public: + /// \brief Perform thickening algorithm + /// \param[in] theShape base shape (Face or Shell) + /// \param[in] theThickness thickness of the resulting solid + /// \param[in] isInside if true, the thickness is applied towards inside + GEOMALGOAPI_EXPORT GeomAlgoAPI_Thickness + (const GeomShapePtr& theShape, + const double theThickness, + const bool isInside); + + /// \brief Perform hollowed solid algorithm + /// \param[in] theShape base shape (Solid) + /// \param[in] theFaces the list of faces to be removed from the result + /// \param[in] theThickness thickness of the resulting solid + /// \param[in] isInside if true, the thickness is applied towards inside + GEOMALGOAPI_EXPORT GeomAlgoAPI_Thickness + (const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theThickness, + const bool isInside); + + /// \return the list of shapes generated from the shape \a theShape. + /// \param[in] theOldShape base shape. + /// \param[out] theNewShapes shapes generated from \a theShape. Does not cleared! + GEOMALGOAPI_EXPORT virtual void generated(const GeomShapePtr theOldShape, + ListOfShape& theNewShapes); + +private: + /// \brief Perform thickening algorithm + void buildThickening(const GeomShapePtr& theShape, + const double theThickness, + const bool isInside); + + /// \brief Perform hollowed solid algorithm + void buildHollowedSolid(const GeomShapePtr& theShape, + const ListOfShape& theFaces, + const double theThickness, + const bool isInside); + +private: + MapModified myGenerated; +}; + +#endif diff --git a/src/PythonAPI/model/features/__init__.py b/src/PythonAPI/model/features/__init__.py index ba9b2542c..c591bfc14 100644 --- a/src/PythonAPI/model/features/__init__.py +++ b/src/PythonAPI/model/features/__init__.py @@ -24,6 +24,7 @@ from FeaturesAPI import addMultiTranslation, addMultiRotation from FeaturesAPI import addExtrusion, addExtrusionCut, addExtrusionFuse from FeaturesAPI import addRevolution, addRevolutionCut, addRevolutionFuse from FeaturesAPI import addPipe, addLoft +from FeaturesAPI import addOffset, addOffsetPartial, addThickness, addHollowedSolid from FeaturesAPI import addCut, addFuse, addCommon, addSmash, addSplit from FeaturesAPI import addIntersection, addPartition, addUnion, addRemoveSubShapes from FeaturesAPI import addRecover diff --git a/src/SHAPERGUI/resources/LightApp.xml.in b/src/SHAPERGUI/resources/LightApp.xml.in index ed2f9f047..14dc236c4 100644 --- a/src/SHAPERGUI/resources/LightApp.xml.in +++ b/src/SHAPERGUI/resources/LightApp.xml.in @@ -106,6 +106,7 @@ + @@ -118,6 +119,7 @@ + diff --git a/src/SHAPERGUI/resources/action_assets.json b/src/SHAPERGUI/resources/action_assets.json index e2cd0c869..efe887529 100644 --- a/src/SHAPERGUI/resources/action_assets.json +++ b/src/SHAPERGUI/resources/action_assets.json @@ -545,6 +545,19 @@ } } }, + "Offset3d": { + "iconPath": "%SHAPER_ROOT_DIR%/share/salome/resources/shaper/icons/Features/offset3D.png", + "langDependentAssets": { + "en": { + "name": "Offset (3D)", + "tooltip": "Offset face or shell" + }, + "fr": { + "name": "Offset (3D)", + "tooltip": "Face ou coque décalée" + } + } + }, "Partition": { "iconPath": "%SHAPER_ROOT_DIR%/share/salome/resources/shaper/icons/Features/partition.png", "langDependentAssets": { @@ -700,6 +713,19 @@ "tooltip": "Effectuer l'opération booléenne division avec des objets" } } + }, + "Thickness": { + "iconPath": "%SHAPER_ROOT_DIR%/share/salome/resources/shaper/icons/Features/thickness.png", + "langDependentAssets": { + "en": { + "name": "Thickness (3D)", + "tooltip": "Thicken faces" + }, + "fr": { + "name": "Thickness (3D)", + "tooltip": "Épaissir les visages" + } + } } } }, diff --git a/src/SketchPlugin/Test/TestOffset1.py b/src/SketchPlugin/Test/TestOffset1.py index 9e7d3667d..65b12733f 100644 --- a/src/SketchPlugin/Test/TestOffset1.py +++ b/src/SketchPlugin/Test/TestOffset1.py @@ -18,7 +18,7 @@ # """ - TestOffset.py + TestOffset1.py Unit test of SketchPlugin_Offset class SketchPlugin_Offset