Voronoi Puzzles

Organic, natural-looking tessellations

Overview

Voronoi puzzles create organic, natural-looking pieces based on Voronoi tessellation. Each piece is a convex polygon formed by the regions closest to a set of seed points. The result resembles patterns found in nature, like honeycomb cells, giraffe spots, or cracked mud.

Cell Count Variations

make_vor_cells <- function(n) {
  ggplot() +
    geom_puzzle_voronoi(
      aes(fill = after_stat(piece_id)),
      n_cells = n,
      seed = 42
    ) +
    scale_fill_viridis_c(option = "turbo", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0(n, " cells"))
}

print(make_vor_cells(10) + make_vor_cells(25) + make_vor_cells(50))
Figure 1
# Generate cell count previews in a grid
cells <- c(10, 25, 50)
items <- lapply(cells, function(n) {
  generate_puzzle(type = "voronoi", grid = c(n), size = c(200, 200), seed = 42, fill_palette = "turbo")
})
render_puzzle_grid(items, ncol = 3, labels = paste(cells, "cells"))
10 cells
25 cells
50 cells
Figure 2
TipAspect Ratios

Voronoi puzzles adapt to any aspect ratio - wide, square, or tall. See the Aspect Ratios Tutorial for examples comparing all puzzle types.

Color Palettes

make_vor_palette <- function(pal) {
  ggplot() +
    geom_puzzle_voronoi(
      aes(fill = after_stat(piece_id)),
      n_cells = 20,
      seed = 42
    ) +
    scale_fill_viridis_c(option = pal, guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = pal)
}

print((make_vor_palette("viridis") + make_vor_palette("magma") + make_vor_palette("plasma")) /
(make_vor_palette("inferno") + make_vor_palette("cividis") + make_vor_palette("turbo")))
Figure 3
# Generate all palette previews and combine into a grid
palettes <- c("viridis", "magma", "plasma", "inferno", "cividis", "turbo")
items <- lapply(palettes, function(pal) {
  generate_puzzle(type = "voronoi", grid = c(20), size = c(200, 200), seed = 42, fill_palette = pal)
})
render_puzzle_grid(items, ncol = 3, labels = palettes)
viridis
magma
plasma
inferno
cividis
turbo
Figure 4

Fill Direction

Control the spatial order of color assignment. This is distinct from palette_invert which reverses the dark↔︎light ends of the palette.

make_direction_plot <- function(dir) {
  ggplot() +
    geom_puzzle_voronoi(
      aes(fill = after_stat(fill_order)),
      n_cells = 20,
      fill_direction = dir,
      seed = 42
    ) +
    scale_fill_viridis_c(option = "viridis", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("fill_direction = \"", dir, "\""))
}

print(make_direction_plot("forward") + make_direction_plot("reverse"))
Figure 5
directions <- c("forward", "reverse")
items <- lapply(directions, function(dir) {
  generate_puzzle(type = "voronoi", grid = c(20), size = c(200, 200), seed = 42, fill_palette = "viridis", fill_direction = dir)
})
render_puzzle_grid(items, ncol = 2, labels = paste0("fill_direction = \"", directions, "\""))
fill_direction = "forward"
fill_direction = "reverse"
Figure 6

Offset (Separation)

make_vor_offset <- function(off) {
  ggplot() +
    geom_puzzle_voronoi(
      aes(fill = after_stat(piece_id)),
      n_cells = 20,
      offset = off, seed = 42
    ) +
    scale_fill_viridis_c(option = "viridis", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("offset = ", off))
}

print(make_vor_offset(0) + make_vor_offset(5) + make_vor_offset(10) + make_vor_offset(20))
Figure 7
# Generate offset previews in a grid
offsets <- c(0, 5, 10, 20)
items <- lapply(offsets, function(off) {
  generate_puzzle(type = "voronoi", grid = c(20), size = c(200, 200), seed = 42, offset = off, fill_palette = "viridis")
})
render_puzzle_grid(items, ncol = 4, labels = paste("offset =", offsets))
offset = 0
offset = 5
offset = 10
offset = 20
Figure 8

Seed Variations

Different seeds create completely different tessellations:

make_vor_seed <- function(s) {
  ggplot() +
    geom_puzzle_voronoi(
      aes(fill = after_stat(piece_id)),
      n_cells = 15,
      seed = s
    ) +
    scale_fill_viridis_c(option = "magma", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("seed = ", s))
}

print(make_vor_seed(1) + make_vor_seed(42) + make_vor_seed(123) + make_vor_seed(999))
Figure 9
# Generate seed variation previews in a grid
seeds <- c(1, 42, 123, 999)
items <- lapply(seeds, function(s) {
  generate_puzzle(type = "voronoi", grid = c(15), size = c(200, 200), seed = s, fill_palette = "magma")
})
render_puzzle_grid(items, ncol = 4, labels = paste("seed =", seeds))
seed = 1
seed = 42
seed = 123
seed = 999
Figure 10

Fusion Groups

Merge pieces together using PILES notation:

# Standard
print(ggplot() +
  geom_puzzle_voronoi(
    aes(fill = after_stat(piece_id)),
    n_cells = 12,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "Standard"))

# Adjacent pieces fused
print(ggplot() +
  geom_puzzle_voronoi(
    aes(fill = after_stat(piece_id)),
    n_cells = 12,
    seed = 42,
    fusion_groups = "1-2-3,5-6"
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "Pieces fused"))
Figure 11: Standard 12-cell
Figure 12: Adjacent pieces fused
# Standard
result_standard <- generate_puzzle(
  type = "voronoi",
  grid = c(12),
  size = c(200, 200),
  seed = 42,
  fill_palette = "magma"
)
render_puzzle_preview(result_standard, title = "Standard")

# Adjacent pieces fused
result_fused <- generate_puzzle(
  type = "voronoi",
  grid = c(12),
  size = c(200, 200),
  seed = 42,
  fill_palette = "magma",
  fusion_groups = "1-2-3,5-6"
)
render_puzzle_preview(result_fused, title = "Pieces fused")
Figure 13: Standard 12-cell
Figure 14: Standard 12-cell

Large Voronoi Puzzle

ggplot() +
  geom_puzzle_voronoi(
    aes(fill = after_stat(piece_id)),
    n_cells = 80,
    width = 400, height = 300,
    seed = 789
  ) +
  scale_fill_viridis_c(option = "turbo", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "80-Cell Voronoi Puzzle")
Figure 15
result <- generate_puzzle(
  type = "voronoi",
  grid = c(80),
  size = c(300, 400),
  seed = 789,
  fill_palette = "turbo"
)
render_puzzle_preview(result, max_width = "600px")
Figure 16

Parameters Reference

Parameter Type Default Description
n_cells integer 12 Number of Voronoi cells (pieces)
seed integer random Random seed for reproducibility
offset numeric 0 (mm) Piece separation distance
tabsize numeric 6 (%) Tab size as percentage of edge length
jitter numeric 2 (%) Randomness in tab shape as percentage
min_tab_size numeric 0 (mm) Minimum tab size in millimeters
max_tab_size numeric Inf (mm) Maximum tab size in millimeters
point_distribution string “fermat” Seed point distribution method
fusion_groups string NULL PILES notation for fusing pieces

About Voronoi Tessellation

Voronoi tessellation divides a plane into regions based on distance to seed points. Each region consists of all points closer to its seed than to any other seed. This creates:

  • Convex polygons: Each piece is a convex polygon
  • Natural appearance: Resembles patterns in nature
  • Variable sizes: Piece sizes depend on seed point distribution

Code Example

ggplot() +
  geom_puzzle_voronoi(
    aes(fill = after_stat(piece_id)),
    n_cells = 35,
    width = 400, height = 300,
    seed = 42,
    offset = 5
  ) +
  scale_fill_viridis_c(option = "turbo", guide = "none") +
  coord_fixed() +
  theme_puzzle()

# Generate puzzle
result <- generate_puzzle(
  type = "voronoi",
  grid = c(35),          # 35 cells
  size = c(300, 400),    # height=300mm, width=400mm
  seed = 42,
  offset = 5
)

# writeLines(result$svg_content, "voronoi_puzzle.svg")

# Preview
render_puzzle_preview(result)

When to Use Voronoi

Voronoi puzzles are ideal for:

  • Organic themes: Nature, landscapes, biological subjects
  • Artistic effects: Modern art, abstract designs
  • Variable difficulty: More cells = harder puzzle
  • Unique designs: Every seed creates a different pattern