Skip to main content

Fortune's Algorithm

Given nn point sites in the plane, the Voronoi diagram partitions the plane into nn cells, one per site, so that every point in a cell is closer to its own site than to any other. The boundary between two neighboring cells lies on the perpendicular bisector of the corresponding pair of sites, and three cells meet at a vertex equidistant from three sites — the circumcenter of that triangle.

Fortune's algorithm, published by Steven Fortune in 1986, computes the diagram in O(nlogn)O(n \log n) time using a plane sweep. It sweeps a horizontal line downward and maintains, along the sweep line, a beach line made of parabolic arcs: the arc above a given xx coordinate belongs to the site whose parabola (with that site as focus and the sweep line as directrix) is currently highest. As the line moves, two kinds of events reshape the beach line — site events introduce a new arc, and circle events remove an arc that has been squeezed to zero width and emit a Voronoi vertex.

A production Fortune implementation needs a self-balancing BST keyed by arc order along the beach line plus a priority queue of events and runs to hundreds of lines. The snippet below is instead a reference brute-force construction: for each pair of sites we compute the perpendicular bisector and use it to carve out that site's half-plane against all other bisectors, producing the Voronoi cell as a convex polygon clipped to a bounding box. This is O(n3)O(n^3) but far shorter and still yields the exact combinatorial structure of the diagram. We report the result by listing, for each site, the sorted indices of its Voronoi neighbors.

Codes

def solve(xs):
"""xs = list of [x, y] site coordinates.
Returns, for each site, the sorted list of neighbor site indices
in the Voronoi diagram, computed by brute-force half-plane clipping."""
sites = [(float(p[0]), float(p[1])) for p in xs]
n = len(sites)
if n == 0:
return []
if n == 1:
return [[]]

# bounding box padded around the sites
pad = 100.0
minx = min(s[0] for s in sites) - pad
maxx = max(s[0] for s in sites) + pad
miny = min(s[1] for s in sites) - pad
maxy = max(s[1] for s in sites) + pad
box = [(minx, miny), (maxx, miny), (maxx, maxy), (minx, maxy)]

def clip(poly, a, b, c):
# keep points where a*x + b*y <= c (Sutherland-Hodgman)
out = []
m = len(poly)
for k in range(m):
p = poly[k]
q = poly[(k + 1) % m]
dp = a * p[0] + b * p[1] - c
dq = a * q[0] + b * q[1] - c
if dp <= 1e-12:
out.append(p)
if (dp < -1e-12 and dq > 1e-12) or (dp > 1e-12 and dq < -1e-12):
t = dp / (dp - dq)
out.append((p[0] + t * (q[0] - p[0]),
p[1] + t * (q[1] - p[1])))
return out

neighbors = [[] for _ in range(n)]
for i in range(n):
sx, sy = sites[i]
cell = list(box)
for j in range(n):
if i == j:
continue
tx, ty = sites[j]
# half-plane: closer to i than to j <=>
# 2*(tx - sx)*x + 2*(ty - sy)*y <= tx*tx + ty*ty - sx*sx - sy*sy
a = 2.0 * (tx - sx)
b = 2.0 * (ty - sy)
c = tx * tx + ty * ty - sx * sx - sy * sy
cell = clip(cell, a, b, c)
if not cell:
break
# after the cell is final, j is a neighbor iff its bisector has
# positive-length intersection with the cell boundary (>= 2 vertices)
real = []
if cell:
for j in range(n):
if i == j:
continue
tx, ty = sites[j]
a = 2.0 * (tx - sx)
b = 2.0 * (ty - sy)
c = tx * tx + ty * ty - sx * sx - sy * sy
on_edge = 0
for p in cell:
if abs(a * p[0] + b * p[1] - c) < 1e-6:
on_edge += 1
if on_edge >= 2:
real.append(j)
neighbors[i] = sorted(real)
return neighbors

Example

The snippet below is the brute-force half-plane version described above. Each input is a list of 2-D sites; the output lists, for each site, its Voronoi neighbors (the sites whose cells share an edge with it).

Loading Python runner...

Description

Run time analysis

Fortune's sweepline is O(nlogn)O(n \log n): the beach line holds O(n)O(n) arcs, every site and circle event is a O(logn)O(\log n) BST operation, and there are O(n)O(n) events in total since the final diagram has O(n)O(n) vertices and edges by Euler's formula on planar graphs.

The brute-force snippet above runs in O(n3)O(n^3): for each of nn sites we clip the cell polygon against n1n-1 half-planes, and each clip touches the polygon's current vertices, which are themselves O(n)O(n) in the worst case.

Space analysis

Fortune's algorithm uses O(n)O(n) working memory for the beach line BST, the event queue, and the output DCEL. The brute-force snippet uses O(n)O(n) space per cell plus O(n)O(n) for the neighbor lists — O(n2)O(n^2) total in the worst case when most pairs of sites are neighbors.

Proof of correctness

Correctness of the Voronoi definition via half-planes. For any two distinct sites sis_i and sjs_j, the set of points strictly closer to sis_i than to sjs_j is the open half-plane on sis_i's side of the perpendicular bisector of the segment sisjs_i s_j. Taking the intersection over all jij \ne i yields the set of points strictly closer to sis_i than to any other site, which is by definition the (open) Voronoi cell of sis_i. The brute-force snippet builds exactly this intersection inside a bounding box, so its output cell equals the true Voronoi cell clipped to that box. Two sites are Voronoi neighbors iff their shared bisector contributes a positive-length edge to both cells, which is the membership test the snippet uses at the end.

Correctness of Fortune's sweepline. The key invariant is that the beach line at height y0y_0 is the lower envelope of the parabolas with foci at all sites already above the sweep line and common directrix y=y0y = y_0. For any point pp on an arc belonging to site ss, the distance from pp to ss equals the distance from pp to the sweep line, so pp is strictly closer to ss than to any site still below the sweep line. Tracking the breakpoints of this envelope traces out the Voronoi edges, site events correctly insert new arcs, and circle events correctly detect the moment three sites become equidistant — the circumcenter, a Voronoi vertex. A full proof with case analysis is in Fortune's original paper and in Computational Geometry: Algorithms and Applications by de Berg et al. \blacksquare

Extensions

Applications

References

  • Fortune, Steven. "A sweepline algorithm for Voronoi diagrams." Algorithmica, 2(1):153-174, 1987. (Conference version: SoCG, 1986.)
  • de Berg, Cheong, van Kreveld, Overmars. Computational Geometry: Algorithms and Applications, 3rd ed., Chapter 7 (Voronoi Diagrams).
  • Aurenhammer, Klein, Lee. Voronoi Diagrams and Delaunay Triangulations, World Scientific, 2013.
  • cp-algorithms — Delaunay triangulation and Voronoi diagram