Add geometry intersection tests. Tighten intersection with Vector from Location and coplanar Planes.

This commit is contained in:
Jonathan Wagenet 2025-09-11 12:32:23 -04:00
parent a291a942a1
commit da1294a390
2 changed files with 91 additions and 19 deletions

View file

@ -909,15 +909,15 @@ class Axis(metaclass=AxisMeta):
"""Find intersection of vector and axis"""
@overload
def intersect(self, location: Location) -> Location | None:
def intersect(self, location: Location) -> Vector | Location | None:
"""Find intersection of location and axis"""
@overload
def intersect(self, axis: Axis) -> Axis | None:
def intersect(self, axis: Axis) -> Vector | Axis | None:
"""Find intersection of axis and axis"""
@overload
def intersect(self, plane: Plane) -> Axis | None:
def intersect(self, plane: Plane) -> Vector | Axis | None:
"""Find intersection of plane and axis"""
def intersect(self, *args, **kwargs):
@ -965,12 +965,12 @@ class Axis(metaclass=AxisMeta):
# Find the "direction" of the location
location_dir = Plane(location).z_dir
# Is the location on the axis with the same direction?
if (
self.intersect(location.position) is not None
and location_dir == self.direction
):
return location
if self.intersect(location.position) is not None:
# Is the location on the axis with the same direction?
if location_dir == self.direction:
return location
else:
return location.position
if shape is not None:
return shape.intersect(self)
@ -1932,15 +1932,15 @@ class Location:
"""Find intersection of vector and location"""
@overload
def intersect(self, location: Location) -> Location | None:
def intersect(self, location: Location) -> Vector | Location | None:
"""Find intersection of location and location"""
@overload
def intersect(self, axis: Axis) -> Location | None:
def intersect(self, axis: Axis) -> Vector | Location | None:
"""Find intersection of axis and location"""
@overload
def intersect(self, plane: Plane) -> Location | None:
def intersect(self, plane: Plane) -> Vector | Location | None:
"""Find intersection of plane and location"""
def intersect(self, *args, **kwargs):
@ -1956,8 +1956,11 @@ class Location:
if vector is not None and self.position == vector:
return vector
if location is not None and self == location:
return self
if location is not None:
if self == location:
return self
elif self.position == location.position:
return self.position
if shape is not None:
return shape.intersect(self)
@ -3131,15 +3134,15 @@ class Plane(metaclass=PlaneMeta):
"""Find intersection of vector and plane"""
@overload
def intersect(self, location: Location) -> Location | None:
def intersect(self, location: Location) -> Vector | Location | None:
"""Find intersection of location and plane"""
@overload
def intersect(self, axis: Axis) -> Axis | Vector | None:
def intersect(self, axis: Axis) -> Vector | Axis | None:
"""Find intersection of axis and plane"""
@overload
def intersect(self, plane: Plane) -> Axis | None:
def intersect(self, plane: Plane) -> Axis | Plane | None:
"""Find intersection of plane and plane"""
@overload
@ -3172,6 +3175,9 @@ class Plane(metaclass=PlaneMeta):
return intersection_point
if plane is not None:
if self.contains(plane.origin) and self.z_dir == plane.z_dir:
return self
surface1 = Geom_Plane(self.wrapped)
surface2 = Geom_Plane(plane.wrapped)
intersector = GeomAPI_IntSS(surface1, surface2, TOLERANCE)
@ -3187,8 +3193,11 @@ class Plane(metaclass=PlaneMeta):
if location is not None:
pln = Plane(location)
if pln.origin == self.origin and pln.z_dir == self.z_dir:
return location
if self.contains(pln.origin):
if self.z_dir == pln.z_dir:
return location
else:
return pln.origin
if shape is not None:
return shape.intersect(self)

View file

@ -59,6 +59,69 @@ def make_params(matrix):
return params
# Geometric test objects
ax1 = Axis.X
ax2 = Axis.Y
ax3 = Axis((0, 0, 5), (1, 0, 0))
pl1 = Plane.YZ
pl2 = Plane.XY
pl3 = Plane.XY.offset(5)
pl4 = Plane((0, 5, 0))
vl1 = Vector(2, 0, 0)
vl2 = Vector(2, 0, 5)
lc1 = Location((2, 0, 0))
lc2 = Location((2, 0, 5))
lc3 = Location((0, 0, 0), (0, 90, 90))
lc4 = Location((2, 0, 0), (0, 90, 90))
# Geometric test matrix
geometry_matrix = [
Case(ax1, ax3, None, "parallel/skew", None),
Case(ax1, ax1, Axis, "collinear", None),
Case(ax1, ax2, Vector, "intersecting", None),
Case(ax1, pl3, None, "parallel", None),
Case(ax1, pl2, Axis, "coplanar", None),
Case(ax1, pl1, Vector, "intersecting", None),
Case(ax1, vl2, None, "non-coincident", None),
Case(ax1, vl1, Vector, "coincident", None),
Case(ax1, lc2, None, "non-coincident", None),
Case(ax1, lc4, Location, "intersecting, co-z", None),
Case(ax1, lc1, Vector, "intersecting", None),
Case(pl2, pl3, None, "parallel", None),
Case(pl2, pl4, Plane, "coplanar", None),
Case(pl1, pl2, Axis, "intersecting", None),
Case(pl3, ax1, None, "parallel", None),
Case(pl2, ax1, Axis, "coplanar", None),
Case(pl1, ax1, Vector, "intersecting", None),
Case(pl1, vl2, None, "non-coincident", None),
Case(pl2, vl1, Vector, "coincident", None),
Case(pl1, lc2, None, "non-coincident", None),
Case(pl1, lc3, Location, "intersecting, co-z", None),
Case(pl2, lc4, Vector, "coincident", None),
Case(vl1, vl2, None, "non-coincident", None),
Case(vl1, vl1, Vector, "coincident", None),
Case(vl1, lc2, None, "non-coincident", None),
Case(vl1, lc1, Vector, "coincident", None),
Case(lc1, lc2, None, "non-coincident", None),
Case(lc1, lc4, Vector, "coincident", None),
Case(lc1, lc1, Location, "coincident, co-z", None),
]
@pytest.mark.parametrize("obj, target, expected", make_params(geometry_matrix))
def test_geometry(obj, target, expected):
run_test(obj, target, expected)
# FreeCAD issue example
c1 = CenterArc((0, 0), 10, 0, 360).edge()
c2 = CenterArc((19, 0), 10, 0, 360).edge()