{"spec_id":"polar-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' polar-basic: Basic Polar Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Updated: 2026-07-25\n\nlibrary(ggplot2)\nlibrary(ragg)\nlibrary(scales)\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(\n    \"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n    \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"\n)\nBRAND <- IMPRINT_PALETTE[1]\n\n# --- Data -----------------------------------------------------------------\n# Wind direction frequency at a coastal weather station, 16-point compass.\ncompass_directions <- c(\n    \"N\", \"NNE\", \"NE\", \"ENE\", \"E\", \"ESE\", \"SE\", \"SSE\",\n    \"S\", \"SSW\", \"SW\", \"WSW\", \"W\", \"WNW\", \"NW\", \"NNW\"\n)\nn_directions <- length(compass_directions)\ndirection_index <- seq_len(n_directions)\nprevailing_index <- 11 # SW - prevailing onshore wind for this station\nland_breeze_index <- 3 # NE - opposite direction, secondary nighttime land-breeze peak\n\nangular_distance <- function(center) {\n    pmin(abs(direction_index - center), n_directions - abs(direction_index - center))\n}\n\n# Dominant onshore SW wind plus a smaller offshore NE land-breeze counter-peak.\nprevailing_bump <- exp(-0.5 * (angular_distance(prevailing_index) / 2.6)^2)\nland_breeze_bump <- 0.35 * exp(-0.5 * (angular_distance(land_breeze_index) / 2.0)^2)\nbase_frequency <- prevailing_bump + land_breeze_bump + 0.05\n\n# A frequency distribution across all 16 directions must sum to 100%, so\n# normalize before adding measurement noise.\nbase_frequency <- base_frequency / sum(base_frequency) * 100\nwind_frequency <- pmax(base_frequency + rnorm(n_directions, 0, 0.4), 0.3)\n\nwind_rose <- tibble::tibble(\n    direction = factor(compass_directions, levels = compass_directions),\n    frequency = wind_frequency\n)\n\n# coord_polar always draws its outer boundary circle at the axis maximum,\n# exactly where the default theta-axis labels sit (a ggplot2 rendering\n# quirk, not a theme setting) - so compass labels are placed manually at a\n# radius comfortably inside that boundary instead of via axis.text.x.\ngrid_max <- ceiling(max(wind_frequency) / 5) * 5\nlabel_radius <- grid_max + 2.5\nboundary_limit <- grid_max + 5\ncompass_labels <- tibble::tibble(\n    direction = factor(compass_directions, levels = compass_directions),\n    label_radius = label_radius\n)\n\n# --- Plot -------------------------------------------------------------------\np <- ggplot(wind_rose, aes(x = direction, y = frequency)) +\n    geom_col(fill = BRAND, width = 0.85, alpha = 0.9) +\n    geom_text(\n        data = compass_labels,\n        aes(x = direction, y = label_radius, label = direction),\n        inherit.aes = FALSE, color = INK, size = 2.8\n    ) +\n    coord_polar(theta = \"x\") +\n    scale_y_continuous(\n        limits = c(0, boundary_limit),\n        breaks = seq(0, grid_max, by = 5),\n        expand = c(0, 0)\n    ) +\n    labs(\n        title = \"polar-basic · r · ggplot2 · anyplot.ai\",\n        x = NULL,\n        y = \"Wind frequency (%)\"\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.x = element_blank(),\n        panel.grid.major.y = element_line(color = scales::alpha(INK, 0.2), linewidth = 0.3),\n        panel.grid.minor = element_blank(),\n        axis.title.x = element_blank(),\n        axis.title.y = element_text(color = INK, size = 10),\n        axis.text.x = element_blank(),\n        axis.text.y = element_text(color = INK_SOFT, size = 7),\n        axis.ticks = element_blank(),\n        plot.title = element_text(color = INK, size = 12, hjust = 0.5, margin = margin(b = 14)),\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"}