Well uhhh it might be too late, but you can do this by just doing algebra. However, there does come a point where you do need to use trig, and it’s impossible to avoid using it. Regardless, this is the method.
The circles in the XY Cartesian plane look something like this:
We know that the equations of the circles are:
(x − a)² + (y − b)² = r₀²
(x − c)² + (y − d)² = r₁²
We also know that, in general, two circles can have up to 4 common tangent lines. These are the outer tangent lines, and the inner tangent lines. We will begin by finding the outer tangent lines. To find the outer tangent lines, we can refer to this image:
We want to calculate the intersection of these two outer tangents, which is at the point P(xₚ, yₚ). To calculate these coordinates, we can use the equations
Where r₀ > r₁. To get the point P(xₚ, yₚ). Now that we have point P, we can continue to find the outer points of both circles. Using the equations
and
We can get the points (xₜ₁, yₜ₁) and (xₜ₂, yₜ₂), which are the outer points on circle 1. We still need to know the outer points for circle 2,so we can use similar equations to solve for the outer tangent points on circle 2.
and
The outer points for circle 2 are now coordinates (xₜ₃, yₜ₃) and (xₜ₄, yₜ₄). We now have the outer tangent points, but we still need to find the inner tangent points (in this case, the ones you are looking for). To find these, we can use a similar method.
In the image above, we can figure out the intersection point of the two inner tangent lines by using the two equations below
Now, we can go ahead and find the inner tangent points of both circles. For circle 1, the tangent points will be given with these equations
and
Next, we can find the inner tangent points of the second circle using the equations below
and
We now have all the tangent points that we can find in these circles. However, there are some things that can go wrong. Wherever we see square roots, there are some solutions that could possibly be imaginary. This table below can show the possible outcomes. For reasons useful for the below table, we will refer to the distance between the centers of both circles as D.
Graph 1:
This type of graph contains 0 common tangent lines. In general, when D < |r₀ - r₁|, the circles will not have any tangent lines in common.
Graph 2:
This type of graph contains 1 common tangent line. In general, when D = |r₀ - r₁|, there will be one common tangent point. This is located at the edge of where both circles meet.
Graph 3:
This type of graph contains 2 common tangent lines. In general, when |r0 − r1 | < D < r0 + r1, the circles will share 2 tangent lines. This happens when one circle is in between the other.
Graph 4:
This type of graph contains 3 common tangent lines. In general, when D = r0 + r1, the circles will share 3 tangent lines. This happens when one circle is at the very edge of the other.
Graph 5:
This type of graph contains 4 common tangent lines. In general, when D > r0 + r1, the circles will share 4 tangent lines. This happens when the circles have space in between each other. However, as D approaches infinity, the number of tangent points shared between the two circles will become 2.
Although this method is almost full proof, it does come with some limitations. For example, when the circles happen to have the same radius, we’re unable to get the tangent points on the outsides of the circle. This is because the outer tangent lines never intersect, so we can’t find the outer lines. However, this method will always give the inner tangent lines, regardless of whether the circles are the same size or not.
In code, this looks something like this:
local circles = {
workspace.C1;
workspace.C2;
}
local function createPoint(name, thickness)
if thickness == nil then
thickness = 1
end
local p = Instance.new("Part", workspace)
p.Locked = true
p.Anchored = true
p.CanCollide = false
p.Size = Vector3.new(1, 1, 1) * thickness
p.Color = Color3.fromRGB(0, 0, 0)
p.Name = name
return p
end
local c1Point = createPoint("C1", 1.5)
local c2Point = createPoint("C2", 1.5)
local outerPoint1 = createPoint("OuterPoint1", 1.5)
local outerPoint2 = createPoint("OuterPoint2", 1.5)
local outerPoint3 = createPoint("OuterPoint3", 1.5)
local outerPoint4 = createPoint("OuterPoint4", 1.5)
local innerPoint1 = createPoint("InnerPoint1", 1.5)
local innerPoint2 = createPoint("InnerPoint2", 1.5)
local innerPoint3 = createPoint("InnerPoint3", 1.5)
local innerPoint4 = createPoint("InnerPoint4", 1.5)
function update()
-- turn into a 2d problem where we focus only on the xz coordinate system (we will reference it as x and y in the code)
local c1, c2 = Vector2.new(circles[1].Position.X, circles[1].Position.Z), Vector2.new(circles[2].Position.X, circles[2].Position.Z)
local radii = {c1 = 0.5 * math.min(circles[1].Size.Y, circles[1].Size.Z), c2 = 0.5 * math.min(circles[2].Size.Y, circles[2].Size.Z)}
local r0, r1 = math.max(radii.c1, radii.c2), math.min(radii.c1, radii.c2)
local a, b, c, d = r0 == radii.c1 and c1.X or c2.X, r0 == radii.c1 and c1.Y or c2.Y, r1 == radii.c1 and c1.X or c2.X, r1 == radii.c1 and c1.Y or c2.Y
local D = math.sqrt((c - a)^2 + (d - b)^2)
-- saved points
local points = {
circle1 = {outerPoints = {}, innerPoints = {}},
circle2 = {outerPoints = {}, innerPoints = {}}
}
-- intersection points for outer tangents
local x_p = (c * r0 - a * r1) / (r0 - r1)
local y_p = (d * r0 - b * r1) / (r0 - r1)
-- find outer tangents in circle 1
local x_t1 = ((r0^2 * (x_p - a) + r0 * (y_p - b) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + a
local x_t2 = ((r0^2 * (x_p - a) - r0 * (y_p - b) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + a
local y_t1 = ((r0^2 * (y_p - b) - r0 * (x_p - a) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + b
local y_t2 = ((r0^2 * (y_p - b) + r0 * (x_p - a) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + b
table.insert(points.circle1.outerPoints, Vector2.new(x_t1, y_t1))
table.insert(points.circle1.outerPoints, Vector2.new(x_t2, y_t2))
-- find outer tangents in circle 2
local x_t3 = ((r1^2 * (x_p - c) + r1 * (y_p - d) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + c
local x_t4 = ((r1^2 * (x_p - c) - r1 * (y_p - d) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + c
local y_t3 = ((r1^2 * (y_p - d) - r1 * (x_p - c) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + d
local y_t4 = ((r1^2 * (y_p - d) + r1 * (x_p - c) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + d
table.insert(points.circle2.outerPoints, Vector2.new(x_t3, y_t3))
table.insert(points.circle2.outerPoints, Vector2.new(x_t4, y_t4))
-- intersection points for inner tangents
local x_p = (c * r0 + a * r1) / (r0 + r1)
local y_p = (d * r0 + b * r1) / (r0 + r1)
-- find inner tangents in circle 1
local x_t1 = ((r0^2 * (x_p - a) + r0 * (y_p - b) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + a
local x_t2 = ((r0^2 * (x_p - a) - r0 * (y_p - b) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + a
local y_t1 = ((r0^2 * (y_p - b) - r0 * (x_p - a) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + b
local y_t2 = ((r0^2 * (y_p - b) + r0 * (x_p - a) * math.sqrt((x_p - a)^2 + (y_p - b)^2 - r0^2)) / ((x_p - a)^2 + (y_p - b)^2)) + b
table.insert(points.circle1.innerPoints, Vector2.new(x_t1, y_t1))
table.insert(points.circle1.innerPoints, Vector2.new(x_t2, y_t2))
-- find inner tangents in circle 2
local x_t3 = ((r1^2 * (x_p - c) + r1 * (y_p - d) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + c
local x_t4 = ((r1^2 * (x_p - c) - r1 * (y_p - d) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + c
local y_t3 = ((r1^2 * (y_p - d) - r1 * (x_p - c) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + d
local y_t4 = ((r1^2 * (y_p - d) + r1 * (x_p - c) * math.sqrt((x_p - c)^2 + (y_p - d)^2 - r1^2)) / ((x_p - c)^2 + (y_p - d)^2)) + d
table.insert(points.circle2.innerPoints, Vector2.new(x_t3, y_t3))
table.insert(points.circle2.innerPoints, Vector2.new(x_t4, y_t4))
-- move outer points
outerPoint1.CFrame = CFrame.new(points.circle1.outerPoints[1].X, 0.5, points.circle1.outerPoints[1].Y)
outerPoint2.CFrame = CFrame.new(points.circle1.outerPoints[2].X, 0.5, points.circle1.outerPoints[2].Y)
outerPoint3.CFrame = CFrame.new(points.circle2.outerPoints[1].X, 0.5, points.circle2.outerPoints[1].Y)
outerPoint4.CFrame = CFrame.new(points.circle2.outerPoints[2].X, 0.5, points.circle2.outerPoints[2].Y)
-- move inner points
innerPoint1.CFrame = CFrame.new(points.circle1.innerPoints[1].X, 0.5, points.circle1.innerPoints[1].Y)
innerPoint2.CFrame = CFrame.new(points.circle1.innerPoints[2].X, 0.5, points.circle1.innerPoints[2].Y)
innerPoint3.CFrame = CFrame.new(points.circle2.innerPoints[1].X, 0.5, points.circle2.innerPoints[1].Y)
innerPoint4.CFrame = CFrame.new(points.circle2.innerPoints[2].X, 0.5, points.circle2.innerPoints[2].Y)
end
for _, circle in pairs(circles) do
circle.Changed:Connect(function(prop)
if prop == "Position" or prop == "Size" then
update()
end
end)
end
update()
The outcome looks like this:
Hopefully this gives insight in another method that doesn’t involve trig!