{"spec_id":"quiver-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' quiver-basic: Basic Quiver Plot\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 92/100 | Created: 2026-07-24\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tidyr)\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\n# Imprint sequential cmap (single-polarity: wind speed magnitude)\nIMPRINT_SEQ_LOW  <- \"#009E73\"\nIMPRINT_SEQ_HIGH <- \"#4467A3\"\n\n# --- Data: cyclonic wind field around a low-pressure eye ----------------\n# Rotational flow (u = -y, v = x) modulated by a radial Gaussian decay so\n# speed peaks in a ring around the calm eye and fades toward the outskirts.\n# WIND_SCALE converts the unitless rotation field into storm-force m/s so the\n# \"Cyclonic\" framing matches the numbers on the legend (peak ~34 m/s, low-end\n# Category 1 hurricane strength).\nWIND_SCALE <- 12\ngrid <- expand_grid(\n    x = seq(-8, 8, by = 1),\n    y = seq(-4.5, 4.5, by = 1)\n)\n\nwind <- grid %>%\n    mutate(\n        radius = sqrt(x^2 + y^2),\n        decay  = exp(-radius^2 / 40),\n        u      = -y * decay * WIND_SCALE,\n        v      = x * decay * WIND_SCALE,\n        speed  = sqrt(u^2 + v^2)\n    )\n\n# Scale arrow length so the fastest vector spans ~80% of the 1-unit grid\n# spacing, keeping neighbouring arrows from overlapping.\narrow_scale <- 0.8 / max(wind$speed)\nwind <- wind %>%\n    mutate(\n        xend = x + u * arrow_scale,\n        yend = y + v * arrow_scale\n    )\n\n# Dashed radial guide marking the ring of peak tangential wind speed, i.e.\n# the radius that maximises r * exp(-r^2/40) (found analytically at r = sqrt(20)).\neyewall_radius <- sqrt(20)\neyewall_theta  <- seq(0, 2 * pi, length.out = 200)\neyewall <- tibble::tibble(\n    x = eyewall_radius * cos(eyewall_theta),\n    y = eyewall_radius * sin(eyewall_theta)\n)\n\n# --- Plot ----------------------------------------------------------------\np <- ggplot(wind, aes(x = x, y = y, xend = xend, yend = yend, color = speed)) +\n    geom_path(\n        data = eyewall, aes(x = x, y = y), inherit.aes = FALSE,\n        linetype = \"dashed\", linewidth = 0.4, color = INK_SOFT, alpha = 0.5\n    ) +\n    geom_segment(\n        linewidth = 0.7,\n        arrow = arrow(length = unit(2, \"mm\"), type = \"closed\", angle = 20)\n    ) +\n    annotate(\n        \"point\", x = 0, y = 0,\n        shape = 21, size = 3, stroke = 1, color = INK_SOFT, fill = PAGE_BG\n    ) +\n    annotate(\n        \"text\", x = 0, y = -0.9, label = \"eye\",\n        color = INK_SOFT, size = 2.6, fontface = \"italic\"\n    ) +\n    coord_fixed(ratio = 1, expand = TRUE) +\n    scale_color_gradient(\n        low = IMPRINT_SEQ_LOW, high = IMPRINT_SEQ_HIGH,\n        name = \"Wind speed (m/s)\"\n    ) +\n    labs(\n        x = \"Distance east of eye (km)\",\n        y = \"Distance north of eye (km)\",\n        title = \"Cyclonic Wind Field · quiver-basic · r · ggplot2 · anyplot.ai\"\n    ) +\n    theme_minimal(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        panel.grid.major   = element_line(color = INK_SOFT, linewidth = 0.15, linetype = \"dotted\"),\n        panel.grid.minor   = element_blank(),\n        axis.title         = element_text(color = INK, size = 10),\n        axis.text          = element_text(color = INK_SOFT, size = 8),\n        axis.ticks         = element_blank(),\n        plot.title         = element_text(color = INK, size = 12),\n        legend.background  = element_rect(fill = ELEVATED_BG, color = NA),\n        legend.text        = element_text(color = INK_SOFT, size = 8),\n        legend.title       = element_text(color = INK, size = 10),\n        legend.key         = element_rect(fill = PAGE_BG, color = NA),\n        plot.margin        = margin(10, 14, 10, 10)\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"}