Sketcher: Add tests for Circle to Line distance constraint

Note that support for secants currently only works for reference
constraints but not for driving ones. If and how this should be done
is still under discussion [1].

[1]: https://github.com/FreeCAD/FreeCAD/pull/9044#issuecomment-1548006842
This commit is contained in:
Jonas Bähr
2023-04-16 23:04:11 +02:00
committed by abdullahtahiriyo
parent 4250d8f991
commit f3595bce0b

View File

@@ -22,8 +22,15 @@
import FreeCAD, os, sys, unittest, Part, Sketcher
from Part import Precision
App = FreeCAD
xy_normal = FreeCAD.Vector(0, 0, 1)
def vec(x, y):
"""Shorthand to create a vector in the XY-plane"""
return FreeCAD.Vector(x, y, 0)
def CreateRectangleSketch(SketchFeature, corner, lengths):
hmin, hmax = corner[0], corner[0] + lengths[0]
vmin, vmax = corner[1], corner[1] + lengths[1]
@@ -268,6 +275,69 @@ class TestSketcherSolver(unittest.TestCase):
sketch.addConstraint(Sketcher.Constraint('Coincident',2,1,1,2))
self.assertEqual(sketch.detectMissingPointOnPointConstraints(0.0001), 0)
def testCircleToLineDistance_Driving_Passant(self):
sketch = self.Doc.addObject('Sketcher::SketchObject','Sketch')
radius = 20
circle = Part.Circle(vec(0, 0), xy_normal, radius)
line = Part.LineSegment(vec(-radius, 2*radius), vec(radius, 2*radius))
c_idx = sketch.addGeometry(circle)
l_idx = sketch.addGeometry(line)
# use a positive distance, other than the initial distance of the line
wanted_distance = radius/2
sketch.addConstraint(Sketcher.Constraint('Distance', c_idx, l_idx, wanted_distance))
self.assertSuccessfulSolve(sketch)
c_shape = sketch.Geometry[c_idx].toShape()
l_shape = sketch.Geometry[l_idx].toShape()
self.assertShapeDistance(c_shape, l_shape, wanted_distance)
@unittest.skip("Support for secants still under discussion, see comments in PR 9044")
def testCircleToLineDistance_Driving_Secant(self):
sketch = self.Doc.addObject('Sketcher::SketchObject','Sketch')
radius = 20
c_idx = sketch.addGeometry(Part.Circle(vec(0, 0), xy_normal, radius))
l_idx = sketch.addGeometry(Part.LineSegment(vec(-radius, 2*radius), vec(radius, 2*radius)))
# use a negative distance to tell "line is within the circle"
wanted_distance = -radius/2
sketch.addConstraint(Sketcher.Constraint('Distance', c_idx, l_idx, wanted_distance))
self.assertSuccessfulSolve(sketch)
c_shape = sketch.Geometry[c_idx].toShape()
l_shape = sketch.Geometry[l_idx].toShape()
self.assertShapeDistance(c_shape, l_shape, 0) # secant intersects circle, thus no distance
def testCircleToLineDistance_Reference_Secant(self):
sketch = self.Doc.addObject('Sketcher::SketchObject','Sketch')
radius = 20
c_idx = sketch.addGeometry(Part.Circle(vec(0, 0), xy_normal, radius))
l_idx = sketch.addGeometry(Part.LineSegment(vec(-radius, radius/2), vec(radius, radius/2)))
# The block constraints are required to ensure the geometry does not move.
# Without this, the solver may find another valid solution than what we assert.
sketch.addConstraint([
Sketcher.Constraint('Block', c_idx),
Sketcher.Constraint('Block', l_idx)])
# use a negative distance to tell "line is within the circle"
expected_distance = -radius/2 # note that we don't set this in the constraint below!
# TODO: addConstraint(constraint) triggers a solve (for godd reasons) however, this way
# one cannot add non-driving constraints. In contrast, addConstraint(list(constraint))
# does not solve automatically, thus we use this "overload".
# Much nicer would be an addConstraint(constraint, isReference=False), like addGeometry
dist_idx = sketch.addConstraint([Sketcher.Constraint('Distance', c_idx, l_idx, 0)])[0]
sketch.setDriving(dist_idx, False)
self.assertSuccessfulSolve(sketch)
actual_distance = sketch.Constraints[dist_idx].Value
self.assertAlmostEqual(expected_distance, actual_distance, delta=Precision.confusion(),
msg="Reference constraint did not return the expected distance.")
def assertSuccessfulSolve(self, sketch, msg=None):
status = sketch.solve()
# TODO: can we get the solver's messages somehow to improve the message?
self.failUnless(status == 0, msg=msg or "solver didn't converge")
def assertShapeDistance(self, shape1, shape2, expected_distance, msg=None):
distance, _, _ = shape1.distToShape(shape2)
self.assertAlmostEqual(distance, expected_distance,
delta=Precision.confusion(),
msg=msg or "The given shapes are not spaced by the expected distance.")
def tearDown(self):
#closing doc
FreeCAD.closeDocument("SketchSolverTest")