{"spec_id":"waffle-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' waffle-basic: Basic Waffle Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 92/100 | Created: 2026-09-09\n\nlibrary(ggplot2)\nlibrary(patchwork)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens -----------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# --- Data ---------------------------------------------------------------\n# Annual department budget allocation, in percentage points (sums to 100)\nbudget <- tibble::tibble(\n  department = factor(\n    c(\"Engineering\", \"Marketing\", \"Operations\", \"Sales\", \"R&D\"),\n    levels = c(\"Engineering\", \"Marketing\", \"Operations\", \"Sales\", \"R&D\")\n  ),\n  share = c(32, 24, 19, 15, 10)\n)\ndept_colors <- setNames(IMPRINT_PALETTE[seq_len(nrow(budget))], levels(budget$department))\n\ngrid_size <- 10\nsquares <- rep(budget$department, times = budget$share)\nwaffle_df <- tibble::tibble(\n  department = squares,\n  square_idx = seq_along(squares) - 1,\n  col        = square_idx %% grid_size + 1,\n  row        = grid_size - square_idx %/% grid_size\n)\n\ntitle_text <- \"Company Budget Allocation · waffle-basic · r · ggplot2 · anyplot.ai\"\ntitle_size <- round(12 * min(1, 67 / nchar(title_text)))\n\n# --- Waffle grid ------------------------------------------------------------\np_grid <- ggplot(waffle_df, aes(x = col, y = row, fill = department)) +\n  geom_tile(color = PAGE_BG, linewidth = 0.6, width = 0.85, height = 0.85) +\n  coord_equal(xlim = c(0.5, 10.5), ylim = c(0.5, 10.5), expand = FALSE) +\n  scale_fill_manual(values = dept_colors, guide = \"none\") +\n  labs(title = title_text) +\n  theme_void(base_size = 8) +\n  theme(\n    plot.background  = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background = element_rect(fill = PAGE_BG, color = NA),\n    plot.title       = element_text(\n      color = INK, size = title_size, hjust = 0.5,\n      margin = margin(b = 20)\n    ),\n    plot.margin      = margin(t = 20, r = 20, b = 4, l = 20)\n  )\n\n# --- Custom legend, each row independently centered under the grid ---------\n# A stock guide_legend(nrow = 2, byrow = TRUE) packs the shorter final row\n# flush-left inside its grid cells; measuring each label's real rendered\n# width lets every row (regardless of length) be centered as a whole block,\n# with no risk of the longest label overrunning the canvas edge.\nlegend_font_pt <- 2.9 * .pt  # geom_text `size` (mm) -> points, matches rendered glyphs\nkey_w_in       <- 0.085\nkey_gap_in     <- 0.05\nitem_gap_in    <- 0.32\navail_width_in <- 6 - 2 * (20 / 72.27)  # matches p_grid's left/right plot.margin\n\ntext_width_in <- function(label) {\n  grid::convertWidth(\n    grid::grobWidth(grid::textGrob(label, gp = grid::gpar(fontsize = legend_font_pt))),\n    \"inches\", valueOnly = TRUE\n  )\n}\n\nlegend_rows <- list(levels(budget$department)[1:3], levels(budget$department)[4:5])\nbuild_legend_row <- function(depts, row_y) {\n  labels  <- paste0(depts, \" (\", budget$share[match(depts, levels(budget$department))], \"%)\")\n  item_w  <- key_w_in + key_gap_in + vapply(labels, text_width_in, numeric(1))\n  row_w   <- sum(item_w) + item_gap_in * (length(depts) - 1)\n  start_x <- (avail_width_in - row_w) / 2\n  key_x   <- start_x + cumsum(c(0, utils::head(item_w, -1) + item_gap_in))\n  data.frame(\n    department = depts,\n    label      = labels,\n    key_x      = key_x / avail_width_in,\n    text_x     = (key_x + key_w_in + key_gap_in) / avail_width_in,\n    y          = row_y,\n    stringsAsFactors = FALSE\n  )\n}\nlegend_df <- do.call(rbind, Map(\n  build_legend_row, legend_rows, rev(seq_along(legend_rows)) - 1\n))\n\np_legend <- ggplot(legend_df) +\n  geom_point(aes(x = key_x, y = y, color = department), shape = 15, size = 4.2) +\n  geom_text(aes(x = text_x, y = y, label = label), hjust = 0, color = INK_SOFT, size = 2.9) +\n  scale_color_manual(values = dept_colors, guide = \"none\") +\n  coord_cartesian(\n    xlim = c(0, 1), ylim = c(-0.6, length(legend_rows) - 0.4), expand = FALSE\n  ) +\n  theme_void() +\n  theme(plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG))\n\np <- (p_grid / p_legend) +\n  plot_layout(heights = c(10, 1.6)) &\n  theme(plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG))\n\n# --- Save -----------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 6,\n  height   = 6,\n  units    = \"in\",\n  dpi      = 400,\n  bg       = PAGE_BG\n)\n"}