Skip to content

Commit

Permalink
Added Plane.find_intersection(Plane) Issue #327
Browse files Browse the repository at this point in the history
  • Loading branch information
gumyr committed Jun 19, 2024
1 parent 2c40e19 commit 35a33f8
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 38 deletions.
115 changes: 78 additions & 37 deletions src/build123d/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from OCP.BRepMesh import BRepMesh_IncrementalMesh
from OCP.BRepTools import BRepTools
from OCP.Geom import Geom_Line, Geom_Plane
from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf, GeomAPI_IntCS
from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf, GeomAPI_IntCS, GeomAPI_IntSS
from OCP.gp import (
gp_Ax1,
gp_Ax2,
Expand Down Expand Up @@ -500,6 +500,10 @@ def location(self) -> Location:
"""Return self as Location"""
return Location(Plane(origin=self.position, z_dir=self.direction))

@overload
def __init__(self, gp_ax1: gp_Ax1): # pragma: no cover
"""Axis: point and direction"""

@overload
def __init__(self, origin: VectorLike, direction: VectorLike): # pragma: no cover
"""Axis: point and direction"""
Expand All @@ -509,12 +513,13 @@ def __init__(self, edge: "Edge"): # pragma: no cover
"""Axis: start of Edge"""

def __init__(self, *args, **kwargs):
origin = None
direction = None
gp_ax1, origin, direction = (None,) * 3
if len(args) == 1:
if type(args[0]).__name__ == "Edge":
origin = args[0].position_at(0)
direction = args[0].tangent_at(0)
elif isinstance(args[0], gp_Ax1):
gp_ax1 = args[0]
else:
origin = args[0]
if len(args) == 2:
Expand All @@ -523,19 +528,31 @@ def __init__(self, *args, **kwargs):

origin = kwargs.get("origin", origin)
direction = kwargs.get("direction", direction)
gp_ax1 = kwargs.get("gp_ax1", gp_ax1)
if "edge" in kwargs and type(kwargs["edge"]).__name__ == "Edge":
origin = kwargs["edge"].position_at(0)
direction = kwargs["edge"].tangent_at(0)

try:
origin = Vector(origin)
direction = Vector(direction)
except TypeError as exc:
raise ValueError("Invalid Axis parameters") from exc

self.wrapped = gp_Ax1(
Vector(origin).to_pnt(), gp_Dir(*Vector(direction).normalized().to_tuple())
unknown_args = ", ".join(
set(kwargs.keys()).difference(["gp_ax1", "origin", "direction", "edge"])
)
if unknown_args:
raise ValueError(f"Unexpected argument(s) {unknown_args}")

if gp_ax1 is not None:
self.wrapped = gp_ax1
else:
try:
origin = Vector(origin)
direction = Vector(direction)
except TypeError as exc:
raise ValueError("Invalid Axis parameters") from exc

self.wrapped = gp_Ax1(
Vector(origin).to_pnt(),
gp_Dir(*Vector(direction).normalized().to_tuple()),
)

self.position = Vector(
self.wrapped.Location().X(),
self.wrapped.Location().Y(),
Expand All @@ -547,21 +564,6 @@ def __init__(self, *args, **kwargs):
self.wrapped.Direction().Z(),
)

@classmethod
def from_occt(cls, axis: gp_Ax1) -> Axis:
"""Create an Axis instance from the occt object"""
position = (
axis.Location().X(),
axis.Location().Y(),
axis.Location().Z(),
)
direction = (
axis.Direction().X(),
axis.Direction().Y(),
axis.Direction().Z(),
)
return Axis(position, direction)

def __copy__(self) -> Axis:
"""Return copy of self"""
return Axis(self.position, self.direction)
Expand All @@ -586,7 +588,7 @@ def __eq__(self, other: object) -> bool:
def located(self, new_location: Location):
"""relocates self to a new location possibly changing position and direction"""
new_gp_ax1 = self.wrapped.Transformed(new_location.wrapped.Transformation())
return Axis.from_occt(new_gp_ax1)
return Axis(new_gp_ax1)

def to_plane(self) -> Plane:
"""Return self as Plane"""
Expand Down Expand Up @@ -678,7 +680,7 @@ def angle_between(self, other: Axis) -> float:

def reverse(self) -> Axis:
"""Return a copy of self with the direction reversed"""
return Axis.from_occt(self.wrapped.Reversed())
return Axis(self.wrapped.Reversed())

def __neg__(self) -> Axis:
"""Flip direction operator -"""
Expand Down Expand Up @@ -2242,17 +2244,56 @@ def contains(
return_value = self.wrapped.Contains(Vector(obj).to_pnt(), tolerance)
return return_value

@overload
def find_intersection(self, axis: Axis) -> Union[Vector, None]:
"""Find intersection of axis and plane"""
geom_line = Geom_Line(axis.wrapped)
geom_plane = Geom_Plane(self.local_coord_system)

intersection_calculator = GeomAPI_IntCS(geom_line, geom_plane)
@overload
def find_intersection(self, plane: Plane) -> Union[Axis, None]:
"""Find intersection of plane and plane"""

if intersection_calculator.IsDone() and intersection_calculator.NbPoints() == 1:
# Get the intersection point
intersection_point = Vector(intersection_calculator.Point(1))
else:
intersection_point = None
def find_intersection(self, *args, **kwargs):
axis, plane = None, None

if args:
if isinstance(args[0], Axis):
axis = args[0]
elif isinstance(args[0], Plane):
plane = args[0]
else:
raise ValueError(f"Unexpected argument type {type(args[0])}")

unknown_args = ", ".join(set(kwargs.keys()).difference(["axis", "plane"]))
if unknown_args:
raise ValueError(f"Unexpected argument(s) {unknown_args}")

axis: Axis = kwargs.get("axis", axis)
plane: Plane = kwargs.get("plane", plane)

if axis is not None:
geom_line = Geom_Line(axis.wrapped)
geom_plane = Geom_Plane(self.local_coord_system)

return intersection_point
intersection_calculator = GeomAPI_IntCS(geom_line, geom_plane)

if (
intersection_calculator.IsDone()
and intersection_calculator.NbPoints() == 1
):
# Get the intersection point
intersection_point = Vector(intersection_calculator.Point(1))
else:
intersection_point = None

return intersection_point

elif plane is not None:
surface1 = Geom_Plane(self.wrapped)
surface2 = Geom_Plane(plane.wrapped)
intersector = GeomAPI_IntSS(surface1, surface2, TOLERANCE)
if intersector.IsDone() and intersector.NbLines() > 0:
# Get the intersection line (axis)
intersection_line = intersector.Line(1)
# Extract the axis from the intersection line
axis = intersection_line.Position()
return Axis(axis)
14 changes: 13 additions & 1 deletion tests/test_direct_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,12 @@ def test_axis_init(self):

with self.assertRaises(ValueError):
Axis("one", "up")
with self.assertRaises(ValueError):
Axis(one="up")

def test_axis_from_occt(self):
occt_axis = gp_Ax1(gp_Pnt(1, 1, 1), gp_Dir(0, 1, 0))
test_axis = Axis.from_occt(occt_axis)
test_axis = Axis(occt_axis)
self.assertVectorAlmostEquals(test_axis.position, (1, 1, 1), 5)
self.assertVectorAlmostEquals(test_axis.direction, (0, 1, 0), 5)

Expand Down Expand Up @@ -2563,6 +2565,16 @@ def test_find_intersection(self):
)
self.assertIsNone(Plane.XY.find_intersection(Axis((1, 2, 3), (0, 1, 0))))

self.assertEqual(Plane.XY.find_intersection(Plane.XZ), Axis.X)

self.assertIsNone(Plane.XY.find_intersection(Plane.XY.offset(1)))

with self.assertRaises(ValueError):
Plane.XY.find_intersection("Plane.XZ")

with self.assertRaises(ValueError):
Plane.XY.find_intersection(pln=Plane.XZ)

def test_from_non_planar_face(self):
flat = Face.make_rect(1, 1)
pln = Plane(flat)
Expand Down

0 comments on commit 35a33f8

Please sign in to comment.