{"spec_id":"gauge-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' gauge-basic: Basic Gauge Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 88/100 | Created: 2026-06-30\n\nlibrary(ggplot2)\nlibrary(ragg)\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\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n\n# Zone colors — semantic exception applies: red=bad, amber=caution, green=good\n# per the Imprint palette's semantic anchors and matte-red role\nZONE_COLORS <- c(bad = \"#AE3030\", caution = \"#DDCC77\", good = \"#009E73\")\n\n# Data: quarterly sales attainment (%)\ncurrent_value  <- 72\nmin_value      <- 0\nmax_value      <- 100\nthreshold_low  <- 30\nthreshold_high <- 70\n\n# Map a value to an angle on the semi-circle (pi = left/min, 0 = right/max)\nval_to_angle <- function(v) {\n  pi * (1 - (v - min_value) / (max_value - min_value))\n}\n\n# Build a filled annular sector polygon (for zone arcs)\nannular_sector <- function(a_start, a_end, r_inner, r_outer, n = 200) {\n  angles_out <- seq(a_start, a_end, length.out = n)\n  angles_in  <- rev(angles_out)\n  data.frame(\n    x = c(r_outer * cos(angles_out), r_inner * cos(angles_in)),\n    y = c(r_outer * sin(angles_out), r_inner * sin(angles_in))\n  )\n}\n\n# Gauge radii\nr_outer <- 1.00\nr_inner <- 0.58\n\n# Zone boundary angles\na_min  <- val_to_angle(min_value)\na_low  <- val_to_angle(threshold_low)\na_high <- val_to_angle(threshold_high)\na_max  <- val_to_angle(max_value)\n\n# Combined zones data frame — zone factor enables fill aesthetic mapping\n# and scale_fill_manual(), leveraging ggplot2's grammar of graphics\nzones_df <- rbind(\n  cbind(annular_sector(a_min,  a_low,  r_inner, r_outer), zone = \"bad\"),\n  cbind(annular_sector(a_low,  a_high, r_inner, r_outer), zone = \"caution\"),\n  cbind(annular_sector(a_high, a_max,  r_inner, r_outer), zone = \"good\")\n)\nzones_df$zone <- factor(zones_df$zone, levels = c(\"bad\", \"caution\", \"good\"))\n\n# Thin separator lines at zone boundaries\nsep_angles <- c(a_low, a_high)\nsep_df <- data.frame(\n  x    = r_inner * cos(sep_angles),\n  y    = r_inner * sin(sep_angles),\n  xend = r_outer * cos(sep_angles),\n  yend = r_outer * sin(sep_angles)\n)\n\n# Decorative boundary arcs (inner + outer rings) — grouped for single geom_path call\narc_angles <- seq(a_min, a_max, length.out = 300)\narcs_df <- data.frame(\n  x    = c(r_outer * cos(arc_angles), r_inner * cos(arc_angles)),\n  y    = c(r_outer * sin(arc_angles), r_inner * sin(arc_angles)),\n  ring = factor(rep(c(\"outer\", \"inner\"), each = 300))\n)\n\n# Needle\nneedle_angle  <- val_to_angle(current_value)\nr_tip         <- 0.90\nr_tail        <- 0.14\nneedle_df <- data.frame(\n  x    = -r_tail * cos(needle_angle),\n  y    = -r_tail * sin(needle_angle),\n  xend =  r_tip  * cos(needle_angle),\n  yend =  r_tip  * sin(needle_angle)\n)\n\n# Tick marks at every 20 units\ntick_values    <- c(0, 20, 40, 60, 80, 100)\ntick_angles    <- val_to_angle(tick_values)\ntick_lines_df <- data.frame(\n  x    = 1.04 * cos(tick_angles),\n  y    = 1.04 * sin(tick_angles),\n  xend = 1.12 * cos(tick_angles),\n  yend = 1.12 * sin(tick_angles)\n)\ntick_labels_df <- data.frame(\n  x     = 1.26 * cos(tick_angles),\n  y     = 1.26 * sin(tick_angles),\n  label = as.character(tick_values)\n)\n\nplot_title <- \"gauge-basic · r · ggplot2 · anyplot.ai\"\n\n# Build plot — zones mapped via fill aesthetic + scale_fill_manual (ggplot2 grammar)\np <- ggplot() +\n  # Colored zone arcs — fill mapped to zone factor, colors via scale\n  geom_polygon(\n    data = zones_df,\n    aes(x = x, y = y, fill = zone, group = zone),\n    alpha = 0.88\n  ) +\n  scale_fill_manual(values = ZONE_COLORS, guide = \"none\") +\n  # Zone boundary separators (PAGE_BG camouflage for seamless look)\n  geom_segment(\n    data = sep_df, aes(x = x, y = y, xend = xend, yend = yend),\n    color = PAGE_BG, linewidth = 1.8\n  ) +\n  # Decorative inner/outer boundary arcs — both rings in one geom_path call\n  geom_path(\n    data = arcs_df, aes(x = x, y = y, group = ring),\n    color = INK_SOFT, linewidth = 0.35, alpha = 0.4\n  ) +\n  # Tick marks\n  geom_segment(\n    data = tick_lines_df, aes(x = x, y = y, xend = xend, yend = yend),\n    color = INK_SOFT, linewidth = 0.5\n  ) +\n  # Tick labels (size=3.8mm for better mobile legibility)\n  geom_text(\n    data = tick_labels_df, aes(x = x, y = y, label = label),\n    color = INK_SOFT, size = 3.8\n  ) +\n  # Needle\n  geom_segment(\n    data = needle_df, aes(x = x, y = y, xend = xend, yend = yend),\n    color = INK, linewidth = 1.4, lineend = \"round\"\n  ) +\n  # Center pivot dot\n  annotate(\"point\", x = 0, y = 0, size = 5, color = INK) +\n  # Current value (large, prominent)\n  annotate(\n    \"text\", x = 0, y = -0.22,\n    label = paste0(current_value, \"%\"),\n    color = INK, size = 17, fontface = \"bold\"\n  ) +\n  # Subtitle below the value\n  annotate(\n    \"text\", x = 0, y = -0.44,\n    label = \"Quarterly Sales Attainment\",\n    color = INK_SOFT, size = 4.2\n  ) +\n  labs(title = plot_title) +\n  coord_fixed(xlim = c(-1.7, 1.7), ylim = c(-0.65, 1.35)) +\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, size = 12, hjust = 0.5,\n      margin = margin(t = 12, b = 4)\n    ),\n    plot.margin = margin(8, 16, 8, 16)\n  )\n\n# Save\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}