diff --git a/plots/polar-basic/implementations/python/matplotlib.py b/plots/polar-basic/implementations/python/matplotlib.py index 6188236fed..3597428e93 100644 --- a/plots/polar-basic/implementations/python/matplotlib.py +++ b/plots/polar-basic/implementations/python/matplotlib.py @@ -1,71 +1,105 @@ -""" anyplot.ai +"""anyplot.ai polar-basic: Basic Polar Chart -Library: matplotlib 3.10.9 | Python 3.13.13 -Quality: 88/100 | Updated: 2026-04-30 +Library: matplotlib 3.11.1 | Python 3.13.14 +Quality: 77/100 | Updated: 2026-07-24 """ import os import matplotlib.pyplot as plt import numpy as np +from matplotlib.ticker import FuncFormatter -# Theme tokens +# Theme tokens (Imprint palette) THEME = os.getenv("ANYPLOT_THEME", "light") PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" -# Data - Hourly temperature pattern (24-hour cycle) +# Data - hourly temperature pattern (24-hour diurnal cycle) np.random.seed(42) hours = np.arange(24) theta = hours * (2 * np.pi / 24) -base_temp = 15 + 8 * np.sin(theta - np.pi / 2) # Peak at 3 PM (hour 15) +base_temp = 15 + 8 * np.sin(theta - np.pi / 2) # smooth diurnal curve, trough near midnight noise = np.random.randn(24) * 1.5 radius = base_temp + noise theta_closed = np.append(theta, theta[0]) radius_closed = np.append(radius, radius[0]) -# Plot - Square format for polar chart (12x12 @ 300 dpi = 3600x3600 px) -fig, ax = plt.subplots(figsize=(12, 12), subplot_kw={"projection": "polar"}, facecolor=PAGE_BG) +peak_idx = int(np.argmax(radius)) +peak_hour, peak_temp = hours[peak_idx], radius[peak_idx] +avg_temp = radius.mean() + +# Plot - square canvas for a symmetric polar chart (6x6in @ 400dpi = 2400x2400 px) +fig, ax = plt.subplots(figsize=(6, 6), dpi=400, subplot_kw={"projection": "polar"}, facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) # Filled area from center to data for visual weight -ax.fill_between(theta_closed, 0, radius_closed, color=BRAND, alpha=0.18) +ax.fill_between(theta_closed, 0, radius_closed, color=BRAND, alpha=0.18, zorder=1) # Connecting line showing the cyclical pattern -ax.plot(theta_closed, radius_closed, color=BRAND, linewidth=3, alpha=0.9, zorder=2) +ax.plot(theta_closed, radius_closed, color=BRAND, linewidth=2.5, alpha=0.9, zorder=2) # Scatter points for individual hourly readings -ax.scatter(theta, radius, s=280, color=BRAND, alpha=0.95, zorder=3, edgecolors=PAGE_BG, linewidth=1.5) +ax.scatter(theta, radius, s=90, color=BRAND, alpha=0.95, zorder=3, edgecolors=PAGE_BG, linewidth=1.0) + +# Emphasize the peak reading with a larger, outlined marker for extra visual hierarchy +ax.scatter(theta[peak_idx], radius[peak_idx], s=170, color=BRAND, alpha=1.0, zorder=4, edgecolors=INK, linewidth=1.5) + +# Dashed reference ring at the daily average - a second radial layer for context +ring_theta = np.linspace(0, 2 * np.pi, 200) +ax.plot( + ring_theta, np.full_like(ring_theta, avg_temp), linestyle="--", linewidth=1.0, color=INK_SOFT, alpha=0.6, zorder=1.5 +) +ax.text(np.deg2rad(315), avg_temp + 1.4, f"avg {avg_temp:.1f}°C", fontsize=7, color=INK_SOFT, ha="center") + +# Callout for the daily peak +ax.annotate( + f"Peak {peak_temp:.1f}°C", + xy=(theta[peak_idx], peak_temp), + xytext=(theta[peak_idx] + 0.4, peak_temp + 4.5), + fontsize=8, + color=INK, + ha="center", + arrowprops={"arrowstyle": "-", "color": INK_SOFT, "linewidth": 1.0}, + bbox={"boxstyle": "round,pad=0.35", "facecolor": ELEVATED_BG, "edgecolor": INK_SOFT, "alpha": 0.95}, + zorder=4, +) # Configure angular axis (hours of day, midnight at top, clockwise) ax.set_theta_zero_location("N") ax.set_theta_direction(-1) hour_labels = ["12 AM", "3 AM", "6 AM", "9 AM", "12 PM", "3 PM", "6 PM", "9 PM"] ax.set_xticks(np.linspace(0, 2 * np.pi, 8, endpoint=False)) -ax.set_xticklabels(hour_labels, fontsize=18) +ax.set_xticklabels(hour_labels, fontsize=9) -# Configure radial axis (temperature) +# Configure radial axis (temperature) with a formatter instead of a static label list ax.set_ylim(0, 30) ax.set_yticks([5, 10, 15, 20, 25]) -ax.set_yticklabels(["5°C", "10°C", "15°C", "20°C", "25°C"], fontsize=16) -ax.set_rlabel_position(67.5) +ax.yaxis.set_major_formatter(FuncFormatter(lambda v, pos: f"{v:.0f}°C")) +ax.tick_params(axis="y", labelsize=8) +ax.set_rlabel_position(247.5) +# Radial labels must draw above the filled data area: the y-Axis container's own +# zorder (2.5 by default) governs the whole tick group's draw order, not each +# Text's zorder, so it needs raising above the scatter (zorder=3) too. +ax.yaxis.set_zorder(6) +for label in ax.get_yticklabels(): + label.set_bbox({"facecolor": PAGE_BG, "edgecolor": "none", "alpha": 0.9, "pad": 2.5}) # Theme-adaptive styling -ax.set_title( - "Hourly Temperature Pattern · polar-basic · matplotlib · anyplot.ai", - fontsize=24, - pad=25, - fontweight="medium", - color=INK, -) -ax.grid(True, alpha=0.15, linewidth=1.0, color=INK) +title = "Hourly Temperature Pattern · polar-basic · python · matplotlib · anyplot.ai" +# Title fontsize scales with title length (baseline: 12pt fits 67 chars on a 3200px-wide +# canvas) and further scales down for this narrower 2400px square canvas so the full +# mandated "anyplot.ai" suffix renders without being clipped. +title_fontsize = max(8, round(12 * 67 / len(title) * (2400 / 3200))) +ax.set_title(title, fontsize=title_fontsize, pad=16, fontweight="medium", color=INK) +ax.grid(True, alpha=0.15, linewidth=0.8, color=INK) ax.spines["polar"].set_color(INK_SOFT) ax.tick_params(colors=INK_SOFT, labelcolor=INK_SOFT) -plt.tight_layout() -plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG) +plt.savefig(f"plot-{THEME}.png", dpi=400, facecolor=PAGE_BG) diff --git a/plots/polar-basic/metadata/python/matplotlib.yaml b/plots/polar-basic/metadata/python/matplotlib.yaml index 25db380975..72879b243d 100644 --- a/plots/polar-basic/metadata/python/matplotlib.yaml +++ b/plots/polar-basic/metadata/python/matplotlib.yaml @@ -2,122 +2,159 @@ library: matplotlib language: python specification_id: polar-basic created: '2025-12-23T19:42:39Z' -updated: '2026-04-30T00:54:00Z' +updated: '2026-07-24T23:47:01Z' generated_by: claude-sonnet -workflow_run: 25140796834 +workflow_run: 30133561866 issue: 800 -python_version: 3.13.13 -library_version: 3.10.9 +language_version: 3.13.14 +library_version: 3.11.1 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/polar-basic/python/matplotlib/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/polar-basic/python/matplotlib/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 88 +quality_score: 77 review: strengths: - - Perfect visual quality in both themes (all text legible, no overlap, proper canvas - and palette) - - Elegant multi-layer polar design (filled area + connecting line + scatter markers) - - Realistic and contextually appropriate data (diurnal temperature cycle) - - 'Full spec compliance: required polar features, proper title format, idiomatic - matplotlib API' + - Correct polar chart type with theta=hour-of-day, radius=temperature, matching + the spec's directional/cyclical use case exactly + - 'Follows the spec''s notes precisely: theta_zero_location(''N'') + theta_direction(-1) + puts midnight at top and time flows clockwise, radial scale starts at 0 so data + isn''t compressed near center' + - 'Good data storytelling elements: dashed average-temperature reference ring plus + a labeled peak annotation give the viewer an immediate anchor for interpreting + the diurnal curve' + - 'Careful detail work: radial tick labels get a background-colored bbox and an + explicitly raised zorder so they stay legible over the filled data area instead + of colliding with it' + - 'Imprint palette and theme-adaptive chrome correctly applied — brand green #009E73 + identical in both renders, INK/INK_SOFT correctly threaded through title, ticks, + grid, spine, and annotation box across both themes' + - 'Clean KISS code structure: seeded reproducible data (np.random.seed(42)), only + necessary imports, no functions/classes, correct plot-{THEME}.png output' weaknesses: - - 'Design excellence is moderate: annotation present in images but not in base code - (PR branch discrepancy); limited styling beyond basic polar chrome' - - Library mastery above baseline but no advanced polar matplotlib techniques beyond - core API (e.g., custom radial tick formatting, multi-ring annotation) + - 'Title is truncated in BOTH renders: the mandated string ends ''...anyplot.ai'' + in the source (line 92) but the saved PNG only shows ''...anyplot.'' — the final + ''ai'' is completely missing, with ~11px of clean background remaining before + the physical canvas edge (verified by pixel-scanning both plot-light.png and plot-dark.png: + last non-background pixel is at x=2389 of 2400, not at the border). This is not + canvas-edge cropping, it looks like the long title (75 chars) at fontsize=12 is + being clipped by an internal bbox/clip region before reaching the figure edge. + Root cause: the title-length-scaling formula from prompts/library/matplotlib.md + (''title_fontsize = max(8, round(12 * 67 / len(title)))'') was not applied — with + len(title)=75 that formula gives ~11pt, and this is a 2400px-wide SQUARE canvas + (not the 3200px landscape the formula''s baseline assumes), so an even smaller + fontsize than 11pt is likely needed here. Fix: compute title_fontsize from the + actual title length and reduce further to account for the narrower square canvas + so ''anyplot.ai'' fully renders within bounds.' + - Because of the above, the mandated title format is not fully displayed — this + is a direct Spec Compliance (SC-04) miss even though the source string itself + is correct. + - 'Design Excellence is solid but not yet exceptional: the peak/average annotations + are a good start but fairly standard (single peak callout + reference ring) rather + than a distinctive, publication-level treatment — consider a bit more contrast/hierarchy + (e.g., subtle color or size emphasis on the peak point itself) to lift Aesthetic + Sophistication and Storytelling further.' + - Peak temperature (23.4°C) occurs at 12 PM in the data model (sin curve peaks exactly + at hour 12); most real diurnal cycles peak a couple hours after solar noon (~2–3 + PM) — a minor realism nit for Appropriate Scale, not a hard violation. image_description: |- Light render (plot-light.png): - Background: Warm off-white #FAF8F1 — correct, not pure white - Chrome: Title "polar-basic · matplotlib · anyplot.ai" in dark ink at top — readable. Angular tick labels (12 AM, 3 AM, 6 AM, 9 AM, 12 PM, 3 PM, 6 PM, 9 PM) at outer ring — readable. Radial labels (5°C–25°C) at ~67.5° in secondary-ink color — readable. "Peak 23.4°C" annotation callout box visible and legible. - Data: Brand green #009E73 filled area (alpha≈0.18), solid connecting line, and scatter markers — first (only) series is correctly #009E73 - Legibility verdict: PASS + Background: Warm off-white, matches #FAF8F1 target, not pure white. + Chrome: Title reads "Hourly Temperature Pattern · polar-basic · python · matplotlib" then "anyplot." — the trailing "ai" is missing entirely (confirmed via pixel scan: last non-background pixel at x=2389 of a 2400px-wide canvas, with clean background for the remaining ~11px before the edge, so this is an internal text-clip, not a canvas-border crop). Angular hour labels (12 AM, 3 AM, 6 AM, 9 AM, 12 PM, 3 PM, 6 PM, 9 PM) and radial °C tick labels (5°C–25°C) are all dark ink, clearly legible against the light background. Peak annotation box and avg-ring label are both legible. + Data: Brand green (#009E73) fill, line, and scatter markers for the 24 hourly readings, forming a smooth diurnal curve with a clear trough near midnight and a peak near noon. Dashed gray-ink reference ring marks the daily average (14.8°C). All data points visible with white-edged markers, no overplotting given only 24 points. + Legibility verdict: FAIL (title truncated — "ai" missing from the mandated "anyplot.ai" suffix in both themes). Dark render (plot-dark.png): - Background: Warm near-black #1A1A17 — correct, not pure black - Chrome: Title and all tick labels in light ink — clearly readable against dark surface. No dark-on-dark text issues. Grid lines remain subtle (faint concentric rings). Annotation callout box visible. - Data: Data colors identical to light render — same brand green #009E73 filled area, line, and scatter points. No color shift between themes. - Legibility verdict: PASS + Background: Warm near-black, matches #1A1A17 target, not pure black. + Chrome: Same title truncation defect reproduces identically — pixel scan confirms the same clipping (text ends at x=2389/2400, background all the way to the edge). All other chrome (hour labels, °C tick labels, avg annotation, peak callout box) uses light ink (INK/INK_SOFT tokens) and is clearly readable against the dark background — no dark-on-dark failures found elsewhere. + Data: Colors are identical to the light render — same #009E73 fill/line/markers, same dashed avg ring, same peak value and position. Only the chrome (background, text ink, grid, annotation box fill) flipped, as expected. + Legibility verdict: FAIL (same title truncation as light render; everything else passes). criteria_checklist: visual_quality: - score: 30 + score: 20 max: 30 items: - id: VQ-01 name: Text Legibility - score: 8 + score: 2 max: 8 - passed: true - comment: Title 24pt, angular ticks 18pt, radial ticks 16pt; all readable in - both themes + passed: false + comment: All present text is well-sized and legible, but the mandated title's + trailing 'ai' is missing entirely in both renders — a hard legibility/completeness + failure on the single most important text element. - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: 8 angular labels at 45 degree intervals no crowding; radial labels - at 67.5 clear; annotation well-positioned + comment: No collisions — radial tick labels use background boxes and a raised + zorder to stay clear of the filled data area; annotations don't collide + with data or each other. - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: Scatter s=280 clearly visible; line linewidth=3; fill alpha=0.18 - appropriate + comment: 24 sparse points with s=90 markers, 2.5pt line, appropriately sized + for this density — nothing barely visible. - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Single brand green series; excellent contrast in both themes + comment: Good contrast throughout, single-hue series with alpha-adjusted fill, + no red-green reliance. - id: VQ-05 name: Layout & Canvas - score: 4 + score: 0 max: 4 - passed: true - comment: 12x12 square 3600x3600 px ideal for polar; generous margins; nothing - clipped + passed: false + comment: Title content is cut off before reaching the canvas edge (missing + 'ai') — falls under 'content cut-off' in the 0-point tier even though it + isn't a canvas-border AR-09 crop. - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Descriptive title with spec-id/library/brand; radial labels include - C unit + comment: Radial axis has a °C unit formatter, angular axis uses clear hour-of-day + labels. - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series #009E73; backgrounds FAF8F1 light / 1A1A17 dark; both - renders correct' + comment: 'First series is #009E73 in both renders, backgrounds and chrome + are theme-correct (#FAF8F1 / #1A1A17), identical data color across themes.' design_excellence: - score: 11 + score: 14 max: 20 items: - id: DE-01 name: Aesthetic Sophistication score: 5 max: 8 - passed: true - comment: Multi-layer fill+line+scatter is deliberate and effective; subtle - fill alpha elegant; above default + passed: false + comment: Above a plain default (filled area, edge-highlighted markers, formatted + radial ticks) but not yet publication-tier polish; the broken title undercuts + overall sophistication. - id: DE-02 name: Visual Refinement - score: 3 + score: 5 max: 6 passed: true - comment: Grid alpha=0.15 subtle; polar spine styled with INK_SOFT; tick params - use theme tokens + comment: Subtle grid (alpha 0.15), background boxes behind radial ticks to + avoid collision, generous whitespace around the polar frame. - id: DE-03 name: Data Storytelling - score: 3 + score: 4 max: 6 - passed: true - comment: Annotation creates focal point; cyclical temperature story legible - from shape + passed: false + comment: Peak callout + average reference ring create real visual hierarchy + and a clear takeaway, though it's a fairly standard treatment rather than + an especially inventive one. spec_compliance: - score: 15 + score: 13 max: 15 items: - id: SC-01 @@ -125,51 +162,56 @@ review: score: 5 max: 5 passed: true - comment: Correct polar chart + comment: Correct polar chart, theta/radius mapping matches the spec exactly. - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Radial gridlines visible; angular labels at 3-hour intervals; midnight - at top clockwise; appropriate radial scale + comment: Visible-not-overwhelming radial gridlines, standard-interval angular + labels, top-start clockwise time direction, radial scale not compressed + near center — all spec notes satisfied. - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Theta=hour, radius=temperature; full 24-hour cycle + comment: theta=hour, radius=temperature correct, all 24 points shown, loop + correctly closed. - id: SC-04 name: Title & Legend - score: 3 + score: 1 max: 3 - passed: true - comment: Title contains spec-id, library, anyplot.ai; single series no legend - needed + passed: false + comment: Source title string is the correct mandated format, but the rendered + title is truncated ('anyplot.' instead of 'anyplot.ai') in both themes — + the displayed title does not match the required format. Legend correctly + omitted for single-series. data_quality: - score: 15 + score: 13 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 6 + score: 5 max: 6 passed: true - comment: Full 24-hour cycle; multi-layer rendering; annotation adds insight + comment: Shows the full diurnal rise/peak/fall/trough cycle with realistic + noise; captures the plot type's main characteristics well. - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Hourly temp canonical polar use case; 15C base + 8C diurnal swing - plausible; neutral + comment: Hourly outdoor temperature is a real, neutral, comprehensible scenario. - id: DQ-03 name: Appropriate Scale - score: 4 + score: 3 max: 4 passed: true - comment: 0-30C radial range appropriate; 24-hour complete; peak near 3 PM - meteorologically correct + comment: Plausible temperature range and noise level; peak occurring exactly + at 12 PM (vs. the more typical ~2-3 PM afternoon lag) is a minor realism + nit. code_quality: score: 10 max: 10 @@ -179,31 +221,32 @@ review: score: 3 max: 3 passed: true - comment: Flat script, no functions or classes + comment: Linear imports -> data -> plot -> save, no functions/classes. - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: np.random.seed(42) set + comment: np.random.seed(42) set before noise generation. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only os, matplotlib.pyplot, numpy; all used + comment: os, numpy, matplotlib.pyplot, FuncFormatter — all used. - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean; no fake UI + comment: Clean and appropriately complex, including a well-commented zorder + workaround for the radial ticks; no fake UI. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot-{THEME}.png + comment: Saves plot-{THEME}.png, no deprecated calls, no bbox_inches='tight'. library_mastery: score: 7 max: 10 @@ -213,22 +256,23 @@ review: score: 4 max: 5 passed: true - comment: Proper polar projection, set_theta_zero_location, set_theta_direction, - set_rlabel_position; Axes-method API + comment: Correct Axes-method usage throughout (fill_between, plot, scatter, + annotate) and proper polar-specific configuration. - id: LM-02 name: Distinctive Features score: 3 max: 5 passed: true - comment: fill_between on polar axes is distinctive; theta direction + zero-location - config is idiomatic - verdict: APPROVED + comment: Uses set_theta_zero_location/set_theta_direction/set_rlabel_position + — genuinely polar-specific API, though a fairly standard combination for + this chart type. + verdict: REJECTED impl_tags: dependencies: [] techniques: - polar-projection - - manual-ticks - annotations + - manual-ticks patterns: - data-generation - explicit-figure @@ -236,3 +280,4 @@ impl_tags: styling: - alpha-blending - edge-highlighting + - grid-styling