I created a github repo containing a Python package called voronoiz
that includes the functions voronoi_l1
(for computing the polygons of an L1 Voronoi diagram) and voronoi_grid
(for computing an image of the Voronoi diagram for any distance metric supported by scipy.spatial.cdist
).
The implementations use brute-force, O(n²) algorithms, so it probably won't work well if you use it with millions of points, but for a small to moderate number of points, you can use it to make nice plots.
For example, these animations of a Voronoi diagram for a set of 10 points, one of which moves around in a circle, are made with voronoi_grid
combined with write_apng
from the numpngw
library:
L1 metric:
Minkowksi metric, p=2 (i.e. standard Euclidean metric):
Minkowski metric, p=4:
Here's the script that generates the animations:
import numpy as np
from voronoiz import voronoi_grid
from numpngw import write_apng
xmin = 0
xmax = 5
ymin = 0
ymax = 5
points = np.array([[0.00, 0.00],
[1.00, 4.51],
[1.20, 0.30],
[2.50, 2.60],
[2.40, 0.80],
[4.40, 3.30],
[1.95, 3.00],
[3.71, 1.90],
[4.50, 3.66],
[4.67, 0.21]])
gridsize = 299
for kwargs in [dict(metric='cityblock'),
dict(metric='minkowski', p=2),
dict(metric='minkowski', p=4)]:
imgs = []
for theta in np.linspace(0, 2*np.pi, 250, endpoint=False):
# points[0] will travel about a circle.
points[0] = 2.5 + 1.5*np.array([np.cos(theta), np.sin(theta)])
img = voronoi_grid(points, xmin, xmax, ymin, ymax,
gridsize=(gridsize, gridsize),
**kwargs)
img = (160//(len(points)+1)*img + 64).astype(np.uint8)
img[img == 64] = 0
for x, y in points:
i = int(gridsize*(x - xmin)/(xmax - xmin))
j = int(gridsize*(y - ymin)/(ymax - ymin))
img[j-1:j+2, i-1:i+2] = 255
img = np.pad(img, pad_width=1, mode='constant', constant_values=255)
imgs.append(img)
tag = '_'.join(f"{key}_{value}" for key, value in kwargs.items())
write_apng(f'animation_{tag}.png', imgs, delay=100)