From 5670f4d1d8063dbd131a0b3e67f881be80a6e2be Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 14 Sep 2018 09:45:04 +0300 Subject: [PATCH] Issue #2618: PlaneGCS errors processing Implement checking degenerated geometry after resolving a set of sketch constraints. --- src/PythonAPI/model/sketcher/tests.py | 18 ++ src/PythonAPI/model/tests/tests.py | 9 - src/SketchPlugin/CMakeLists.txt | 4 + src/SketchPlugin/Test/Test2119.py | 147 +++++++++++++++++ src/SketchPlugin/Test/Test2224.py | 71 ++++++++ src/SketchPlugin/Test/Test2427.py | 132 +++++++++++++++ .../Test/TestDegeneratedGeometry.py | 156 ++++++++++++++++++ .../PlaneGCSSolver_EdgeWrapper.cpp | 31 ++++ .../PlaneGCSSolver_EdgeWrapper.h | 2 + .../PlaneGCSSolver/PlaneGCSSolver_Solver.h | 1 + .../PlaneGCSSolver/PlaneGCSSolver_Storage.cpp | 11 ++ .../PlaneGCSSolver/PlaneGCSSolver_Storage.h | 5 + src/SketchSolver/SketchSolver_Error.h | 9 + src/SketchSolver/SketchSolver_Group.cpp | 22 ++- src/SketchSolver/SketchSolver_Storage.h | 5 + src/SketchSolver/SketchSolver_msg_en.ts | 4 + 16 files changed, 615 insertions(+), 12 deletions(-) create mode 100644 src/SketchPlugin/Test/Test2119.py create mode 100644 src/SketchPlugin/Test/Test2224.py create mode 100644 src/SketchPlugin/Test/Test2427.py create mode 100644 src/SketchPlugin/Test/TestDegeneratedGeometry.py diff --git a/src/PythonAPI/model/sketcher/tests.py b/src/PythonAPI/model/sketcher/tests.py index ea690813d..4ddea3ca4 100644 --- a/src/PythonAPI/model/sketcher/tests.py +++ b/src/PythonAPI/model/sketcher/tests.py @@ -91,3 +91,21 @@ def assertArcValidity(theArc): aDistCE = tools.distancePointPoint(aCenterPnt, aEndPnt) assert math.fabs(aDistCS - aDistCE) < TOLERANCE, "Wrong arc: center-start distance {}, center-end distance {}".format(aDistCS, aDistCE) assert math.fabs(aRadius.value() - aDistCS) < TOLERANCE, "Wrong arc: radius is {}, expected {}".format(aRadius.value(), aDistCS) + + +def checkSketch(theSketch, theDOF = -1): + """ Tests the sketch is valid and DoF is equal to the given + """ + assert(theSketch.feature().error() == ""), "Sketch failed: {}".format(theSketch.feature().error()) + assert(theSketch.solverError().value() == ""), "Sketch solver failed: {}".format(theSketch.solverError().value()) + if theDOF != -1: + aDOF = sketcher.tools.dof(theSketch) + assert(aDOF == theDOF), "Sketch DoF {} is wrong. Expected {}".format(aDOF, theDOF) + + +def checkSketchErrorDegenerated(theSketch): + """ Verify the sketch reports error about degenerated geometry + """ + errorValue = theSketch.solverError().value() + assert(errorValue != "") + assert(errorValue.find("degenerated") >= 0) diff --git a/src/PythonAPI/model/tests/tests.py b/src/PythonAPI/model/tests/tests.py index 45c8ffbf3..92ff8a609 100644 --- a/src/PythonAPI/model/tests/tests.py +++ b/src/PythonAPI/model/tests/tests.py @@ -302,15 +302,6 @@ def checkResult(theFeature,theModel,NbRes,NbSubRes,NbSolid,NbFace,NbEdge,NbVerte theModel.testNbSubShapes(theFeature, GeomAPI_Shape.EDGE, NbEdge) theModel.testNbSubShapes(theFeature, GeomAPI_Shape.VERTEX, NbVertex) -def checkSketch(theSketch, theDOF = -1): - """ Tests the sketch is valid and DoF is equal to the given - """ - assert(theSketch.feature().error() == ""), "Sketch failed: {}".format(theSketch.feature().error()) - assert(theSketch.solverError().value() == ""), "Sketch solver failed: {}".format(theSketch.solverError().value()) - if theDOF != -1: - aDOF = sketcher.tools.dof(theSketch) - assert(aDOF == theDOF), "Sketch DoF {} is wrong. Expected {}".format(aDOF, theDOF) - def checkGroup(theGroup, theShapeType): """ Check that all selected shapes in group have correct shape type and unique name """ diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index bb2106161..1b843cd85 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -253,6 +253,10 @@ ADD_UNIT_TESTS(TestSketchPointLine.py Test2034_2.py Test2034_3.py Test2134.py + TestDegeneratedGeometry.py + Test2119.py + Test2224.py + Test2427.py ) if(${SKETCHER_CHANGE_RADIUS_WHEN_MOVE}) diff --git a/src/SketchPlugin/Test/Test2119.py b/src/SketchPlugin/Test/Test2119.py new file mode 100644 index 000000000..1c949e8cb --- /dev/null +++ b/src/SketchPlugin/Test/Test2119.py @@ -0,0 +1,147 @@ +## Copyright (C) 2018-20xx 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 +## + +""" + Test2119.py + Test case for issue #2119 "Sketch lost when creating a tangent constraint" +""" + +from salome.shaper import model + +from ModelAPI import * +from SketchAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +model.addParameter(Part_1_doc, "a", "63") +model.addParameter(Part_1_doc, "b", "50") +model.addParameter(Part_1_doc, "c", "100") +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOZ")) +SketchLine_1 = Sketch_1.addLine(-45, 0, 0, 0) +SketchLine_2 = Sketch_1.addLine(0, 0, 23.33965093306752, 8.494938217797719) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(33.83092017818969, 6.16907982180411, 40, 0) +SketchLine_4 = Sketch_1.addLine(40, 0, 55, 0) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchLine_5 = Sketch_1.addLine(55, 0, 55, 48) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchLine_6 = Sketch_1.addLine(55, 48, 15, 48) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchLine_7 = Sketch_1.addLine(15, 48, 15, 63) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint()) +SketchLine_8 = Sketch_1.addLine(15, 63, -15, 63) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint()) +SketchLine_9 = Sketch_1.addLine(-15, 63, -15, 35) +SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_6.result()) +SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_8.result()) +SketchConstraintCollinear_1 = Sketch_1.setCollinear(SketchLine_1.result(), SketchLine_4.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_5.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_9.result()) +SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_7.result()) +SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_3.endPoint(), SketchLine_4.endPoint(), 15, False) +SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_3.endPoint(), SketchLine_1.endPoint(), 40, False) +SketchConstraintDistance_3 = Sketch_1.setDistance(SketchLine_1.startPoint(), SketchLine_4.endPoint(), "c", False) +SketchConstraintDistance_4 = Sketch_1.setDistance(SketchLine_8.startPoint(), SketchLine_6.result(), 15, False) +SketchConstraintDistance_5 = Sketch_1.setDistance(SketchLine_8.startPoint(), SketchLine_5.result(), 40, False) +SketchLine_10 = Sketch_1.addLine(-45, 5, -45, 0) +SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_10.endPoint()) +SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_10.result()) +SketchConstraintDistance_6 = Sketch_1.setDistance(SketchLine_9.startPoint(), SketchLine_1.result(), "a", False) +SketchArc_1 = Sketch_1.addArc(-45, 35, -45, 5, -15, 35, False) +SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_10.result()) +SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_10.startPoint()) +SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_1.result(), 45) +SketchConstraintAngle_2 = Sketch_1.setAngleBackward(SketchLine_2.result(), SketchLine_4.result(), 20) +SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_9.endPoint()) +SketchConstraintTangent_1 = Sketch_1.setTangent(SketchArc_1.results()[1], SketchLine_9.result()) +SketchConstraintDistance_7 = Sketch_1.setDistance(SketchArc_1.center(), SketchLine_1.result(), 35, False) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], 30) +SketchArc_2 = Sketch_1.addArc(26.75985236632421, -0.9019879900613652, 33.83092017818969, 6.16907982180411, 23.33965093306752, 8.494938217797719, False) +SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchArc_2.startPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchArc_2.endPoint(), SketchLine_2.endPoint()) +SketchConstraintTangent_2 = Sketch_1.setTangent(SketchArc_2.results()[1], SketchLine_2.result()) +SketchConstraintTangent_3 = Sketch_1.setTangent(SketchArc_2.results()[1], SketchLine_3.result()) +SketchConstraintRadius_2 = Sketch_1.setRadius(SketchArc_2.results()[1], 10) +SketchPoint_1 = Sketch_1.addPoint(model.selection("VERTEX", "PartSet/Origin")) +SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchPoint_1.coordinates()) +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1f-SketchLine_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f-SketchLine_7f-SketchLine_8f-SketchLine_9f-SketchLine_10f-SketchArc_1_2r-SketchArc_2_2r")], model.selection(), "b/2", "b/2") +Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face_1")) +SketchProjection_1 = Sketch_2.addProjection(model.selection("VERTEX", "Extrusion_1_1/Generated_Face_3&Extrusion_1_1/Generated_Face_2&Extrusion_1_1/To_Face_1"), False) +SketchPoint_2 = SketchProjection_1.createdFeature() +SketchCircle_1 = Sketch_2.addCircle(-15, 35, 25) +SketchConstraintCoincidence_15 = Sketch_2.setCoincident(SketchCircle_1.center(), SketchPoint_2.result()) +SketchConstraintRadius_3 = Sketch_2.setRadius(SketchCircle_1.results()[1], "50/2") +model.do() +ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [model.selection("WIRE", "Sketch_2/Wire-SketchCircle_1_2f")], model.selection(), 0, 13, [model.selection("SOLID", "Extrusion_1_1")]) +Sketch_3 = model.addSketch(Part_1_doc, model.selection("FACE", "ExtrusionCut_1_1/Modfied_4")) +SketchProjection_2 = Sketch_3.addProjection(model.selection("VERTEX", "ExtrusionCut_1_1/Modfied_2&ExtrusionCut_1_1/Modfied_3&ExtrusionCut_1_1/Modfied_4"), False) +SketchPoint_3 = SketchProjection_2.createdFeature() +SketchProjection_3 = Sketch_3.addProjection(model.selection("EDGE", "ExtrusionCut_1_1/Modfied_3&ExtrusionCut_1_1/Modfied_4"), False) +SketchLine_11 = SketchProjection_3.createdFeature() +SketchArc_3 = Sketch_3.addArc(-15, 35, -15, 46.0103340429751, -17.02045759563166, 24.17663606626138, True) +SketchConstraintCoincidence_16 = Sketch_3.setCoincident(SketchPoint_3.result(), SketchArc_3.center()) +SketchConstraintCoincidence_17 = Sketch_3.setCoincident(SketchLine_11.result(), SketchArc_3.startPoint()) +SketchProjection_4 = Sketch_3.addProjection(model.selection("EDGE", "ExtrusionCut_1_1/Modfied_2&ExtrusionCut_1_1/Modfied_4"), False) +SketchArc_4 = SketchProjection_4.createdFeature() +SketchConstraintCoincidence_18 = Sketch_3.setCoincident(SketchArc_3.endPoint(), SketchArc_4.results()[1]) +SketchLine_12 = Sketch_3.addLine(-15, 46.0103340429751, -15, 35) +SketchConstraintCoincidence_19 = Sketch_3.setCoincident(SketchArc_3.startPoint(), SketchLine_12.startPoint()) +SketchArc_5 = Sketch_3.addArc(-24.92022800465918, 31.2515971841183, -15, 35, -17.02045759563166, 24.17663606626138, True) +SketchConstraintCoincidence_20 = Sketch_3.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchArc_5.startPoint()) +SketchConstraintCoincidence_21 = Sketch_3.setCoincident(SketchArc_3.endPoint(), SketchArc_5.endPoint()) +model.do() +model.end() + + +# case 1: undo the error +model.begin() +aTangent = Sketch_3.setTangent(SketchArc_5.results()[1], SketchArc_4.results()[1]) +model.do() +model.end() +model.checkSketchErrorDegenerated(Sketch_3) +model.undo() +model.checkSketch(Sketch_3) + +# case 2: remove degeneracy-producting constraint +model.begin() +aTangent = Sketch_3.setTangent(SketchArc_5.results()[1], SketchArc_4.results()[1]) +model.do() +model.checkSketchErrorDegenerated(Sketch_3) +Part_1_doc.removeFeature(aTangent.feature()) +model.do() +model.end() +model.checkSketch(Sketch_3) + +# case 3: remove degenerated edge +model.begin() +aTangent = Sketch_3.setTangent(SketchArc_5.results()[1], SketchArc_4.results()[1]) +model.do() +model.checkSketchErrorDegenerated(Sketch_3) +ModelAPI.removeFeaturesAndReferences(FeatureSet([SketchArc_5.feature()])) +model.do() +model.end() +model.checkSketch(Sketch_3) + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/Test2224.py b/src/SketchPlugin/Test/Test2224.py new file mode 100644 index 000000000..d5ee63d33 --- /dev/null +++ b/src/SketchPlugin/Test/Test2224.py @@ -0,0 +1,71 @@ +## Copyright (C) 2018-20xx 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 +## + +""" + Test2224.py + Test case for issue #2224 "tangent arc problem when end point is on edge that is tangent to arc" +""" + +from salome.shaper import model + +from ModelAPI import * +from SketchAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(8.985073018276022, 21.83713948745567, 31.50462311471469, 21.83713948745567) +SketchArc_1 = Sketch_1.addArc(31.50462311471469, -6.977379981512561, 31.50462311471469, 21.83713948745567, 25.29690414212808, 21.16050830785517, True) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchArc_1.startPoint()) +SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchArc_1.results()[1]) +model.do() +model.end() + + +# case 1: undo the error +model.begin() +aCoincidence = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.result()) +model.do() +model.end() +model.checkSketchErrorDegenerated(Sketch_1) +model.undo() +model.checkSketch(Sketch_1) + +# case 2: remove degeneracy-producting constraint +model.begin() +aCoincidence = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.result()) +model.do() +model.checkSketchErrorDegenerated(Sketch_1) +Part_1_doc.removeFeature(aCoincidence.feature()) +model.do() +model.end() +model.checkSketch(Sketch_1) + +# case 3: remove degenerated edge +model.begin() +aCoincidence = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.result()) +model.do() +model.checkSketchErrorDegenerated(Sketch_1) +ModelAPI.removeFeaturesAndReferences(FeatureSet([SketchArc_1.feature()])) +model.do() +model.end() +model.checkSketch(Sketch_1) diff --git a/src/SketchPlugin/Test/Test2427.py b/src/SketchPlugin/Test/Test2427.py new file mode 100644 index 000000000..e390a8114 --- /dev/null +++ b/src/SketchPlugin/Test/Test2427.py @@ -0,0 +1,132 @@ +## Copyright (C) 2018-20xx 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 +## + +""" + Test2427.py + Test case for issue #2427 "error in sketch solver" +""" + +from SketchAPI import * + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, 0, 9.085455512028094, 9.085455512028094) +SketchLine_1.setAuxiliary(True) +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_2 = SketchProjection_1.createdFeature() +SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_2.result(), SketchLine_1.result(), 45) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchAPI_Line(SketchLine_2).startPoint()) +SketchArc_1 = Sketch_1.addArc(0, 0, 3.427521359192312, 2.062061427864143, 2.06206142786378, 3.427521359191917, True) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchArc_1.center()) +SketchLine_3 = Sketch_1.addLine(6, 7.365459931328136, 6, 12) +SketchLine_4 = Sketch_1.addLine(6, 12, -6, 12) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchLine_5 = Sketch_1.addLine(-6, 12, -6, 7.365459931328136) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], 4) +SketchLine_6 = Sketch_1.addLine(-5.8309518948453, -7.5, -11, -7.5) +SketchLine_7 = Sketch_1.addLine(-11, -7.5, -11, -10) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint()) +SketchLine_8 = Sketch_1.addLine(-11, -10, 11, -10) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint()) +SketchLine_9 = Sketch_1.addLine(11, -10, 11, -7.5) +SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint()) +SketchLine_10 = Sketch_1.addLine(11, -7.5, 5.830951894845301, -7.5) +SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_6.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_10.result()) +SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_8.result()) +SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_4.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_7.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_9.result()) +SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_3.result()) +SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_5.result()) +SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_8.result(), 22) +SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_4.result(), 12) +SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_4.result(), SketchLine_7.endPoint(), 22, True) +SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_7.result(), 2.5) +SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_8.endPoint(), SketchLine_2.result(), 10, True) +SketchPoint_1 = Sketch_1.addPoint(0, -10) +SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_8.result()) +SketchConstraintMiddle_1 = Sketch_1.setMiddlePoint(SketchLine_8.result(), SketchPoint_1.coordinates()) +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_11 = SketchProjection_2.createdFeature() +SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchLine_11.result(), SketchPoint_1.coordinates()) +SketchLine_12 = Sketch_1.addLine(2.06206142786378, 3.427521359191917, 6, 7.365459931328136) +SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_12.startPoint()) +SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_12.endPoint(), SketchLine_3.startPoint()) +SketchConstraintParallel_1 = Sketch_1.setParallel(SketchLine_12.result(), SketchLine_1.result()) +SketchLine_13 = Sketch_1.addLine(3.427521359192312, 2.062061427864143, 8.000256151511394, 5.123075395720331) +SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_13.startPoint()) +SketchArc_2 = Sketch_1.addArc(0, 0, 8.000256151511394, 5.123075395720331, 5.830951894845301, -7.5, True) +SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchArc_2.center()) +SketchConstraintCoincidence_15 = Sketch_1.setCoincident(SketchArc_2.startPoint(), SketchLine_13.endPoint()) +SketchConstraintCoincidence_16 = Sketch_1.setCoincident(SketchLine_10.endPoint(), SketchArc_2.endPoint()) +SketchConstraintRadius_2 = Sketch_1.setRadius(SketchArc_2.results()[1], 9.5) +SketchLine_14 = Sketch_1.addLine(3.427521359192312, 2.062061427864143, 2.06206142786378, 3.427521359191917) +SketchLine_14.setAuxiliary(True) +SketchConstraintCoincidence_17 = Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_14.startPoint()) +SketchConstraintCoincidence_18 = Sketch_1.setCoincident(SketchLine_14.endPoint(), SketchArc_1.endPoint()) +SketchPoint_2 = Sketch_1.addPoint(2.744791393527834, 2.744791393527835) +SketchConstraintCoincidence_19 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_14.result()) +SketchConstraintMiddle_2 = Sketch_1.setMiddlePoint(SketchLine_14.result(), SketchPoint_2.coordinates()) +SketchConstraintCoincidence_20 = Sketch_1.setCoincident(SketchLine_14.result(), SketchPoint_2.coordinates()) +SketchConstraintCoincidence_21 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_1.result()) +SketchArc_3 = Sketch_1.addArc(0, 0, -6, 7.365459931328136, -5.8309518948453, -7.5, False) +SketchConstraintCoincidence_22 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchArc_3.startPoint()) +SketchConstraintCoincidence_23 = Sketch_1.setCoincident(SketchLine_6.startPoint(), SketchArc_3.endPoint()) +SketchConstraintEqual_1 = Sketch_1.setEqual(SketchArc_2.results()[1], SketchArc_3.results()[1]) +SketchPoint_3 = Sketch_1.addPoint(0, 12) +SketchConstraintMiddle_3 = Sketch_1.setMiddlePoint(SketchLine_4.result(), SketchPoint_3.coordinates()) +SketchConstraintCoincidence_24 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_11.result()) +SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_7.result(), SketchLine_9.result()) +SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_6.result(), SketchLine_10.result()) +SketchConstraintEqual_4 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_5.result()) +SketchConstraintDistance_3 = Sketch_1.setDistance(SketchLine_13.endPoint(), SketchLine_12.result(), 3, True) +SketchConstraintCoincidence_25 = Sketch_1.setCoincident(SketchArc_3.center(), SketchLine_1.result()) +model.do() +model.end() + + +# Test 1. Add constraint to make auxiliary line degenerated, then undo +model.begin() +aParallel = Sketch_1.setParallel(SketchLine_1.result(), SketchLine_13.result()) +model.do() +model.end() +model.checkSketchErrorDegenerated(Sketch_1) +model.undo() +model.checkSketch(Sketch_1) + +# Test 2. Add constraint to make auxiliary line degenerated, then remove such constraint +model.begin() +aParallel = Sketch_1.setParallel(SketchLine_1.result(), SketchLine_13.result()) +model.do() +model.checkSketchErrorDegenerated(Sketch_1) +Part_1_doc.removeFeature(aParallel.feature()) +model.do() +model.end() +model.checkSketch(Sketch_1) + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestDegeneratedGeometry.py b/src/SketchPlugin/Test/TestDegeneratedGeometry.py new file mode 100644 index 000000000..e9b2cfca3 --- /dev/null +++ b/src/SketchPlugin/Test/TestDegeneratedGeometry.py @@ -0,0 +1,156 @@ +## Copyright (C) 2018-20xx 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 * +from ModelAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +####################################### +# Test 1. Make a circle degenerated +####################################### +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_1 = SketchProjection_1.createdFeature() +SketchCircle_1 = Sketch_1.addCircle(25, 20, 25) +SketchConstraintTangent_1 = Sketch_1.setTangent(SketchLine_1.result(), SketchCircle_1.results()[1]) +model.do() +model.end() + +# case 1: undo the error +model.begin() +aCoincidence = Sketch_1.setCoincident(SketchAPI_Line(SketchLine_1).startPoint(), SketchCircle_1.center()) +model.do() +model.end() +model.checkSketchErrorDegenerated(Sketch_1) +model.undo() +model.checkSketch(Sketch_1) + +# case 2: remove degeneracy-producting constraint +model.begin() +aCoincidence = Sketch_1.setCoincident(SketchAPI_Line(SketchLine_1).startPoint(), SketchCircle_1.center()) +model.do() +model.checkSketchErrorDegenerated(Sketch_1) +Part_1_doc.removeFeature(aCoincidence.feature()) +model.do() +model.end() +model.checkSketch(Sketch_1) + +# case 3: remove degenerated edge +model.begin() +aCoincidence = Sketch_1.setCoincident(SketchAPI_Line(SketchLine_1).startPoint(), SketchCircle_1.center()) +model.do() +model.checkSketchErrorDegenerated(Sketch_1) +ModelAPI.removeFeaturesAndReferences(FeatureSet([SketchCircle_1.feature()])) +model.do() +model.end() +model.checkSketch(Sketch_1) + + +####################################### +# Test 2. Make a line degenerated +####################################### +model.begin() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_2 = Sketch_2.addLine(15, 15, 40, 40) +SketchProjection_2 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OX"), True) +SketchLine_3 = SketchProjection_2.createdFeature() +SketchProjection_3 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OY"), True) +SketchLine_4 = SketchProjection_3.createdFeature() +model.do() +model.end() + +# case 1: undo the error +model.begin() +aParallel1 = Sketch_2.setParallel(SketchLine_2.result(), SketchLine_3.result()) +aParallel2 = Sketch_2.setParallel(SketchLine_2.result(), SketchLine_4.result()) +model.do() +model.end() +model.checkSketchErrorDegenerated(Sketch_2) +model.undo() +model.do() +model.checkSketch(Sketch_2) + +# case 2: remove degeneracy-producting constraint +model.begin() +aParallel1 = Sketch_2.setParallel(SketchLine_2.result(), SketchLine_3.result()) +aParallel2 = Sketch_2.setParallel(SketchLine_2.result(), SketchLine_4.result()) +model.do() +model.checkSketchErrorDegenerated(Sketch_2) +Part_1_doc.removeFeature(aParallel1.feature()) +Part_1_doc.removeFeature(aParallel2.feature()) +model.do() +model.end() +model.checkSketch(Sketch_2) + +# case 3: remove degenerated edge +model.begin() +aParallel1 = Sketch_2.setParallel(SketchLine_2.result(), SketchLine_3.result()) +aParallel2 = Sketch_2.setParallel(SketchLine_2.result(), SketchLine_4.result()) +model.do() +model.checkSketchErrorDegenerated(Sketch_2) +ModelAPI.removeFeaturesAndReferences(FeatureSet([SketchLine_2.feature()])) +model.do() +model.end() +model.checkSketch(Sketch_2) + + +####################################### +# Test 3. Make an arc degenerated +####################################### +model.begin() +Sketch_3 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchArc_1 = Sketch_3.addArc(-23.95261096318143, -9.48105223640866, -4.386575834490306, -17.91131183048461, -20.45766592507274, 11.53523589116715, False) +model.do() +model.end() + +# case 1: undo the error +model.begin() +aCoincidence = Sketch_3.setCoincident(SketchArc_1.startPoint(), SketchArc_1.endPoint()) +model.do() +model.end() +model.checkSketchErrorDegenerated(Sketch_3) +model.undo() +model.do() +model.checkSketch(Sketch_3) + +# case 2: remove degeneracy-producting constraint +model.begin() +aCoincidence = Sketch_3.setCoincident(SketchArc_1.startPoint(), SketchArc_1.endPoint()) +model.do() +model.checkSketchErrorDegenerated(Sketch_3) +Part_1_doc.removeFeature(aCoincidence.feature()) +model.do() +model.end() +model.checkSketch(Sketch_3) + +# case 3: remove degenerated edge +model.begin() +aCoincidence = Sketch_3.setCoincident(SketchArc_1.startPoint(), SketchArc_1.endPoint()) +model.do() +model.checkSketchErrorDegenerated(Sketch_3) +ModelAPI.removeFeaturesAndReferences(FeatureSet([SketchArc_1.feature()])) +model.do() +model.end() +assert(Sketch_3.solverError().value() == "") diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp index a294ba565..c3b0fbcf8 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp @@ -19,6 +19,7 @@ // #include +#include PlaneGCSSolver_EdgeWrapper::PlaneGCSSolver_EdgeWrapper(const GCSCurvePtr theEntity) : myEntity(theEntity) @@ -34,3 +35,33 @@ PlaneGCSSolver_EdgeWrapper::PlaneGCSSolver_EdgeWrapper(const GCSCurvePtr theEnti } } } + +static double squareDistance(const GCS::Point& theP1, const GCS::Point& theP2) +{ + double dx = *theP1.x - *theP2.x; + double dy = *theP1.y - *theP2.y; + return dx*dx + dy*dy; +} + +bool PlaneGCSSolver_EdgeWrapper::isDegenerated() const +{ + static const double aSqTol = tolerance * 1e-2; + static const double aMaxRadius = 1e8; + if (myType == ENTITY_LINE) { + std::shared_ptr aLine = std::dynamic_pointer_cast(myEntity); + return squareDistance(aLine->p1, aLine->p2) < aSqTol; + } + else if (myType == ENTITY_CIRCLE) { + std::shared_ptr aCircle = std::dynamic_pointer_cast(myEntity); + return *aCircle->rad < tolerance || *aCircle->rad > aMaxRadius; + } + else if (myType == ENTITY_ARC) { + static const double anAngleTol = 1.e-5; + std::shared_ptr anArc = std::dynamic_pointer_cast(myEntity); + double anAngleDiff = fabs(*anArc->startAngle - *anArc->endAngle); + double aSqRadius = squareDistance(anArc->center, anArc->start); + return aSqRadius < aSqTol || aSqRadius > aMaxRadius * aMaxRadius || // <- arc radius + anAngleDiff < anAngleTol || fabs(anAngleDiff - 2*PI) < anAngleTol; // <- arc angle + } + return false; +} diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h index e60545ac8..8bdb7cdd6 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h @@ -43,6 +43,8 @@ public: virtual SketchSolver_EntityType type() const { return myType; } + bool isDegenerated() const; + private: SketchSolver_EntityType myType; GCSCurvePtr myEntity; diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.h index 7f302d8b6..c1fe8be56 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.h @@ -35,6 +35,7 @@ public: STATUS_OK, STATUS_INCONSISTENT, STATUS_EMPTYSET, + STATUS_DEGENERATED, STATUS_FAILED, // set if no one other status is applicable STATUS_UNKNOWN // set for newly created groups }; diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp index e02c69934..93399ff9a 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp @@ -488,3 +488,14 @@ void PlaneGCSSolver_Storage::refresh() const for (; aFIt != anUpdatedFeatures.end(); ++aFIt) notify(*aFIt); } + +PlaneGCSSolver_Solver::SolveStatus PlaneGCSSolver_Storage::checkDegeneratedGeometry() const +{ + std::map::const_iterator aFIt = myFeatureMap.begin(); + for (; aFIt != myFeatureMap.end(); ++aFIt) { + EdgeWrapperPtr anEdge = std::dynamic_pointer_cast(aFIt->second); + if (anEdge && anEdge->isDegenerated()) + return PlaneGCSSolver_Solver::STATUS_DEGENERATED; + } + return PlaneGCSSolver_Solver::STATUS_OK; +} diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.h index 48526b9c7..1b6ab890c 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.h @@ -71,6 +71,11 @@ public: /// \return \c true if the constraint and all its parameters are removed successfully virtual bool removeConstraint(ConstraintPtr theConstraint); + /// \brief Verify, the sketch contains degenerated geometry + /// after resolving the set of constraints + /// \return STATUS_OK if the geometry is valid, STATUS_DEGENERATED otherwise. + virtual PlaneGCSSolver_Solver::SolveStatus checkDegeneratedGeometry() const; + /// \brief Update SketchPlugin features after resolving constraints virtual void refresh() const; diff --git a/src/SketchSolver/SketchSolver_Error.h b/src/SketchSolver/SketchSolver_Error.h index 3093e7bc9..bac5b642a 100644 --- a/src/SketchSolver/SketchSolver_Error.h +++ b/src/SketchSolver/SketchSolver_Error.h @@ -109,6 +109,15 @@ class SketchSolver_Error static const std::string MY_ERROR_VALUE("Unsupported type of constraint"); return MY_ERROR_VALUE; } + /// Sketch contains degenerated geometry + inline static const std::string& DEGENERATED_GEOMETRY() + { + static const std::string MY_ERROR_VALUE( + "The set of constraints lead to degenerated geometry. " + "To fix this, you can either undo your operation or " + "remove a constraint or the degenerated geometry."); + return MY_ERROR_VALUE; + } }; #endif diff --git a/src/SketchSolver/SketchSolver_Group.cpp b/src/SketchSolver/SketchSolver_Group.cpp index 4c005e9d3..eea48cbd7 100644 --- a/src/SketchSolver/SketchSolver_Group.cpp +++ b/src/SketchSolver/SketchSolver_Group.cpp @@ -217,6 +217,9 @@ bool SketchSolver_Group::resolveConstraints() removeTemporaryConstraints(); aResult = mySketchSolver->solve(); } + // check degenerated geometry after constraints resolving + if (aResult == PlaneGCSSolver_Solver::STATUS_OK) + aResult = myStorage->checkDegeneratedGeometry(); } catch (...) { getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR()) ->setValue(SketchSolver_Error::SOLVESPACE_CRASH()); @@ -262,8 +265,10 @@ bool SketchSolver_Group::resolveConstraints() mySketchSolver->undo(); if (!myConstraints.empty()) { // the error message should be changed before sending the message - getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR()) - ->setValue(SketchSolver_Error::CONSTRAINTS()); + const std::string& aErrorMsg = aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED ? + SketchSolver_Error::DEGENERATED_GEOMETRY() : + SketchSolver_Error::CONSTRAINTS(); + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(aErrorMsg); if (myPrevResult != aResult || myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN || myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) { @@ -287,8 +292,19 @@ bool SketchSolver_Group::resolveConstraints() } } - } else if (isGroupEmpty && isWorkplaneValid()) + } + else if (isGroupEmpty && isWorkplaneValid()) { + // clear error related to previously degenerated entities + if (myPrevResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED) { + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(""); + myPrevResult = PlaneGCSSolver_Solver::STATUS_OK; + // the error message should be changed before sending the message + myConflictingConstraints.clear(); + sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints); + } + computeDoF(); + } removeTemporaryConstraints(); myStorage->setNeedToResolve(false); return aResolved; diff --git a/src/SketchSolver/SketchSolver_Storage.h b/src/SketchSolver/SketchSolver_Storage.h index 801770348..28a3370ee 100644 --- a/src/SketchSolver/SketchSolver_Storage.h +++ b/src/SketchSolver/SketchSolver_Storage.h @@ -129,6 +129,11 @@ public: /// \brief Return list of conflicting constraints std::set getConflictingConstraints(SolverPtr theSolver) const; + /// \brief Verify, the sketch contains degenerated geometry + /// after resolving the set of constraints + /// \return STATUS_OK if the geometry is valid, STATUS_DEGENERATED otherwise. + virtual PlaneGCSSolver_Solver::SolveStatus checkDegeneratedGeometry() const = 0; + /// \brief Update SketchPlugin features after resolving constraints virtual void refresh() const = 0; diff --git a/src/SketchSolver/SketchSolver_msg_en.ts b/src/SketchSolver/SketchSolver_msg_en.ts index f3b66c609..9d2ebc74f 100755 --- a/src/SketchSolver/SketchSolver_msg_en.ts +++ b/src/SketchSolver/SketchSolver_msg_en.ts @@ -15,6 +15,10 @@ Caution: SolveSpace crash! Constraints are wrong Caution: SolveSpace crash! Constraints are wrong + + The set of constraints lead to degenerated geometry. To fix this, you can either undo your operation or remove a constraint or the degenerated geometry. + The set of constraints lead to degenerated geometry. To fix this, you can either undo your operation or remove a constraint or the degenerated geometry. + -- 2.39.2