{"spec_id":"donut-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' donut-basic: Basic Donut Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 87/100 | Created: 2026-06-25\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\n\nIMPRINT_PALETTE <- c(\n  \"#009E73\",  # 1 — brand green\n  \"#C475FD\",  # 2 — lavender\n  \"#4467A3\",  # 3 — blue\n  \"#BD8233\",  # 4 — ochre\n  \"#AE3030\",  # 5 — matte red\n  \"#2ABCCD\",  # 6 — cyan\n  \"#954477\",  # 7 — rose\n  \"#99B314\"   # 8 — lime\n)\n\n# Data — annual budget allocation by department (millions USD)\ndepartments <- c(\"Technology\", \"Operations\", \"Marketing\", \"Finance\", \"HR\", \"R&D\")\nbudgets     <- c(2.8, 2.2, 1.9, 1.3, 1.1, 0.7)\n\ndf <- data.frame(\n  category = factor(departments, levels = departments),\n  value    = budgets\n) |>\n  mutate(\n    pct   = value / sum(value),\n    label = paste0(sprintf(\"%.0f\", pct * 100), \"%\")\n  )\n\n# geom_col stacks in reverse factor level order (last level first at y=0).\n# Compute midpoints in the same reverse order so labels land in their segments.\ndf_label <- df |>\n  arrange(desc(as.integer(category))) |>\n  mutate(\n    ymax = cumsum(pct),\n    ymin = lag(ymax, default = 0),\n    ymid = (ymax + ymin) / 2\n  )\n\ntotal_label <- paste0(\"$\", sprintf(\"%.1f\", sum(budgets)), \"M\\nTotal Budget\")\n\n# Plot\np <- ggplot(df) +\n  geom_col(\n    aes(x = 2, y = pct, fill = category),\n    width     = 1,\n    color     = PAGE_BG,\n    linewidth = 0.6\n  ) +\n  geom_text(\n    data     = df_label,\n    aes(x = 2, y = ymid, label = label),\n    color    = \"#FFFFFF\",\n    size     = 3.5,\n    fontface = \"bold\"\n  ) +\n  annotate(\n    \"text\",\n    x          = 0,\n    y          = 0,\n    label      = total_label,\n    color      = INK,\n    size       = 5,\n    fontface   = \"bold\",\n    hjust      = 0.5,\n    vjust      = 0.5,\n    lineheight = 1.3\n  ) +\n  coord_polar(theta = \"y\", start = 0) +\n  scale_x_continuous(limits = c(-0.5, 2.5), expand = c(0, 0)) +\n  scale_fill_manual(\n    values = setNames(IMPRINT_PALETTE, departments)\n  ) +\n  labs(\n    title = \"donut-basic · r · ggplot2 · anyplot.ai\",\n    fill  = \"Department\"\n  ) +\n  theme_void() +\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,\n      size   = 12,\n      hjust  = 0.5,\n      margin = margin(b = 10)\n    ),\n    plot.title.position = \"plot\",\n    legend.position   = \"right\",\n    legend.text       = element_text(color = INK_SOFT, size = 8),\n    legend.title      = element_text(color = INK, size = 10),\n    legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.3),\n    legend.key.size   = unit(14, \"pt\"),\n    plot.margin       = margin(20, 20, 20, 20)\n  )\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)\n"}