{"spec_id":"gauge-activity-rings","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\ngauge-activity-rings: Activity Rings Progress Chart\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 85/100 | Created: 2026-06-14\n\"\"\"\n\nimport os\nimport sys\nfrom pathlib import Path\n\n\n# Remove current directory from sys.path to avoid self-import (file is named seaborn.py)\n_orig_path = sys.path.copy()\nsys.path = [p for p in sys.path if p not in (\"\", \".\", str(Path(__file__).parent))]\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\n\n\nsys.path = _orig_path\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\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 palette — positions 1–3 for the three rings\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Daily fitness summary: Move, Exercise, Stand\nmetrics = [\"Move\", \"Exercise\", \"Stand\"]\nvalues = [420, 25, 9]\ngoals = [600, 30, 12]\nunits = [\"kcal\", \"min\", \"hr\"]\nring_colors = IMPRINT_PALETTE[:3]\n\n# Geometry: outer ring → inner ring\nring_radii = [0.80, 0.56, 0.32]\nLW = 26  # ring stroke width in points (rounded caps)\nTRACK_ALPHA = 0.14\n\n# Full theme-adaptive rc — follows seaborn.md template exactly\nsns.set_theme(\n    style=\"white\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\nsns.set_palette(ring_colors)\n\n# Fractions and arc end-point positions for seaborn progress-tip markers\nfractions = [min(v / g, 1.0) for v, g in zip(values, goals, strict=False)]\nend_angles = [np.pi / 2 - f * 2 * np.pi for f in fractions]\nend_xs = [r * np.cos(a) for r, a in zip(ring_radii, end_angles, strict=False)]\nend_ys = [r * np.sin(a) for r, a in zip(ring_radii, end_angles, strict=False)]\n\n# Square canvas — 2400×2400 px (symmetric plot)\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\nax.set_aspect(\"equal\")\nax.set_xlim(-1.5, 1.7)  # wider right margin for value labels\nax.set_ylim(-1.1, 1.2)  # reduced lower whitespace, better composition\nax.axis(\"off\")\n\n# Draw rings outer → inner\nfor _metric, value, goal, _unit, color, radius in zip(\n    metrics, values, goals, units, ring_colors, ring_radii, strict=False\n):\n    fraction = min(value / goal, 1.0)\n\n    # Background track: full faint circle\n    theta_bg = np.linspace(np.pi / 2, np.pi / 2 - 2 * np.pi, 500)\n    ax.plot(\n        radius * np.cos(theta_bg),\n        radius * np.sin(theta_bg),\n        color=color,\n        alpha=TRACK_ALPHA,\n        linewidth=LW,\n        solid_capstyle=\"round\",\n    )\n\n    # Progress arc: clockwise from 12 o'clock\n    if fraction > 0:\n        sweep = fraction * 2 * np.pi\n        n_pts = max(3, int(sweep * 300))\n        theta_arc = np.linspace(np.pi / 2, np.pi / 2 - sweep, n_pts)\n        ax.plot(\n            radius * np.cos(theta_arc), radius * np.sin(theta_arc), color=color, linewidth=LW, solid_capstyle=\"round\"\n        )\n\n# Seaborn: progress-tip indicators at the end of each arc — idiomatic hue+palette usage\nmarker_s = int(np.pi * (LW / 2) ** 2)  # circle area matching arc linewidth\nsns.scatterplot(\n    x=end_xs,\n    y=end_ys,\n    hue=metrics,\n    palette=dict(zip(metrics, ring_colors, strict=False)),\n    s=marker_s,\n    edgecolor=PAGE_BG,\n    linewidths=2.0,\n    ax=ax,\n    legend=False,\n    zorder=11,\n)\nsns.despine(ax=ax, left=True, bottom=True, top=True, right=True)\n\n# Center label: primary metric completion\npct_move = int(values[0] / goals[0] * 100)\nax.text(0, 0.06, f\"{pct_move}%\", ha=\"center\", va=\"center\", fontsize=22, fontweight=\"bold\", color=INK, zorder=10)\nax.text(0, -0.1, \"Move\", ha=\"center\", va=\"center\", fontsize=11, color=INK_SOFT, zorder=10)\n\n# Per-ring labels to the right, stacked at ring heights\nx_lbl = 1.02\nfor metric, value, goal, unit, color, radius in zip(\n    metrics, values, goals, units, ring_colors, ring_radii, strict=False\n):\n    pct = int(value / goal * 100)\n    ax.text(x_lbl, radius + 0.05, metric, ha=\"left\", va=\"center\", fontsize=9, fontweight=\"bold\", color=color)\n    ax.text(\n        x_lbl, radius - 0.07, f\"{value} / {goal} {unit}  ({pct}%)\", ha=\"left\", va=\"center\", fontsize=9, color=INK_MUTED\n    )\n\n# Title — 51 chars, under 67-char baseline → fontsize stays at 12\ntitle = \"gauge-activity-rings · python · seaborn · anyplot.ai\"\nn = len(title)\nratio = 67 / n if n > 67 else 1.0\ntitle_fs = max(8, round(12 * ratio))\nax.set_title(title, fontsize=title_fs, fontweight=\"medium\", color=INK, pad=14)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\nplt.close()\n"}