Example 9 - Constructing the Voronoi diagram

Example 9 - Constructing the Voronoi diagram#

The dual of a Delaunay triangulation is its Voronoi diagram. Stripy can efficiently calculate the voronoi points from a triangulation and, optionally, build the Voronoi regions for you.

Contents#

import numpy as np
import stripy
import matplotlib.pyplot as plt

%matplotlib inline
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 3
      1 import numpy as np
      2 import stripy
----> 3 import matplotlib.pyplot as plt
      5 get_ipython().run_line_magic('matplotlib', 'inline')

ModuleNotFoundError: No module named 'matplotlib'
import stripy as stripy

xmin = 0.0
xmax = 10.0
ymin = 0.0
ymax = 10.0
extent = [xmin, xmax, ymin, ymax]

spacingX = 0.5
spacingY = 0.5

## Set up a coarse mesh for visual clarity

mesh = stripy.cartesian_meshes.elliptical_mesh(extent, spacingX, spacingY)
fig = plt.figure(figsize=(10, 8), facecolor="none")

ax = fig.add_subplot(111, title="Delaunay triangulation")
ax.axis('off')
ax.triplot(mesh.x, mesh.y, mesh.simplices, c='LightGrey', zorder=1)
ax.scatter(mesh.x, mesh.y, c='C0', marker='.', zorder=2)

plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 fig = plt.figure(figsize=(10, 8), facecolor="none")
      3 ax = fig.add_subplot(111, title="Delaunay triangulation")
      4 ax.axis('off')

NameError: name 'plt' is not defined

Voronoi points#

A voronoi point (or circumcentre) exists for every triangle in the mesh. They are calculated by finding a constant radius (the circumradius) that is shared between each point in the triangle. Depending on the mesh, some voronoi points will reside outside the area contained within a triangle.

To calculate the Voronoi points from the mesh, use the voronoi_points method. This method may optionally return the circumradius, signed triangle area, or aspect ratio for each voronoi point.

vx, vy = mesh.voronoi_points()


fig = plt.figure(figsize=(10, 8), facecolor="none")

ax = fig.add_subplot(111, title="Delaunay triangulation + Voronoi points")
ax.axis('off')
ax.triplot(mesh.x, mesh.y, mesh.simplices, c='LightGrey', zorder=1)
ax.scatter(mesh.x, mesh.y, c='C0', marker='.', zorder=2)
ax.scatter(vx, vy, c='C1', marker='*', zorder=3)

plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 4
      1 vx, vy = mesh.voronoi_points()
----> 4 fig = plt.figure(figsize=(10, 8), facecolor="none")
      6 ax = fig.add_subplot(111, title="Delaunay triangulation + Voronoi points")
      7 ax.axis('off')

NameError: name 'plt' is not defined

Voronoi regions#

Often it is desirable to obtain the corresponding region enclosed by voronoi points. We can easily find the line segments connecting each voronoi point by retrieving the neighbour simplices.

neighbours = mesh.neighbour_simplices()

circumcentres = np.column_stack([vx, vy])
voronoi_edges = circumcentres[neighbours]
voronoi_edges[neighbours == -1] = np.nan # remove edges at infinity


lines = []
lines.extend(zip(circumcentres, voronoi_edges[:,0,:]))
lines.extend(zip(circumcentres, voronoi_edges[:,1,:]))
lines.extend(zip(circumcentres, voronoi_edges[:,2,:]))
# Plot it
from matplotlib.collections import LineCollection

linesC = LineCollection(lines, edgecolor='k')

fig = plt.figure(figsize=(10, 8), facecolor="none")
ax = fig.add_subplot(111, title='Voronoi diagram')
ax.axis('off')
ax.scatter(mesh.x, mesh.y, c='C0', marker='.', zorder=2)
ax.scatter(vx, vy, c='C1', marker='*', zorder=3)
ax.add_collection(linesC)

plt.show()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[6], line 2
      1 # Plot it
----> 2 from matplotlib.collections import LineCollection
      4 linesC = LineCollection(lines, edgecolor='k')
      6 fig = plt.figure(figsize=(10, 8), facecolor="none")

ModuleNotFoundError: No module named 'matplotlib'

Looks good! But in order to find which voronoi points belong to a region, we need to iterate through all of the triangles. Since each vertex in the Delaunay represents a voronoi “site”, we can accumulate all of the circumcentres into a list and sort them using voronoi_points_and_regions.

vx, vy, regions = mesh.voronoi_points_and_regions()


linesC = LineCollection(lines, edgecolor='k')

fig = plt.figure(figsize=(10, 8), facecolor="none")
ax = fig.add_subplot(111, title='Voronoi diagram')
ax.axis('off')
ax.scatter(mesh.x, mesh.y, c='C0', marker='.', zorder=2)
ax.scatter(vx, vy, c='C1', marker='*', zorder=3)
ax.add_collection(linesC)

# highlight specific region
r = 1
region = regions[r]

# iterate through region and connect up the points
for i in range(len(region)):
    i0 = region[i - 1]
    i1 = region[i]

    xx = [vx[i0], vx[i1]]
    yy = [vy[i0], vy[i1]]
    ax.plot(xx, yy, c='r', linewidth=5)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 4
      1 vx, vy, regions = mesh.voronoi_points_and_regions()
----> 4 linesC = LineCollection(lines, edgecolor='k')
      6 fig = plt.figure(figsize=(10, 8), facecolor="none")
      7 ax = fig.add_subplot(111, title='Voronoi diagram')

NameError: name 'LineCollection' is not defined