Line Intersection

Representations

FormGood for
Two points input format, cross product tests
Point + direction parametric queries, sweeps
solving systems, half-planes

Converting from two points to the implicit form:

This is : the implicit form’s normal is the direction rotated .

Intersecting two lines

Solve by Cramer’s rule with :

bool lineIntersect(double A1, double B1, double C1, double A2, double B2, double C2, PD& out) {
    double D = A1 * B2 - A2 * B1;
    if (fabs(D) < 1e-9) return false;                // parallel or identical
    out.x = (C1 * B2 - C2 * B1) / D;
    out.y = (A1 * C2 - A2 * C1) / D;
    return true;
}

equals the cross product of the two direction vectors, so means parallel.

The vector form — usually cleaner

Line 1 through with direction ; line 2 through with direction :

bool lineInter(P a, P b, P c, P d, PD& out) {
    P u = b - a, v = d - c;
    ll den = cross(u, v);
    if (den == 0) return false;                      // parallel
    double t = (double)cross(c - a, v) / den;
    out = {a.x + u.x * t, a.y + u.y * t};
    return true;
}

den is exact in integers, so the parallel test is exact even though the intersection point is floating point. That separation — exact predicate, approximate construction — is the right pattern throughout geometry.

Parallel vs identical

// cross(u,v) == 0: parallel or identical.
// cross(u, c - a) == 0 too: c lies on line ab, so the lines are IDENTICAL; otherwise PARALLEL (disjoint).

Segment intersection is a different problem

Two lines almost always meet; two segments usually do not. After computing (and the corresponding on the other segment), require — see below for the full case analysis including collinear overlap.

PD foot(P p, P a, P b) {                              // the perpendicular foot from p onto line ab
    double t = (double)dot(p - a, b - a) / norm2(b - a);
    return toPD(a) + toPD(b - a) * t;
}
PD midpoint(P a, P b) { return (toPD(a) + toPD(b)) * 0.5; }
P bisectorDir(P a, P b) { return perp(b - a); }        // direction of the perpendicular bisector of ab
ConstructionMethod
Circumcentre of a triangleintersect two perpendicular bisectors (or the closed form)
Incentreintersect two angle bisectors, or the weighted average
Orthocentreintersect two altitudes (or )
Centroid
Reflection of across line reflectAcross(p,a,b) from Primitives,
Angle bisector direction at normalise and , then add

Numerical care

Nearly parallel lines

When is tiny but non-zero, the intersection point is enormous and its relative error is huge. If the algorithm then feeds that point into further computations, the error compounds badly. Mitigations: keep the exact cross for all decisions and only construct the point when it is genuinely needed; scale coordinates so they are comparable in magnitude; use long double.

Lines through many points

Detecting collinear triples among points: for each point, sort the others by direction around it and group equal directions — . Canonicalise each direction by dividing by the gcd and fixing the sign, so (2,4) and (-1,-2) compare equal:

pair<ll,ll> canonicalDir(P d) {
    ll g = __gcd(llabs(d.x), llabs(d.y));
    if (g) { d.x /= g; d.y /= g; }
    if (d.x < 0 || (d.x == 0 && d.y < 0)) { d.x = -d.x; d.y = -d.y; }
    return {d.x, d.y};
}

Segment Intersection

Do segments and intersect? The exact-integer answer uses the four orientation tests of properIntersect, plus a collinear case.

bool segmentsIntersect(P a, P b, P c, P d) {
    int o1 = orient(a, b, c), o2 = orient(a, b, d);
    int o3 = orient(c, d, a), o4 = orient(c, d, b);
 
    if (o1 != o2 && o3 != o4) return true;            // proper crossing
 
    if (o1 == 0 && onSegment(c, a, b)) return true;   // collinear / touching cases
    if (o2 == 0 && onSegment(d, a, b)) return true;
    if (o3 == 0 && onSegment(a, c, d)) return true;
    if (o4 == 0 && onSegment(b, c, d)) return true;
    return false;
}

(onSegment, orient: Primitives.) Everything here is exact integer arithmetic.

The degenerate cases

CaseHandled by
Segments cross properlyo1 != o2 && o3 != o4
An endpoint lies on the other segmentthe collinear checks
Segments are collinear and overlapthe collinear checks
Segments are collinear and disjointall checks fail — correct
A segment is a single pointonSegment still works
Segments share exactly one endpointreported as intersecting

Decide what "touching" means

Many problems distinguish “properly cross” from “merely touch at an endpoint”. Read the statement and use properIntersect (the first condition only, from Primitives) when touching should not count.

The intersection point

// assumes they intersect and are not parallel
PD intersectionPoint(P a, P b, P c, P d) {
    P u = b - a, v = d - c;
    double t = (double)cross(c - a, v) / cross(u, v);
    return toPD(a) + toPD(u) * t;
}

This is exactly lineInter above. For the collinear overlap case there is no single point — the intersection is a sub-segment; compute it by projecting all four endpoints onto the common direction and taking the overlapping interval of parameters.

Exact rational intersection

When the point must be exact (to compare with others, or to deduplicate), keep the same as a fraction instead of evaluating it:

storing numerator and denominator separately and reducing by their gcd. Avoids all floating-point comparison problems, at the cost of __int128 (the numerator is a product of three terms) and some care.

Any-intersection among segments

QuestionMethodTime
Do any two of segments intersect?Shamos-Hoey sweep
Report all intersectionsBentley-Ottmann
Count intersections onlysweep + BIT for special cases (axis-parallel)
Brute forceall pairs

For , the loop is 4 million exact integer tests — fast, simple, and unhackable. Reach for a sweep only when is large. The general sweeps are covered in Sweep Line.

Axis-parallel special case

When all segments are horizontal or vertical, counting intersections is a classic sweep + Fenwick problem:

  1. Sweep from left to right.
  2. A horizontal segment starting activate its in the Fenwick; ending deactivate.
  3. A vertical segment at this spanning query how many active lie in that range.

, exact, and much simpler than the general Bentley-Ottmann.

Ray-segment and ray-polygon

For point-in-polygon by ray casting, you need “does a horizontal ray from cross this edge”. The standard robust form avoids computing intersection points entirely:

bool crosses(P p, P a, P b) {
    if (a.y > b.y) swap(a, b);
    if (p.y <= a.y || p.y > b.y) return false;        // half-open: counts each vertex once
    return orient(a, b, p) > 0;
}