Salome HOME
Issue #2618: PlaneGCS errors processing
authorazv <azv@opencascade.com>
Fri, 14 Sep 2018 06:45:04 +0000 (09:45 +0300)
committerazv <azv@opencascade.com>
Fri, 14 Sep 2018 08:54:26 +0000 (11:54 +0300)
Implement checking degenerated geometry after resolving a set of sketch constraints.

16 files changed:
src/PythonAPI/model/sketcher/tests.py
src/PythonAPI/model/tests/tests.py
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/Test/Test2119.py [new file with mode: 0644]
src/SketchPlugin/Test/Test2224.py [new file with mode: 0644]
src/SketchPlugin/Test/Test2427.py [new file with mode: 0644]
src/SketchPlugin/Test/TestDegeneratedGeometry.py [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Solver.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.h
src/SketchSolver/SketchSolver_Error.h
src/SketchSolver/SketchSolver_Group.cpp
src/SketchSolver/SketchSolver_Storage.h
src/SketchSolver/SketchSolver_msg_en.ts

index ea690813d4eefae9eb724940779b376ad8e6c869..4ddea3ca4aab6f6bee916173557850b42d885655 100644 (file)
@@ -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)
index 45c8ffbf35599dd27fe5f24cc743479d7c90e6d3..92ff8a609bc19296865ceed3a4436be036c3c0fd 100644 (file)
@@ -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
   """
index bb21061611e7b26a8a61919c9fee36cb65ddb385..1b843cd85487df52ca34cd8b9e4a3a99c66326e4 100644 (file)
@@ -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 (file)
index 0000000..1c949e8
--- /dev/null
@@ -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<mailto: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 (file)
index 0000000..d5ee63d
--- /dev/null
@@ -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<mailto: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 (file)
index 0000000..e390a81
--- /dev/null
@@ -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<mailto: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 (file)
index 0000000..e9b2cfc
--- /dev/null
@@ -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<mailto: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() == "")
index a294ba5654d3953e1045933ed1dfc9ecced3900d..c3b0fbcf8ced0c35dcb52226943245b8e3120ae7 100644 (file)
@@ -19,6 +19,7 @@
 //
 
 #include <PlaneGCSSolver_EdgeWrapper.h>
+#include <cmath>
 
 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<GCS::Line> aLine = std::dynamic_pointer_cast<GCS::Line>(myEntity);
+    return squareDistance(aLine->p1, aLine->p2) < aSqTol;
+  }
+  else if (myType == ENTITY_CIRCLE) {
+    std::shared_ptr<GCS::Circle> aCircle = std::dynamic_pointer_cast<GCS::Circle>(myEntity);
+    return *aCircle->rad < tolerance || *aCircle->rad > aMaxRadius;
+  }
+  else if (myType == ENTITY_ARC) {
+    static const double anAngleTol = 1.e-5;
+    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(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;
+}
index e60545ac8b5eea01b9e0e9bda8e28dd1a7807b5d..8bdb7cdd6648312646ce80f2689f31a05dcdb2ee 100644 (file)
@@ -43,6 +43,8 @@ public:
   virtual SketchSolver_EntityType type() const
   { return myType; }
 
+  bool isDegenerated() const;
+
 private:
   SketchSolver_EntityType myType;
   GCSCurvePtr myEntity;
index 7f302d8b66011b4f119faebeb8c58e2caae7ff3a..c1fe8be563ede356a6753f725b27f141c8731c6f 100644 (file)
@@ -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
   };
index e02c6993499010e231cf162b0f8217c69122a871..93399ff9a239bae7a5fe5fb96eb56a5e4e423ba2 100644 (file)
@@ -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<FeaturePtr, EntityWrapperPtr>::const_iterator aFIt = myFeatureMap.begin();
+  for (; aFIt != myFeatureMap.end(); ++aFIt) {
+    EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aFIt->second);
+    if (anEdge && anEdge->isDegenerated())
+      return PlaneGCSSolver_Solver::STATUS_DEGENERATED;
+  }
+  return PlaneGCSSolver_Solver::STATUS_OK;
+}
index 48526b9c734cd83d524f8b790a35c0a981061680..1b6ab890c474a587848cad04ec089205d6fc4642 100644 (file)
@@ -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;
 
index 3093e7bc9ac20f73330e224af1021104ef8d2178..bac5b642ad56d0ddfac7353cddee6d92ba1fd7bb 100644 (file)
@@ -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
index 4c005e9d318e52134c9fc4afe3acd0564d699821..eea48cbd7138f7f3075243cf5f3f998a08f819a4 100644 (file)
@@ -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;
index 8017703488077d7add53fd5d4859fd7eb992a3e6..28a3370ee6f61fb0052b7e3246122856927a66d1 100644 (file)
@@ -129,6 +129,11 @@ public:
   /// \brief Return list of conflicting constraints
   std::set<ObjectPtr> 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;
 
index f3b66c60984ff2a31af9f06a11a8ea72606e88dc..9d2ebc74f373b6a769dd8a9a14271734782c5c3c 100755 (executable)
       <source>Caution: SolveSpace crash! Constraints are wrong</source>
       <translation>Caution: SolveSpace crash! Constraints are wrong</translation>
     </message>
+    <message>
+      <source>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.</source>
+      <translation>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.</translation>
+    </message>
   </context>
 </TS>