Skip to content

Commit fa014b4

Browse files
committed
Solve the plane equations for a three-plane intersection
1 parent 824a459 commit fa014b4

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

‎spatialmath/geom3d.py‎

100755100644
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def intersection(pi1: Plane3, pi2: Plane3, pi3: Plane3) -> R3:
145145
"""
146146
A = np.vstack([pi1.n, pi2.n, pi3.n])
147147
b = np.array([pi1.d, pi2.d, pi3.d])
148-
return np.linalg.det(A) @ b
148+
return np.linalg.solve(A, -b)
149149

150150
@property
151151
def n(self) -> R3:

‎tests/test_geom3d.py‎

100755100644
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,34 @@ def test_line_distance_parallel_tolerance():
4747
assert line1.distance(line2, tol=1e8) == pytest.approx(np.sqrt(5))
4848

4949

50+
@pytest.mark.parametrize("point", [[0, 0, 0], [1, 2, 3], [-1.5, 2, 3.25]])
51+
@pytest.mark.parametrize(
52+
"normals", [np.eye(3), np.array([[1, 2, 0], [0, 1, 3], [2, 0, 1]])]
53+
)
54+
@pytest.mark.parametrize("scales", [[1, 1, 1], [-2, 0.5, 3]])
55+
def test_three_plane_intersection(point, normals, scales):
56+
normals = normals * np.array(scales)[:, None]
57+
planes = [Plane3.PointNormal(point, normal) for normal in normals]
58+
intersection = Plane3.intersection(*planes)
59+
60+
assert intersection.shape == (3,)
61+
nt.assert_allclose(intersection, point, atol=1e-12)
62+
# Each plane is defined by n @ x + d = 0, regardless of coefficient scale.
63+
nt.assert_allclose(
64+
normals @ intersection + np.array([plane.d for plane in planes]),
65+
0,
66+
atol=1e-12,
67+
)
68+
nt.assert_allclose(Plane3.intersection(*reversed(planes)), point, atol=1e-12)
69+
70+
71+
@pytest.mark.parametrize("offset", [0, 1])
72+
def test_three_plane_intersection_singular(offset):
73+
planes = [Plane3([1, 0, 0, 0]), Plane3([1, 0, 0, offset]), Plane3([0, 1, 0, 0])]
74+
with pytest.raises(np.linalg.LinAlgError):
75+
Plane3.intersection(*planes)
76+
77+
5078
class Line3Test(unittest.TestCase):
5179
# Primitives
5280
def test_constructor1(self):

0 commit comments

Comments
 (0)