Title: Find the tangent lines between two circles in Python
This example shows how you can find the tangent lines between two circles.
First, a couple of definitions:
- An inner tangent is one that intersects the segment joining the circles' centers.
- An outer tangent is one that does not intersect the segment joining the circles' centers.
- The point where the internal tangents intersect is called the internal homeothetic center and it lies along the segment connecting the circles' centers.
- The point where the extensions of the external tangents intersect is called the external homeothetic center and it also lies along the segment connecting the circles' centers. If the circles have the same radii, then the external tangents are parallel and the external homeothetic center is at infinity.
Depending on how the circles are arranged, they can have 0, 1, 2, 3, or 4 tangent lines. Figure 1 shows the possible arrangements.
Suppose the two circles C1 and C2 have radii r1 and r2 where r2 ≥ r1. To find the outer tangents, consider Figure 2. The dashed circle has the same center as C2 and radius r2 - r1. By using the method used by the example Find the tangent lines between a point and a circle in Python, you can find the tangent lines between C1's center and this dashed circle. Now move that line perpendicularly to itself distance r1 to get the outer tangent shown in Figure 2. Offset the other point/circle tangent perpendicular to itself to get the other outer tangent.
To find the inner tangents, consider Figure 3. Here the dashed circle has the same center as C1 and radius r1 + r2. By again using the methods of example Find the tangent lines between a point and a circle in Python, you can find the tangent lines between C2's center and this dashed circle to get the dashed red line segment. Now move that line perpendicularly to itself distance r2 to get the inner tangent (the solid red segment) shown in Figure 3. Offset the other point/circle tangent perpendicular to itself to get the other inner tangent.
The basic idea here is somewhat tricky, but if you stare at the pictures long enough, you should be able to see how it works. You might also try drawing some pictures of your own.
The find_circle_circle_tangents method shown in the following code uses this technique to find the tangents between two circles.
def find_circle_circle_tangents(c1, radius1, c2, radius2):
'''Return the 4, 2, or 0 tangent points for these two circles.'''
# Make sure radius1 <= radius2.
if radius1 > radius2:
# Swap them.
c1, c2 = c2, c1
radius1, radius2 = radius2, radius1
# ***************************
# * Find the outer tangents *
# ***************************
radius2a = radius2 - radius1
tangents = find_point_circle_tangents(c2, radius2a, c1)
if len(tangents) == 0:
# There are no tangents.
return ()
# Unpack the tangent points.
outer1_p2, outer2_p2 = tangents
# Get the vector perpendicular to the
# first tangent with length radius1.
v1x = -(outer1_p2[1] - c1[1])
v1y = outer1_p2[0] - c1[0]
v1_length = math.sqrt(v1x * v1x + v1y * v1y)
v1x *= radius1 / v1_length
v1y *= radius1 / v1_length
# Offset the tangent vector's points.
outer1_p1 = (c1[0] + v1x, c1[1] + v1y)
outer1_p2 = (outer1_p2[0] + v1x, outer1_p2[1] + v1y)
# Get the vector perpendicular to the
# second tangent with length radius1.
v2x = outer2_p2[1] - c1[1]
v2y = -(outer2_p2[0] - c1[0])
v2_length = math.sqrt(v2x * v2x + v2y * v2y)
v2x *= radius1 / v2_length
v2y *= radius1 / v2_length
# Offset the tangent vector's points.
outer2_p1 = (c1[0] + v2x, c1[1] + v2y)
outer2_p2 = (
outer2_p2[0] + v2x,
outer2_p2[1] + v2y)
# If the circles intersect, then there are no inner tangents.
dx = c2[0] - c1[0]
dy = c2[1] - c1[1]
dist = math.sqrt(dx * dx + dy * dy)
if dist < radius1 + radius2:
return (
(outer1_p1, outer1_p2),
(outer2_p1, outer2_p2)
)
# ***************************
# * Find the inner tangents *
# ***************************
radius1a = radius1 + radius2
tangents = find_point_circle_tangents(c1, radius1a, c2)
# Unpack the tangent points.
inner1_p2, inner2_p2 = tangents
# Get the vector perpendicular to the
# first tangent with length radius2.
v1x = inner1_p2[1] - c2[1]
v1y = -(inner1_p2[0] - c2[0])
v1_length = math.sqrt(v1x * v1x + v1y * v1y)
v1x *= radius2 / v1_length
v1y *= radius2 / v1_length
# Offset the tangent vector's points.
inner1_p1 = (c2[0] + v1x, c2[1] + v1y)
inner1_p2 = (
inner1_p2[0] + v1x,
inner1_p2[1] + v1y)
# Get the vector perpendicular to the
# second tangent with length radius2.
v2x = -(inner2_p2[1] - c2[1])
v2y = inner2_p2[0] - c2[0]
v2_length = math.sqrt(v2x * v2x + v2y * v2y)
v2x *= radius2 / v2_length
v2y *= radius2 / v2_length
# Offset the tangent vector's points.
inner2_p1 = (c2[0] + v2x, c2[1] + v2y)
inner2_p2 = (
inner2_p2[0] + v2x,
inner2_p2[1] + v2y)
return (
(outer1_p1, outer1_p2),
(outer2_p1, outer2_p2),
(inner1_p1, inner1_p2),
(inner2_p1, inner2_p2)
)
Note that this problem is closely related to the "Belt Problem" where the goal is to calculate the length of a belt needed to connect two pulleys directly (using the outer tangents) or crossed (using the inner tangents). Sometimes the direct (external tangent) version is called the "Pulley Problem."
Download the example to see all of the details.
|