Circle-Line

Circle centre , radius ; line through with direction .

The method

  1. Find the foot of the perpendicular from to the line.
  2. Compare its distance to : greater no intersection; equal tangent; less two points.
  3. Step along the line by in both directions.
vector<PD> circleLine(PD c, double r, PD a, PD b) {
    PD d = b - a;
    double t = dot(c - a, d) / norm2(d);              // parameter of the foot
    PD foot = a + d * t;
    double distSq = norm2(foot - c);
    if (distSq > r * r + EPS) return {};              // no intersection
    double h = sqrt(max(0.0, r * r - distSq)) / abs_(d);
    if (h < EPS) return {foot};                       // tangent
    return {foot - d * h, foot + d * h};
}

For a segment

Compute the line intersections, then keep only those with parameter — equivalently, check `onSegment`-style dot(p-a,p-b) <= 0 for each candidate point.

Exact tests without constructing points

Many questions do not need the intersection points at all:

QuestionExact test
Does the line meet the circle?
Is the line tangent?equality above
Is point inside the circle?
Does the segment meet the circle?[[19-geometry/01-primitives
Is the circle entirely inside a polygon?centre inside and distance to every edge

With integer input these are all exact — no epsilon required. Prefer them.

Chord length

where is the distance from the centre to the line. The circular segment cut off has area

which is what you need for circle-polygon intersection areas.

Circle-polygon intersection area

A standard and genuinely useful routine: the area of the intersection of a circle with a polygon. Decompose the polygon into triangles from the circle’s centre, and for each triangle compute the signed circle-triangle intersection area:

  • both points inside the whole triangle;
  • both outside and the edge misses the circle a circular sector;
  • otherwise a mixture of sectors and triangles, split at the chord intersections (found by circleLine above).

Sum with signs (using the cross product orientation) and the outside parts cancel — the same principle as the shoelace formula. The full case analysis, generalised to a union of circles, is its own note.

Numerical care

Tangency is fragile

distSq == r*r almost never holds exactly in floating point. Use max(0.0, r*r - distSq) before the square root to avoid NaN from a tiny negative value, and treat as tangency.

If the input is integral, do the decision in exact integers and only then construct the points in floating point.

TaskMethod
Circle through 3 pointsintersect two perpendicular bisectors — circumcenter
Circle through 2 points with radius midpoint perpendicular offset
Circle-circle intersectionradical line, then circle-line
Tangent lines from a pointTangents
Smallest circle enclosing pointsWelzl
Point on the circle nearest to

Circle-Circle

Circles and with .

The cases

ConditionResult
separate — 0 points
externally tangent — 1 point
2 points
, internally tangent — 1 point
one inside the other — 0 points
, identical — infinitely many

Test with squared distances to stay exact: compare against and .

The construction

Both intersection points lie on the radical line, perpendicular to . Let

Then the base point is with , and the intersections are .

vector<PD> circleCircle(PD c1, double r1, PD c2, double r2) {
    PD u = c2 - c1;
    double d2 = norm2(u), d = sqrt(d2);
    if (d < EPS) return {};                                  // concentric
    if (d > r1 + r2 + EPS) return {};
    if (d < fabs(r1 - r2) - EPS) return {};
 
    double a = (d2 + r1 * r1 - r2 * r2) / (2 * d);
    double h2 = r1 * r1 - a * a;
    PD base = c1 + u * (a / d);
    if (h2 < EPS) return {base};                             // tangent
    double h = sqrt(h2);
    PD perpU = perp(u) * (h / d);
    return {base - perpU, base + perpU};
}

Tested: circleLine and circleCircle’s returned points checked to lie on both input curves (distance to each centre matches its radius within ) and matched against a fine brute-force sweep along the line / around one circle for the number and approximate location of intersections, across all cases in both tables (0, 1, 2 intersections, tangent, concentric, nested).

The radical line and radical centre

The radical axis of two circles is the locus of points with equal power to both:

Setting the two powers equal gives a line (the terms cancel):

For three circles the three radical axes meet at the radical centre — the point from which tangent lengths to all three are equal. This is the clean way to solve “find the point equidistant in tangent length” problems, and it reduces circle problems to line intersections.

Areas

Lens (intersection) area of two overlapping circles:

with and symmetrically for (swap the roles of ).

Union of circles: for each circle, walk its boundary and determine which arcs are not covered by any other circle; then apply a Green’s-theorem-style sum over arcs and chords. — see 13-circle-union-and-angular-sweep.

TaskMethod
Circle through 3 pointsperpendicular bisectors, or the determinant formula
Circle tangent to 2 circles with radius intersect circles of radii and
Apollonius circlethe locus with
Common tangent linesTangents
Circle inversionmaps circles and lines to circles and lines
Smallest enclosing circleWelzl

Precision

Near-tangency

When is close to , is a tiny difference of large numbers and loses most of its significant digits. Always clamp with max(0.0, h2) before the square root, and decide the case using exact integer comparisons on squared quantities when the input is integral.

Circle Inversion

Inversion in a circle : maps points, and circles / lines to circles / lines, turning tangency problems into line problems.

PD invertPoint(PD p, PD o, double r) { PD d = p - o; return o + d * (r * r / norm2(d)); }    // |oq| * |op| = r^2, p != o
 
// image of a circle (center c, radius rc): {isLine, c2, r2, a, b}
// isLine: the circle passed through o and the image is the line through a, b; else the image is circle (c2, r2)
tuple<bool, PD, double, PD, PD> invertCircle(PD c, double rc, PD o, double r) {
    PD d = c - o;
    double dist = abs_(d);
    if (fabs(dist - rc) < 1e-9) {                      // circle through o -> line
        PD u = d * (1.0 / dist);
        PD far = invertPoint(o + u * (2 * rc), o, r);  // the antipode of o maps to the closest point of the image line
        PD pr = perp(u);
        return {true, PD{}, 0, far, far + pr};
    }
    PD u = d * (1.0 / dist);
    PD p1 = invertPoint(o + u * (dist - rc), o, r), p2 = invertPoint(o + u * (dist + rc), o, r);
    return {false, (p1 + p2) * 0.5, abs_(p1 - p2) * 0.5, PD{}, PD{}};
}
 
pair<PD, double> invertLine(PD a, PD b, PD o, double r) {        // image of a line not through o: a circle through o
    PD ab = b - a;
    PD foot = a + ab * (dot(o - a, ab) / norm2(ab));              // closest point of the line to o
    PD img = invertPoint(foot, o, r);                             // farthest point of the image circle from o
    return {(o + img) * 0.5, abs_(img - o) * 0.5};
}

Complexity: . Circles through the centre become lines and vice versa; tangency and angles are preserved. Typical use: circles tangent to two circles through a common point (Apollonius-like problems) become lines tangent to two circles (common tangents), then invert back.

Tested: invertPoint is self-inverse (invertPoint(invertPoint(p,o,r),o,r) == p) and preserves ; invertCircle and invertLine checked by sampling many points on the original circle/line, inverting each individually with invertPoint, and confirming they all lie on the claimed image circle or line (and that the two routines are mutual inverses: inverting the image of a circle-through- as a line, then inverting that line back, recovers the original circle).