{"spec_id":"radar-innovation-timeline","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' radar-innovation-timeline: Innovation Radar with Time-Horizon Rings\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 80/100 | Created: 2026-05-29\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens — Imprint palette\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\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n\nIMPRINT_PALETTE <- c(\n  \"#009E73\",  # 1 — brand green → AI & ML\n  \"#C475FD\",  # 2 — lavender    → Cloud & Infra\n  \"#4467A3\",  # 3 — blue        → Security\n  \"#BD8233\"   # 4 — ochre       → Dev Tools\n)\n\nsector_names  <- c(\"AI & ML\", \"Cloud & Infra\", \"Security\", \"Dev Tools\")\nring_labels_v <- c(\"Now\", \"Near-term\", \"Mid-term\", \"Future\")\ncolor_map     <- setNames(IMPRINT_PALETTE, sector_names)\nsector_starts <- setNames(c(0, 90, 180, 270), sector_names)\n\n# 16 technology items: one per (sector × ring) cell\nitems <- tibble::tibble(\n  name = c(\n    \"LLM Deployment\", \"Agentic AI\", \"Foundation Models\", \"Neuromorphic Chips\",\n    \"Kubernetes Automation\", \"FinOps Tooling\", \"Edge ML Platforms\", \"Quantum Cloud\",\n    \"Zero-Trust Network\", \"AI-Powered SIEM\", \"Homomorphic Encryption\", \"Post-Quantum Crypto\",\n    \"AI Code Assistance\", \"Platform Engineering\", \"WebAssembly\", \"Autonomous Testing\"\n  ),\n  sector = rep(sector_names, each = 4),\n  ring_r = rep(1:4, times = 4),\n  # Spread angles evenly within each 90-degree sector (15/40/62/78 → 25°+ gaps)\n  angle_offset = rep(c(15, 40, 62, 78), times = 4)\n)\n\nitems$angle      <- sector_starts[items$sector] + items$angle_offset\nitems$color      <- color_map[items$sector]\n# Larger points for more-immediate rings to convey urgency/immediacy\nitems$point_size <- c(6.5, 5.5, 4.5, 3.5)[items$ring_r]\n\n# Helper: annular polygon for filled ring bands\nmake_ring_poly <- function(r_inner, r_outer, grp, n = 300) {\n  theta <- seq(0, 360, length.out = n + 1)\n  data.frame(\n    angle = c(theta, rev(theta)),\n    r     = c(rep(r_outer, n + 1), rep(r_inner, n + 1)),\n    grp   = grp,\n    stringsAsFactors = FALSE\n  )\n}\n\n# Rings 1 and 3 get a subtle tinted fill (alternating)\nRING_FILL    <- if (THEME == \"light\") \"#E6E3DB\" else \"#232320\"\nring_band_df <- rbind(\n  make_ring_poly(0.5, 1.5, grp = \"r1\"),\n  make_ring_poly(2.5, 3.5, grp = \"r3\")\n)\n\n# Ring boundary circles\nn_circ     <- 301\nangles_seq <- seq(0, 360, length.out = n_circ)\ncircle_df  <- do.call(rbind, lapply(c(0.5, 1.5, 2.5, 3.5, 4.5), function(r) {\n  data.frame(angle = angles_seq, r = r, grp = as.character(r))\n}))\n\n# Sector divider spokes (radial lines at 0, 90, 180, 270 degrees)\nspoke_df <- do.call(rbind, lapply(c(0, 90, 180, 270), function(a) {\n  data.frame(angle = c(a, a), r = c(0.5, 4.5), grp = as.character(a))\n}))\n\n# Ring annotation labels — placed just inside each ring at a small angle\nring_ann_df <- tibble::tibble(\n  angle = 5,\n  r     = 1:4,\n  label = ring_labels_v\n)\n\n# Sector headers outside the outermost ring boundary\nsector_hdr_df <- tibble::tibble(\n  angle = c(45, 135, 225, 315),\n  r     = 5.05,\n  label = sector_names\n)\n\ntitle_str <- \"radar-innovation-timeline · r · ggplot2 · anyplot.ai\"\ntitle_sz  <- max(8L, round(12 * 67 / nchar(title_str)))\n\np <- ggplot() +\n  # Alternating ring fills (rings 1 and 3)\n  geom_polygon(\n    data = ring_band_df,\n    aes(x = angle, y = r, group = grp),\n    fill = RING_FILL, color = NA, alpha = 0.75\n  ) +\n  # Ring boundary circles\n  geom_path(\n    data = circle_df,\n    aes(x = angle, y = r, group = grp),\n    color = INK_SOFT, linewidth = 0.25, alpha = 0.5\n  ) +\n  # Sector divider spokes\n  geom_path(\n    data = spoke_df,\n    aes(x = angle, y = r, group = grp),\n    color = INK_SOFT, linewidth = 0.45, alpha = 0.6\n  ) +\n  # Sector header labels\n  geom_text(\n    data = sector_hdr_df,\n    aes(x = angle, y = r, label = label),\n    size = 3.0, fontface = \"bold\", color = INK, hjust = 0.5\n  ) +\n  # Ring annotation labels (ring names near the 0-degree spoke)\n  geom_text(\n    data = ring_ann_df,\n    aes(x = angle, y = r, label = label),\n    size = 2.3, color = INK_MUTED, fontface = \"bold\", hjust = 0\n  ) +\n  # Technology item points — larger for more-immediate rings\n  geom_point(\n    data = items,\n    aes(x = angle, y = ring_r, color = sector, size = point_size),\n    alpha = 0.95\n  ) +\n  scale_size_identity(guide = \"none\") +\n  # Technology item labels (offset outward; angles are spread 25°+ so no overlap)\n  geom_text(\n    data = items,\n    aes(x = angle, y = ring_r + 0.42, label = name, color = sector),\n    size = 2.5, hjust = 0.5, vjust = 0\n  ) +\n  coord_polar(theta = \"x\", start = 0) +\n  scale_x_continuous(limits = c(0, 360), breaks = NULL, expand = c(0, 0)) +\n  scale_y_continuous(limits = c(0, 5.8), breaks = NULL) +\n  scale_color_manual(values = color_map) +\n  labs(title = title_str, color = \"Sector\") +\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_sz, hjust = 0.5,\n      margin = margin(b = 14)\n    ),\n    legend.position   = \"bottom\",\n    legend.direction  = \"horizontal\",\n    legend.background = element_rect(\n      fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.3\n    ),\n    legend.text     = element_text(color = INK_SOFT, size = 8),\n    legend.title    = element_text(color = INK, size = 10),\n    legend.key.size = unit(0.45, \"cm\"),\n    plot.margin     = margin(15, 30, 30, 30)\n  )\n\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"}