Rectangular Puzzles

Classic jigsaw puzzles with interlocking tabs

Overview

Rectangular puzzles are the classic jigsaw format with interlocking tabs on all sides. Each piece connects to its neighbors through curved tabs that can point inward or outward.

Size Variations

Explore different grid sizes:

print(ggplot() +
  geom_puzzle_rect(
    aes(fill = after_stat(piece_id)),
    cols = 2, rows = 2,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "viridis", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "2x2"))

print(ggplot() +
  geom_puzzle_rect(
    aes(fill = after_stat(piece_id)),
    cols = 4, rows = 3,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "viridis", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "3x4"))

print(ggplot() +
  geom_puzzle_rect(
    aes(fill = after_stat(piece_id)),
    cols = 6, rows = 5,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "viridis", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "5x6"))
Figure 1: 2x2 (4 pieces)
Figure 2: 3x4 (12 pieces)
Figure 3: 5x6 (30 pieces)
result_2x2 <- generate_puzzle(
  type = "rectangular",
  grid = c(2, 2),
  size = c(200, 200),
  seed = 42,
  fill_palette = "viridis"
)
render_puzzle_preview(result_2x2, title = "2x2")

result_4x3 <- generate_puzzle(
  type = "rectangular",
  grid = c(3, 4),
  size = c(300, 400),
  seed = 42,
  fill_palette = "viridis"
)
render_puzzle_preview(result_4x3, title = "3x4")

result_6x5 <- generate_puzzle(
  type = "rectangular",
  grid = c(5, 6),
  size = c(500, 600),
  seed = 42,
  fill_palette = "viridis"
)
render_puzzle_preview(result_6x5, title = "5x6")
Figure 4: 2x2 (4 pieces)
Figure 5: 2x2 (4 pieces)
Figure 6: 2x2 (4 pieces)

Color Palettes

All viridis palettes work beautifully with puzzle pieces:

make_palette_plot <- function(pal) {
  ggplot() +
    geom_puzzle_rect(
      aes(fill = after_stat(piece_id)),
      cols = 4, rows = 3,
      seed = 42
    ) +
    scale_fill_viridis_c(option = pal, guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = pal)
}

print((make_palette_plot("viridis") + make_palette_plot("magma") + make_palette_plot("plasma")) /
(make_palette_plot("inferno") + make_palette_plot("cividis") + make_palette_plot("turbo")))
Figure 7
# 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 = "rectangular", grid = c(3, 4), size = c(300, 400), seed = 42, fill_palette = pal)
})
render_puzzle_grid(items, ncol = 3, labels = palettes)
viridis
magma
plasma
inferno
cividis
turbo
Figure 8

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_rect(
      aes(fill = after_stat(fill_order)),
      cols = 3, rows = 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 9
directions <- c("forward", "reverse")
items <- lapply(directions, function(dir) {
  generate_puzzle(type = "rectangular", grid = c(3, 3), size = c(300, 300), 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 10

Offset (Separation)

Control spacing between pieces:

make_offset_plot <- function(off) {
  ggplot() +
    geom_puzzle_rect(
      aes(fill = after_stat(piece_id)),
      cols = 3, rows = 3,
      offset = off,
      seed = 42
    ) +
    scale_fill_viridis_c(option = "plasma", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("offset = ", off))
}

print(make_offset_plot(0) + make_offset_plot(5) + make_offset_plot(10) + make_offset_plot(20))
Figure 11
# Generate offset previews in a grid
offsets <- c(0, 5, 10, 20)
items <- lapply(offsets, function(off) {
  generate_puzzle(type = "rectangular", grid = c(3, 3), size = c(300, 300), offset = off, seed = 42, fill_palette = "plasma")
})
render_puzzle_grid(items, ncol = 4, labels = paste("offset =", offsets))
offset = 0
offset = 5
offset = 10
offset = 20
Figure 12

Fusion Groups

Merge pieces together using PILES notation:

# Standard
print(ggplot() +
  geom_puzzle_rect(
    aes(fill = after_stat(piece_id)),
    cols = 4, rows = 3,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "Standard"))

# With row fusion
print(ggplot() +
  geom_puzzle_rect(
    aes(fill = after_stat(piece_id)),
    cols = 4, rows = 3,
    seed = 42,
    fusion_groups = "R1,R2,R3"
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "Row fusion"))
Figure 13: Standard 3x4
Figure 14: Row fusion: R1,R2,R3
# Standard
result_standard <- generate_puzzle(
  type = "rectangular",
  grid = c(3, 4),
  size = c(300, 400),
  seed = 42,
  fill_palette = "magma"
)
render_puzzle_preview(result_standard, title = "Standard")

# With row fusion
result_fusion <- generate_puzzle(
  type = "rectangular",
  grid = c(3, 4),
  size = c(300, 400),
  seed = 42,
  fill_palette = "magma",
  fusion_groups = "R1,R2,R3"
)
render_puzzle_preview(result_fusion, title = "Row fusion")
Figure 15: Standard 3x4
Figure 16: Standard 3x4
TipAspect Ratios

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

Parameters Reference

Parameter Type Default Description
cols integer 3 Number of columns
rows integer 3 Number of rows
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
fusion_groups string NULL PILES notation for fusing pieces
fusion_style string “none” Style for fused edges: “none”, “dashed”, “solid”

Code Example

# Visualize with ggplot2
ggplot() +
  geom_puzzle_rect(
    aes(fill = after_stat(piece_id)),
    cols = 6, rows = 4,
    seed = 42, offset = 5
  ) +
  scale_fill_viridis_c() +
  coord_fixed() +
  theme_puzzle()


# ggsave("rectangular_puzzle.png", width = 10, height = 8, dpi = 300)
# Generate puzzle
result <- generate_puzzle(
  type = "rectangular",
  grid = c(4, 6),      # 4 rows, 6 columns
  size = c(400, 600),  # height=400mm, width=600mm
  seed = 42,
  offset = 5           # Slight separation
)

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