From: jfa Date: Wed, 11 May 2022 10:57:50 +0000 (+0300) Subject: bos #29476 Offset in sketch X-Git-Tag: V9_9_1b1~19^2 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=38a602debeae7064ef49610717250620f8b01447;p=modules%2Fshaper.git bos #29476 Offset in sketch --- diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.cpp index 34673a7e9..6c6c9ab95 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.cpp @@ -22,12 +22,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include @@ -66,14 +68,64 @@ GeomAlgoAPI_Fillet1D::GeomAlgoAPI_Fillet1D(const GeomShapePtr& theBaseWire, build(theBaseWire, theFilletVertices, theFilletRadius); } -void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire, +void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseShape, const ListOfShape& theFilletVertices, const double theRadius) { - if (!theBaseWire || theFilletVertices.empty() || theRadius < 0.) + if (!theBaseShape.get() || theFilletVertices.empty() || theRadius < 0.) return; myFailedVertices.clear(); + GeomShapePtr aShape; + + if (theBaseShape->isWire()) + aShape = buildWire(theBaseShape, theFilletVertices, theRadius); + else if (theBaseShape->isCompound()) { + std::list aShapes; + for (GeomAPI_ShapeIterator it (theBaseShape); it.more(); it.next()) { + GeomShapePtr aSubShape = it.current(); + if (aSubShape->isWire()) { + // get only fillet vertices of current wire + ListOfShape aFilletVertices; + std::set aFilletVerticesSet + (theFilletVertices.begin(), theFilletVertices.end()); + for (GeomAPI_WireExplorer anExp(aSubShape->wire()); anExp.more(); anExp.next()) { + if (aFilletVerticesSet.find(anExp.currentVertex()) != aFilletVerticesSet.end()) + aFilletVertices.push_back(anExp.currentVertex()); + } + + GeomShapePtr aShape_i = buildWire(aSubShape, aFilletVertices, theRadius); + if (aShape_i.get() != NULL) + aShapes.push_back(aShape_i); + else + aShapes.push_back(aSubShape); + } + else { + setDone(false); + myError = "Input shape for fillet is neither a wire nor a compound of wires"; + return; + } + } + aShape = GeomAlgoAPI_CompoundBuilder::compound(aShapes); + myModified[theBaseShape].push_back(aShape); + } else { + setDone(false); + myError = "Input shape for fillet is neither a wire nor a compound"; + return; + } + + setShape(aShape); + setDone(myFailedVertices.empty()); +} + +GeomShapePtr GeomAlgoAPI_Fillet1D::buildWire(const GeomShapePtr& theBaseWire, + const ListOfShape& theFilletVertices, + const double theRadius) +{ + std::shared_ptr aShape; + if (!theBaseWire.get() || theFilletVertices.empty() || theRadius < 0.) + return aShape; + // store all edges of a base wire as modified, because they will be rebuild by ShapeFix for (GeomAPI_WireExplorer aWExp(theBaseWire->wire()); aWExp.more(); aWExp.next()) { GeomShapePtr aCurrent = aWExp.current(); @@ -103,7 +155,7 @@ void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire, GeomPlanePtr aPlane = GeomAlgoAPI_ShapeTools::findPlane(anEdges); if (!aPlane) - return; // non-planar edges + return aShape; // non-planar edges TopoDS_Edge anEdge1 = TopoDS::Edge(anEdges.front()->impl()); TopoDS_Edge anEdge2 = TopoDS::Edge(anEdges.back()->impl()); @@ -148,9 +200,10 @@ void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire, 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) + + ListOfShape aNewEdges1; + generated(aWExp.currentVertex(), aNewEdges1); + for (ListOfShape::iterator anIt = aNewEdges1.begin(); anIt != aNewEdges1.end(); ++anIt) aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl())); } // fix the wire connectivity @@ -161,7 +214,7 @@ void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire, aNewWire = aFixWire.WireAPIMake(); if (aNewWire.IsNull()) { myFailedVertices = theFilletVertices; - return; + return aShape; } // update the map of modified shapes, because the edges are changed by ShapeFix @@ -197,12 +250,10 @@ void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire, aNewWire = aReorderedWire; } - std::shared_ptr aShape(new GeomAPI_Shape()); - aShape->setImpl(new TopoDS_Shape(aNewWire)); - myModified[theBaseWire].push_back(aShape); - - setShape(aShape); - setDone(myFailedVertices.empty()); + std::shared_ptr aResShape(new GeomAPI_Shape); + aResShape->setImpl(new TopoDS_Shape(aNewWire)); + myModified[theBaseWire].push_back(aResShape); + return aResShape; } void GeomAlgoAPI_Fillet1D::generated(const GeomShapePtr theOldShape, diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.h b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.h index 702b11398..e046c74c6 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Fillet1D.h @@ -34,8 +34,8 @@ class GeomAlgoAPI_Fillet1D : public GeomAlgoAPI_MakeShape 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 theBaseWire a wire or a compound of wires + /// \param theFilletVertices list of vertices the fillet is performed on /// \param theFilletRadius radius of the fillet GEOMALGOAPI_EXPORT GeomAlgoAPI_Fillet1D(const GeomShapePtr& theBaseWire, const ListOfShape& theFilletVertices, @@ -57,14 +57,22 @@ public: const ListOfShape& failedVertices() const { return myFailedVertices; } private: - /// Perform 1d-fillet on wire - /// \param theBaseWire a changing wire - /// \param theFilletVertices list of vertices of filler + /// Perform 1d-fillet on a wire or a compound of wires + /// \param theBaseShape the base wire or a compound of wires for fillet + /// \param theFilletVertices list of vertices of fillet /// \param theRadius fillet radius - void build(const GeomShapePtr& theBaseWire, + void build(const GeomShapePtr& theBaseShape, const ListOfShape& theFilletVertices, const double theRadius); + /// Perform 1d-fillet on wire + /// \param theBaseWire the base wire for fillet + /// \param theFilletVertices list of vertices of fillet + /// \param theRadius fillet radius + GeomShapePtr buildWire(const GeomShapePtr& theBaseWire, + const ListOfShape& theFilletVertices, + const double theRadius); + private: MapModified myGenerated; MapModified myModified; diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp index 886bca14b..6032c421a 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp @@ -64,7 +64,8 @@ void GeomAlgoAPI_Offset::generated(const GeomShapePtr theOldShape, GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomPlanePtr& thePlane, const GeomShapePtr& theEdgeOrWire, - const double theOffsetValue) + const double theOffsetValue, + const GeomAlgoAPI_OffsetJoint theJoint) { // 1. Make wire from edge, if need TopoDS_Wire aWire; @@ -92,8 +93,14 @@ GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomPlanePtr& thePlane, setImpl(aParal); setBuilderType(OCCT_BRepBuilderAPI_MakeShape); + // Joint type + GeomAbs_JoinType aJoin = GeomAbs_Arc; // default mode, corresponding to KeepDistance + if (theJoint == GeomAlgoAPI_OffsetJoint::Lines) + aJoin = GeomAbs_Intersection; + // for GeomAlgoAPI_OffsetJoint::Arcs do the same as for KeepDistance + Standard_Boolean isOpenResult = !aWire.Closed(); - aParal->Init(aFace, GeomAbs_Arc, isOpenResult); + aParal->Init(aFace, aJoin, isOpenResult); aParal->Perform(theOffsetValue, 0.); if (aParal->IsDone()) { TopoDS_Shape anOffset = aParal->Shape(); diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h index 5b0ffc0b3..a4be309a7 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Offset.h @@ -25,6 +25,16 @@ class GeomAPI_Pln; +/// \enum GeomAlgoAPI_OffsetJoint +/// \brief Type of joint of straight edges in 2D offset +/// \a KeepDistance Keep the distance from initial contour to the resulting one, +/// creating contour from lines and arcs where it is needed +/// \a Arcs On connection of straight edges, tangent arcs are created, +/// except the case of too short edges. Radius of arcs equals to the offset value. +/// \a Lines No arcs is created on the connection of the resulting lines, +/// adjacent lines are prolonged to the point of their intersection. +enum class GeomAlgoAPI_OffsetJoint { KeepDistance, Arcs, Lines }; + /// \class GeomAlgoAPI_Offset /// \ingroup DataAlgo /// \brief Perform 3D offset for the shape @@ -39,9 +49,12 @@ public: /// \param[in] thePlane base plane for all offsets /// \param[in] theEdgesOrWire base shapes /// \param[in] theOffsetValue offset distance, it can be negative - GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset(const std::shared_ptr& thePlane, - const GeomShapePtr& theEdgeOrWire, - const double theOffsetValue); + /// \param[in] theJointType type of joint of straight edges + GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset + (const std::shared_ptr& thePlane, + const GeomShapePtr& theEdgeOrWire, + const double theOffsetValue, + const GeomAlgoAPI_OffsetJoint theJoint = GeomAlgoAPI_OffsetJoint::KeepDistance); /// \return the list of shapes generated from the shape \a theShape. /// \param[in] theOldShape base shape. diff --git a/src/Model/Model_AttributeIntArray.cpp b/src/Model/Model_AttributeIntArray.cpp index 927b29e39..55edd27e6 100644 --- a/src/Model/Model_AttributeIntArray.cpp +++ b/src/Model/Model_AttributeIntArray.cpp @@ -54,7 +54,8 @@ void Model_AttributeIntArray::setSize(const int theSize, bool sendUpdated) } else { // reset the old array if (theSize) { if (theSize != myArray->Length()) { // old data is not kept, a new array is created - Handle(TColStd_HArray1OfInteger) aNewArray = new TColStd_HArray1OfInteger(0, theSize - 1); + Handle(TColStd_HArray1OfInteger) aNewArray = + new TColStd_HArray1OfInteger(0, theSize - 1, 0); myArray->ChangeArray(aNewArray); if (sendUpdated) owner()->data()->sendAttributeUpdated(this); diff --git a/src/SketchAPI/SketchAPI_Offset.cpp b/src/SketchAPI/SketchAPI_Offset.cpp index d23d153ff..3be3b0c7d 100644 --- a/src/SketchAPI/SketchAPI_Offset.cpp +++ b/src/SketchAPI/SketchAPI_Offset.cpp @@ -34,13 +34,15 @@ SketchAPI_Offset::SketchAPI_Offset (const std::shared_ptr & th SketchAPI_Offset::SketchAPI_Offset (const std::shared_ptr & theFeature, const std::list > & theObjects, const ModelHighAPI_Double & theOffsetValue, - bool theIsReversed) + const bool theIsReversed, + const std::string & theJointType) : ModelHighAPI_Interface(theFeature) { if (initialize()) { fillAttribute(theObjects, edgesList()); fillAttribute(theOffsetValue, value()); fillAttribute(theIsReversed, reversed()); + fillAttribute(theJointType, joint()); execute(); } @@ -72,6 +74,7 @@ void SketchAPI_Offset::dump (ModelHighAPI_Dumper& theDumper) const AttributeRefListPtr aOffsetObjects = edgesList(); AttributeDoublePtr aValue = value(); AttributeBooleanPtr aReversed = reversed(); + AttributeStringPtr aJoint = joint(); // Check all attributes are already dumped. If not, store the feature as postponed. if (!theDumper.isDumped(aOffsetObjects)) { @@ -80,7 +83,7 @@ void SketchAPI_Offset::dump (ModelHighAPI_Dumper& theDumper) const } theDumper << aBase << " = " << aSketchName << ".addOffset(" << aOffsetObjects << ", " - << aValue << ", " << aReversed << ")" << std::endl; + << aValue << ", " << aReversed << ", " << aJoint << ")" << std::endl; // Dump variables for a list of created features theDumper << "["; diff --git a/src/SketchAPI/SketchAPI_Offset.h b/src/SketchAPI/SketchAPI_Offset.h index 0446c792d..6e201bd25 100644 --- a/src/SketchAPI/SketchAPI_Offset.h +++ b/src/SketchAPI/SketchAPI_Offset.h @@ -50,12 +50,13 @@ public: SketchAPI_Offset(const std::shared_ptr & theFeature, const std::list > & theObjects, const ModelHighAPI_Double & theOffsetValue, - bool theIsReversed); + const bool theIsReversed = false, + const std::string & theJointType = SketchPlugin_Offset::JOINT_KEEP_DISTANCE()); /// Destructor SKETCHAPI_EXPORT virtual ~SketchAPI_Offset(); - INTERFACE_3(SketchPlugin_Offset::ID(), + INTERFACE_4(SketchPlugin_Offset::ID(), edgesList, SketchPlugin_Offset::EDGES_ID(), ModelAPI_AttributeRefList, /** Offset edges list */, @@ -64,7 +65,10 @@ public: ModelAPI_AttributeDouble, /** Value */, reversed, SketchPlugin_Offset::REVERSED_ID(), - ModelAPI_AttributeBoolean, /** Negative value */ + ModelAPI_AttributeBoolean, /** Negative value */, + + joint, SketchPlugin_Offset::JOINT_ID(), + ModelAPI_AttributeString, /** Joint type */ ) /// List of created objects diff --git a/src/SketchAPI/SketchAPI_Sketch.cpp b/src/SketchAPI/SketchAPI_Sketch.cpp index 97ba31d54..7ff5622b6 100644 --- a/src/SketchAPI/SketchAPI_Sketch.cpp +++ b/src/SketchAPI/SketchAPI_Sketch.cpp @@ -930,11 +930,12 @@ std::shared_ptr SketchAPI_Sketch::addMirror( std::shared_ptr SketchAPI_Sketch::addOffset( const std::list > & theObjects, const ModelHighAPI_Double & theValue, - const bool theReversed) + const bool theReversed, + const std::string & theJointType) { std::shared_ptr aFeature = compositeFeature()->addFeature(SketchPlugin_Offset::ID()); - return OffsetPtr(new SketchAPI_Offset(aFeature, theObjects, theValue, theReversed)); + return OffsetPtr(new SketchAPI_Offset(aFeature, theObjects, theValue, theReversed, theJointType)); } //-------------------------------------------------------------------------------------- diff --git a/src/SketchAPI/SketchAPI_Sketch.h b/src/SketchAPI/SketchAPI_Sketch.h index 51affdef3..498a124e6 100644 --- a/src/SketchAPI/SketchAPI_Sketch.h +++ b/src/SketchAPI/SketchAPI_Sketch.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -378,7 +379,8 @@ public: std::shared_ptr addOffset( const std::list > & theObjects, const ModelHighAPI_Double & theValue, - const bool theReversed); + const bool theReversed = false, + const std::string & theJointType = SketchPlugin_Offset::JOINT_KEEP_DISTANCE()); /// Add translation SKETCHAPI_EXPORT diff --git a/src/SketchPlugin/SketchPlugin_Offset.cpp b/src/SketchPlugin/SketchPlugin_Offset.cpp index 9f0ee1a23..27d957a51 100644 --- a/src/SketchPlugin/SketchPlugin_Offset.cpp +++ b/src/SketchPlugin/SketchPlugin_Offset.cpp @@ -40,15 +40,19 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include +#include +#include #include #include @@ -61,6 +65,8 @@ #include #include +#include + static const double tolerance = 1.e-7; SketchPlugin_Offset::SketchPlugin_Offset() @@ -86,6 +92,11 @@ void SketchPlugin_Offset::initAttributes() registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B()); ModelAPI_Session::get()->validators()-> registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C()); + + AttributeStringPtr aJointAttr = std::dynamic_pointer_cast + (data()->addAttribute(JOINT_ID(), ModelAPI_AttributeString::typeId())); + if (!aJointAttr->isInitialized()) + aJointAttr->setValue(JOINT_KEEP_DISTANCE()); } void SketchPlugin_Offset::execute() @@ -93,6 +104,20 @@ void SketchPlugin_Offset::execute() SketchPlugin_Sketch* aSketch = sketch(); if (!aSketch) return; + // 0. Joint type + AttributeStringPtr aJointAttr = string(JOINT_ID()); + std::string aType = JOINT_KEEP_DISTANCE(); + if (aJointAttr->isInitialized()) + aType = aJointAttr->value(); + + GeomAlgoAPI_OffsetJoint aJoint; + if (aType == JOINT_ARCS()) + aJoint = GeomAlgoAPI_OffsetJoint::Arcs; + else if (aType == JOINT_LINES()) + aJoint = GeomAlgoAPI_OffsetJoint::Lines; + else // Default mode + aJoint = GeomAlgoAPI_OffsetJoint::KeepDistance; + // 1. Sketch plane std::shared_ptr aPlane = aSketch->plane(); @@ -189,14 +214,21 @@ void SketchPlugin_Offset::execute() } // 5.d. Make offset for the wire - std::shared_ptr anOffsetShape( - new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign)); + std::shared_ptr anOffsetShape + (new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign, aJoint)); if (anOffsetShape->isDone()) { - std::shared_ptr aMakeList(new GeomAlgoAPI_MakeShapeList); - aMakeList->appendAlgo(aWireBuilder); - aMakeList->appendAlgo(anOffsetShape); - anOffsetAlgos.push_back(aMakeList); + if (aJoint == GeomAlgoAPI_OffsetJoint::Arcs) { + // For Arcs joint make fillet at all straight edges intersections + // of the wire, resulting from GeomAlgoAPI_Offset algorithm + makeFillet(fabs(aValue), aWireBuilder, anOffsetShape, anOffsetAlgos); + } + else { + std::shared_ptr aMakeList (new GeomAlgoAPI_MakeShapeList); + aMakeList->appendAlgo(aWireBuilder); + aMakeList->appendAlgo(anOffsetShape); + anOffsetAlgos.push_back(aMakeList); + } } else { setError("Offset algorithm failed"); @@ -753,7 +785,6 @@ bool SketchPlugin_Offset::findWires() } } } - // TODO: hilight selection in the viewer data()->blockSendAttributeUpdated(aWasBlocked); return true; @@ -769,3 +800,111 @@ AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious) thePrevious); return anAIS; } + + +void SketchPlugin_Offset::makeFillet + (const double theValue, + const std::shared_ptr& theWireBuilder, + const std::shared_ptr& theOffsetShape, + ListOfMakeShape& theOffsetAlgos) +{ + std::shared_ptr aMakeList (new GeomAlgoAPI_MakeShapeList); + aMakeList->appendAlgo(theWireBuilder); + aMakeList->appendAlgo(theOffsetShape); + + bool isOK = true; + + GeomShapePtr aResWire = theOffsetShape->shape(); + GeomAlgoAPI_MapShapesAndAncestors aMapVE + (aResWire, GeomAPI_Shape::VERTEX, GeomAPI_Shape::EDGE); + const MapShapeToShapes& aSubshapes = aMapVE.map(); + + // find vertices for fillet + 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; + + // both edges should be linear + ListOfShape anEdges; + anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end()); + GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front())); + GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back())); + if (!anEdge1->isLine() || !anEdge2->isLine()) + continue; + + // skip vertices, which smoothly connect adjacent edges + GeomVertexPtr aSharedVertex(new GeomAPI_Vertex(anIt->first)); + if (GeomAlgoAPI_ShapeTools::isTangent(anEdge1, anEdge2, aSharedVertex)) + continue; + + aFilletVertices.insert(anIt->first); + } + + if (!aFilletVertices.empty()) { + isOK = false; // the wire needs correction + ListOfShape aVerticesList (aFilletVertices.begin(), aFilletVertices.end()); + + // Fillet1D on all linear edges intersections + std::shared_ptr aFilletBuilder + (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList, theValue)); + + std::string anError; + if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed + (aFilletBuilder, getKind(), anError)) { + aMakeList->appendAlgo(aFilletBuilder); + isOK = true; + } + else { + ListOfShape aFailedVertices = aFilletBuilder->failedVertices(); + if (aFailedVertices.size() != 0) { + // Exclude failed vertices and also vertices, joined + // with failed by one edge, and run algorithm once again + ListOfShape::iterator itVertices = aFailedVertices.begin(); + for (; itVertices != aFailedVertices.end(); itVertices++) { + GeomShapePtr aFailedVertex = *itVertices; + aFilletVertices.erase(aFailedVertex); + // remove also neighbour vertices + MapShapeToShapes::const_iterator anIt = aSubshapes.find(aFailedVertex); + if (anIt != aSubshapes.end()) { // should be always true + ListOfShape anEdges; + anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end()); + GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front())); + GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back())); + GeomVertexPtr V1, V2; + anEdge1->vertices(V1, V2); + if (V1->isEqual(aFailedVertex)) V1 = V2; + aFilletVertices.erase(V1); + anEdge2->vertices(V1, V2); + if (V1->isEqual(aFailedVertex)) V1 = V2; + aFilletVertices.erase(V1); + } + } + if (aFilletVertices.size() == 0) { + // there are no suitable vertices for fillet + isOK = true; + } + else { + // Fillet1D one more try + ListOfShape aVerticesList1 (aFilletVertices.begin(), aFilletVertices.end()); + + std::shared_ptr aFilletBuilder1 + (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList1, theValue)); + + if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed + (aFilletBuilder1, getKind(), anError)) { + aMakeList->appendAlgo(aFilletBuilder1); + isOK = true; + } + } + } + } + } + + if (isOK) + theOffsetAlgos.push_back(aMakeList); + else + setError("Offset algorithm failed"); +} diff --git a/src/SketchPlugin/SketchPlugin_Offset.h b/src/SketchPlugin/SketchPlugin_Offset.h index fbfd5c39f..fd43ff721 100644 --- a/src/SketchPlugin/SketchPlugin_Offset.h +++ b/src/SketchPlugin/SketchPlugin_Offset.h @@ -28,6 +28,8 @@ #include class GeomAlgoAPI_MakeShape; +class GeomAlgoAPI_Offset; +class GeomAlgoAPI_WireBuilder; /**\class SketchPlugin_Offset * \ingroup Plugins @@ -50,6 +52,34 @@ public: return MY_KIND; } + /// Type of joint + inline static const std::string& JOINT_ID() + { + static const std::string ID("offset_joint"); + return ID; + } + + /// Keep distance joint (add arcs where needed) + inline static const std::string& JOINT_KEEP_DISTANCE() + { + static const std::string ID("KeepDistance"); + return ID; + } + + /// Arcs joint (make fillets on all straight lines intersections) + inline static const std::string& JOINT_ARCS() + { + static const std::string ID("Arcs"); + return ID; + } + + /// Lines joint (do not add new arcs, prolongate and intersect adjacent lines) + inline static const std::string& JOINT_LINES() + { + static const std::string ID("Lines"); + return ID; + } + /// list of offset edges inline static const std::string& EDGES_ID() { @@ -140,6 +170,11 @@ private: std::set& theProcessedEdgesSet, std::list& theChain, const bool isPrepend = false); + + void makeFillet (const double theValue, + const std::shared_ptr&, + const std::shared_ptr&, + std::list< std::shared_ptr >& theOffsetAlgos); }; #endif diff --git a/src/SketchPlugin/Test/TestOffset3.py b/src/SketchPlugin/Test/TestOffset3.py new file mode 100644 index 000000000..f5e1f422d --- /dev/null +++ b/src/SketchPlugin/Test/TestOffset3.py @@ -0,0 +1,106 @@ +# Copyright (C) 2020-2021 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model +from SketchAPI import * + +model.begin() +partSet = model.moduleDocument() + +### Create Sketch +Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(0, 0, 0, 130) + +### Create SketchProjection +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchAPI_Point(SketchPoint_1).coordinates()) +Sketch_1.setVertical(SketchLine_1.result()) +Sketch_1.setLength(SketchLine_1.result(), 130) + +### Create SketchLine +SketchLine_2 = Sketch_1.addLine(0, 130, 52.63824701112046, 101.2039073554021) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +Sketch_1.setLength(SketchLine_2.result(), 60) + +### Create SketchLine +SketchLine_3 = Sketch_1.addLine(52.63824701112046, 101.2039073554021, 199.7207674554361, 130.6442229064876) +Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +Sketch_1.setLength(SketchLine_3.result(), 150) + +### Create SketchConstraintAngle +Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), 140, type = "Direct") + +### Create SketchLine +SketchLine_4 = Sketch_1.addLine(158.0815949372134, 7.493180231252017, 199.7207674554361, 130.6442229064876) +Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.endPoint()) +Sketch_1.setLength(SketchLine_4.result(), 130) + +### Create SketchConstraintAngle +Sketch_1.setAngle(SketchLine_3.result(), SketchLine_4.result(), 60, type = "Direct") + +### Create SketchLine +SketchLine_5 = Sketch_1.addLine(158.0815949372134, 7.493180231252017, 106.6596776583922, 54.98830022657437) +Sketch_1.setCoincident(SketchLine_4.startPoint(), SketchLine_5.startPoint()) +Sketch_1.setLength(SketchLine_5.result(), 70) + +### Create SketchLine +SketchLine_6 = Sketch_1.addLine(106.6596776583922, 54.98830022657437, 0, 0) +Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_1.startPoint()) +Sketch_1.setLength(SketchLine_6.result(), 120) + +### Create SketchConstraintAngle +Sketch_1.setAngle(SketchLine_6.result(), SketchLine_5.result(), 110, type = "Direct") + +### SketchOffset objects +SketchOffset_objects = [SketchLine_1.result(), SketchLine_2.result(), SketchLine_3.result(), SketchLine_4.result(), SketchLine_5.result(), SketchLine_6.result()] + +### 1. "KeepDistance" mode + +SketchOffset_1 = Sketch_1.addOffset(SketchOffset_objects, 8, False, "KeepDistance") # outside +SketchOffset_2 = Sketch_1.addOffset(SketchOffset_objects, 8, True, "KeepDistance") # inside +model.do() + +assert(len(SketchOffset_1.offset()) == 10) +assert(len(SketchOffset_2.offset()) == 8) + +### 2. "Arcs" mode + +SketchOffset_3 = Sketch_1.addOffset(SketchOffset_objects, 16, False, "Arcs") # outside +SketchOffset_4 = Sketch_1.addOffset(SketchOffset_objects, 16, True, "Arcs") # inside +model.do() + +assert(len(SketchOffset_3.offset()) == 12) +assert(len(SketchOffset_4.offset()) == 12) + +### 3. "Lines" mode + +SketchOffset_5 = Sketch_1.addOffset(SketchOffset_objects, 24, False, "Lines") # outside +SketchOffset_6 = Sketch_1.addOffset(SketchOffset_objects, 24, True, "Lines") # inside +model.do() + +assert(len(SketchOffset_5.offset()) == 6) +assert(len(SketchOffset_6.offset()) == 6) + +model.end() + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/doc/examples/offset.py b/src/SketchPlugin/doc/examples/offset.py index 881db9a40..79e8ae9e1 100644 --- a/src/SketchPlugin/doc/examples/offset.py +++ b/src/SketchPlugin/doc/examples/offset.py @@ -16,7 +16,18 @@ Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.endPoint()) Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint()) SketchOffset_1_objects = [SketchLine_1.result(), SketchLine_2.result(), SketchLine_3.result(), SketchLine_4.result()] + +### KeepDistance (default) mode, not reversed (outside) SketchOffset_1 = Sketch_1.addOffset(SketchOffset_1_objects, 10.0, False) +### KeepDistance mode, reversed (inside) +SketchOffset_2 = Sketch_1.addOffset(SketchOffset_1_objects, 25.0, True, "KeepDistance") + +### Arcs mode, reversed (inside) +SketchOffset_3 = Sketch_1.addOffset(SketchOffset_1_objects, 15.0, True, "Arcs") + +### Lines mode, not reversed (outside) +SketchOffset_4 = Sketch_1.addOffset(SketchOffset_1_objects, 20.0, False, "Lines") + model.do() model.end() diff --git a/src/SketchPlugin/doc/images/Offset_panel.png b/src/SketchPlugin/doc/images/Offset_panel.png index d34ad6c3c..de9741e54 100644 Binary files a/src/SketchPlugin/doc/images/Offset_panel.png and b/src/SketchPlugin/doc/images/Offset_panel.png differ diff --git a/src/SketchPlugin/doc/offsetFeature.rst b/src/SketchPlugin/doc/offsetFeature.rst index ce42254df..65af062c9 100644 --- a/src/SketchPlugin/doc/offsetFeature.rst +++ b/src/SketchPlugin/doc/offsetFeature.rst @@ -4,10 +4,24 @@ Offset ====== Offset operation offsets sketch entities on a given distance. -Gaps are filled by arcs. Offset is performed outside a closed contour or to the right of an open one, unless the **Reversed** flag is not set. +Edges junctions are filled in accordance with selected offset mode. +The following modes are implemented: + - **Keep distance** mode, enabled by default. It keeps the distance from initial + contour to the resulting one, creating additional arcs where it is needed. + - **Arcs mode**, where on connection of straight segments, tangent arcs are created. + Only connection of straight lines is supported; with other types of curves, + offset will work as in **Keep distance** mode. Radius of arcs equals to the offset value. + These arcs cut off the initially connected lines, so, if in result at least one + such line becomes invalid (removed from the result), the arc is not created + in this connection, the line is created in this point as in the **Lines** mode. + - **Lines** mode, where no arcs is created on the connection of the resulting lines. + Adjacent lines are prolonged to the point of their intersection. If the offset + arguments are not straigh lines (arcs, bspline, etc), the offset will work + as in the **Keep distance** mode in these locations. + To create an Offset in the active Sketch: #. select in the Main Menu *Sketch - > Offset* item or @@ -23,6 +37,7 @@ Property panel: Input fields: +- Offset mode can be **Keep distance**, **Arcs** or **Lines** - **Edges** is the list of segments (lines, circles, arcs) selected in the view. - **Offset value** is the offset distance. - **Reversed** sets the reversed offset side (inside a closed contour or to the left of an open one). @@ -35,11 +50,12 @@ Button: **TUI Command**: -.. py:function:: Sketch_1.addOffset(Objects, Distance, isReversed) +.. py:function:: Sketch_1.addOffset(Objects, Distance, isReversed, Mode) :param list: A list of objects. :param real: An offset distance. :param boolean: Reversed flag. + :param string: Offset mode. Can be "KeepDistance", "Arcs" or "Lines". :return: Result object. Result diff --git a/src/SketchPlugin/icons/offset_arcs_32x32.png b/src/SketchPlugin/icons/offset_arcs_32x32.png new file mode 100644 index 000000000..046c60a57 Binary files /dev/null and b/src/SketchPlugin/icons/offset_arcs_32x32.png differ diff --git a/src/SketchPlugin/icons/offset_keep_distance_32x32.png b/src/SketchPlugin/icons/offset_keep_distance_32x32.png new file mode 100644 index 000000000..ae7dd5bbe Binary files /dev/null and b/src/SketchPlugin/icons/offset_keep_distance_32x32.png differ diff --git a/src/SketchPlugin/icons/offset_lines_32x32.png b/src/SketchPlugin/icons/offset_lines_32x32.png new file mode 100644 index 000000000..486f6e93c Binary files /dev/null and b/src/SketchPlugin/icons/offset_lines_32x32.png differ diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index 824e6281b..86b234c51 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -1020,6 +1020,17 @@ tooltip="Offset a curve to a distance" icon="icons/Sketch/offset.png" helpfile="offsetFeature.html"> + + + + +