From e14a0493c3a3a7c475cf146e2ec55329d4164681 Mon Sep 17 00:00:00 2001 From: Artem Zhidkov Date: Sat, 4 Jul 2020 10:45:53 +0300 Subject: [PATCH] Task #3231: Sketcher Offset of a curve. Fix issues --- src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp | 62 ++++++++++++++++++- src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h | 5 +- src/SketchPlugin/CMakeLists.txt | 3 +- src/SketchPlugin/SketchPlugin_Offset.cpp | 49 +++++++++++---- .../Test/{TestOffset.py => TestOffset1.py} | 22 ++++--- src/SketchPlugin/Test/TestOffset2.py | 61 ++++++++++++++++++ 6 files changed, 180 insertions(+), 22 deletions(-) rename src/SketchPlugin/Test/{TestOffset.py => TestOffset1.py} (94%) create mode 100644 src/SketchPlugin/Test/TestOffset2.py diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp index 53bc674a4..a90d1e953 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp @@ -24,11 +24,14 @@ #include #include +#include #include #include +#include #include #include #include +#include #include static GeomShapePtr fromTopoDS(const TopoDS_Shape& theShape) @@ -38,7 +41,8 @@ static GeomShapePtr fromTopoDS(const TopoDS_Shape& theShape) return aResultShape; } -GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes) +GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes, + const bool theForceOpenWire) { TopTools_ListOfShape aListOfEdges; @@ -61,14 +65,68 @@ GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes) } } + bool isSplitWire = false; + gp_Pnt aSplitPoint; + if (theForceOpenWire) { + // find a vertex to split the wire + TopoDS_Vertex V1[2]; + TopExp::Vertices(TopoDS::Edge(aListOfEdges.First()), V1[0], V1[1]); + TopoDS_Vertex V2[2]; + TopExp::Vertices(TopoDS::Edge(aListOfEdges.Last()), V2[0], V2[1]); + gp_Pnt P1[2] = { BRep_Tool::Pnt(V1[0]), BRep_Tool::Pnt(V1[1]) }; + gp_Pnt P2[2] = { BRep_Tool::Pnt(V2[0]), BRep_Tool::Pnt(V2[1]) }; + double Tol1[2] = { BRep_Tool::Tolerance(V1[0]), BRep_Tool::Tolerance(V1[1]) }; + double Tol2[2] = { BRep_Tool::Tolerance(V2[0]), BRep_Tool::Tolerance(V2[1]) }; + for (int i = 0; i < 2 && !isSplitWire; ++i) + for (int j = 0; j < 2 && !isSplitWire; ++j) + if (P1[i].Distance(P2[i]) < Max(Tol1[i], Tol2[j])) { + aSplitPoint = P1[i]; + isSplitWire = true; + } + } + BRepBuilderAPI_MakeWire* aWireBuilder = new BRepBuilderAPI_MakeWire; aWireBuilder->Add(aListOfEdges); if (aWireBuilder->Error() == BRepBuilderAPI_WireDone) { setImpl(aWireBuilder); setBuilderType(OCCT_BRepBuilderAPI_MakeShape); - // store generated/modified shapes + // split the result wire TopoDS_Wire aWire = aWireBuilder->Wire(); + if (isSplitWire && BRep_Tool::IsClosed(aWire)) { + TopoDS_Wire aNewWire; + BRep_Builder aBuilder; + aBuilder.MakeWire(aNewWire); + for (TopExp_Explorer anExp(aWire, TopAbs_EDGE); anExp.More(); anExp.Next()) { + TopoDS_Edge aNewCurrent = TopoDS::Edge(anExp.Current()); + if (isSplitWire) { + bool isToReshape = false; + BRepTools_ReShape aReshape; + TopoDS_Vertex aVF, aVL; + TopExp::Vertices(aNewCurrent, aVF, aVL); + gp_Pnt aPF = BRep_Tool::Pnt(aVF); + double aTolF = BRep_Tool::Tolerance(aVF); + gp_Pnt aPL = BRep_Tool::Pnt(aVL); + double aTolL = BRep_Tool::Tolerance(aVL); + if (aSplitPoint.SquareDistance(aPF) < aTolF * aTolF) { + aReshape.Replace(aVF, aReshape.CopyVertex(aVF)); + isToReshape = true; + } + else if (aSplitPoint.SquareDistance(aPL) < aTolL * aTolL) { + aReshape.Replace(aVL, aReshape.CopyVertex(aVL)); + isToReshape = true; + } + if (isToReshape) { + aNewCurrent = TopoDS::Edge(aReshape.Apply(aNewCurrent)); + isSplitWire = false; // no need to continue splitting + } + } + aBuilder.Add(aNewWire, aNewCurrent); + } + aWire = aNewWire; + } + + // store generated/modified shapes for (TopTools_ListOfShape::Iterator aBaseIt(aListOfEdges); aBaseIt.More(); aBaseIt.Next()) { TopoDS_Edge aBaseCurrent = TopoDS::Edge(aBaseIt.Value()); Standard_Real aFirst, aLast; diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h index 6824c1550..9c4370c5f 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h @@ -33,9 +33,12 @@ class GeomAlgoAPI_WireBuilder : public GeomAlgoAPI_MakeShapeCustom public: /// \brief Creates a wire from edges and wires. /// \param[in] theShapes list of shapes. Only edges and wires allowed. + /// \param[in] theForceOpenWire indicates the necessity to split wire + /// in the first vertex if it becomes closed. /// The edges are not to be consecutive. /// But they are to be all connected geometrically or topologically. - GEOMALGOAPI_EXPORT GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes); + GEOMALGOAPI_EXPORT GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes, + const bool theForceOpenWire = false); /// \brief Creates a wire from edges and wires. /// \param[in] theShapes list of shapes. Only edges and wires allowed. diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 59f65d257..401570245 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -328,7 +328,8 @@ ADD_UNIT_TESTS( TestMultiRotation05.py TestMultiRotationWithParameter.py TestMultiTranslation.py - TestOffset.py + TestOffset1.py + TestOffset2.py TestPresentation.py TestProjection.py TestProjectionBSpline.py diff --git a/src/SketchPlugin/SketchPlugin_Offset.cpp b/src/SketchPlugin/SketchPlugin_Offset.cpp index 7ea47ee0e..f3f8d2b89 100644 --- a/src/SketchPlugin/SketchPlugin_Offset.cpp +++ b/src/SketchPlugin/SketchPlugin_Offset.cpp @@ -143,10 +143,14 @@ void SketchPlugin_Offset::execute() // 5.b. Find a chain of edges std::list aChain; aChain.push_back(aFeature); - if (aStartPoint && anEndPoint) { // not closed edge - bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aProcessedEdgesSet, aChain, true); - if (!isClosed) - findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aProcessedEdgesSet, aChain, false); + bool isClosed = !(aStartPoint && anEndPoint); // not closed edge + if (!isClosed) { + isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, + aProcessedEdgesSet, aChain, true); + if (!isClosed) { + isClosed = findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, + aProcessedEdgesSet, aChain, false); + } } aProcessedEdgesSet.insert(aFeature); @@ -161,7 +165,7 @@ void SketchPlugin_Offset::execute() } } std::shared_ptr aWireBuilder( - new GeomAlgoAPI_WireBuilder(aTopoChain)); + new GeomAlgoAPI_WireBuilder(aTopoChain, !isClosed)); GeomShapePtr aWireShape = aWireBuilder->shape(); GeomWirePtr aWire (new GeomAPI_Wire (aWireShape)); @@ -283,7 +287,8 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge, } // 5. Continue gathering the chain (recursive) - return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theProcessedEdgesSet, theChain, isPrepend); + return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, + theProcessedEdgesSet, theChain, isPrepend); } static void setRefListValue(AttributeRefListPtr theList, int theListSize, @@ -647,8 +652,27 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult, void SketchPlugin_Offset::attributeChanged(const std::string& theID) { -//// if (theID == EDGES_ID()) -//// removeCreated(); + if (theID == EDGES_ID()) { + AttributeRefListPtr aSelected = reflist(EDGES_ID()); + if (aSelected->size() == 0) { + // Clear list of objects + AttributeRefListPtr anOffsetAttr = reflist(SketchPlugin_Constraint::ENTITY_B()); + std::list anOffsetList = anOffsetAttr->list(); + std::set aFeaturesToBeRemoved; + for (std::list::iterator anIt = anOffsetList.begin(); + anIt != anOffsetList.end(); ++anIt) { + FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt); + if (aFeature) + aFeaturesToBeRemoved.insert(aFeature); + } + + reflist(SketchPlugin_Constraint::ENTITY_A())->clear(); + anOffsetAttr->clear(); + intArray(SketchPlugin_Constraint::ENTITY_C())->setSize(0); + + ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved); + } + } } bool SketchPlugin_Offset::customAction(const std::string& theActionId) @@ -701,9 +725,12 @@ bool SketchPlugin_Offset::findWires() std::list aChain; aChain.push_back(aFeature); - bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aProcessedEdgesSet, aChain, true); - if (!isClosed) - findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aProcessedEdgesSet, aChain, false); + bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, + aProcessedEdgesSet, aChain, true); + if (!isClosed) { + findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, + aProcessedEdgesSet, aChain, false); + } std::list::iterator aChainIt = aChain.begin(); for (; aChainIt != aChain.end(); ++aChainIt) { diff --git a/src/SketchPlugin/Test/TestOffset.py b/src/SketchPlugin/Test/TestOffset1.py similarity index 94% rename from src/SketchPlugin/Test/TestOffset.py rename to src/SketchPlugin/Test/TestOffset1.py index b673927cf..24b162247 100644 --- a/src/SketchPlugin/Test/TestOffset.py +++ b/src/SketchPlugin/Test/TestOffset1.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2019 CEA/DEN, EDF R&D +# 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 @@ -141,7 +141,7 @@ aLine2EndPoint.setValue(-20., 0.) aSession.finishOperation() assert (model.dof(aSketchFeature) == 13) #========================================================================= -# Link arc points and lines points by the coincidence constraint +# Link arc points and lines points by the coincidence constraints #========================================================================= aSession.startOperation() aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence") @@ -159,7 +159,15 @@ reflistA.setAttr(anArcStartPoint) reflistB.setAttr(aLine2StartPoint) aConstraint.execute() aSession.finishOperation() -assert (model.dof(aSketchFeature) == 9) +aSession.startOperation() +aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence") +reflistA = aConstraint.refattr("ConstraintEntityA") +reflistB = aConstraint.refattr("ConstraintEntityB") +reflistA.setAttr(aLine1EndPoint) +reflistB.setAttr(aLine2EndPoint) +aConstraint.execute() +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 7) #========================================================================= # Make offset for objects created above #========================================================================= @@ -175,7 +183,7 @@ anOffset.real("offset_value").setValue(VALUE) anOffset.boolean("reversed").setValue(IS_REVERSED) anOffset.execute() aSession.finishOperation() -assert (model.dof(aSketchFeature) == 9) +assert (model.dof(aSketchFeature) == 7) #========================================================================= # Verify all offset objects #========================================================================= @@ -183,7 +191,7 @@ aRefListA = anOffset.reflist("ConstraintEntityA") aRefListB = anOffset.reflist("ConstraintEntityB") anOffsetToBaseMap = anOffset.intArray("ConstraintEntityC") checkOffset(aRefListA, aRefListB, anOffsetToBaseMap, VALUE, IS_REVERSED, 3, 6) -assert (model.dof(aSketchFeature) == 9) +assert (model.dof(aSketchFeature) == 7) #========================================================================= # Remove object from offset @@ -192,7 +200,7 @@ aSession.startOperation() aRefListInitial.remove(aSketchLine2.lastResult()) aSession.finishOperation() checkOffset(aRefListA, aRefListB, anOffsetToBaseMap, VALUE, IS_REVERSED, 2, 4) -assert (model.dof(aSketchFeature) == 9) +assert (model.dof(aSketchFeature) == 7) #========================================================================= # Clear list of objects @@ -205,7 +213,7 @@ aRefListInitial.clear() aRefListInitial.append(aSketchArc1.lastResult()) aSession.finishOperation() checkOffset(aRefListA, aRefListB, anOffsetToBaseMap, VALUE, IS_REVERSED, 1, 1) -assert (model.dof(aSketchFeature) == 9) +assert (model.dof(aSketchFeature) == 7) #========================================================================= # End of test diff --git a/src/SketchPlugin/Test/TestOffset2.py b/src/SketchPlugin/Test/TestOffset2.py new file mode 100644 index 000000000..e15e35d7c --- /dev/null +++ b/src/SketchPlugin/Test/TestOffset2.py @@ -0,0 +1,61 @@ +# 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 + +SKETCH_DOF = 9 + +model.begin() +partSet = model.moduleDocument() + +### Create Sketch +Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY")) + +### Create SketchArc +SketchArc_1 = Sketch_1.addArc(10, 10, 50, 0, 0, 50, False) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(0, 50, -20, 0) + +### Create SketchLine +SketchLine_2 = Sketch_1.addLine(50, 0, -20, 0) +Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.startPoint()) +Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_2.startPoint()) +model.do() + +assert(model.dof(Sketch_1) == SKETCH_DOF) + +### Create SketchOffset +SketchOffset_1_objects = [SketchLine_1.result(), SketchArc_1.results()[1], SketchLine_2.result()] +SketchOffset_1 = Sketch_1.addOffset(SketchOffset_1_objects, 13, False) +model.do() + +# DoF should not change +assert(model.dof(Sketch_1) == SKETCH_DOF) +# check number of features +assert(len(SketchOffset_1.offset()) == 5) +model.testNbSubFeatures(Sketch_1, "SketchPoint", 0) +model.testNbSubFeatures(Sketch_1, "SketchLine", 4) +model.testNbSubFeatures(Sketch_1, "SketchArc", 4) +model.testNbSubFeatures(Sketch_1, "SketchBSpline", 0) +model.testNbSubFeatures(Sketch_1, "SketchBSplinePeriodic", 0) + +model.end() + +assert(model.checkPythonDump()) -- 2.39.2