From: Artem Zhidkov Date: Wed, 22 Apr 2020 08:55:13 +0000 (+0300) Subject: Issue #3222: 1d fillet X-Git-Tag: V9_6_0a1~60^2~73 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=c82235c24f79a496fd29c6af4cf37109af96b234;p=modules%2Fshaper.git Issue #3222: 1d fillet * Implement the 1D-fillet feature * Unit tests for 1D-fillet --- diff --git a/src/BuildPlugin/BuildPlugin_Wire.cpp b/src/BuildPlugin/BuildPlugin_Wire.cpp index c7caa8581..04e6f6fe5 100644 --- a/src/BuildPlugin/BuildPlugin_Wire.cpp +++ b/src/BuildPlugin/BuildPlugin_Wire.cpp @@ -122,7 +122,10 @@ void BuildPlugin_Wire::execute() for (ListOfShape::const_iterator anIt = anEdges.cbegin(); anIt != anEdges.cend(); ++anIt) { std::shared_ptr anEdgeInList(new GeomAPI_Edge(*anIt)); if (anEdgeInList->isEqual(anEdgeInResult)) { - aResultBody->modified(anEdgeInList, anEdgeInResult); + if (anEdgeInList->isSame(anEdgeInResult)) + aResultBody->generated(anEdgeInResult, "Edge"); + else + aResultBody->modified(anEdgeInList, anEdgeInResult); break; } } diff --git a/src/FeaturesAPI/FeaturesAPI.i b/src/FeaturesAPI/FeaturesAPI.i index 010ddfa51..2024975c6 100644 --- a/src/FeaturesAPI/FeaturesAPI.i +++ b/src/FeaturesAPI/FeaturesAPI.i @@ -68,6 +68,8 @@ %shared_ptr(FeaturesAPI_ExtrusionCut) %shared_ptr(FeaturesAPI_ExtrusionFuse) %shared_ptr(FeaturesAPI_Fillet) +%shared_ptr(FeaturesAPI_Fillet1D) +%shared_ptr(FeaturesAPI_Fillet2D) %shared_ptr(FeaturesAPI_Intersection) %shared_ptr(FeaturesAPI_MultiRotation) %shared_ptr(FeaturesAPI_MultiTranslation) diff --git a/src/FeaturesAPI/FeaturesAPI_Fillet.cpp b/src/FeaturesAPI/FeaturesAPI_Fillet.cpp index d84e00175..5cb598acb 100644 --- a/src/FeaturesAPI/FeaturesAPI_Fillet.cpp +++ b/src/FeaturesAPI/FeaturesAPI_Fillet.cpp @@ -21,19 +21,111 @@ #include #include -////#include +#include #include +static GeomAPI_Shape::ShapeType typeOfSelection( + const std::list& theBaseObjects) +{ + std::string aType = theBaseObjects.empty() ? "SHAPE" : theBaseObjects.front().shapeType(); + return GeomAPI_Shape::shapeTypeByStr(aType); +} + +//================================================================================================== + FeaturesAPI_Fillet::FeaturesAPI_Fillet(const std::shared_ptr& theFeature) : ModelHighAPI_Interface(theFeature) +{ +} + +//================================================================================================== + +FeaturesAPI_Fillet1D::FeaturesAPI_Fillet1D(const std::shared_ptr& theFeature) + : FeaturesAPI_Fillet(theFeature) { initialize(); } -FeaturesAPI_Fillet::FeaturesAPI_Fillet(const std::shared_ptr& theFeature, - const std::list& theBaseObjects, - const ModelHighAPI_Double& theRadius) - : ModelHighAPI_Interface(theFeature) +FeaturesAPI_Fillet1D::FeaturesAPI_Fillet1D(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const ModelHighAPI_Double& theRadius) + : FeaturesAPI_Fillet(theFeature) +{ + if (initialize()) { + setBase(theBaseObjects); + fillAttribute(theRadius, myradius); + + execIfBaseNotEmpty(); + } +} + +FeaturesAPI_Fillet1D::~FeaturesAPI_Fillet1D() +{ +} + +void FeaturesAPI_Fillet1D::setBase(const std::list& theBaseObjects) +{ + mybaseWires->clear(); + mybaseVertices->clear(); + + if (typeOfSelection(theBaseObjects) == GeomAPI_Shape::WIRE) { + fillAttribute(FeaturesPlugin_Fillet1D::CREATION_BY_WIRES(), mycreationMethod); + fillAttribute(theBaseObjects, mybaseWires); + } + else { + fillAttribute(FeaturesPlugin_Fillet1D::CREATION_BY_VERTICES(), mycreationMethod); + fillAttribute(theBaseObjects, mybaseVertices); + } + + execIfBaseNotEmpty(); +} + +void FeaturesAPI_Fillet1D::setRadius(const ModelHighAPI_Double& theRadius) +{ + fillAttribute(theRadius, myradius); + execIfBaseNotEmpty(); +} + +void FeaturesAPI_Fillet1D::execIfBaseNotEmpty() +{ + if (mybaseWires->size() > 0 || mybaseVertices->size() > 0) + execute(); +} + +void FeaturesAPI_Fillet1D::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + AttributeSelectionListPtr anAttrObjects; + if (creationMethod()->value() == FeaturesPlugin_Fillet1D::CREATION_BY_WIRES()) + anAttrObjects = aBase->selectionList(FeaturesPlugin_Fillet1D::WIRE_LIST_ID()); + else if (creationMethod()->value() == FeaturesPlugin_Fillet1D::CREATION_BY_VERTICES()) + anAttrObjects = aBase->selectionList(FeaturesPlugin_Fillet1D::VERTEX_LIST_ID()); + + AttributeDoublePtr anAttrRadius = aBase->real(FeaturesPlugin_Fillet1D::RADIUS_ID()); + + theDumper << aBase << " = model.addFillet(" << aDocName << ", " << anAttrObjects + << ", " << anAttrRadius; + + if (!aBase->data()->version().empty()) + theDumper << ", keepSubResults = True"; + + theDumper << ")" << std::endl; +} + +//================================================================================================== + +FeaturesAPI_Fillet2D::FeaturesAPI_Fillet2D(const std::shared_ptr& theFeature) + : FeaturesAPI_Fillet(theFeature) +{ + initialize(); +} + +FeaturesAPI_Fillet2D::FeaturesAPI_Fillet2D(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const ModelHighAPI_Double& theRadius) + : FeaturesAPI_Fillet(theFeature) { if (initialize()) { fillAttribute(FeaturesPlugin_Fillet::CREATION_METHOD_SINGLE_RADIUS(), mycreationMethod); @@ -44,11 +136,11 @@ FeaturesAPI_Fillet::FeaturesAPI_Fillet(const std::shared_ptr& } } -FeaturesAPI_Fillet::FeaturesAPI_Fillet(const std::shared_ptr& theFeature, - const std::list& theBaseObjects, - const ModelHighAPI_Double& theRadius1, - const ModelHighAPI_Double& theRadius2) - : ModelHighAPI_Interface(theFeature) +FeaturesAPI_Fillet2D::FeaturesAPI_Fillet2D(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const ModelHighAPI_Double& theRadius1, + const ModelHighAPI_Double& theRadius2) + : FeaturesAPI_Fillet(theFeature) { if (initialize()) { fillAttribute(FeaturesPlugin_Fillet::CREATION_METHOD_VARYING_RADIUS(), mycreationMethod); @@ -60,12 +152,11 @@ FeaturesAPI_Fillet::FeaturesAPI_Fillet(const std::shared_ptr& } } -FeaturesAPI_Fillet::~FeaturesAPI_Fillet() +FeaturesAPI_Fillet2D::~FeaturesAPI_Fillet2D() { } -//================================================================================================== -void FeaturesAPI_Fillet::setBase(const std::list& theBaseObjects) +void FeaturesAPI_Fillet2D::setBase(const std::list& theBaseObjects) { mybaseObjects->clear(); fillAttribute(theBaseObjects, mybaseObjects); @@ -73,7 +164,7 @@ void FeaturesAPI_Fillet::setBase(const std::list& theBas execIfBaseNotEmpty(); } -void FeaturesAPI_Fillet::setRadius(const ModelHighAPI_Double& theRadius) +void FeaturesAPI_Fillet2D::setRadius(const ModelHighAPI_Double& theRadius) { fillAttribute(FeaturesPlugin_Fillet::CREATION_METHOD_SINGLE_RADIUS(), mycreationMethod); fillAttribute(theRadius, myradius); @@ -81,8 +172,8 @@ void FeaturesAPI_Fillet::setRadius(const ModelHighAPI_Double& theRadius) execIfBaseNotEmpty(); } -void FeaturesAPI_Fillet::setRadius(const ModelHighAPI_Double& theRadius1, - const ModelHighAPI_Double& theRadius2) +void FeaturesAPI_Fillet2D::setRadius(const ModelHighAPI_Double& theRadius1, + const ModelHighAPI_Double& theRadius2) { fillAttribute(FeaturesPlugin_Fillet::CREATION_METHOD_VARYING_RADIUS(), mycreationMethod); fillAttribute(theRadius1, mystartRadius); @@ -91,7 +182,7 @@ void FeaturesAPI_Fillet::setRadius(const ModelHighAPI_Double& theRadius1, execIfBaseNotEmpty(); } -void FeaturesAPI_Fillet::dump(ModelHighAPI_Dumper& theDumper) const +void FeaturesAPI_Fillet2D::dump(ModelHighAPI_Dumper& theDumper) const { FeaturePtr aBase = feature(); const std::string& aDocName = theDumper.name(aBase->document()); @@ -118,7 +209,7 @@ void FeaturesAPI_Fillet::dump(ModelHighAPI_Dumper& theDumper) const theDumper << ")" << std::endl; } -void FeaturesAPI_Fillet::execIfBaseNotEmpty() +void FeaturesAPI_Fillet2D::execIfBaseNotEmpty() { if (mybaseObjects->size() > 0) execute(); @@ -133,14 +224,20 @@ FilletPtr addFillet(const std::shared_ptr& thePart, const ModelHighAPI_Double& theRadius2, const bool keepSubResults) { - std::shared_ptr aFeature = thePart->addFeature(FeaturesAPI_Fillet::ID()); + GeomAPI_Shape::ShapeType aType = typeOfSelection(theBaseObjects); + bool is1D = aType == GeomAPI_Shape::WIRE || aType == GeomAPI_Shape::VERTEX; + + FeaturePtr aFeature = + thePart->addFeature(is1D ? FeaturesAPI_Fillet1D::ID() : FeaturesAPI_Fillet2D::ID()); if (!keepSubResults) aFeature->data()->setVersion(""); FilletPtr aFillet; - if (theRadius2.value() < 0.0) - aFillet.reset(new FeaturesAPI_Fillet(aFeature, theBaseObjects, theRadius1)); + if (is1D) + aFillet.reset(new FeaturesAPI_Fillet1D(aFeature, theBaseObjects, theRadius1)); + else if (theRadius2.value() < 0.0) + aFillet.reset(new FeaturesAPI_Fillet2D(aFeature, theBaseObjects, theRadius1)); else - aFillet.reset(new FeaturesAPI_Fillet(aFeature, theBaseObjects, theRadius1, theRadius2)); + aFillet.reset(new FeaturesAPI_Fillet2D(aFeature, theBaseObjects, theRadius1, theRadius2)); return aFillet; } diff --git a/src/FeaturesAPI/FeaturesAPI_Fillet.h b/src/FeaturesAPI/FeaturesAPI_Fillet.h index b9adb3ff3..dd15caef9 100644 --- a/src/FeaturesAPI/FeaturesAPI_Fillet.h +++ b/src/FeaturesAPI/FeaturesAPI_Fillet.h @@ -23,6 +23,7 @@ #include "FeaturesAPI.h" #include +#include #include #include @@ -35,27 +36,103 @@ class ModelHighAPI_Selection; /// \brief Interface for Fillet feature. class FeaturesAPI_Fillet: public ModelHighAPI_Interface { +public: + /// Destructor. + virtual ~FeaturesAPI_Fillet() {} + + virtual std::shared_ptr radius() const = 0; + + /// Modify base objects of the fillet. + virtual void setBase(const std::list& theBaseObjects) = 0; + + /// Modify fillet to have fixed radius + virtual void setRadius(const ModelHighAPI_Double& theRadius) = 0; + +protected: + FeaturesAPI_Fillet(const std::shared_ptr& theFeature); +}; + +/// Pointer on the fillet object. +typedef std::shared_ptr FilletPtr; + + +/// \class FeaturesAPI_Fillet1D +/// \ingroup CPPHighAPI +/// \brief Interface for Fillet1D feature - fillet on vertices of a wire. +class FeaturesAPI_Fillet1D : public FeaturesAPI_Fillet +{ public: /// Constructor without values. FEATURESAPI_EXPORT - explicit FeaturesAPI_Fillet(const std::shared_ptr& theFeature); + explicit FeaturesAPI_Fillet1D(const std::shared_ptr& theFeature); /// Constructor with values. FEATURESAPI_EXPORT - explicit FeaturesAPI_Fillet(const std::shared_ptr& theFeature, - const std::list& theBaseObjects, - const ModelHighAPI_Double& theRadius); + explicit FeaturesAPI_Fillet1D(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const ModelHighAPI_Double& theRadius); + + /// Destructor. + FEATURESAPI_EXPORT + virtual ~FeaturesAPI_Fillet1D(); + + INTERFACE_4(FeaturesPlugin_Fillet1D::ID(), + creationMethod, FeaturesPlugin_Fillet1D::CREATION_METHOD(), + ModelAPI_AttributeString, + /** Creation method */, + baseWires, FeaturesPlugin_Fillet1D::WIRE_LIST_ID(), + ModelAPI_AttributeSelectionList, + /** Base objects */, + baseVertices, FeaturesPlugin_Fillet1D::VERTEX_LIST_ID(), + ModelAPI_AttributeSelectionList, + /** Base objects */, + radius, FeaturesPlugin_Fillet1D::RADIUS_ID(), + ModelAPI_AttributeDouble, + /** Value of the fixed radius fillet */) + + /// Modify base objects of the fillet. + FEATURESAPI_EXPORT + virtual void setBase(const std::list& theBaseObjects); + + /// Modify fillet to have fixed radius + FEATURESAPI_EXPORT + virtual void setRadius(const ModelHighAPI_Double& theRadius); + + /// Dump wrapped feature + FEATURESAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; + +private: + void execIfBaseNotEmpty(); +}; + + +/// \class FeaturesAPI_Fillet2D +/// \ingroup CPPHighAPI +/// \brief Interface for Fillet feature - fillet edges on a solid. +class FeaturesAPI_Fillet2D : public FeaturesAPI_Fillet +{ +public: + /// Constructor without values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Fillet2D(const std::shared_ptr& theFeature); + + /// Constructor with values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Fillet2D(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const ModelHighAPI_Double& theRadius); /// Constructor with values. FEATURESAPI_EXPORT - explicit FeaturesAPI_Fillet(const std::shared_ptr& theFeature, - const std::list& theBaseObjects, - const ModelHighAPI_Double& theRadius1, - const ModelHighAPI_Double& theRadius2); + explicit FeaturesAPI_Fillet2D(const std::shared_ptr& theFeature, + const std::list& theBaseObjects, + const ModelHighAPI_Double& theRadius1, + const ModelHighAPI_Double& theRadius2); /// Destructor. FEATURESAPI_EXPORT - virtual ~FeaturesAPI_Fillet(); + virtual ~FeaturesAPI_Fillet2D(); INTERFACE_5(FeaturesPlugin_Fillet::ID(), creationMethod, FeaturesPlugin_Fillet::CREATION_METHOD(), @@ -76,11 +153,11 @@ public: /// Modify base objects of the fillet. FEATURESAPI_EXPORT - void setBase(const std::list& theBaseObjects); + virtual void setBase(const std::list& theBaseObjects); /// Modify fillet to have fixed radius FEATURESAPI_EXPORT - void setRadius(const ModelHighAPI_Double& theRadius); + virtual void setRadius(const ModelHighAPI_Double& theRadius); /// Modify fillet to have varying radius FEATURESAPI_EXPORT @@ -94,8 +171,6 @@ private: void execIfBaseNotEmpty(); }; -/// Pointer on Fillet object. -typedef std::shared_ptr FilletPtr; /// \ingroup CPPHighAPI /// \brief Create Fillet feature. diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index 51d00ae40..b75a1a17e 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -57,6 +57,7 @@ SET(PROJECT_HEADERS FeaturesPlugin_MultiTranslation.h FeaturesPlugin_MultiRotation.h FeaturesPlugin_Fillet.h + FeaturesPlugin_Fillet1D.h FeaturesPlugin_Measurement.h FeaturesPlugin_FusionFaces.h FeaturesPlugin_RemoveResults.h @@ -103,6 +104,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_MultiTranslation.cpp FeaturesPlugin_MultiRotation.cpp FeaturesPlugin_Fillet.cpp + FeaturesPlugin_Fillet1D.cpp FeaturesPlugin_Measurement.cpp FeaturesPlugin_FusionFaces.cpp FeaturesPlugin_RemoveResults.cpp @@ -140,6 +142,7 @@ SET(XML_RESOURCES multitranslation_widget.xml multirotation_widget.xml fillet_widget.xml + fillet1d_widget.xml measurement_widget.xml fusion_faces_widget.xml chamfer_widget.xml @@ -658,4 +661,17 @@ ADD_UNIT_TESTS(TestExtrusion.py TestChamfer_MultiLevelCompound_v95_4.py Test18836.py Test2817.py + TestFillet1D_ErrorMsg.py + TestFillet1D_Vertices_1.py + TestFillet1D_Vertices_2.py + TestFillet1D_Vertices_3.py + TestFillet1D_Vertices_4.py + TestFillet1D_Vertices_5.py + TestFillet1D_Vertices_6.py + TestFillet1D_Vertices_7.py + TestFillet1D_Vertices_8.py + TestFillet1D_Wire_1.py + TestFillet1D_Wire_2.py + TestFillet1D_Wire_3.py + TestFillet1D_Wire_4.py ) diff --git a/src/FeaturesPlugin/FeaturesPlugin_Fillet1D.cpp b/src/FeaturesPlugin/FeaturesPlugin_Fillet1D.cpp new file mode 100644 index 000000000..d62f6acf4 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Fillet1D.cpp @@ -0,0 +1,160 @@ +// Copyright (C) 2020 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +FeaturesPlugin_Fillet1D::FeaturesPlugin_Fillet1D() +{ +} + +void FeaturesPlugin_Fillet1D::initAttributes() +{ + data()->addAttribute(CREATION_METHOD(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(WIRE_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); + data()->addAttribute(VERTEX_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()); + data()->addAttribute(RADIUS_ID(), ModelAPI_AttributeDouble::typeId()); +} + +void FeaturesPlugin_Fillet1D::execute() +{ + MapShapeSubs aWireVertices; + if (!baseShapes(aWireVertices)) + return; + + int aResultIndex = 0; + for (MapShapeSubs::iterator anIt = aWireVertices.begin(); anIt != aWireVertices.end(); ++anIt) + if (!performFillet(anIt->first, anIt->second, aResultIndex++)) + break; + removeResults(aResultIndex); +} + +bool FeaturesPlugin_Fillet1D::baseShapes(MapShapeSubs& theWireVertices) +{ + std::string aMethod = string(CREATION_METHOD())->value(); + if (aMethod == CREATION_BY_WIRES()) { + AttributeSelectionListPtr aSelList = selectionList(WIRE_LIST_ID()); + + std::set aProcessedWires; + int aNbSel = aSelList->size(); + for (int ind = 0; ind < aNbSel; ++ind) { + AttributeSelectionPtr aCurSel = aSelList->value(ind); + GeomShapePtr aWire = aCurSel->context()->shape(); + if (aProcessedWires.find(aWire) != aProcessedWires.end()) + continue; + + aProcessedWires.insert(aWire); + // get vertices applicable for fillet + GeomAlgoAPI_MapShapesAndAncestors aMapVE(aWire, GeomAPI_Shape::VERTEX, GeomAPI_Shape::EDGE); + const MapShapeToShapes& aSubshapes = aMapVE.map(); + std::set aFilletVertices; + for (MapShapeToShapes::const_iterator anIt = aSubshapes.begin(); + anIt != aSubshapes.end(); ++anIt) { + // vertex should have 2 adjacent edges + if (anIt->second.size() != 2) + continue; + + // skip vertices, which adjacent edges are not on the same plane + ListOfShape anEdges; + anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end()); + GeomPlanePtr aPlane = GeomAlgoAPI_ShapeTools::findPlane(anEdges); + if (!aPlane) + continue; + + // skip vertices, which smoothly connect adjacent edges + GeomEdgePtr anEdge1(new GeomAPI_Edge(anEdges.front())); + GeomEdgePtr anEdge2(new GeomAPI_Edge(anEdges.back())); + GeomVertexPtr aSharedVertex(new GeomAPI_Vertex(anIt->first)); + if (GeomAlgoAPI_ShapeTools::isTangent(anEdge1, anEdge2, aSharedVertex)) + continue; + + aFilletVertices.insert(anIt->first); + } + + if (aFilletVertices.empty()) { + setError("Wire has no vertices for fillet."); + return false; + } + + + // keep the sequence of fillet vertices stable + for (GeomAPI_WireExplorer anExp(aWire->wire()); anExp.more(); anExp.next()) { + GeomShapePtr aVertex = anExp.currentVertex(); + if (aFilletVertices.find(aVertex) != aFilletVertices.end()) + theWireVertices[aWire].push_back(aVertex); + } + } + } + else if (aMethod == CREATION_BY_VERTICES()) { + AttributeSelectionListPtr aSelList = selectionList(VERTEX_LIST_ID()); + int aNbSel = aSelList->size(); + for (int ind = 0; ind < aNbSel; ++ind) { + AttributeSelectionPtr aCurSel = aSelList->value(ind); + GeomShapePtr aWire = aCurSel->context()->shape(); + GeomShapePtr aVertex = aCurSel->value(); + theWireVertices[aWire].push_back(aVertex); + } + } + return true; +} + +bool FeaturesPlugin_Fillet1D::performFillet(const GeomShapePtr& theWire, + const ListOfShape& theVertices, + const int theResultIndex) +{ + double aRadius = real(RADIUS_ID())->value(); + + // perform fillet operation + std::shared_ptr aFilletBuilder( + new GeomAlgoAPI_Fillet1D(theWire, theVertices, aRadius)); + + std::string anError; + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aFilletBuilder, getKind(), anError)) { + setError(anError); + return false; + } + + static const std::string THE_PREFIX = "Fillet1D"; + + // store result + ResultBodyPtr aResult = document()->createBody(data(), theResultIndex); + ListOfShape anOriginal; + anOriginal.push_back(theWire); + FeaturesPlugin_Tools::loadModifiedShapes(aResult, anOriginal, ListOfShape(), + aFilletBuilder, aFilletBuilder->shape(), THE_PREFIX); + setResult(aResult, theResultIndex); + // store new edges generated from vertices + for (ListOfShape::const_iterator anIt = theVertices.begin(); anIt != theVertices.end(); ++anIt) + aResult->loadGeneratedShapes(aFilletBuilder, *anIt, GeomAPI_Shape::VERTEX, THE_PREFIX, true); + + return true; +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Fillet1D.h b/src/FeaturesPlugin/FeaturesPlugin_Fillet1D.h new file mode 100644 index 000000000..de2a38024 --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Fillet1D.h @@ -0,0 +1,112 @@ +// Copyright (C) 2020 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef FeaturesPlugin_Fillet1D_H_ +#define FeaturesPlugin_Fillet1D_H_ + +#include + +#include +#include + +#include + +/// \class FeaturesPlugin_Fillet1D +/// \ingroup Plugins +/// \brief Feature for appling fillet on vertices of 3D wire. +class FeaturesPlugin_Fillet1D : public ModelAPI_Feature +{ + typedef std::map MapShapeSubs; + +public: + /// Feature kind. + inline static const std::string& ID() + { + static const std::string MY_ID("Fillet1D"); + return MY_ID; + } + + /// \return the kind of a feature. + FEATURESPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = FeaturesPlugin_Fillet1D::ID(); + return MY_KIND; + } + + inline static const std::string& CREATION_METHOD() + { + static std::string MY_CREATION_METHOD("creation_method"); + return MY_CREATION_METHOD; + } + + inline static const std::string CREATION_BY_WIRES() + { + static std::string MY_SINGLE_RADIUS("by_wires"); + return MY_SINGLE_RADIUS; + } + + inline static const std::string CREATION_BY_VERTICES() + { + static std::string MY_VARYING_RADIUS("by_vertices"); + return MY_VARYING_RADIUS; + } + + /// Attribute name of selected wires. + inline static const std::string& WIRE_LIST_ID() + { + static const std::string MY_OBJECT_LIST_ID("main_wires"); + return MY_OBJECT_LIST_ID; + } + + /// Attribute name of selected vertices. + inline static const std::string& VERTEX_LIST_ID() + { + static const std::string MY_OBJECT_LIST_ID("main_vertices"); + return MY_OBJECT_LIST_ID; + } + + /// Attribute name of radius. + inline static const std::string& RADIUS_ID() + { + static const std::string MY_RADIUS_ID("radius"); + return MY_RADIUS_ID; + } + + /// Request for initialization of data model of the feature: adding all attributes. + FEATURESPLUGIN_EXPORT virtual void initAttributes(); + + /// Performs the fillet algorithm and stores it in the data structure. + FEATURESPLUGIN_EXPORT virtual void execute(); + + /// Use plugin manager for features creation. + FeaturesPlugin_Fillet1D(); + +private: + /// Get the list of wires and fillet vertices + /// \retun \c false if errors occured + bool baseShapes(MapShapeSubs& theWireVertices); + + /// Run fillet operation and store result. + /// \return \c false if fillet failed. + bool performFillet(const GeomShapePtr& theWire, + const ListOfShape& theVertices, + const int theResultIndex); +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index 5f226c11b..94daac07e 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,8 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin() new FeaturesPlugin_ValidatorConcealedResult); aFactory->registerValidator("FeaturesPlugin_ValidatorFilletSelection", new FeaturesPlugin_ValidatorFilletSelection); + aFactory->registerValidator("FeaturesPlugin_ValidatorFillet1DSelection", + new FeaturesPlugin_ValidatorFillet1DSelection); aFactory->registerValidator("FeaturesPlugin_ValidatorCircular", new FeaturesPlugin_ValidatorCircular); aFactory->registerValidator("FeaturesPlugin_ValidatorBooleanArguments", @@ -180,6 +183,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new FeaturesPlugin_MultiRotation); } else if (theFeatureID == FeaturesPlugin_Fillet::ID()) { return FeaturePtr(new FeaturesPlugin_Fillet); + } else if (theFeatureID == FeaturesPlugin_Fillet1D::ID()) { + return FeaturePtr(new FeaturesPlugin_Fillet1D); } else if (theFeatureID == FeaturesPlugin_Measurement::ID()) { return FeaturePtr(new FeaturesPlugin_Measurement); } else if (theFeatureID == FeaturesPlugin_RemoveResults::ID()) { diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp index 1c7a4da10..538a5097f 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp @@ -54,6 +54,7 @@ #include #include +#include #include #include #include @@ -938,6 +939,79 @@ bool FeaturesPlugin_ValidatorFilletSelection::isValid(const AttributePtr& theAtt return true; } + +//================================================================================================== +bool FeaturesPlugin_ValidatorFillet1DSelection::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + AttributeSelectionListPtr anAttrSelectionList = + std::dynamic_pointer_cast(theAttribute); + if (!anAttrSelectionList.get()) { + // LCOV_EXCL_START + theError = + "Error: This validator can only work with selection list attributes in \"Fillet\" feature."; + return false; + // LCOV_EXCL_STOP + } + + // check each selected vertex is a sharp corner between adjacent edges, + // and these edges are in the same plane + std::map aWireSubshapes; + int aNbSel = anAttrSelectionList->size(); + for (int ind = 0; ind < aNbSel; ++ind) { + AttributeSelectionPtr aCurSel = anAttrSelectionList->value(ind); + GeomShapePtr aContext = aCurSel->context()->shape(); + GeomShapePtr aVertex = aCurSel->value(); + // check wire already processed, if not, store all vertices and edges, sharing them + std::map::iterator aProcessed = aWireSubshapes.find(aContext); + if (aProcessed == aWireSubshapes.end()) { + if (aContext->shapeType() != GeomAPI_Shape::WIRE) { + theError = "Selected vertex is not a wire corner"; + return false; + } + if (aVertex->shapeType() != GeomAPI_Shape::VERTEX) { + theError = "Selected shape it not a vertex"; + return false; + } + + GeomAlgoAPI_MapShapesAndAncestors aMapVE(aContext, GeomAPI_Shape::VERTEX, + GeomAPI_Shape::EDGE); + aWireSubshapes[aContext] = aMapVE.map(); + aProcessed = aWireSubshapes.find(aContext); + } + + // check the vertex + MapShapeToShapes::iterator aFound = aProcessed->second.find(aVertex); + if (aFound == aProcessed->second.end()) { + theError = "Selected vertex does not exist in the wire"; + return true; + } + else if (aFound->second.size() != 2) { + theError = "Vertex should be shared between 2 edges exactly"; + return false; + } + + ListOfShape anEdges; + anEdges.insert(anEdges.end(), aFound->second.begin(), aFound->second.end()); + GeomPlanePtr aPlane = GeomAlgoAPI_ShapeTools::findPlane(anEdges); + if (!aPlane) { + theError = "Error: Edges are not planar"; + return false; + } + + GeomEdgePtr anEdge1(new GeomAPI_Edge(anEdges.front())); + GeomEdgePtr anEdge2(new GeomAPI_Edge(anEdges.back())); + GeomVertexPtr aSharedVertex(new GeomAPI_Vertex(aVertex)); + if (GeomAlgoAPI_ShapeTools::isTangent(anEdge1, anEdge2, aSharedVertex)) { + theError = "Error: Edges are tangent"; + 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 da3222d1e..8d86d2a2e 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.h @@ -188,6 +188,22 @@ public: Events_InfoMessage& theError) const; }; +/// \class FeaturesPlugin_ValidatorFillet1DSelection +/// \ingroup Validators +/// \brief Validates selection for 1d-fillet operation. +class FeaturesPlugin_ValidatorFillet1DSelection : public ModelAPI_AttributeValidator +{ +public: + /// \return True if the attribute is valid. It checks whether the selection + /// is acceptable for fillet on wire (vertex is a sharp corner). + /// \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/Test/TestFillet1D_ErrorMsg.py b/src/FeaturesPlugin/Test/TestFillet1D_ErrorMsg.py new file mode 100644 index 000000000..d491c3700 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_ErrorMsg.py @@ -0,0 +1,80 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(35.66344827586208, 18.52827586206897, -10.58758620689655, 18.52827586206897) +Sketch_1.setHorizontal(SketchLine_1.result()) + +### Create SketchProjection +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_2 = SketchProjection_1.createdFeature() + +### Create SketchArc +SketchArc_1 = Sketch_1.addArc(-10.58758620689655, 34.59034482758621, -10.58758620689655, 18.52827586206897, 0, 46.66896551724138, True) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchArc_1.startPoint()) +Sketch_1.setTangent(SketchLine_1.result(), SketchArc_1.results()[1]) +Sketch_1.setCoincident(SketchLine_2.result(), SketchArc_1.endPoint()) +model.do() + +### Create Sketch +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("YOZ")) + +### Create SketchLine +SketchLine_3 = Sketch_2.addLine(46.66896551724138, 0, 49.59252615395944, 57.3324122669822) + +### Create SketchProjection +SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "Sketch_1/SketchArc_1_2_StartVertex"), False) +SketchPoint_1 = SketchProjection_2.createdFeature() +Sketch_2.setCoincident(SketchLine_3.startPoint(), SketchPoint_1.result()) +model.do() + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchArc_1_2"), model.selection("EDGE", "Sketch_2/SketchLine_3")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) +model.end() + + +### Check errors on 1D-fillet +vertices = ["Sketch_1/SketchLine_1_StartVertex", + "Sketch_2/SketchLine_3_EndVertex", + "[Wire_1_1/Modified_Edge&Sketch_1/SketchLine_1]e[Wire_1_1/Modified_Edge&Sketch_1/SketchArc_1_2]e", + "[Wire_1_1/Modified_Edge&Sketch_1/SketchArc_1_2]e[Wire_1_1/Modified_Edge&Sketch_2/SketchLine_3]e"] +for v in vertices: + model.begin() + Fillet1D = model.addFillet(Part_1_doc, [model.selection("VERTEX", v)], 1) + model.end() + assert(Fillet1D.feature().error() != "") + +### Wire has no vertices applicable for 1D-fillet +model.begin() +Fillet1D = model.addFillet(Part_1_doc, [model.selection("WIRE", "Wire_1_1")], 1) +model.end() +assert(Fillet1D.feature().error() != "") diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_1.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_1.py new file mode 100644 index 000000000..078f4950c --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_1.py @@ -0,0 +1,63 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +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, 10, 10, 10) + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_1]e[Wire_1_1/Edge_2]e")], 2) + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [6]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [12]) +model.testResultsVolumes(Fillet1D_1, [0]) + +### Create Fillet1D +Fillet1D_2 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[(Wire_1_1/Edge_4)(Wire_1_1/Edge_5)2_Fillet1D_1_1]e[Wire_1_1/Edge_4]e")], 2) + +model.testHaveNamingByType(Fillet1D_2, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_2, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_2, 1) +model.testNbSubResults(Fillet1D_2, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.EDGE, [7]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.VERTEX, [14]) +model.testResultsVolumes(Fillet1D_2, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_2.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_2.py new file mode 100644 index 000000000..8bd49a151 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_2.py @@ -0,0 +1,74 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +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, 10, 10, 10) + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"), model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_4]e[Wire_1_1/Edge_6]e")], 2) + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [9]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [18]) +model.testResultsVolumes(Fillet1D_1, [0]) + +### Create Fillet1D +Fillet1D_2 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_1]e[Wire_1_1/Edge_3]e")], 1) + +model.testNbResults(Fillet1D_2, 1) +model.testNbSubResults(Fillet1D_2, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.EDGE, [10]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.VERTEX, [20]) +model.testResultsVolumes(Fillet1D_2, [0]) + +### Create Fillet1D +Fillet1D_3 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[(Wire_1_1/Edge_8)4(Wire_1_1/Edge_5)4_Fillet1D_2_1]e[(Wire_1_1/Edge_8)3(Wire_1_1/Edge_7)4_Fillet1D_2_1]e")], 5) + +model.testHaveNamingByType(Fillet1D_3, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_3, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_3, 1) +model.testNbSubResults(Fillet1D_3, [0]) +model.testNbSubShapes(Fillet1D_3, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_3, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_3, GeomAPI_Shape.EDGE, [11]) +model.testNbSubShapes(Fillet1D_3, GeomAPI_Shape.VERTEX, [22]) +model.testResultsVolumes(Fillet1D_3, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_3.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_3.py new file mode 100644 index 000000000..37e0ee235 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_3.py @@ -0,0 +1,63 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Cylinder +Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10, 45) +Wire_1_objects = [model.selection("EDGE", "[Cylinder_1_1/Face_1][Cylinder_1_1/Face_2]"), model.selection("EDGE", "[Cylinder_1_1/Face_2][Cylinder_1_1/Face_5]"), model.selection("EDGE", "[Cylinder_1_1/Face_4][Cylinder_1_1/Face_5]"), model.selection("EDGE", "[Cylinder_1_1/Face_3][Cylinder_1_1/Face_4]"), model.selection("EDGE", "[Cylinder_1_1/Face_1][Cylinder_1_1/Face_3]")] + +### Create Wire +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_1]e[Wire_1_1/Edge_2]e")], 1) + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [6]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [12]) +model.testResultsVolumes(Fillet1D_1, [0]) + +### Create Fillet1D +Fillet1D_2 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_4]e[Wire_1_1/Edge_3]e")], 3) + +model.testHaveNamingByType(Fillet1D_2, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_2, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_2, 1) +model.testNbSubResults(Fillet1D_2, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.EDGE, [7]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.VERTEX, [14]) +model.testResultsVolumes(Fillet1D_2, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_4.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_4.py new file mode 100644 index 000000000..9112fcda4 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_4.py @@ -0,0 +1,78 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(-21.09850585253612, 7.838733928946021, 28.18352301808598, -36.47460859551143) + +### Create SketchLine +SketchLine_2 = Sketch_1.addLine(28.18352301808598, -36.47460859551143, -24.86780397022334, -36.47460859551143) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +Sketch_1.setHorizontal(SketchLine_2.result()) + +### Create SketchArc +SketchArc_1 = Sketch_1.addArc(-24.86780397022334, -14.15762886307242, -24.86780397022334, -36.47460859551143, -21.09850585253612, 7.838733928946021, True) +Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchArc_1.startPoint()) +Sketch_1.setTangent(SketchLine_2.result(), SketchArc_1.results()[1]) +Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.startPoint()) +model.do() +Wire_1_objects = [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2"), model.selection("EDGE", "Sketch_1/SketchArc_1_2")] + +### Create Wire +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Modified_Edge&Sketch_1/SketchLine_1]e[Wire_1_1/Modified_Edge&Sketch_1/SketchLine_2]e")], 2) + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [4]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [8]) +model.testResultsVolumes(Fillet1D_1, [0]) + +### Create Fillet1D +Fillet1D_2 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Fillet1D_1_1/ME:Fillet1D&Sketch_1/SketchLine_1]e[_weak_name_1_Fillet1D_1_1]e")], 20) + +model.testHaveNamingByType(Fillet1D_2, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_2, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_2, 1) +model.testNbSubResults(Fillet1D_2, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.EDGE, [5]) +model.testNbSubShapes(Fillet1D_2, GeomAPI_Shape.VERTEX, [10]) +model.testResultsVolumes(Fillet1D_2, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_5.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_5.py new file mode 100644 index 000000000..a2406906f --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_5.py @@ -0,0 +1,52 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +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, 10, 10, 10) + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_2]e[Wire_1_1/Edge_3]e"), model.selection("VERTEX", "[Wire_1_1/Edge_3]e[Wire_1_1/Edge_4]e")], 2) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [7]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [14]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_6.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_6.py new file mode 100644 index 000000000..46ffa866a --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_6.py @@ -0,0 +1,52 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +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, 10, 10, 10) + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"), model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_4]e[Wire_1_1/Edge_6]e"), model.selection("VERTEX", "[Wire_1_1/Edge_2]e[Wire_1_1/Edge_4]e"), model.selection("VERTEX", "[Wire_1_1/Edge_1]e[Wire_1_1/Edge_3]e")], 2) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [11]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [22]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_7.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_7.py new file mode 100644 index 000000000..777ecc0ac --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_7.py @@ -0,0 +1,52 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Cylinder +Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10, 45) +Wire_1_objects = [model.selection("EDGE", "[Cylinder_1_1/Face_1][Cylinder_1_1/Face_2]"), model.selection("EDGE", "[Cylinder_1_1/Face_2][Cylinder_1_1/Face_5]"), model.selection("EDGE", "[Cylinder_1_1/Face_4][Cylinder_1_1/Face_5]"), model.selection("EDGE", "[Cylinder_1_1/Face_3][Cylinder_1_1/Face_4]"), model.selection("EDGE", "[Cylinder_1_1/Face_1][Cylinder_1_1/Face_3]")] + +### Create Wire +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Edge_1]e[Wire_1_1/Edge_2]e"), model.selection("VERTEX", "[Wire_1_1/Edge_3]e[Wire_1_1/Edge_4]e")], 2) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [7]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [14]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Vertices_8.py b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_8.py new file mode 100644 index 000000000..ac9bdcf21 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Vertices_8.py @@ -0,0 +1,67 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(-21.09850585253612, 7.838733928946021, 28.18352301808598, -36.47460859551143) + +### Create SketchLine +SketchLine_2 = Sketch_1.addLine(28.18352301808598, -36.47460859551143, -24.86780397022334, -36.47460859551143) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +Sketch_1.setHorizontal(SketchLine_2.result()) + +### Create SketchArc +SketchArc_1 = Sketch_1.addArc(-24.86780397022334, -14.15762886307242, -24.86780397022334, -36.47460859551143, -21.09850585253612, 7.838733928946021, True) +Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchArc_1.startPoint()) +Sketch_1.setTangent(SketchLine_2.result(), SketchArc_1.results()[1]) +Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.startPoint()) +model.do() +Wire_1_objects = [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2"), model.selection("EDGE", "Sketch_1/SketchArc_1_2")] + +### Create Wire +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("VERTEX", "[Wire_1_1/Modified_Edge&Sketch_1/SketchLine_1]e[Wire_1_1/Modified_Edge&Sketch_1/SketchLine_2]e"), model.selection("VERTEX", "[Wire_1_1/Modified_Edge&Sketch_1/SketchLine_1]e[Wire_1_1/Modified_Edge&Sketch_1/SketchArc_1_2]e")], 15) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [5]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [10]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Wire_1.py b/src/FeaturesPlugin/Test/TestFillet1D_Wire_1.py new file mode 100644 index 000000000..aaad4ecdf --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Wire_1.py @@ -0,0 +1,52 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +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, 10, 10, 10) + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("WIRE", "Wire_1_1")], 2) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [9]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [18]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Wire_2.py b/src/FeaturesPlugin/Test/TestFillet1D_Wire_2.py new file mode 100644 index 000000000..dea6667ea --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Wire_2.py @@ -0,0 +1,52 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +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, 10, 10, 10) + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"), model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Left]")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("WIRE", "Wire_1_1")], 2) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [16]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [32]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Wire_3.py b/src/FeaturesPlugin/Test/TestFillet1D_Wire_3.py new file mode 100644 index 000000000..834dd2b1a --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Wire_3.py @@ -0,0 +1,52 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Cylinder +Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10, 45) +Wire_1_objects = [model.selection("EDGE", "[Cylinder_1_1/Face_1][Cylinder_1_1/Face_2]"), model.selection("EDGE", "[Cylinder_1_1/Face_2][Cylinder_1_1/Face_5]"), model.selection("EDGE", "[Cylinder_1_1/Face_4][Cylinder_1_1/Face_5]"), model.selection("EDGE", "[Cylinder_1_1/Face_3][Cylinder_1_1/Face_4]"), model.selection("EDGE", "[Cylinder_1_1/Face_1][Cylinder_1_1/Face_3]")] + +### Create Wire +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("WIRE", "Wire_1_1")], 1) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [9]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [18]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/Test/TestFillet1D_Wire_4.py b/src/FeaturesPlugin/Test/TestFillet1D_Wire_4.py new file mode 100644 index 000000000..5249526f7 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestFillet1D_Wire_4.py @@ -0,0 +1,67 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(-21.09850585253612, 7.838733928946021, 28.18352301808598, -36.47460859551143) + +### Create SketchLine +SketchLine_2 = Sketch_1.addLine(28.18352301808598, -36.47460859551143, -24.86780397022334, -36.47460859551143) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +Sketch_1.setHorizontal(SketchLine_2.result()) + +### Create SketchArc +SketchArc_1 = Sketch_1.addArc(-24.86780397022334, -14.15762886307242, -24.86780397022334, -36.47460859551143, -21.09850585253612, 7.838733928946021, True) +Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchArc_1.startPoint()) +Sketch_1.setTangent(SketchLine_2.result(), SketchArc_1.results()[1]) +Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.startPoint()) +model.do() +Wire_1_objects = [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2"), model.selection("EDGE", "Sketch_1/SketchArc_1_2")] + +### Create Wire +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Fillet1D +Fillet1D_1 = model.addFillet(Part_1_doc, [model.selection("WIRE", "Wire_1_1")], 2) + +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.VERTEX) +model.testHaveNamingByType(Fillet1D_1, model, Part_1_doc, GeomAPI_Shape.EDGE) +model.end() + +model.testNbResults(Fillet1D_1, 1) +model.testNbSubResults(Fillet1D_1, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.SOLID, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.FACE, [0]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.EDGE, [5]) +model.testNbSubShapes(Fillet1D_1, GeomAPI_Shape.VERTEX, [10]) +model.testResultsVolumes(Fillet1D_1, [0]) + +assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/FeaturesPlugin/fillet1d_widget.xml b/src/FeaturesPlugin/fillet1d_widget.xml new file mode 100644 index 000000000..d96706396 --- /dev/null +++ b/src/FeaturesPlugin/fillet1d_widget.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/FeaturesPlugin/icons/fillet1d.png b/src/FeaturesPlugin/icons/fillet1d.png new file mode 100644 index 000000000..a94aba335 Binary files /dev/null and b/src/FeaturesPlugin/icons/fillet1d.png differ diff --git a/src/FeaturesPlugin/icons/fillet1d_points.png b/src/FeaturesPlugin/icons/fillet1d_points.png new file mode 100644 index 000000000..c0f751a92 Binary files /dev/null and b/src/FeaturesPlugin/icons/fillet1d_points.png differ diff --git a/src/FeaturesPlugin/icons/fillet1d_wire.png b/src/FeaturesPlugin/icons/fillet1d_wire.png new file mode 100644 index 000000000..62eed227d Binary files /dev/null and b/src/FeaturesPlugin/icons/fillet1d_wire.png differ diff --git a/src/FeaturesPlugin/plugin-Features.xml b/src/FeaturesPlugin/plugin-Features.xml index 5fbe8a1f4..10bd904de 100644 --- a/src/FeaturesPlugin/plugin-Features.xml +++ b/src/FeaturesPlugin/plugin-Features.xml @@ -116,6 +116,15 @@ + + + diff --git a/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp b/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp index bf5805e15..3bb282186 100644 --- a/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp +++ b/src/FiltersPlugin/FiltersPlugin_OppositeToEdge.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/src/GeomAPI/GeomAPI_Edge.cpp b/src/GeomAPI/GeomAPI_Edge.cpp index 9364605b9..03e44e808 100644 --- a/src/GeomAPI/GeomAPI_Edge.cpp +++ b/src/GeomAPI/GeomAPI_Edge.cpp @@ -461,6 +461,22 @@ void GeomAPI_Edge::setLastPointTolerance(const double theTolerance) BRep_Builder().UpdateVertex(aVLast, theTolerance); } +double GeomAPI_Edge::firstPointTolerance() const +{ + TopoDS_Edge anEdge = impl(); + TopoDS_Vertex aVFirst, aVLast; + TopExp::Vertices(anEdge, aVFirst, aVLast); + return BRep_Tool::Tolerance(aVFirst); +} + +double GeomAPI_Edge::lastPointTolerance() const +{ + TopoDS_Edge anEdge = impl(); + TopoDS_Vertex aVFirst, aVLast; + TopExp::Vertices(anEdge, aVFirst, aVLast); + return BRep_Tool::Tolerance(aVLast); +} + GeomPointPtr GeomAPI_Edge::middlePoint() const { GeomPointPtr aMiddlePoint; diff --git a/src/GeomAPI/GeomAPI_Edge.h b/src/GeomAPI/GeomAPI_Edge.h index 3e926d8d0..7823f97b3 100644 --- a/src/GeomAPI/GeomAPI_Edge.h +++ b/src/GeomAPI/GeomAPI_Edge.h @@ -135,6 +135,10 @@ public: GEOMAPI_EXPORT void setLastPointTolerance(const double theTolerance); + GEOMAPI_EXPORT double firstPointTolerance() const; + + GEOMAPI_EXPORT double lastPointTolerance() const; + /// Return middle point on the edge GEOMAPI_EXPORT virtual std::shared_ptr middlePoint() const; diff --git a/src/GeomAPI/GeomAPI_WireExplorer.cpp b/src/GeomAPI/GeomAPI_WireExplorer.cpp index efd37a0ae..edae95865 100644 --- a/src/GeomAPI/GeomAPI_WireExplorer.cpp +++ b/src/GeomAPI/GeomAPI_WireExplorer.cpp @@ -18,6 +18,9 @@ // #include + +#include +#include #include #include @@ -49,11 +52,19 @@ void GeomAPI_WireExplorer::next() MY_EXPLORER->Next(); } -std::shared_ptr GeomAPI_WireExplorer::current() +std::shared_ptr GeomAPI_WireExplorer::current() +{ + const TopoDS_Edge& aShape = MY_EXPLORER->Current(); + std::shared_ptr aGeomShape(new GeomAPI_Edge()); + aGeomShape->setImpl(new TopoDS_Edge(aShape)); + return aGeomShape; +} + +std::shared_ptr GeomAPI_WireExplorer::currentVertex() { - const TopoDS_Shape& aShape = MY_EXPLORER->Current(); - std::shared_ptr aGeomShape(new GeomAPI_Shape()); - aGeomShape->setImpl(new TopoDS_Shape(aShape)); + const TopoDS_Vertex& aShape = MY_EXPLORER->CurrentVertex(); + std::shared_ptr aGeomShape(new GeomAPI_Vertex()); + aGeomShape->setImpl(new TopoDS_Vertex(aShape)); return aGeomShape; } diff --git a/src/GeomAPI/GeomAPI_WireExplorer.h b/src/GeomAPI/GeomAPI_WireExplorer.h index 432858299..b68cee6de 100644 --- a/src/GeomAPI/GeomAPI_WireExplorer.h +++ b/src/GeomAPI/GeomAPI_WireExplorer.h @@ -23,7 +23,9 @@ #include #include +class GeomAPI_Edge; class GeomAPI_Shape; +class GeomAPI_Vertex; class GeomAPI_Wire; /** \class GeomAPI_WireExplorer @@ -53,9 +55,13 @@ public: /// if there are no more shapes to explore. GEOMAPI_EXPORT void next(); - /// \return the current shape in the exploration or empty pointer + /// \return the current edge in the exploration or empty pointer /// if this explorer has no more shapes to explore. - GEOMAPI_EXPORT std::shared_ptr current(); + GEOMAPI_EXPORT std::shared_ptr current(); + + /// \return the current vertex in the exploration or empty pointer + /// if this explorer has no more shapes to explore. + GEOMAPI_EXPORT std::shared_ptr currentVertex(); /// Clears the content of the explorer. It will return False on more(). GEOMAPI_EXPORT void clear(); diff --git a/src/GeomAlgoAPI/CMakeLists.txt b/src/GeomAlgoAPI/CMakeLists.txt index 391acc8f6..50546b4f2 100644 --- a/src/GeomAlgoAPI/CMakeLists.txt +++ b/src/GeomAlgoAPI/CMakeLists.txt @@ -75,6 +75,7 @@ SET(PROJECT_HEADERS GeomAlgoAPI_Circ2dBuilder.h GeomAlgoAPI_UnifySameDomain.h GeomAlgoAPI_Fillet.h + GeomAlgoAPI_Fillet1D.h GeomAlgoAPI_SortListOfShapes.h GeomAlgoAPI_Filling.h GeomAlgoAPI_CurveBuilder.h @@ -139,6 +140,7 @@ SET(PROJECT_SOURCES GeomAlgoAPI_Circ2dBuilder.cpp GeomAlgoAPI_UnifySameDomain.cpp GeomAlgoAPI_Fillet.cpp + GeomAlgoAPI_Fillet1D.cpp GeomAlgoAPI_SortListOfShapes.cpp GeomAlgoAPI_Filling.cpp GeomAlgoAPI_CurveBuilder.cpp diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.cpp new file mode 100644 index 000000000..5eca5b879 --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.cpp @@ -0,0 +1,201 @@ +// Copyright (C) 2020 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include +#include +#include + +#include +#include +#include +#include +#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; +} + +static void substituteNewEdge(GeomEdgePtr theEdge, + std::map& theMap) +{ + std::map::iterator anIt = theMap.begin(); + for (; anIt != theMap.end(); ++anIt) { + for (ListOfShape::iterator aEIt = anIt->second.begin(); aEIt != anIt->second.end(); ++aEIt) + if (theEdge->isEqual(*aEIt)) { + // substitute edge and stop iteration + *aEIt = theEdge; + return; + } + } +} + + +GeomAlgoAPI_Fillet1D::GeomAlgoAPI_Fillet1D(const GeomShapePtr& theBaseWire, + const ListOfShape& theFilletVertices, + const double theFilletRadius) +{ + build(theBaseWire, theFilletVertices, theFilletRadius); +} + +void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire, + const ListOfShape& theFilletVertices, + const double theRadius) +{ + if (!theBaseWire || theFilletVertices.empty() || theRadius < 0.) + return; + + GeomAlgoAPI_MapShapesAndAncestors aMapVE(theBaseWire, GeomAPI_Shape::VERTEX, + GeomAPI_Shape::EDGE); + + for (ListOfShape::const_iterator aVIt = theFilletVertices.begin(); + aVIt != theFilletVertices.end(); ++aVIt) { + // get edges to perform fillet + MapShapeToShapes::const_iterator aVE = aMapVE.map().find(*aVIt); + if (aVE == aMapVE.map().end()) + continue; + ListOfShape anEdges; + for (SetOfShapes::const_iterator aEIt = aVE->second.begin(); + aEIt != aVE->second.end(); ++aEIt) { + ListOfShape aNewEdges; + modified(*aEIt, aNewEdges); + if (aNewEdges.empty()) + anEdges.push_back(*aEIt); + else + anEdges.insert(anEdges.end(), aNewEdges.begin(), aNewEdges.end()); + } + + GeomPlanePtr aPlane = GeomAlgoAPI_ShapeTools::findPlane(anEdges); + if (!aPlane) + return; // non-planar edges + + TopoDS_Edge anEdge1 = TopoDS::Edge(anEdges.front()->impl()); + TopoDS_Edge anEdge2 = TopoDS::Edge(anEdges.back()->impl()); + + // create fillet builder + GEOMImpl_Fillet1d aFilletBuilder(anEdge1, anEdge2, aPlane->impl()); + if (!aFilletBuilder.Perform(theRadius)) + return; // fillet is failed, no way to continue + + GeomPointPtr aPoint = aVE->first->vertex()->point(); + TopoDS_Edge aFilletEdge = aFilletBuilder.Result(aPoint->impl(), anEdge1, anEdge2); + + // store modified shapes + myGenerated[aVE->first].push_back(convert(aFilletEdge)); + SetOfShapes::const_iterator aEIt = aVE->second.begin(); + myModified[*aEIt].clear(); + myModified[*aEIt].push_back(convert(anEdge1)); + myModified[*(++aEIt)].clear(); + myModified[*aEIt].push_back(convert(anEdge2)); + } + + // compose a new wire + TopoDS_Wire aNewWire; + BRep_Builder aBuilder; + aBuilder.MakeWire(aNewWire); + GeomWirePtr aBaseWire = theBaseWire->wire(); + GeomAPI_WireExplorer aWExp(aBaseWire); + GeomShapePtr aBaseFirstEdge = aWExp.current(); + for (; aWExp.more(); aWExp.next()) { + ListOfShape aNewEdges; + modified(aWExp.current(), aNewEdges); + if (aNewEdges.empty()) + aNewEdges.push_back(aWExp.current()); + for (ListOfShape::iterator anIt = aNewEdges.begin(); anIt != aNewEdges.end(); ++anIt) + aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl())); + } + for (MapModified::iterator aGenIt = myGenerated.begin(); aGenIt != myGenerated.end(); ++aGenIt) { + for (ListOfShape::iterator anIt = aGenIt->second.begin(); anIt != aGenIt->second.end(); ++anIt) + aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl())); + } + // fix the wire connectivity + ShapeFix_Wire aFixWire; + aFixWire.Load(aNewWire); + aFixWire.ClosedWireMode() = aBaseWire->isClosed(); + aFixWire.FixReorder(); + aNewWire = aFixWire.WireAPIMake(); + if (aNewWire.IsNull()) + return; + + // update the map of modified shapes, because the edges are changed by ShapeFix + for (BRepTools_WireExplorer anExp(aNewWire); anExp.More(); anExp.Next()) { + GeomEdgePtr aCurrent(new GeomAPI_Edge); + aCurrent->setImpl(new TopoDS_Edge(anExp.Current())); + substituteNewEdge(aCurrent, myGenerated); + substituteNewEdge(aCurrent, myModified); + } + + // rebuild the wire once again to get the first edge of fillet wire correspond + // to the first edge of original wire + TopoDS_Edge aFirstEdge = TopoDS::Edge(aBaseFirstEdge->impl()); + ListOfShape aNewEdges; + modified(aBaseFirstEdge, aNewEdges); + if (!aNewEdges.empty()) + aFirstEdge = TopoDS::Edge(aNewEdges.front()->impl()); + TopTools_ListOfShape aKeptForEnd; + BRepTools_WireExplorer aNewExp(aNewWire); + for (; aNewExp.More(); aNewExp.Next()) + if (aNewExp.Current().IsEqual(aFirstEdge)) + break; + if (aNewExp.More()) { + TopoDS_Wire aReorderedWire; + aBuilder.MakeWire(aReorderedWire); + for (; aNewExp.More(); aNewExp.Next()) + aBuilder.Add(aReorderedWire, aNewExp.Current()); + for (aNewExp.Init(aNewWire); aNewExp.More(); aNewExp.Next()) { + if (aNewExp.Current().IsEqual(aFirstEdge)) + break; + aBuilder.Add(aReorderedWire, aNewExp.Current()); + } + aNewWire = aReorderedWire; + } + + std::shared_ptr aShape(new GeomAPI_Shape()); + aShape->setImpl(new TopoDS_Shape(aNewWire)); + myModified[theBaseWire].push_back(aShape); + + setShape(aShape); + setDone(true); +} + +void GeomAlgoAPI_Fillet1D::generated(const GeomShapePtr theOldShape, + ListOfShape& theNewShapes) +{ + MapModified::iterator aFound = myGenerated.find(theOldShape); + if (aFound != myGenerated.end()) + theNewShapes = aFound->second; +} + +void GeomAlgoAPI_Fillet1D::modified(const GeomShapePtr theOldShape, + ListOfShape& theNewShapes) +{ + MapModified::iterator aFound = myModified.find(theOldShape); + if (aFound != myModified.end()) + theNewShapes = aFound->second; +} diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.h b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.h new file mode 100644 index 000000000..b386bc44a --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.h @@ -0,0 +1,70 @@ +// Copyright (C) 2020 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef GeomAlgoAPI_Fillet1D_H_ +#define GeomAlgoAPI_Fillet1D_H_ + +#include +#include + +#include + +/// \class GeomAlgoAPI_Fillet1D +/// \ingroup DataAlgo +/// \brief Perform fillet on vertices of a wire +class GeomAlgoAPI_Fillet1D : public GeomAlgoAPI_MakeShape +{ + typedef std::map MapModified; + +public: + /// Run fillet operation on a set of vertices with fixed radius. + /// \param theBaseWire a changing Wire + /// \param theFilletVertices list of edges the fillet is performed on + /// \param theFilletRadius radius of the fillet + GEOMALGOAPI_EXPORT GeomAlgoAPI_Fillet1D(const GeomShapePtr& theBaseWire, + const ListOfShape& theFilletVertices, + const double theFilletRadius); + + /// \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); + + /// \return the list of shapes modified from the shape \a theShape. + /// \param[in] theOldShape base shape. + /// \param[out] theNewShapes shapes modified from \a theShape. Does not cleared! + GEOMALGOAPI_EXPORT virtual void modified(const GeomShapePtr theOldShape, + ListOfShape& theNewShapes); + +private: + /// Perform 1d-fillet on wire + /// \param theBaseWire a changing wire + /// \param theFilletVertices list of vertices of filler + /// \param theRadius fillet radius + void build(const GeomShapePtr& theBaseWire, + const ListOfShape& theFilletVertices, + const double theRadius); + +private: + MapModified myGenerated; + MapModified myModified; +}; + +#endif diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp index 77aeaad15..0a7037184 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -857,6 +858,44 @@ std::shared_ptr return anOuterWire; } +//================================================================================================== +static bool boundaryOfEdge(const std::shared_ptr theEdge, + const std::shared_ptr theVertex, + double& theParam) +{ + GeomPointPtr aPoint = theVertex->point(); + GeomPointPtr aFirstPnt = theEdge->firstPoint(); + double aFirstPntTol = theEdge->firstPointTolerance(); + GeomPointPtr aLastPnt = theEdge->lastPoint(); + double aLastPntTol = theEdge->lastPointTolerance(); + + double aFirst, aLast; + theEdge->getRange(aFirst, aLast); + + bool isFirst = aPoint->distance(aFirstPnt) <= aFirstPntTol; + bool isLast = aPoint->distance(aLastPnt) <= aLastPntTol; + if (isFirst) + theParam = aFirst; + else if (isLast) + theParam = aLast; + + return isFirst != isLast; +} + +bool GeomAlgoAPI_ShapeTools::isTangent(const std::shared_ptr theEdge1, + const std::shared_ptr theEdge2, + const std::shared_ptr theTgPoint) +{ + double aParE1 = 0, aParE2 = 0; + if (!boundaryOfEdge(theEdge1, theTgPoint, aParE1) || + !boundaryOfEdge(theEdge2, theTgPoint, aParE2)) + return false; + + BRepAdaptor_Curve aC1(theEdge1->impl()); + BRepAdaptor_Curve aC2(theEdge2->impl()); + return BRepLProp::Continuity(aC1, aC2, aParE1, aParE2) >= GeomAbs_G1; +} + //================================================================================================== bool GeomAlgoAPI_ShapeTools::isParallel(const std::shared_ptr theEdge, const std::shared_ptr theFace) diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h index 49b343244..90a065b0b 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h @@ -141,6 +141,11 @@ public: GEOMALGOAPI_EXPORT static std::shared_ptr getFaceOuterWire(const std::shared_ptr theFace); + /// \return \c true if edges are tangent in the specified point + GEOMALGOAPI_EXPORT static bool isTangent(const std::shared_ptr theEdge1, + const std::shared_ptr theEdge2, + const std::shared_ptr theTgPoint); + /// \return true if edge is parallel to face. GEOMALGOAPI_EXPORT static bool isParallel(const std::shared_ptr theEdge, const std::shared_ptr theFace); diff --git a/src/GeomAlgoImpl/CMakeLists.txt b/src/GeomAlgoImpl/CMakeLists.txt index 7a7da432b..e28bf968e 100644 --- a/src/GeomAlgoImpl/CMakeLists.txt +++ b/src/GeomAlgoImpl/CMakeLists.txt @@ -22,10 +22,12 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) SET(PROJECT_HEADERS GeomAlgoImpl.h GEOMAlgo_Splitter.hxx + GEOMImpl_Fillet1d.hxx ) SET(PROJECT_SOURCES GEOMAlgo_Splitter.cxx + GEOMImpl_Fillet1d.cxx ) SET(PROJECT_LIBRARIES diff --git a/src/GeomAlgoImpl/GEOMImpl_Fillet1d.cxx b/src/GeomAlgoImpl/GEOMImpl_Fillet1d.cxx new file mode 100644 index 000000000..b9aef213c --- /dev/null +++ b/src/GeomAlgoImpl/GEOMImpl_Fillet1d.cxx @@ -0,0 +1,909 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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 +// + +// File : GEOMImpl_Fillet1d.cxx +// Module : GEOMImpl + +#include "GEOMImpl_Fillet1d.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/** + * This function returns Standard_True if it is possible to divide edge, i.e. + * if one parameter either start or end one is inside the edge. This function + * is used in the method GEOMImpl_Fillet1d::Result. + * + * \param theEdge the edge + * \param theStart the start parameter + * \param theEnd the end parameter + * \return Standard_True if it is possible to split edge; + * Standard_False otherwise. + */ +static Standard_Boolean IsDivideEdge(const TopoDS_Edge &theEdge, + const Standard_Real theStart, + const Standard_Real theEnd) +{ + Standard_Real aFirst; + Standard_Real aLast; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve(theEdge, aFirst, aLast); + gp_Pnt aPStart = aCurve->Value(theStart); + gp_Pnt aPEnd = aCurve->Value(theEnd); + TopoDS_Vertex aVFirst = TopExp::FirstVertex(theEdge); + TopoDS_Vertex aVLast = TopExp::LastVertex(theEdge); + Standard_Real aTolFirst = BRep_Tool::Tolerance(aVFirst); + Standard_Real aTolLast = BRep_Tool::Tolerance(aVLast); + Standard_Real aTolConf = Precision::Confusion(); + gp_Pnt aPFirst = BRep_Tool::Pnt(aVFirst); + gp_Pnt aPLast = BRep_Tool::Pnt(aVLast); + Standard_Real aDistSF = aPStart.Distance(aPFirst); + Standard_Real aDistSL = aPStart.Distance(aPLast); + Standard_Real aDistEF = aPEnd.Distance(aPFirst); + Standard_Real aDistEL = aPEnd.Distance(aPLast); + Standard_Boolean isSplit = Standard_True; + + if (aDistSF <= aTolFirst + aTolConf || + aDistSL <= aTolLast + aTolConf) { + if (aDistEF <= aTolFirst + aTolConf || + aDistEL <= aTolLast + aTolConf) { + + isSplit = Standard_False; + // in this case the original edge is thrown, and distance (gap) from new arc end + // to a vertex of original wire can reach (aVertexTolerance + Precision::Confusion()). + // Resulting wire is fixed (Mantis issue 0023411) in GEOMImpl_Fillet1dDriver::MakeFillet() + } + } + + return isSplit; +} + +/** + * class GEOMImpl_Fillet1d + */ + +//======================================================================= +//function : Constructor +//purpose : +//======================================================================= +GEOMImpl_Fillet1d::GEOMImpl_Fillet1d(const TopoDS_Edge& theEdge1, + const TopoDS_Edge& theEdge2, + const gp_Pln& thePlane) +: myEdgesExchnged( Standard_False ) +{ + myPlane = new Geom_Plane(thePlane); + + BRepAdaptor_Curve aBAC1(theEdge1); + BRepAdaptor_Curve aBAC2(theEdge2); + if (aBAC1.GetType() < aBAC2.GetType()) + { // first curve must be more complicated + myEdge1 = theEdge2; + myEdge2 = theEdge1; + myEdgesExchnged = Standard_True; + } + else + { + myEdge1 = theEdge1; + myEdge2 = theEdge2; + } + + Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(myEdge1, myStart1, myEnd1); + Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(myEdge2, myStart2, myEnd2); + + myCurve1 = GeomProjLib::Curve2d(aCurve1, myStart1, myEnd1, myPlane); + myCurve2 = GeomProjLib::Curve2d(aCurve2, myStart2, myEnd2, myPlane); + + while (myCurve1->IsPeriodic() && myStart1 >= myEnd1) + myEnd1 += myCurve1->Period(); + while (myCurve2->IsPeriodic() && myStart2 >= myEnd2) + myEnd2 += myCurve2->Period(); + + if (aBAC1.GetType() == aBAC2.GetType()) + { + if (myEnd2 - myStart2 < myEnd1 - myStart1) + { // first curve must be parametrically shorter + TopoDS_Edge anEdge = myEdge1; + myEdge1 = myEdge2; + myEdge2 = anEdge; + Handle(Geom2d_Curve) aCurve = myCurve1; + myCurve1 = myCurve2; + myCurve2 = aCurve; + Standard_Real a = myStart1; + myStart1 = myStart2; + myStart2 = a; + a = myEnd1; + myEnd1 = myEnd2; + myEnd2 = a; + myEdgesExchnged = Standard_True; + } + } +} + +//======================================================================= +//function : isRadiusIntersected +//purpose : local function +//======================================================================= +static Standard_Boolean isRadiusIntersected(const Handle(Geom2d_Curve)& theCurve, + const gp_Pnt2d theStart, + const gp_Pnt2d theEnd, + const Standard_Boolean theStartConnected) +{ + const Standard_Real aTol = Precision::Confusion(); + const Standard_Real anAngTol = Precision::Angular(); + Handle(Geom2d_Line) aRadiusLine = new Geom2d_Line (theStart, gp_Dir2d(gp_Vec2d(theStart, theEnd))); + Geom2dAPI_InterCurveCurve anInter (theCurve, aRadiusLine, aTol); + Standard_Integer a; + gp_Pnt2d aPoint; + for(a = anInter.NbPoints(); a > 0; a--) + { + aPoint = anInter.Point(a); + if ( aPoint.Distance(theStart) < aTol && !theStartConnected ) + return Standard_True; + if (aPoint.Distance(theEnd) < aTol * 200) + return Standard_True; + if (gp_Vec2d(aPoint, theStart).IsOpposite(gp_Vec2d(aPoint, theEnd), anAngTol)) + return Standard_True; + } + Handle(Geom2d_Curve) aCurve; + for(a = anInter.NbSegments(); a > 0; a--) + { + // Porting to DEV version of OCCT 10.02.2017 BEGIN + Standard_NotImplemented::Raise("The treatment of tangential intersection is not implemented"); + // Porting to DEV version of OCCT 10.02.2017 END + } + return Standard_False; +} + + +//======================================================================= +//function : fillPoint +//purpose : +//======================================================================= +void GEOMImpl_Fillet1d::fillPoint(GEOMImpl_Fillet1dPoint* thePoint) +{ + gp_Pnt2d aPoint; + gp_Vec2d aVec; + const Standard_Real aTol = Precision::Confusion(); + myCurve1->D1(thePoint->GetParam(), aPoint, aVec); + if (aVec.SquareMagnitude() < aTol) + return; + + gp_Vec2d aPerp(((myStartSide)?-1:1) * aVec.Y(), ((myStartSide)?1:-1) * aVec.X()); + aPerp.Normalize(); + aPerp.Multiply(myRadius); + gp_Pnt2d aCenter = aPoint.Translated(aPerp); + thePoint->SetCenter(aCenter); + + // on the intersection point + Standard_Boolean aValid = Standard_True; + Geom2dAPI_ProjectPointOnCurve aProjInt(aPoint, myCurve2); + if (aProjInt.NbPoints() && aPoint.Distance(aProjInt.NearestPoint()) < aTol) + aValid = Standard_False; + else + aValid = !isRadiusIntersected(myCurve2, aPoint, aCenter, Standard_True); + + Geom2dAPI_ProjectPointOnCurve aProj(aCenter, myCurve2); + Standard_Integer a, aNB = aProj.NbPoints(); + for(a = aNB; a > 0; a--) + { + if (aPoint.Distance(aProj.Point(a)) < aTol) + continue; + + Standard_Boolean aValid2 = aValid; + if (aValid2) + aValid2 = !isRadiusIntersected(myCurve1, aCenter, aProj.Point(a), Standard_False); + + // checking the right parameter + Standard_Real aParam = aProj.Parameter(a); + while(myCurve2->IsPeriodic() && aParam < myStart2) + aParam += myCurve2->Period(); + + thePoint->AddValue(aProj.Distance(a) * aProj.Distance(a) - myRadius * myRadius, + (aParam >= myStart2 && aParam <= myEnd2 && aValid2)); + if (fabs(fabs(aProj.Distance(a)) - myRadius) < aTol) + thePoint->SetParam2(aParam); + } +} + +//======================================================================= +//function : fillDiff +//purpose : +//======================================================================= +void GEOMImpl_Fillet1d::fillDiff(GEOMImpl_Fillet1dPoint* thePoint, Standard_Real theDiffStep, Standard_Boolean theFront) +{ + GEOMImpl_Fillet1dPoint* aDiff = + new GEOMImpl_Fillet1dPoint(thePoint->GetParam() + (theFront?(theDiffStep):(-theDiffStep))); + fillPoint(aDiff); + if (!thePoint->ComputeDifference(aDiff)) + { + aDiff->SetParam(thePoint->GetParam() + (theFront?(-theDiffStep):(theDiffStep))); + fillPoint(aDiff); + thePoint->ComputeDifference(aDiff); + } + delete aDiff; +} + +//======================================================================= +//function : Perform +//purpose : +//======================================================================= +Standard_Boolean GEOMImpl_Fillet1d::Perform(const Standard_Real theRadius) +{ + myDegreeOfRecursion = 0; + myResultParams.Clear(); + myResultOrientation.Clear(); + + Standard_Integer aNBSteps = 100; + Geom2dAdaptor_Curve aGAC(myCurve1); + switch (aGAC.GetType()) + { + case GeomAbs_Line: + aNBSteps = 2; + break; + case GeomAbs_Circle: + aNBSteps = 4; + break; + case GeomAbs_Ellipse: + aNBSteps = 5; + break; + case GeomAbs_BezierCurve: + case GeomAbs_BSplineCurve: + aNBSteps = 2 + aGAC.Degree() * aGAC.NbPoles(); + break; + default: // unknown: maximum + aNBSteps = 100; + } + + myRadius = theRadius; + + // Compute the intervals. + const Standard_Real aTol = Precision::Confusion(); + Geom2dAPI_InterCurveCurve anAPIInter(myCurve1, myCurve2, aTol); + const Geom2dInt_GInter &anInter = anAPIInter.Intersector(); + Standard_Integer aNb = anInter.NbPoints(); + Standard_Integer i; + TColStd_ListOfReal aParams; + TColStd_ListIteratorOfListOfReal anIter; + + // Treat intersection points. + for(i = 1; i <= aNb; i++) { + const IntRes2d_IntersectionPoint &aPoint = anInter.Point(i); + Standard_Real aParam = aPoint.ParamOnFirst(); + + // Adjust parameter on periodic curve. + if (myCurve1->IsPeriodic()) { + aParam = ElCLib::InPeriod + (aParam, myStart1, myStart1 + myCurve1->Period()); + } + + if (aParam > myStart1 + aTol && aParam < myEnd1 - aTol) { + // Add the point in the list in increasing order. + for(anIter.Initialize(aParams); anIter.More(); anIter.Next()) { + if (anIter.Value() > aParam) { + aParams.InsertBefore(aParam, anIter); + break; + } + } + + if (!anIter.More()) { + aParams.Append(aParam); + } + } + } + + // Treat intersection segments. + aNb = anInter.NbSegments(); + + for(i = 1; i <= aNb; i++) { + const IntRes2d_IntersectionSegment &aSegment = anInter.Segment(i); + + if (aSegment.HasFirstPoint() && aSegment.HasLastPoint()) { + Standard_Real aParam1 = aSegment.FirstPoint().ParamOnFirst(); + Standard_Real aParam2 = aSegment.LastPoint().ParamOnFirst(); + + // Adjust parameters on periodic curve. + if (myCurve1->IsPeriodic()) { + ElCLib::AdjustPeriodic(myStart1, myStart1 + myCurve1->Period(), + aTol, aParam1, aParam2); + } + + if (aParam1 > myStart1 + aTol && aParam1 < myEnd1 - aTol && + aParam2 > myStart1 + aTol && aParam2 < myEnd1 - aTol) { + // Add the point in the list in increasing order. + const Standard_Real aParam = 0.5*(aParam1 + aParam2); + + for(anIter.Initialize(aParams); anIter.More(); anIter.Next()) { + if (anIter.Value() > aParam) { + aParams.InsertBefore(aParam, anIter); + break; + } + } + + if (!anIter.More()) { + aParams.Append(aParam); + } + } + } + } + + // Add start and end parameters to the list. + aParams.Prepend(myStart1); + aParams.Append(myEnd1); + anIter.Initialize(aParams); + + // Perform each interval. + Standard_Real aStart = anIter.Value(); + + for (anIter.Next(); anIter.More(); anIter.Next()) { + const Standard_Real anEnd = anIter.Value(); + + // Perform the interval. + performInterval(aStart, anEnd, aNBSteps); + aStart = anEnd; + } + + if (myResultParams.Extent()) + return Standard_True; + + return Standard_False; +} + +//======================================================================= +//function : performInterval +//purpose : +//======================================================================= +void GEOMImpl_Fillet1d::performInterval(const Standard_Real theStart, + const Standard_Real theEnd, + const Standard_Integer theNBSteps) +{ + Standard_Real aParam, aStep, aDStep; + aStep = (theEnd - theStart) / theNBSteps; + aDStep = aStep/1000.; + + Standard_Integer aCycle; + for(aCycle = 2, myStartSide = Standard_False; aCycle; myStartSide = !myStartSide, aCycle--) + { + GEOMImpl_Fillet1dPoint *aLeft = NULL, *aRight = NULL; + + for(aParam = theStart + aStep; aParam < theEnd || fabs(theEnd - aParam) < Precision::Confusion(); aParam += aStep) + { + if (!aLeft) + { + aLeft = new GEOMImpl_Fillet1dPoint(aParam - aStep); + fillPoint(aLeft); + fillDiff(aLeft, aDStep, Standard_True); + } + + aRight = new GEOMImpl_Fillet1dPoint(aParam); + fillPoint(aRight); + fillDiff(aRight, aDStep, Standard_False); + + aLeft->FilterPoints(aRight); + performNewton(aLeft, aRight); + + delete aLeft; + aLeft = aRight; + } + delete aLeft; + } +} + +//======================================================================= +//function : processPoint +//purpose : +//======================================================================= +Standard_Boolean GEOMImpl_Fillet1d::processPoint(GEOMImpl_Fillet1dPoint* theLeft, + GEOMImpl_Fillet1dPoint* theRight, + Standard_Real theParameter) +{ + if (theParameter >= theLeft->GetParam() && theParameter < theRight->GetParam()) + { + Standard_Real aDX = theRight->GetParam() - theLeft->GetParam(); + if (theParameter - theLeft->GetParam() < aDX / 100.) + { + theParameter = theLeft->GetParam() + aDX / 100.; + } + if (theRight->GetParam() - theParameter < aDX / 100.) + { + theParameter = theRight->GetParam() - aDX / 100.; + } + + // Protection on infinite loop. + myDegreeOfRecursion++; + Standard_Real diffx = 0.001 * aDX; + if (myDegreeOfRecursion > 1000) + { + diffx *= 10.0; + if (myDegreeOfRecursion > 10000) + { + diffx *= 10.0; + if (myDegreeOfRecursion > 100000) + { + return Standard_True; + } + } + } + + GEOMImpl_Fillet1dPoint* aPoint1 = theLeft->Copy(); + GEOMImpl_Fillet1dPoint* aPoint2 = new GEOMImpl_Fillet1dPoint(theParameter); + fillPoint(aPoint2); + fillDiff(aPoint2, diffx, Standard_True); + + aPoint1->FilterPoints(aPoint2); + performNewton(aPoint1, aPoint2); + aPoint2->FilterPoints(theRight); + performNewton(aPoint2, theRight); + + delete aPoint1; + delete aPoint2; + return Standard_True; + } + + return Standard_False; +} + +//======================================================================= +//function : performNewton +//purpose : +//======================================================================= +void GEOMImpl_Fillet1d::performNewton(GEOMImpl_Fillet1dPoint* theLeft, + GEOMImpl_Fillet1dPoint* theRight) +{ + Standard_Integer a; + // check the left: if this is solution store it and remove it from the list of researching points of theLeft + a = theLeft->HasSolution(myRadius); + if (a) + { + if (theLeft->IsValid(a)) + { + myResultParams.Append(theLeft->GetParam()); + myResultOrientation.Append(myStartSide); + } + return; + } + + Standard_Real aDX = theRight->GetParam() - theLeft->GetParam(); + if ( aDX < Precision::Confusion() / 1000000.) + { + a = theRight->HasSolution(myRadius); + if (a) + if (theRight->IsValid(a)) + { + myResultParams.Append(theRight->GetParam()); + myResultOrientation.Append(myStartSide); + } + return; + } + + for(a = 1; a <= theLeft->GetNBValues(); a++) + { + Standard_Integer aNear = theLeft->GetNear(a); + + Standard_Real aA = (theRight->GetDiff(aNear) - theLeft->GetDiff(a)) / aDX; + Standard_Real aB = theLeft->GetDiff(a) - aA * theLeft->GetParam(); + Standard_Real aC = theLeft->GetValue(a) - theLeft->GetDiff(a) * theLeft->GetParam() + + aA * theLeft->GetParam() * theLeft->GetParam() / 2.0; + Standard_Real aDet = aB * aB - 2.0 * aA * aC; + + if ( fabs(aDet) < gp::Resolution() ) + continue; + + if (fabs(aA) < Precision::Confusion()) + { // linear case + if (fabs(aB) > 10e-20) + { + Standard_Real aX0 = - aC / aB; // use extremum + if (aX0 > theLeft->GetParam() && aX0 < theRight->GetParam()) + processPoint(theLeft, theRight, aX0); + } + else + { + processPoint(theLeft, theRight, theLeft->GetParam() + aDX / 2.0); // linear division otherwise + } + } + else + { + if (fabs(aB) > fabs(aDet * 1000000.)) + { // possible floating point operations accuracy errors + processPoint(theLeft, theRight, theLeft->GetParam() + aDX / 2.0); // linear division otherwise + } + else + { + if (aDet > 0) + { // two solutions + aDet = sqrt(aDet); + Standard_Boolean aRes = processPoint(theLeft, theRight, (- aB + aDet) / aA); + if (!aRes) + aRes = processPoint(theLeft, theRight, (- aB - aDet) / aA); + if (!aRes) + processPoint(theLeft, theRight, theLeft->GetParam() + aDX / 2.0); // linear division otherwise + } + else + { + Standard_Real aX0 = - aB / aA; // use extremum + if (aX0 > theLeft->GetParam() && aX0 < theRight->GetParam()) + processPoint(theLeft, theRight, aX0); + else + processPoint(theLeft, theRight, theLeft->GetParam() + aDX / 2.0); // linear division otherwise + } + } + } + } +} + +//======================================================================= +//function : Result +//purpose : +//======================================================================= +TopoDS_Edge GEOMImpl_Fillet1d::Result(const gp_Pnt& thePoint, + TopoDS_Edge& theEdge1, + TopoDS_Edge& theEdge2) +{ + TopoDS_Edge aResult; + gp_Pnt2d aTargetPoint2d; + Standard_Real aX, aY; + ElSLib::PlaneParameters(myPlane->Pln().Position(), thePoint, aX, aY); + aTargetPoint2d.SetCoord(aX, aY); + + // choose the nearest circle + Standard_Real aDistance, aP; + GEOMImpl_Fillet1dPoint *aNearest; + Standard_Integer a; + TColStd_ListIteratorOfListOfReal anIter(myResultParams); + for(aNearest = NULL, a = 1; anIter.More(); anIter.Next(), a++) + { + myStartSide = (myResultOrientation.Value(a)) ? Standard_True : Standard_False; + GEOMImpl_Fillet1dPoint *aPoint = new GEOMImpl_Fillet1dPoint(anIter.Value()); + fillPoint(aPoint); + if (!aPoint->HasSolution(myRadius)) + continue; + aP = fabs(aPoint->GetCenter().Distance(aTargetPoint2d) - myRadius); + if (!aNearest || aP < aDistance) + { + aNearest = aPoint; + aDistance = aP; + } + else + { + delete aPoint; + } + } + + if (!aNearest) + return aResult; + + // create circle edge + gp_Pnt aCenter = ElSLib::PlaneValue(aNearest->GetCenter().X(), + aNearest->GetCenter().Y(), + myPlane->Pln().Position()); + Handle(Geom_Circle) aCircle = + new Geom_Circle(gp_Ax2(aCenter, myPlane->Pln().Axis().Direction()), myRadius); + gp_Pnt2d aPoint2d1, aPoint2d2; + myCurve1->D0(aNearest->GetParam(), aPoint2d1); + myCurve2->D0(aNearest->GetParam2(), aPoint2d2); + gp_Pnt aPoint1 = ElSLib::PlaneValue(aPoint2d1.X(), aPoint2d1.Y(), myPlane->Pln().Position()); + gp_Pnt aPoint2 = ElSLib::PlaneValue(aPoint2d2.X(), aPoint2d2.Y(), myPlane->Pln().Position()); + + GeomAPI_ProjectPointOnCurve aProj(thePoint, aCircle); + Standard_Real aTarGetParam = aProj.LowerDistanceParameter(); + gp_Pnt aPointOnCircle = aProj.NearestPoint(); + + // Check extrema point manually, because there is a bug in Open CASCADE + // in calculation of nearest point to a circle near the parameter 0.0 + gp_Pnt p0 = ElCLib::Value(0.0, aCircle->Circ()); + if (p0.Distance(thePoint) < aPointOnCircle.Distance(thePoint)) + { + aTarGetParam = 0.0; + aPointOnCircle = p0; + } + + aProj.Perform(aPoint1); + Standard_Real aParam1 = aProj.LowerDistanceParameter(); + aProj.Perform(aPoint2); + Standard_Real aParam2 = aProj.LowerDistanceParameter(); + Standard_Boolean aIsOut = ((aParam1 < aTarGetParam && aParam2 < aTarGetParam) || + (aParam1 > aTarGetParam && aParam2 > aTarGetParam)); + if (aParam1 > aParam2) + aIsOut = !aIsOut; + BRepBuilderAPI_MakeEdge aBuilder(aCircle->Circ(), + aIsOut ? aParam2 : aParam1, + aIsOut? aParam1 : aParam2); + aResult = aBuilder.Edge(); + + // divide edges + Standard_Real aStart, anEnd; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve(myEdge1, aStart, anEnd); + gp_Vec aDir; + aCurve->D1(aNearest->GetParam(), aPoint1, aDir); + + gp_Vec aCircleDir; + aCircle->D1(aParam1, aPoint1, aCircleDir); + if ((aCircleDir.Angle(aDir) > M_PI / 2.0) ^ aIsOut) + aStart = aNearest->GetParam(); + else + anEnd = aNearest->GetParam(); + + if (IsDivideEdge(myEdge1, aStart, anEnd)) + { + //Divide edge + BRepBuilderAPI_MakeEdge aDivider1(aCurve, aStart, anEnd); + if (myEdgesExchnged) + theEdge2 = aDivider1.Edge(); + else + theEdge1 = aDivider1.Edge(); + } + + aCurve = BRep_Tool::Curve(myEdge2, aStart, anEnd); + aCurve->D1(aNearest->GetParam2(), aPoint2, aDir); + + aCircle->D1(aParam2, aPoint2, aCircleDir); + if ((aCircleDir.Angle(aDir) > M_PI / 2.0) ^ (!aIsOut)) + aStart = aNearest->GetParam2(); + else + anEnd = aNearest->GetParam2(); + + if (IsDivideEdge(myEdge2, aStart, anEnd)) + { + BRepBuilderAPI_MakeEdge aDivider2(aCurve, aStart, anEnd); + if (myEdgesExchnged) + theEdge1 = aDivider2.Edge(); + else + theEdge2 = aDivider2.Edge(); + } + + delete aNearest; + return aResult; +} + +//======================================================================= +//function : AddValue +//purpose : +//======================================================================= +void GEOMImpl_Fillet1dPoint::AddValue(Standard_Real theValue, Standard_Boolean theValid) +{ + Standard_Integer a; + for(a = 1; a <= myV.Length(); a++) + { + if (theValue < myV.Value(a)) + { + myV.InsertBefore(a, theValue); + myValid.InsertBefore(a, (Standard_Integer)theValid); + return; + } + } + myV.Append(theValue); + myValid.Append((Standard_Integer)theValid); +} + +//======================================================================= +//function : ComputeDifference +//purpose : +//======================================================================= +Standard_Boolean GEOMImpl_Fillet1dPoint::ComputeDifference(GEOMImpl_Fillet1dPoint* thePoint) +{ + Standard_Integer a; + Standard_Boolean aDiffsSet = (myD.Length() != 0); + Standard_Real aDX = thePoint->GetParam() - myParam, aDY; + if (thePoint->myV.Length() == myV.Length()) + { // absolutely the same points + for(a = 1; a <= myV.Length(); a++) + { + aDY = thePoint->myV.Value(a) - myV.Value(a); + if ( aDiffsSet ) + myD.SetValue(a, fabs(aDX) > gp::Resolution() ? (aDY/aDX) : 0); + else + myD.Append( fabs(aDX) > gp::Resolution() ? (aDY/aDX) : 0); + } + return Standard_True; + } + // between the diffeerent points searching for nearest analogs + Standard_Integer b; + for(a = 1; a <= myV.Length(); a++) + { + for(b = 1; b <= thePoint->myV.Length(); b++) + { + if (b == 1 || fabs(thePoint->myV.Value(b) - myV.Value(a)) < fabs(aDY)) + aDY = thePoint->myV.Value(b) - myV.Value(a); + } + if (aDiffsSet) + { + if ( fabs(aDX) > gp::Resolution() && fabs(aDY / aDX) < fabs(myD.Value(a))) + myD.SetValue(a, aDY / aDX); + else + myD.SetValue(a, 0); + } + else + { + myD.Append( fabs(aDX) > gp::Resolution() ? aDY/aDX : 0); + } + } + + return Standard_False; +} + +//======================================================================= +//function : FilterPoints +//purpose : +//======================================================================= +void GEOMImpl_Fillet1dPoint::FilterPoints(GEOMImpl_Fillet1dPoint* thePoint) +{ + Standard_Integer a, b; + TColStd_SequenceOfReal aDiffs; + Standard_Real aY, aY2, aDX = thePoint->GetParam() - myParam; + for(a = 1; a <= myV.Length(); a++) + { + // searching for near point from thePoint + Standard_Integer aNear = 0; + Standard_Real aDiff = aDX * 10000.; + aY = myV.Value(a) + myD.Value(a) * aDX; + for(b = 1; b <= thePoint->myV.Length(); b++) + { + // calculate hypothesis value of the Y2 with the constant first and second derivative + aY2 = aY + aDX * (thePoint->myD.Value(b) - myD.Value(a)) / 2.0; + if (aNear == 0 || fabs(aY2 - thePoint->myV.Value(b)) < fabs(aDiff)) + { + aNear = b; + aDiff = aY2 - thePoint->myV.Value(b); + } + }//for b... + + if (aNear) + { + if (myV.Value(a) * thePoint->myV.Value(aNear) > 0) + {// the same sign at the same sides of the interval + if (myV.Value(a) * myD.Value(a) > 0) + { + if (fabs(myD.Value(a)) > Precision::Confusion()) + aNear = 0; + } + else + { + if (fabs(myV.Value(a)) > fabs(thePoint->myV.Value(aNear))) + if (thePoint->myV.Value(aNear) * thePoint->myD.Value(aNear) < 0 && + fabs(thePoint->myD.Value(aNear)) > Precision::Confusion()) + { + aNear = 0; + } + } + } + } + + if (aNear) + { + if (myV.Value(a) * thePoint->myV.Value(aNear) > 0) + { + if ((myV.Value(a) + myD.Value(a) * aDX) * myV.Value(a) > Precision::Confusion() && + (thePoint->myV.Value(aNear) + thePoint->myD.Value(aNear) * aDX) * thePoint->myV.Value(aNear) > Precision::Confusion()) + { + aNear = 0; + } + } + } + + if (aNear) + { + if ( fabs(aDX) < gp::Resolution() || fabs(aDiff / aDX) > 1.e+7) + { + aNear = 0; + } + } + + if (aNear == 0) + { // there is no near: remove it from the list + myV.Remove(a); + myD.Remove(a); + myValid.Remove(a); + a--; + } + else + { + Standard_Boolean aFound = Standard_False; + for(b = 1; b <= myNear.Length(); b++) + { + if (myNear.Value(b) == aNear) + { + if (fabs(aDiffs.Value(b)) < fabs(aDiff)) + { // return this 'near' + aFound = Standard_True; + myV.Remove(a); + myD.Remove(a); + myValid.Remove(a); + a--; + break; + } + else + { // remove the old 'near' + myV.Remove(b); + myD.Remove(b); + myValid.Remove(b); + myNear.Remove(b); + aDiffs.Remove(b); + a--; + break; + } + } + }//for b... + if (!aFound) + { + myNear.Append(aNear); + aDiffs.Append(aDiff); + } + } + }//for a... +} + +//======================================================================= +//function : Copy +//purpose : +//======================================================================= +GEOMImpl_Fillet1dPoint* GEOMImpl_Fillet1dPoint::Copy() +{ + GEOMImpl_Fillet1dPoint* aCopy = new GEOMImpl_Fillet1dPoint(myParam); + Standard_Integer a; + for(a = 1; a <= myV.Length(); a++) + { + aCopy->myV.Append(myV.Value(a)); + aCopy->myD.Append(myD.Value(a)); + aCopy->myValid.Append(myValid.Value(a)); + } + return aCopy; +} + +//======================================================================= +//function : HasSolution +//purpose : +//======================================================================= +Standard_Integer GEOMImpl_Fillet1dPoint::HasSolution(const Standard_Real theRadius) +{ + Standard_Integer a; + for(a = 1; a <= myV.Length(); a++) + { + if (fabs(sqrt(fabs(fabs(myV.Value(a)) + theRadius * theRadius)) - theRadius) < Precision::Confusion() / 10.) + return a; + } + return 0; +} + +//======================================================================= +//function : RemoveSolution +//purpose : +//======================================================================= +void GEOMImpl_Fillet1dPoint::RemoveSolution(Standard_Integer theIndex) +{ + myV.Remove(theIndex); + myD.Remove(theIndex); + myValid.Remove(theIndex); + myNear.Remove(theIndex); +} diff --git a/src/GeomAlgoImpl/GEOMImpl_Fillet1d.hxx b/src/GeomAlgoImpl/GEOMImpl_Fillet1d.hxx new file mode 100644 index 000000000..f49294f2c --- /dev/null +++ b/src/GeomAlgoImpl/GEOMImpl_Fillet1d.hxx @@ -0,0 +1,147 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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 +// + +// File : GEOMImpl_Fillet1d.hxx +// Module : GEOMImpl + +#ifndef _GEOMImpl_Fillet1d_HeaderFile +#define _GEOMImpl_Fillet1d_HeaderFile + +#include + +#include +#include + +#include + +#include +#include +#include + +class GEOMImpl_Fillet1dPoint; + +/** +* GEOMImpl_Fillet1d is 1D fillet algorithm on two planar edges with given radius +*/ + +class GEOMImpl_Fillet1d +{ +public: + //! Constructor + //! The fillet 1D algorithm is initialised by two edges and plane + Standard_EXPORT GEOMImpl_Fillet1d(const TopoDS_Edge& theEdge1, + const TopoDS_Edge& theEdge2, + const gp_Pln& thePlane); + //! Makes fillet with given radius + //! @returns Standard_True, if at least one result computed + Standard_EXPORT Standard_Boolean Perform(const Standard_Real theRadius); + + //! Returns result fillet edge and modified edges as out parameters + Standard_EXPORT TopoDS_Edge Result(const gp_Pnt& thePoint, TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2); + +private: + //! private methods + void performInterval(const Standard_Real theStart, + const Standard_Real theEnd, + const Standard_Integer theNBSteps); + void fillPoint(GEOMImpl_Fillet1dPoint*); + void fillDiff(GEOMImpl_Fillet1dPoint*, Standard_Real, Standard_Boolean); + void performNewton(GEOMImpl_Fillet1dPoint*, GEOMImpl_Fillet1dPoint*); + Standard_Boolean processPoint(GEOMImpl_Fillet1dPoint*, GEOMImpl_Fillet1dPoint*, Standard_Real); + +private: + //! private fields + TopoDS_Edge myEdge1, myEdge2; + Handle(Geom_Plane) myPlane; + Handle(Geom2d_Curve) myCurve1, myCurve2; + Standard_Real myStart1, myEnd1, myStart2, myEnd2, myRadius; + TColStd_ListOfReal myResultParams; + TColStd_SequenceOfInteger myResultOrientation; + Standard_Boolean myStartSide, myEdgesExchnged; + Standard_Integer myDegreeOfRecursion; +}; + + +/** +* GEOMImpl_Fillet1dPoint is an internal class for 1D fillet algorithm +* to store and compare computed solutions on edges +*/ + +class GEOMImpl_Fillet1dPoint +{ +public: + //! Puiblic methods + + //! Constructor + Standard_EXPORT GEOMImpl_Fillet1dPoint(Standard_Real theParam) + {myParam = theParam;} + + //! Make copy of point + //!WARNING: Copies only field values: myParam, myV, myD, myValid + Standard_EXPORT GEOMImpl_Fillet1dPoint* Copy(); // warning: this is not the full copy! + + //! Set/Get parameter + Standard_EXPORT inline void SetParam(Standard_Real theParam) + {myParam = theParam;} + Standard_EXPORT inline Standard_Real GetParam() const + {return myParam;} + Standard_EXPORT inline void SetParam2(const Standard_Real theParam2) + {myParam2 = theParam2;} + Standard_EXPORT inline Standard_Real GetParam2() + { return myParam2 ; } + + //! Returns validity + Standard_EXPORT inline Standard_Boolean IsValid(int theIndex) + {return (Standard_Boolean)myValid.Value(theIndex);} + + //! Get values + Standard_EXPORT inline Standard_Integer GetNBValues() {return myV.Length();} + Standard_EXPORT inline Standard_Real GetValue(Standard_Integer theIndex) + {return myV.Value(theIndex);} + Standard_EXPORT inline Standard_Real GetDiff(Standard_Integer theIndex) + {return myD.Value(theIndex);} + Standard_EXPORT inline Standard_Integer GetNear(Standard_Integer theIndex) + {return myNear.Value(theIndex);} + + //! Set/Get center point + Standard_EXPORT inline void SetCenter(const gp_Pnt2d thePoint) + {myCenter = thePoint;} + Standard_EXPORT inline const gp_Pnt2d GetCenter() + {return myCenter;} + + Standard_EXPORT void AddValue(Standard_Real theValue, Standard_Boolean theIsValid); + + //! compute difference between this and given point + Standard_EXPORT Standard_Boolean ComputeDifference(GEOMImpl_Fillet1dPoint*); + Standard_EXPORT void FilterPoints(GEOMImpl_Fillet1dPoint*); + + //! Checks if point contains solution and returns the index of it if any + Standard_EXPORT Standard_Integer HasSolution(Standard_Real theRadius); + //! Remove solution by index + void RemoveSolution(Standard_Integer theIndex); + +private: + //! Private fields + gp_Pnt2d myCenter; + Standard_Real myParam, myParam2; + TColStd_SequenceOfReal myV, myD; + TColStd_SequenceOfInteger myValid, myNear; +}; + +#endif diff --git a/src/ModelHighAPI/ModelHighAPI_Macro.h b/src/ModelHighAPI/ModelHighAPI_Macro.h index 7fd21232c..01da052aa 100644 --- a/src/ModelHighAPI/ModelHighAPI_Macro.h +++ b/src/ModelHighAPI/ModelHighAPI_Macro.h @@ -50,7 +50,7 @@ // Used in INTERFACE_N for create variable and getter #define DEFINE_ATTRIBUTE(NAME, TYPE, COMMENT) \ COMMENT \ - std::shared_ptr NAME() const { return VAR_NAME(NAME); } \ + virtual std::shared_ptr NAME() const { return VAR_NAME(NAME); } \ protected: \ std::shared_ptr VAR_NAME(NAME); \ public: