Computational Geometry
The Convex Hull: Wrapping a Rubber Band Around Points
The Convex Hull of a set of points is the smallest convex polygon that contains them all — exactly the shape a stretched rubber band would snap into if released around a scatter of nails. It is the foundational primitive of computational geometry, underlying collision detection, shape analysis, and pattern recognition.- Best known timeΘ(n log n)
- Gift wrappingO(nh), h = hull size
- SpaceO(n)
- Lower boundΩ(n log n)
- Graham scan1972
- Jarvis march1973
Interactive visualization
Press play, or step through manually. The visualization is yours to drive — try it before reading on.
Watch the 60-second explainer
A condensed visual walkthrough — narrated, captioned, under a minute.
What "convex" actually means
A region is convex if, for any two points inside it, the entire straight segment between them also lies inside. A star or a crescent is not convex; a disk, a triangle, or any regular polygon is. The convex hull of a finite point set P is the intersection of all convex sets containing P — equivalently, the smallest-area (and smallest-perimeter) convex polygon enclosing every point.
Two facts make the hull tractable. First, its vertices are always a subset of the input points — the rubber band snaps to actual nails, never to empty space. Second, a point is a hull vertex if and only if there exists a line through it with all other points strictly on one side (a supporting line). Interior points and points lying on an edge between two vertices are not vertices. Every algorithm below is, at heart, a strategy for deciding which points earn that status.
The one primitive that powers everything: orientation
Almost every hull algorithm reduces to a single question about three points: does the path a → b → c turn left, turn right, or go straight? This is the orientation test, computed from the sign of a 2D cross product:
cross(a,b,c) = (b.x − a.x)·(c.y − a.y) − (b.y − a.y)·(c.x − a.x)
> 0→ counter-clockwise (left turn)< 0→ clockwise (right turn)= 0→ the three points are collinear
Geometrically, this value is twice the signed area of triangle abc. It needs only subtractions and multiplications — no division, no square roots, no trigonometry — which is why robust hull code prefers it over comparing angles directly. Using exact integer or rational arithmetic, the orientation test can be made provably correct; with naive floating point it is the single biggest source of hull bugs.
Gift wrapping (the Jarvis march): the rubber band, literally
Gift wrapping mimics the physical intuition most directly. Start at a point guaranteed to be on the hull — the lowest point (smallest y, ties broken by smallest x). From the current hull point, pick the next point so that every other point lies to the right of the segment to it. That candidate is the most clockwise point; the wrapping line pivots to it and repeats until it returns to the start.
p = lowest_point(P)hull = []repeat: hull.append(p) q = any point ≠ p for r in P: if cross(p, q, r) < 0: q = r # r is more clockwise p = quntil q == hull[0]return hull
The loop invariant is that after each pivot, p is a confirmed hull vertex and the boundary has been traced in order. Each outer iteration emits one hull vertex and scans all n points, so the cost is O(nh), where h is the number of hull vertices. This is output-sensitive: if only a handful of points are on the hull it is nearly linear, but for points on a circle where h ≈ n it degrades to Θ(n²).
Graham scan: sort once, then a single sweep
Graham scan (R. L. Graham, 1972) breaks the quadratic ceiling. Pick the lowest point as the pivot, sort the remaining points by polar angle around it, then walk the sorted list maintaining a stack of the hull-so-far. At each new point, pop any stack top that would create a right turn (a clockwise, non-left turn), because such a vertex cannot be on the hull:
sort P by polar angle around pivotstack = [P[0], P[1]]for r in P[2..]: while len(stack) ≥ 2 and cross(stack[−2], stack[−1], r) ≤ 0: stack.pop() stack.push(r)
The invariant is that the stack always holds a convex, counter-clockwise chain. The sort costs Θ(n log n); the scan is Θ(n) amortized because each point is pushed once and popped at most once. So total time is Θ(n log n), dominated entirely by the sort. Andrew's monotone chain variant sorts lexicographically by (x, y) instead of by angle and builds the lower and upper hulls separately — same asymptotics, but it dodges angle computation and its numerical hazards, which is why it is the more common production choice.
Why Θ(n log n) is optimal — and how Chan beats it
You cannot do better than Ω(n log n) in the comparison model: sorting n real numbers reduces to a hull problem (map each number x to the point (x, x²) on a parabola; the hull returns them in sorted order). So any general hull algorithm inherits sorting's lower bound. Graham scan therefore is optimal for the worst case.
But that bound is about n, not output. Chan's algorithm (1996) is the clever synthesis: partition the points into groups of size m, run Graham scan on each, then run a gift-wrapping march across the groups, using binary search on each group's mini-hull to find the tangent in O(log m). By doubling a guess for m until it exceeds h, Chan achieves O(n log h) — optimal in both input and output size. When h is small (say h = 12 vertices around 10⁶ points), that is dramatically faster than n log n while never being worse.
Pitfalls: collinearity, degeneracy, and floating point
The convex hull is a minefield of edge cases that a textbook description glosses over:
- Collinear points. Three or more points on a hull edge force a decision: do you keep only the two endpoints, or all of them? Using
< 0vs≤ 0in the turn test changes the answer. Both are valid hulls; pick one convention and apply it consistently. - Fewer than 3 points, or all points collinear. The hull degenerates to a segment or a single point. Real code must special-case
n ≤ 2and the all-collinear input, or it will loop forever or return garbage. - Floating-point orientation. A cross product near zero can flip sign under rounding, producing a non-convex or self-intersecting result. Libraries like CGAL use exact geometric predicates (adaptive precision arithmetic) precisely to make the orientation test bulletproof.
- Duplicate points and the pivot tie-break. Angle sorting is undefined for coincident points; dedupe first and break angle ties by distance from the pivot.
A common misconception is that the convex hull is a clustering or smoothing tool — it is not. It captures only the extremal outline; a single outlier can balloon the hull, which is exactly why the hull is used for outlier-aware bounding but is a poor descriptor of a set's dense core.
| Algorithm | Time | Output-sensitive? | Core idea | When to prefer |
|---|---|---|---|---|
| Gift wrapping (Jarvis) | O(nh) | Yes | Wrap point-to-point along the boundary | Very small hull (h ≪ n) |
| Graham scan | Θ(n log n) | No | Sort by angle, scan with a stack | General 2D, simple to code |
| Andrew's monotone chain | Θ(n log n) | No | Sort by x, build lower + upper chains | Robust default, avoids angles |
| Chan's algorithm | O(n log h) | Yes | Combine Graham + gift wrapping | Optimal when h is unknown/small |
| QuickHull | O(n log n) avg, O(n²) worst | Partly | Divide by farthest-point splits | Practical, cache-friendly |
Frequently asked questions
Gift wrapping vs Graham scan — which should I use?
Use Graham scan (or Andrew's monotone chain) as your default: it is Θ(n log n) regardless of the data. Prefer gift wrapping only when you know the hull has very few vertices relative to n, since its O(nh) cost becomes nearly linear there but blows up to Θ(n²) when most points are on the hull, as with points sampled on a circle.
Why is Θ(n log n) the best possible for a general convex hull?
Because sorting reduces to it. Mapping each number x to the point (x, x²) places them on a convex parabola, so the hull must output them in sorted order. Since comparison-based sorting requires Ω(n log n), so does any hull algorithm that could solve it — making Graham scan worst-case optimal.
What is the orientation test and why avoid angles?
It is the sign of the cross product (b−a)×(c−a), telling you whether a→b→c turns left, right, or goes straight. It uses only multiply and subtract — no division or trig — so it is both faster and far more numerically robust than comparing polar angles, which involve atan2 and rounding error.
How does the convex hull extend to 3D?
In 3D the hull is a polyhedron with faces, edges, and vertices. By Euler's formula a hull of n points has O(n) faces, and algorithms like the 3D QuickHull or incremental insertion compute it in O(n log n) expected time. The plane's orientation test generalizes to a signed-volume (4-point determinant) test.
Can the convex hull handle collinear points on an edge?
Yes, but you must choose a convention. Treating the turn test as strict (< 0) keeps only the two endpoints of a straight edge; using ≤ 0 keeps every collinear point. Both describe the same geometric hull, so decide based on whether your downstream code needs those boundary points.
What are convex hulls actually used for?
Collision detection (hulls are cheap proxies for complex shapes via the GJK algorithm), shape and pattern analysis, computing the diameter or width of a point set, image processing, and as a preprocessing step for Delaunay triangulation and nearest-neighbor structures. They also give the smallest convex bounding region for path planning.