← Back to index

Apollonius problem (3-circle tangent)

Given three circles, find a fourth that's tangent to all of them. Picking one sign-pattern (here: externally tangent to each given circle) makes the problem tractable: two of the tangency equations differ by linear terms only, so eliminating squares reduces the system to a single quadratic in r. The general problem has up to 8 solutions across all sign choices. Two circles are fixed; the third follows the mouse.

Note: the quadratic can yield two positive roots, corresponding to a smaller and a larger tangent circle. This demo keeps only the smaller one, which is the visually intuitive "circle nestled between the three" — the larger root tends to be a huge enclosing circle that hides the rest of the picture.

See also: Tangent lines from an external point — the 1D version of tangency; Circumcircle and Incircle for the closely related triangle constructions.

Pseudocode

// Want (x, y, r) such that distance(centre_i, (x, y)) = r + r_i for i = 1, 2, 3.
// Subtract eq i=1 from i=2 and i=3 to kill the quadratic terms in x, y:
//   A_ij x + B_ij y - D_ij r = -E_ij
// Solve those two for x, y in terms of r, then substitute back into eq 1
// to get a quadratic A r² + B r + C = 0. The roots are the candidate radii.

Source

function apollonius(c1, c2, c3) {
    const A12 = 2*(c1.x - c2.x),  B12 = 2*(c1.y - c2.y),  D12 = 2*(c2.r - c1.r)
    const E12 = (c2.x*c2.x - c1.x*c1.x) + (c2.y*c2.y - c1.y*c1.y) - (c2.r*c2.r - c1.r*c1.r)
    const A13 = 2*(c1.x - c3.x),  B13 = 2*(c1.y - c3.y),  D13 = 2*(c3.r - c1.r)
    const E13 = (c3.x*c3.x - c1.x*c1.x) + (c3.y*c3.y - c1.y*c1.y) - (c3.r*c3.r - c1.r*c1.r)

    const det = A12 * B13 - A13 * B12
    if (Math.abs(det) < 1e-9) return []
    // x = px + qx*r,   y = py + qy*r
    const qx = (B13 * D12 - B12 * D13) / det
    const px = (B12 * E13 - B13 * E12) / det
    const qy = (A12 * D13 - A13 * D12) / det
    const py = (A13 * E12 - A12 * E13) / det

    const u = px - c1.x,  v = py - c1.y
    const A = qx*qx + qy*qy - 1
    const B = 2 * (qx*u + qy*v - c1.r)
    const C = u*u + v*v - c1.r*c1.r

    const disc = B*B - 4*A*C
    if (disc < 0) return null
    const sq = Math.sqrt(disc)
    const positive = [(-B + sq) / (2*A), (-B - sq) / (2*A)].filter(r => r > 0)
    if (positive.length === 0) return null
    const r = Math.min(...positive)        // keep only the smaller tangent circle
    return { x: px + qx*r, y: py + qy*r, r }
}