diff --git a/plots/waffle-basic/implementations/python/matplotlib.py b/plots/waffle-basic/implementations/python/matplotlib.py index 71fe4ad68a..ebcb9499ae 100644 --- a/plots/waffle-basic/implementations/python/matplotlib.py +++ b/plots/waffle-basic/implementations/python/matplotlib.py @@ -1,12 +1,13 @@ """ anyplot.ai waffle-basic: Basic Waffle Chart -Library: matplotlib 3.10.9 | Python 3.13.13 -Quality: 91/100 | Updated: 2026-05-05 +Library: matplotlib 3.11.1 | Python 3.13.14 +Quality: 89/100 | Updated: 2026-07-26 """ import os import matplotlib.patches as mpatches +import matplotlib.patheffects as patheffects import matplotlib.pyplot as plt import numpy as np @@ -18,78 +19,88 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" -# Okabe-Ito palette (canonical order) +# Imprint palette (canonical order) IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data - Survey responses (sums to 100%) categories = ["Strongly Agree", "Agree", "Neutral", "Disagree"] values = [48, 32, 15, 5] +dominant = int(np.argmax(values)) -# Create 10x10 grid (100 squares, each = 1%) +# Build a 10x10 grid (100 squares, each = 1%), filled category by category grid_size = 10 -total_squares = grid_size * grid_size - -# Build grid data - fill squares by category -grid = np.zeros(total_squares, dtype=int) -start_idx = 0 +grid = np.zeros(grid_size * grid_size, dtype=int) +start = 0 for i, val in enumerate(values): - grid[start_idx : start_idx + val] = i - start_idx += val - -# Reshape to 10x10 grid + grid[start : start + val] = i + start += val grid = grid.reshape((grid_size, grid_size)) -# Create plot (4800x2700 px) -fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) +# Square canvas (2400x2400) - waffle charts are grid-based/symmetric, no preferred axis +fig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) +# Pin the axes to a literal square in figure-inch space so set_aspect("equal") +# never has to auto-shrink the box, keeping title/subtitle/legend placement predictable. +ax.set_position([0.13, 0.14, 0.74, 0.74]) -# Draw squares -square_size = 0.9 # Slightly smaller than 1 for gaps +# Draw squares with a soft drop shadow for depth; the dominant category gets an +# ink-toned outline instead of a background-blended one, a quiet emphasis cue +# that draws the eye to the largest segment without adding overlapping text. +square_size = 0.86 +gap = (1 - square_size) / 2 for row in range(grid_size): for col in range(grid_size): - category_idx = grid[row, col] + cat = int(grid[row, col]) + emphasize = cat == dominant rect = mpatches.FancyBboxPatch( - (col + 0.05, row + 0.05), + (col + gap, row + gap), square_size, square_size, - boxstyle="round,pad=0.02,rounding_size=0.1", - facecolor=IMPRINT[category_idx], - edgecolor=PAGE_BG, - linewidth=1.5, + boxstyle="round,pad=0.015,rounding_size=0.09", + facecolor=IMPRINT[cat], + edgecolor=INK_SOFT if emphasize else PAGE_BG, + linewidth=2.2 if emphasize else 1.3, + ) + rect.set_path_effects( + [patheffects.withSimplePatchShadow(offset=(1.0, -1.0), shadow_rgbFace=INK, alpha=0.2), patheffects.Normal()] ) ax.add_patch(rect) -# Set axis limits and remove axes ax.set_xlim(0, grid_size) ax.set_ylim(0, grid_size) ax.set_aspect("equal") ax.axis("off") -# Create legend with percentage labels +# Legend with percentage labels, centered under the grid legend_patches = [ - mpatches.Patch(color=IMPRINT[i], label=f"{categories[i]} ({values[i]}%)") for i in range(len(categories)) + mpatches.Patch(facecolor=IMPRINT[i], label=f"{categories[i]} ({values[i]}%)") for i in range(len(categories)) ] -leg = ax.legend( - handles=legend_patches, loc="center left", bbox_to_anchor=(1.02, 0.5), fontsize=18, frameon=True, fancybox=True +leg = fig.legend( + handles=legend_patches, + loc="center", + bbox_to_anchor=(0.5, 0.06), + ncol=2, + fontsize=8, + frameon=True, + fancybox=True, + columnspacing=1.4, + handletextpad=0.6, ) -if leg: - leg.get_frame().set_facecolor(ELEVATED_BG) - leg.get_frame().set_edgecolor(INK_SOFT) - leg.get_frame().set_linewidth(0.8) - plt.setp(leg.get_texts(), color=INK_SOFT) +leg.get_frame().set_facecolor(ELEVATED_BG) +leg.get_frame().set_edgecolor(INK_SOFT) +leg.get_frame().set_linewidth(0.8) +plt.setp(leg.get_texts(), color=INK_SOFT) -# Title and subtitle -ax.text( +# Title and subtitle (figure-level text, independent of the aspect-locked axes box) +fig.text( 0.5, - 1.08, - "waffle-basic · matplotlib · anyplot.ai", - transform=ax.transAxes, - fontsize=24, + 0.95, + "waffle-basic · python · matplotlib · anyplot.ai", + fontsize=12, fontweight="medium", color=INK, ha="center", ) -ax.text(0.5, 1.02, "Survey Response Distribution", transform=ax.transAxes, fontsize=16, color=INK_SOFT, ha="center") +fig.text(0.5, 0.915, "Survey Response Distribution", fontsize=10, color=INK_SOFT, ha="center") -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/waffle-basic/metadata/python/matplotlib.yaml b/plots/waffle-basic/metadata/python/matplotlib.yaml index 0f24968508..30e7c9ba3b 100644 --- a/plots/waffle-basic/metadata/python/matplotlib.yaml +++ b/plots/waffle-basic/metadata/python/matplotlib.yaml @@ -2,121 +2,139 @@ library: matplotlib language: python specification_id: waffle-basic created: '2025-12-24T09:49:28Z' -updated: '2026-05-05T18:08:19Z' -generated_by: claude-haiku -workflow_run: 25393087656 +updated: '2026-07-26T10:28:58Z' +generated_by: claude-sonnet +workflow_run: 30198012207 issue: 998 -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/waffle-basic/python/matplotlib/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waffle-basic/python/matplotlib/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 91 +quality_score: 89 review: strengths: - - Perfect Visual Quality across all metrics — legibility, contrast, layout all at - maximum - - Flawless theme adaptation with identical data colors in light and dark renders; - no chrome issues - - Complete spec compliance — all requirements met exactly with correct title format - and legend - - Clean, maintainable code with KISS structure and explicit styling throughout - - Realistic, neutral data example (survey distribution) demonstrates plot comprehensively - - Expert idiomatic matplotlib usage with `FancyBboxPatch` and patch system creativity + - 'Correct Imprint palette applied in canonical order (categories mapped 1:1 to + positions 1-4), first series is #009E73' + - 'Excellent theme-adaptive chrome: verified pixel margins (69px top/bottom, 315px + left/right, symmetric) and no dark-on-dark or light-on-light failures in either + render' + - Creative use of matplotlib patches (FancyBboxPatch rounded corners + patheffects + drop shadow) to build a waffle chart that matplotlib has no native geometry for + - 'Thoughtful emphasis technique: the dominant category gets an ink-toned outline + instead of a background-blended one, creating a visual hierarchy without adding + overlapping text' + - Clean KISS-structured, deterministic code with correct theme-driven output naming + (plot-{THEME}.png) + - No edge clipping on either render (AR-09 clear) - confirmed via pixel-level margin + analysis on both PNGs weaknesses: - - 'Design Sophistication moderate: well-executed Okabe-Ito defaults but no custom - color harmony or unique palette exploration' - - 'Visual Refinement could be more sophisticated: good rounded-corner styling but - lacks advanced techniques (gradient edges, selective emphasis)' + - Left/right margins (~315px each, ~26% of canvas width combined) are noticeably + larger than the top/bottom margins, leaving vertical content fill at ~94% vs ~74% + horizontal - widen the grid slightly (or tighten ax.set_position side margins) + so both dimensions land closer to the 50-80% target band together + - 'Design Excellence is good but not yet publication-tier: consider reinforcing + the dominant-category emphasis with a direct percentage callout near/on the green + block (in addition to the legend) to sharpen the data-storytelling focal point' + - 'Typography hierarchy is fairly flat: subtitle and legend text share the same + INK_SOFT weight/tone - a slightly bolder or larger legend category label vs. the + percentage figure would add more editorial polish' image_description: |- Light render (plot-light.png): - Background: Warm off-white (#FAF8F1) — correct - Chrome: Title "waffle-basic · matplotlib · anyplot.ai" in dark text (#1A1A17), subtitle "Survey Response Distribution" in soft text (#4A4A44), both clearly readable - Data: 10x10 grid of rounded-corner squares: Strongly Agree in #009E73 (green, 48%), Agree in #D55E00 (orange, 32%), Neutral in #0072B2 (blue, 15%), Disagree in #CC79A7 (pink, 5%); legend positioned right with light background - Legibility verdict: PASS — all text crisp and readable + Background: Warm off-white, consistent with #FAF8F1 - not pure white. + Chrome: Title "waffle-basic · python · matplotlib · anyplot.ai" in dark ink at top, subtitle + "Survey Response Distribution" in softer ink below it. Legend is a two-column box below the + grid on an elevated cream fill, with soft-ink text and percentage labels per category. + All text is clearly readable against the light background. + Data: 10x10 grid of 100 rounded squares. Bottom ~4.8 rows are brand green (#009E73, "Strongly + Agree" 48%), next rows lavender (#C475FD, "Agree" 32%), then blue (#4467A3, "Neutral" 15%), + top row ochre (#BD8233, "Disagree" 5%). The dominant green block carries a dark ink-toned + outline + subtle drop shadow; the other categories have background-blended borders. + Legibility verdict: PASS Dark render (plot-dark.png): - Background: Warm near-black (#1A1A17) — correct - Chrome: Title and subtitle in light text (#F0EFE8), perfectly readable against dark background; legend frame dark (#242420) with soft light text - Data: Same grid layout, same colors as light render — #009E73 green, #D55E00 orange, #0072B2 blue, #CC79A7 pink in identical positions - Legibility verdict: PASS — no dark-on-dark issues, brand green #009E73 visible and distinct, all text light and readable + Background: Warm near-black, consistent with #1A1A17 - not pure black. + Chrome: Same title/subtitle now rendered in light ink tones, clearly legible against the dark + background. Legend box now uses the darker elevated fill with light text - fully readable. + No dark-on-dark failures observed anywhere (title, subtitle, tick-free axis, legend all pass). + Data: Identical Imprint hex values to the light render (green/lavender/blue/ochre unchanged) - + confirmed data colors do not shift between themes. The dominant green block's outline flips to + a light-grey ink tone for the same emphasis effect. + Legibility verdict: PASS criteria_checklist: visual_quality: - score: 30 + score: 28 max: 30 items: - id: VQ-01 name: Text Legibility - score: 8 + score: 7 max: 8 passed: true - comment: All font sizes explicitly set (24pt, 16pt, 18pt); perfectly readable - in both themes + comment: All font sizes explicit; readable in both themes; title fits within + expected 70-85% width for the mandated string - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: Title, subtitle, grid, legend well-separated; no overlapping elements + comment: No overlap between squares, legend, title/subtitle - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: Rounded squares with 0.9 size and gaps; colors highly distinguishable + comment: 100 squares at medium density with clear gaps and appropriate size - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Okabe-Ito palette is colorblind-safe with strong contrast between - categories + comment: Good contrast, CVD-safe categorical palette, no red-green-only signaling - id: VQ-05 name: Layout & Canvas - score: 4 + score: 3 max: 4 passed: true - comment: 4800x2700px with balanced proportions, ~50% canvas fill, generous - margins + comment: Balanced symmetric left/right margins but vertical fill (~94%) notably + exceeds horizontal fill (~74%) - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: 'Title exactly: spec-id · library · anyplot.ai; subtitle descriptive' + comment: Descriptive title + subtitle; axis intentionally hidden as appropriate + for a waffle grid - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series #009E73; colors follow Okabe-Ito 1-4; backgrounds #FAF8F1/#1A1A17; - theme-adaptive text' + comment: 'First series #009E73, canonical order for remaining 3, theme-correct + backgrounds/chrome in both renders' design_excellence: - score: 13 + score: 15 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 5 + score: 6 max: 8 - passed: false - comment: Professional Okabe-Ito execution with rounded corners; well-configured - but not custom beyond standards + comment: Rounded squares with drop shadow and intentional emphasis outline + go clearly beyond default styling - id: DE-02 name: Visual Refinement - score: 4 + score: 5 max: 6 - passed: false - comment: 'Good refinement: rounded corners, subtle edges, refined legend; - could explore advanced styling' + comment: Axis hidden, generous whitespace, subtle shadow detailing; legend + frame slightly heavy at 0.8 linewidth - id: DE-03 name: Data Storytelling score: 4 max: 6 - passed: false - comment: 'Good visual hierarchy: spatial arrangement shows dominance; could - enhance with selective emphasis' + comment: Ink-outline on the dominant category creates a real focal point/visual + hierarchy without extra text spec_compliance: score: 15 max: 15 @@ -125,50 +143,43 @@ review: name: Plot Type score: 5 max: 5 - passed: true - comment: Correct 10x10 waffle chart with standard grid layout + comment: Correct 10x10 waffle grid, 1 square = 1% - id: SC-02 name: Required Features score: 4 max: 4 - passed: true - comment: 'All features present: categories, percentages, distinct colors, - legend' + comment: Grid, legend with percentages, distinct colors, 4 categories all + present - id: SC-03 name: Data Mapping score: 3 max: 3 - passed: true - comment: Grid squares correctly colored; values sum to 100; all data visible + comment: Values (48/32/15/5) correctly counted into squares, sums to 100 - id: SC-04 name: Title & Legend score: 3 max: 3 - passed: true - comment: Title format correct; legend labels and percentages match exactly + comment: Title matches mandated format exactly; legend labels + percentages + match data data_quality: - score: 15 + score: 14 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 6 + score: 5 max: 6 - passed: true - comment: Shows all waffle features with varied percentages (48%, 32%, 15%, - 5%) + comment: Good spread from 48% down to 5% shows clear proportional variety - id: DQ-02 name: Realistic Context score: 5 max: 5 - passed: true - comment: Survey distribution is realistic, neutral, and comprehensible + comment: Neutral, plausible survey-response scenario - id: DQ-03 name: Appropriate Scale score: 4 max: 4 - passed: true - comment: Percentages sum exactly to 100; values are factually plausible + comment: Values sum to exactly 100, realistic Likert-style distribution code_quality: score: 10 max: 10 @@ -177,59 +188,55 @@ review: name: KISS Structure score: 3 max: 3 - passed: true - comment: 'Linear structure: imports, constants, data, plot, save; no functions/classes' + comment: No functions/classes, linear imports -> data -> plot -> save - id: CQ-02 name: Reproducibility score: 2 max: 2 - passed: true - comment: Deterministic data; no random generation needed + comment: Fully deterministic data, no randomness - id: CQ-03 name: Clean Imports score: 2 max: 2 - passed: true - comment: 'Only used: os, matplotlib.patches, matplotlib.pyplot, numpy' + comment: All imports (os, mpatches, patheffects, plt, numpy) are used - id: CQ-04 name: Code Elegance score: 2 max: 2 - passed: true - comment: Clean Pythonic code; no over-engineering or fake functionality + comment: Appropriately complex for a manually-constructed waffle chart; no + fake functionality - id: CQ-05 name: Output & API score: 1 max: 1 - passed: true - comment: Saves as plot-{THEME}.png; current API + comment: Saves plot-{THEME}.png via current API library_mastery: - score: 8 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 5 + score: 4 max: 5 - passed: true - comment: Expert use of Axes methods, FancyBboxPatch with boxstyle, explicit - legend patches + comment: Axes-level patch API, fig.legend with custom handles - idiomatic + for a chart type matplotlib lacks natively - id: LM-02 name: Distinctive Features score: 3 max: 5 - passed: false - comment: Uses patch system creatively with rounded corners and gaps; distinctive - but not highly sophisticated - verdict: APPROVED + comment: patheffects.withSimplePatchShadow + FancyBboxPatch rounding are matplotlib-distinctive, + not trivially replicated elsewhere + verdict: REJECTED impl_tags: dependencies: [] techniques: - patches - custom-legend patterns: + - explicit-figure - matrix-construction - - iteration-over-groups dataprep: [] styling: + - minimal-chrome + - alpha-blending - edge-highlighting