Computational geometry is the algorithmic toolkit that powers graphics, GIS, robotics, and game physics: “does this polygon contain this point”, “do these two segments cross”, “what is the smallest enclosing polygon of these city points”. The primitives look simple buteed precision — the corner cases (collinear points, integer overflow, parallel lines) are where naive implementations fail. This topic covers the four algorithms that close 80% of geometry problems.
Orientation by Cross Product
The single invariant underpinning every geometry algorithm: the cross product decides orientation.
For three points p, q, r, the orientation of the path p → q → r is the sign of:
orient(p, q, r) = (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x)
↑ cross product of (q-p) and (r-p)
Sign of orient(p, q, r) | Geometric meaning |
|---|---|
> 0 | r lies to the left of p → q (counterclockwise turn) |
< 0 | r lies to the right of p → q (clockwise turn) |
= 0 | p, q, r are collinear |
The cross product is by far the most-used primitive — most geometry algorithms are a sequence of cross products. Two implementation truths that matter:
- Integer arithmetic — if all inputs are integers, the cross product stays integer; no floating-point error. Use integer coordinates wherever possible; only fall to floating point when the input forces it.
- Order of operations — compute the cross product as exactly written; don’t simplify in a way that introduces cancellation error on near-collinear points. The sign matters more than the magnitude.
Two consequences that recur everywhere:
- Segments
pqandrsintersect iff the orientationsorient(p, q, r)andorient(p, q, s)differ andorient(r, s, p)andorient(r, s, q)differ. (With collinear cases handled as overlaps, separately.) - Point
rlies on segmentpq(assumingorient(p, q, r) == 0) iffr.xis betweenmin(p.x, q.x)andmax(p.x, q.x)and similar fory.
Sweep Line
The sweep line is the meta-pattern of computational geometry. The setup:
- Sort the geometric objects (segments, points) by
x-coordinate (ory). - Imagine a vertical line sweeping left-to-right; at each event (an endpoint of a segment, a vertex of a polygon, a candidate collision) the algorithm updates state.
- The state at this moment is enough to answer the question with the data seen so far.
The standard win: O(n²) brute-force becomes O(n log n) because exposure order is sorted once and each event is processed in O(log n) (via balanced BST or a heap).
| Classic problem | Sweep argument | Complexity |
|---|---|---|
| Closest pair of points | Sweep vertical line; active set = points within δ of the sweep line | O(n log n) |
| Segment intersections | Sweep with the segments crossing the sweep line ordered by y at the sweep position; intersections appear as adjacent pairs swap order | O((n + k) log n) (Bentley-Ottmann) |
| Line arrangement | Sweep left-to-right; maintain ordered active line set | O(n log n) per query |
| Rectangle area union | Sweep x; interval-union y-ranges between sweeps | O(n log n) |
The mental shift: geometric queries change as their inputs move; the sweep line exposes these changes in O(log n) per event instead of O(n) per query.
Convex Hull
The convex hull of a point set is the smallest convex polygon containing every point. Two classic algorithms:
Graham Scan — O(n log n)
- Pick the bottommost point as the anchor
p0. - Sort all other points by polar angle around
p0. - Walk the sorted points, maintaining a stack: at each new point, while the top two points of the stack plus the new one form a right turn (or are collinear), pop the stack. Then push the new point.
The stack at the end contains the hull vertices in counterclockwise order. The walk is O(n); the sort dominates at O(n log n).
Andrew’s Monotone Chain — O(n log n), the production-grade choice
- Sort points lexicographically by
(x, y). - Lower hull: walk left to right, maintaining a stack; pop while the last three make a non-strict-left turn.
- Upper hull: walk right to left, same rule.
- Concatenate the two halves (without duplicating endpoints).
The monoticity of the input — already sorted by x — makes the algorithm clean: each pop is amortised O(1). Andrew’s is preferred over Graham because it’s easier to implement robustly (no polar-angle tie-breaking) and uses integer arithmetic throughout.
| Algorithm | Time | Notes |
|---|---|---|
| Naive gift-wrapping (Jarvis) | O(nh) where h is hull size | Linear for small hulls; O(n²) worst case |
| Graham scan | O(n log n) | Polar-angle sort needs floating-point or rational slopes |
| Andrew’s monotone chain | O(n log n) | Integer arithmetic, the de-facto standard |
| QuickHull | O(n log n) average O(n²) worst | Recursive; common in informal implementations |
Segment Intersection
Detecting all intersections among n segments: O(n²) naive, O((n + k) log n) by Bentley-Ottmann’s sweep (k = number of intersections).
The sweep:
- Sort all segment endpoints by
x. - Sweep left-to-right; maintain a balanced BST of segments ordered by their
y-coordinate at the current sweep position. - At each event (left endpoint, right endpoint, or potential intersection), update the BST; segments that become adjacent in the tree are tested for intersection using the cross-product.
- If two segments intersect, add the intersection as a future event; when the sweep reaches it, swap the two segments’ positions in the tree.
Point-in-Polygon
Two algorithms; one is much harder to break:
| Algorithm | How it works | Failure mode |
|---|---|---|
| Ray casting (even-odd rule) | Cast a ray from the point; count edge crossings. Inside iff odd. | Vertices and edges on the ray cause multiple-count bugs; the naive implementation gets this wrong |
| Winding number (non-zero rule) | Sum the signed angles around the polygon. Inside iff nonzero. | Slightly more complex; correct on all corner cases if angles are tracked with sign |
The production discipline:
- For convex polygons: precompute vertex normals and use orientation tests —
O(log n)via binary search per polygon. - For simple polygons: winding number is the right answer — handles concavities and self-touching cases gracefully.
- Avoid floating-point near degeneracy: when the query point lies on a polygon edge, the geometry says “boundary”; implementations must decide an explicit policy.
Practice Trajectory
- Implement
orient(p, q, r)with integer arithmetic. Use it to implement segment-segment intersection handling all collinear overlaps. Run 50 test cases verifying the six possible relative configurations. - Build Andrew’s monotone chain convex hull on a random point set of 1000 points. Verify the hull passes the left-turn invariant at every vertex (every three consecutive hull vertices make a strict left turn).
- Find all intersections among
nsegments usingO(n²)brute force, then implement Bentley-Ottmann and verify the answer matches on randomly generated segments. Compare runtimes asngrows. - Implement point-in-polygon with both even-odd and winding number. Generate a polygon with a “spike” (a vertex lying exactly on a ray candidate); confirm which algorithm correctly classifies points near the spike.
- Find the closest pair among
n2D points using sweep (O(n log n)) — or alternatively, divide-and-conquer. Compare against brute-force on 10K random points.
When It’s the Right Tool
| Situation | Takeaway |
|---|---|
| Integer coordinates everywhere, including all derived computations | Stay in integer arithmetic — no floating-point drift |
| A list of points, find convex hull | Andrew’s monotone chain; O(n log n), integer-safe |
| Many segment-intersection queries | Sweep line; the naive O(n²) is correct but O((n + k) log n) lag is a memory order |
| Convex polygon point containment | Binary search over edges; O(log n) per query |
| Concave polygon point containment | Winding number avoids the corner-case bugs of even-odd ray casting |