Hexagonal Puzzles

Beautiful honeycomb patterns with curved edges

Overview

Hexagonal puzzles create stunning honeycomb patterns. Each hexagonal piece connects to up to six neighbors through elegantly curved tabs. The result is a visually striking circular puzzle.

Ring Variations

Hexagonal puzzles are defined by the number of rings:

# 2 rings
print(ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 2, seed = 42
  ) +
  scale_fill_viridis_c(option = "plasma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "2 rings"))

# 3 rings
print(ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 3, seed = 42
  ) +
  scale_fill_viridis_c(option = "plasma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "3 rings"))

# 4 rings
print(ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 4, seed = 42
  ) +
  scale_fill_viridis_c(option = "plasma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "4 rings"))
Figure 1: 2 rings (7 pieces)
Figure 2: 3 rings (19 pieces)
Figure 3: 4 rings (37 pieces)
# 2 rings
result_2 <- generate_puzzle(
  type = "hexagonal",
  grid = c(2),
  size = c(200),
  seed = 42,
  do_warp = TRUE,
  do_trunc = TRUE,
  fill_palette = "plasma"
)
render_puzzle_preview(result_2)  # 2 rings

# 3 rings
result_3 <- generate_puzzle(
  type = "hexagonal",
  grid = c(3),
  size = c(200),
  seed = 42,
  do_warp = TRUE,
  do_trunc = TRUE,
  fill_palette = "plasma"
)
render_puzzle_preview(result_3)  # 3 rings

# 4 rings
result_4 <- generate_puzzle(
  type = "hexagonal",
  grid = c(4),
  size = c(200),
  seed = 42,
  do_warp = TRUE,
  do_trunc = TRUE,
  fill_palette = "plasma"
)
render_puzzle_preview(result_4)  # 4 rings
Figure 4: 2 rings (7 pieces)
Figure 5: 2 rings (7 pieces)
Figure 6: 2 rings (7 pieces)

Piece Count Formula

The number of pieces in a hexagonal puzzle follows the formula:

\[n = 3r(r-1) + 1\]

Where \(r\) is the number of rings.

Rings Pieces
2 7
3 19
4 37
5 61
6 91

Warp and Truncation

Hexagonal puzzles support two key transformations:

# Default: warped and truncated
print(ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 3,
    do_warp = TRUE, do_trunc = TRUE,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "warp + trunc"))

# Warped but not truncated (extends beyond circle)
print(ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 3,
    do_warp = TRUE, do_trunc = FALSE,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "warp only"))
Figure 7: warp=TRUE, trunc=TRUE (Default)
Figure 8: warp=TRUE, trunc=FALSE
# Default: warped and truncated
result_warp_trunc <- generate_puzzle(
  type = "hexagonal",
  grid = c(3),
  size = c(200),
  seed = 42,
  do_warp = TRUE,
  do_trunc = TRUE,
  fill_palette = "magma"
)
render_puzzle_preview(result_warp_trunc)

# Warped but not truncated
result_warp_only <- generate_puzzle(
  type = "hexagonal",
  grid = c(3),
  size = c(200),
  seed = 42,
  do_warp = TRUE,
  do_trunc = FALSE,
  fill_palette = "magma"
)
render_puzzle_preview(result_warp_only)
Figure 9: warp=TRUE, trunc=TRUE (Default)
Figure 10: warp=TRUE, trunc=TRUE (Default)

Color Palettes

make_hex_palette <- function(pal) {
  ggplot() +
    geom_puzzle_hex(
      aes(fill = after_stat(piece_id)),
      rings = 3, seed = 42
    ) +
    scale_fill_viridis_c(option = pal, guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = pal)
}

print((make_hex_palette("viridis") + make_hex_palette("magma") + make_hex_palette("plasma")) /
(make_hex_palette("inferno") + make_hex_palette("cividis") + make_hex_palette("turbo")))
Figure 11
# 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 = "hexagonal", grid = c(3), size = c(200), seed = 42, do_warp = TRUE, do_trunc = TRUE, fill_palette = pal)
})
render_puzzle_grid(items, ncol = 3, labels = palettes)
viridis
magma
plasma
inferno
cividis
turbo
Figure 12

Fill Direction

Control the spatial order of color assignment. For hexagonal puzzles, "reverse" reverses colors within each ring while keeping the center piece unchanged. This is distinct from palette_invert which reverses the dark↔︎light ends of the palette.

make_direction_plot <- function(dir) {
  ggplot() +
    geom_puzzle_hex(
      aes(fill = after_stat(fill_order)),
      rings = 3,
      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 13
directions <- c("forward", "reverse")
items <- lapply(directions, function(dir) {
  generate_puzzle(type = "hexagonal", grid = c(3), size = c(200), seed = 42, do_warp = TRUE, do_trunc = TRUE, fill_palette = "viridis", fill_direction = dir)
})
render_puzzle_grid(items, ncol = 2, labels = paste0("fill_direction = \"", directions, "\""))
fill_direction = "forward"
fill_direction = "reverse"
Figure 14

Offset (Separation)

make_hex_offset <- function(off) {
  ggplot() +
    geom_puzzle_hex(
      aes(fill = after_stat(piece_id)),
      rings = 3,
      offset = off, seed = 42
    ) +
    scale_fill_viridis_c(option = "viridis", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("offset = ", off))
}

print(make_hex_offset(0) + make_hex_offset(5) + make_hex_offset(10) + make_hex_offset(15))
Figure 15
# Generate offset previews in a grid
offsets <- c(0, 5, 10, 15)
items <- lapply(offsets, function(off) {
  generate_puzzle(type = "hexagonal", grid = c(3), size = c(200), seed = 42, do_warp = TRUE, do_trunc = TRUE, offset = off, fill_palette = "viridis")
})
render_puzzle_grid(items, ncol = 4, labels = paste("offset =", offsets))
offset = 0
offset = 5
offset = 10
offset = 15
Figure 16

Fusion Groups

Special keywords for hexagonal puzzles:

# Center merged with ring 1
print(ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 3,
    fusion_groups = "center-ring1",
    seed = 42
  ) +
  scale_fill_viridis_c(option = "plasma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "center + ring1"))

# Ring 2 pieces merged
print(ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 3,
    fusion_groups = "ring2",
    seed = 42
  ) +
  scale_fill_viridis_c(option = "plasma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "ring2"))
Figure 17: center + ring1
Figure 18: All of ring2
# Center merged with ring 1
result_center_ring1 <- generate_puzzle(
  type = "hexagonal",
  grid = c(3),
  size = c(200),
  seed = 42,
  do_warp = TRUE,
  do_trunc = TRUE,
  fusion_groups = "center-ring1",
  fill_palette = "plasma"
)
render_puzzle_preview(result_center_ring1)

# Ring 2 pieces merged
result_ring2 <- generate_puzzle(
  type = "hexagonal",
  grid = c(3),
  size = c(200),
  seed = 42,
  do_warp = TRUE,
  do_trunc = TRUE,
  fusion_groups = "ring2",
  fill_palette = "plasma"
)
render_puzzle_preview(result_ring2)
Figure 19: center + ring1
Figure 20: center + ring1

Large Puzzles

ggplot() +
  geom_puzzle_hex(
    aes(fill = after_stat(piece_id)),
    rings = 5,
    seed = 123
  ) +
  scale_fill_viridis_c(option = "turbo", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "5-Ring Hexagonal Puzzle (61 pieces)")
Figure 21
result <- generate_puzzle(
  type = "hexagonal",
  grid = c(5),
  size = c(300),
  seed = 123,
  do_warp = TRUE,
  do_trunc = TRUE,
  fill_palette = "turbo"
)
render_puzzle_preview(result, max_width = "500px")
Figure 22

Parameters Reference

Parameter Type Default Description
rings integer 3 Number of concentric rings
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
do_warp logical FALSE (API) / TRUE (ggpuzzle) Apply circular warping
do_trunc logical FALSE (API) / TRUE (ggpuzzle) Truncate to boundary
fusion_groups string NULL PILES notation for fusing pieces

PILES Keywords

Special keywords available for hexagonal puzzles:

Keyword Description
center The center piece (piece 1)
ring1 All pieces in ring 1
ring2 All pieces in ring 2
ringN All pieces in ring N
boundary All outer boundary pieces

Code Example

# Generate puzzle
result <- generate_puzzle(
  type = "hexagonal",
  grid = c(4),         # 4 rings
  size = c(250),       # 250mm diameter
  seed = 42,
  do_warp = TRUE,
  do_trunc = TRUE,
  offset = 5
)

# writeLines(result$svg_content, "hexagonal_puzzle.svg")
render_puzzle_preview(result)

# Access piece info
n_pieces <- length(result$pieces)
cli::cli_alert_info("Generated {n_pieces} pieces")