{"spec_id":"rose-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nrose-basic: Basic Rose Chart\nLibrary: matplotlib 3.11.1 | Python 3.13.14\nQuality: 90/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme-adaptive chrome tokens (Imprint)\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint categorical palette — position 1 is always brand green\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nBRAND = IMPRINT_PALETTE[0]\n\n# Data - Monthly rainfall (mm) showing seasonal patterns\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nvalues = np.array([85, 72, 95, 110, 145, 160, 180, 165, 130, 105, 90, 80])\n\n# Equal-angle segments, one per month\nn_categories = len(months)\nangles = np.linspace(0, 2 * np.pi, n_categories, endpoint=False)\n\n# Square canvas (2400x2400 px at 400 dpi) — a circular chart fills a square frame\n# far better than 16:9, which leaves dead space on both sides.\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, subplot_kw={\"projection\": \"polar\"}, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Orientation - set BEFORE plotting\nax.set_theta_zero_location(\"N\")  # 12 o'clock start\nax.set_theta_direction(-1)  # Clockwise\n\n# Bar width to fill the circle, leaving a small angular gap between wedges for\n# separation — no edge stroke needed on top of that gap\nwidth = 2 * np.pi / n_categories * 0.9\n\n# Per-wedge alpha scaled by value adds a subtle saturation gradient (the spec\n# calls out \"single color with varying saturation\") and gives the peak month\n# extra visual weight without leaving the brand-green hue.\nalpha_min, alpha_max = 0.55, 0.95\nalphas = alpha_min + (values - values.min()) / (values.max() - values.min()) * (alpha_max - alpha_min)\nbars = ax.bar(angles, values, width=width, bottom=0, color=BRAND)\nfor bar, a in zip(bars, alphas, strict=True):\n    bar.set_alpha(a)\n\n# Month labels around the circumference\nax.set_xticks(angles)\nax.set_xticklabels(months, fontsize=9, fontweight=\"bold\", color=INK_SOFT)\n\n# Radial gridlines and tick labels — position them in the angular gap between\n# Jan and Feb (15°) instead of the default 0° so they never sit on top of a wedge.\nax.set_ylim(0, max(values) * 1.32)\nax.set_rlabel_position(15)\nax.tick_params(axis=\"y\", labelsize=8, colors=INK_MUTED)\nax.grid(True, alpha=0.2, linestyle=\"--\", linewidth=1, color=INK)\nax.spines[\"polar\"].set_color(INK_SOFT)\nax.spines[\"polar\"].set_linewidth(1.2)\n\n# Callout on the peak month for a clearer visual story — placed with enough\n# headroom (1.32 ylim padding above) to clear the outer boundary spine.\npeak_idx = int(np.argmax(values))\nax.annotate(\n    f\"Peak: {months[peak_idx]} {values[peak_idx]}mm\",\n    xy=(angles[peak_idx], values[peak_idx]),\n    xytext=(angles[peak_idx], values[peak_idx] * 1.15),\n    ha=\"center\",\n    va=\"center\",\n    fontsize=8,\n    fontweight=\"bold\",\n    color=INK,\n    arrowprops={\"arrowstyle\": \"-\", \"color\": INK_SOFT, \"lw\": 1},\n)\n\n# Title — scale fontsize down from the 12pt/67-char landscape baseline for this\n# narrower (2400px) square canvas so a mandated title this length doesn't overflow.\ntitle = \"Monthly Rainfall (mm) · rose-basic · python · matplotlib · anyplot.ai\"\nsquare_char_budget = round(67 * 2400 / 3200)\ntitle_fontsize = 12 if len(title) <= square_char_budget else max(8, round(12 * square_char_budget / len(title)))\nax.set_title(title, fontsize=title_fontsize, fontweight=\"bold\", color=INK, pad=20)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}