Polygons & Proximity
Four classics that live on top of the orientation primitive and the convex hull. Closest pair falls to divide-and-conquer in , where a packing argument caps the cross-boundary combine at seven neighbours per point.
╌╌╌╌
The first three lessons built the planar toolkit: the orientation primitive, the convex hull, and the plane sweep. This lesson applies those tools to four problems every geometry library ships, each fast and exact by a counting or sign argument: the closest pair of a point set, the point-in-polygon test, the area of a polygon, and the diameter and width of a convex polygon. Three of them need nothing beyond the cross product; the fourth turns the hull into a linear scan by walking two pointers in lock-step.
Closest pair of points
The brute force compares all pairs in . The classic improvement is divide-and-conquer, and it is the geometry that makes it work: a packing bound caps the cross-boundary work at a constant per point. This is the full treatment — the Selection lesson introduces it as a divide-and-conquer classic alongside sorting, and the sweep-line lesson gives a one-pass alternative on a dynamic strip.
Divide. Sort by and split at the median into a left half and a right half by a vertical line . Conquer. Recurse on each half; let and be the best distances found, and . Combine. The closest pair overall is either one of these two or a pair that straddles the line — one point in , one in — at distance . The whole difficulty is bounding that straddling case.
A straddling pair closer than must have both endpoints within horizontal distance of the line, so both lie in a vertical strip of width centred on . Discard everything outside the strip; sort the survivors by (in practice, filter the already--sorted list); then walk up the strip. The decisive fact is that each point need only be compared against a constant number of its successors in -order.
The combine step therefore makes distance checks per strip point, for work. The recurrence is the familiar one,
which the Master Theorem solves in its balanced case ( with ), giving . The one implementation subtlety is the inner sort: a naive -sort of the strip each level would add a factor. Sorting once by up front and filtering that order at each level keeps the combine linear, so the bound holds.1
- 1
- 2if then
- 3return brute-force closest pairbase case,
- 4median as the dividing line
- 5split into left , right at index
- 6partition into by side ofkeeps each half -sorted
- 7
- 8
- 9
- 10strip, still -sorted
- 11for each in , in -order do
- 12for each of the next points after in do
- 13if then update best pair and
- 14return the closest of the left, right, and strip pairs
The distance comparisons can stay exact: comparing against is comparing squared distances against , which are integers for integer inputs — no square root, no rounding, the same discipline as the orientation primitive.
Point in polygon
Given a simple polygon (vertices in order) and a query point , is inside, outside, or on the boundary? Two tests dominate; both were named in the primitives lesson, and we make them precise here, boundary caveats included.
Ray casting (the parity test)
Shoot a ray from in a fixed direction — conventionally along — and count how many polygon edges it crosses. The Jordan curve theorem guarantees that a ray from an interior point crosses the boundary an odd number of times, and a ray from an exterior point an even number. So an odd crossing count means inside.
The arithmetic per edge is a two-line test: the edge can cross the horizontal ray at only if one endpoint is strictly above and the other not above it, and then the crossing must lie to the right of . Both conditions reduce to sign comparisons; the second avoids dividing by cross-multiplying.
The pitfalls are all degenerate rays — those grazing a vertex or running along an edge — and they are what make naive implementations report nonsense.
Winding number
The parity test is blind to how the boundary wraps around , which is why it breaks on self-intersecting polygons. The winding number counts the net number of full turns the boundary makes around as it is traversed once. For a simple polygon inside (sign by orientation) and outside; for a self-intersecting polygon the magnitude can exceed , and the nonzero rule ( inside iff ) is the one graphics systems use.
Computed naively this would sum signed angles — irrational, floating point. The exact version sums signed edge crossings of the ray instead, weighting by direction: an edge crossing the ray upward contributes , downward. The sum is , computed entirely from orientation signs.
Both tests are per query. When the polygon is convex and many queries are expected, the primitives lesson's fan binary search wins after an preprocess.
Polygon area: the shoelace formula
The primitives lesson introduced the shoelace formula for the area of a simple polygon with vertices in order (indices mod ):
Here we prove it. The cleanest argument is the trapezoid sum: each edge, with the -axis, bounds a trapezoid whose signed area is added as the boundary is walked, and the trapezoids below exterior edges cancel against those below interior edges, leaving exactly the enclosed region.
The sum is an integer for integer vertices, so the area is an exact half-integer.2 Dropping the absolute value keeps the sign, which gives the winding direction for free: positive means the vertices are counterclockwise, negative clockwise — the cheapest orientation test for a whole polygon.
Rotating calipers: diameter and width
The convex hull lesson closed by promising that the hull turns many extremal queries into linear scans. The technique is rotating calipers, and the cleanest instance is the diameter — the greatest distance between any two points of a set.
Checking all hull-vertex pairs is . Rotating calipers
do it in by exploiting monotonicity: imagine two parallel lines
(calipers
) squeezing the hull, and rotate them together through . As the
calipers turn, each touches a hull vertex; the contact points advance around the
hull in step, never backtracking, because the support direction is monotone.
Every antipodal pair is realized as some caliper orientation, so walking the two
contact pointers once around the hull visits all candidate pairs.
Concretely, for hull vertices in counterclockwise order, the area of the triangle (a cross product) is maximized, over , at the vertex farthest from edge . As advances by one edge, the farthest vertex only moves forward, so a single linear walk of keeps pace with . Each antipodal pair encountered is a diameter candidate.
- 1
- 2if then
- 3return the only pair
- 4= current farthest vertex
- 5for to do
- 6// advance while the next vertex is farther from edge
- 7while do
- 8
- 9
- 10return
Because only ever advances and wraps once, the inner while does total
work across all — the pointer makes a single trip around the hull. The
diameter scan is therefore , and end-to-end once the hull is
built, against for all-pairs.3 The area calls are
cross products, so distances enter only at the final max (and even there,
comparing squared distances keeps it exact).
The same caliper sweep answers the dual question, the width — the smallest distance between two parallel supporting lines, i.e. the narrowest slab containing the polygon. The minimum-width slab always has one line flush with a hull edge, so rotate one caliper along each edge in turn and track the antipodal vertex's perpendicular distance (a cross product divided by the edge length); the minimum over edges is the width. It drives smallest-enclosing-rectangle and collision-clearance queries, again in after the hull.
Voronoi, Delaunay, and spatial trees
Closest-pair answers one proximity question for one query. Real systems ask it repeatedly — nearest hospital, nearest cell tower, nearest neighbor for a classifier — and want a structure, built once, that answers each query fast. The two canonical structures are duals of each other.
The Voronoi diagram of sites partitions the plane into one cell per site, the cell of site being every point closer to than to any other site. Its edges are the perpendicular bisectors between neighboring sites, and a nearest-site query becomes point location in this diagram. The Delaunay triangulation is its dual: connect two sites whenever their Voronoi cells share an edge. Delaunay triangulations have a defining property — the circumcircle of every triangle is empty of other sites — that makes them maximize the minimum angle, so they avoid the sliver triangles that wreck interpolation and mesh quality.4 Both are built in , by Fortune's sweep from the previous lesson or by randomized incremental insertion, and either one yields the closest pair as a by-product: the two nearest sites are always joined by a Delaunay edge.
When the data is high-dimensional or the queries are approximate, Voronoi diagrams
become impractical (their size grows as ), and the tool of
choice is a spatial tree. A k-d tree recursively splits the points by
alternating coordinate axes, giving expected nearest-neighbor queries
in low dimensions; R-trees group nearby objects into bounding rectangles for
disk-resident spatial databases; and for the high-dimensional nearest-neighbor
search behind modern embeddings, exact methods lose to locality-sensitive
hashing and graph-based approximate indexes such as HNSW.5 All of these
structures rest on the same primitive this module started with: every one
decides which side
and closer to which
with orientation and
distance comparisons, and keeps them exact where correctness of the combinatorics
depends on it.
Takeaways
- Closest pair is divide-and-conquer: split at the median , recurse, then scan a width- strip. The strip-packing lemma caps the combine at comparisons per point, giving by the Master Theorem. Compare squared distances to stay exact.
- Point-in-polygon by ray casting: an odd count of -ray crossings means inside (Jordan curve), with half-open-edge tie-breaking for vertex/horizontal grazes and a separate on-segment test for the boundary.
- The winding number sums signed crossings ( up, down); means inside and stays correct for self-intersecting polygons, where parity fails.
- The shoelace formula is proved by the trapezoid sum: top edges add, bottom edges subtract, telescoping to the enclosed area; its sign gives the winding direction.
- Rotating calipers read the diameter (farthest pair, an antipodal hull pair) and the width (narrowest slab, flush with a hull edge) in by advancing one monotone pointer around the hull — after the hull versus brute force.
Footnotes
- CLRS, Ch. 33 — Computational Geometry (§33.4): divide-and-conquer closest pair, the -strip, and the constant-neighbour packing bound that makes the combine linear. ↩
- Skiena, § — Computational Geometry: the shoelace (surveyor's) formula as a signed sum of cross products, whose sign encodes vertex orientation. ↩
- Skiena, § — Computational Geometry: rotating calipers on the convex hull for the diameter (farthest pair) and width, in linear time after the hull. ↩
- The Voronoi/Delaunay duality and the empty-circumcircle (max-min-angle) property are treated in Mark de Berg et al., Computational Geometry: Algorithms and Applications (3rd ed.), Chs. 7 and 9; both structures build in via Fortune's sweep or randomized incremental insertion. ↩
- Piotr Indyk and Rajeev Motwani,
Approximate Nearest Neighbors: Towards Removing the Curse of Dimensionality,
STOC 1998 (locality-sensitive hashing); Yu. A. Malkov and D. A. Yashunin,Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs,
IEEE TPAMI 42(4), 2020 (HNSW). ↩
╌╌ END ╌╌