Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 53 additions & 23 deletions plots/waffle-basic/implementations/python/altair.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
""" anyplot.ai
"""anyplot.ai
waffle-basic: Basic Waffle Chart
Library: altair 6.1.0 | Python 3.13.13
Quality: 83/100 | Updated: 2026-05-05
Library: altair 6.2.2 | Python 3.13.14
Quality: 83/100 | Updated: 2026-07-26
"""

import os
import sys

import pandas as pd
from PIL import Image


# Fix import conflict: import from parent directory to avoid local module shadowing
Expand All @@ -26,7 +27,7 @@
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# Okabe-Ito palette (first series is #009E73)
# Imprint palette (first series is always #009E73)
IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"]

# Data - Budget allocation with 5 categories including a small one
Expand All @@ -36,19 +37,30 @@
# Build 10x10 grid (100 squares, each = 1%)
squares = []
square_idx = 0
for cat, val, color in zip(categories, values, IMPRINT, strict=True):
for cat, val in zip(categories, values, strict=True):
for _ in range(val):
row = square_idx // 10
col = square_idx % 10
squares.append({"category": cat, "row": row, "col": col, "color": color})
squares.append({"category": cat, "row": row, "col": col})
square_idx += 1

df = pd.DataFrame(squares)

# Create waffle chart
# Mandated title format, plus a data-driven subtitle for storytelling
title_text = "waffle-basic · python · altair · anyplot.ai"
subtitle_text = f"{categories[0]} commands {values[0]}% of the budget — the single largest allocation"
title_fontsize = max(11, round(16 * min(1.0, 67 / len(title_text))))

# Click a legend entry to isolate its share of the grid (idiomatic altair
# interactivity — dims the rest; only visible in the exported HTML, the
# static PNG keeps the default "nothing selected yet" full-opacity state).
legend_click = alt.selection_point(fields=["category"], bind="legend")

label_map = ", ".join(f"'{cat}': '{val}'" for cat, val in zip(categories, values, strict=True))

chart = (
alt.Chart(df)
.mark_rect(stroke="white", strokeWidth=2, cornerRadius=4)
.mark_rect(stroke=ELEVATED_BG, strokeWidth=3, cornerRadius=5)
.encode(
x=alt.X("col:O", axis=None),
y=alt.Y("row:O", axis=None, sort="descending"),
Expand All @@ -57,33 +69,51 @@
scale=alt.Scale(domain=categories, range=IMPRINT),
legend=alt.Legend(
title="Category",
titleFontSize=22,
labelFontSize=18,
symbolSize=300,
titleFontSize=14,
labelFontSize=12,
symbolSize=220,
orient="right",
labelExpr="datum.label + ' (' + {"
+ ", ".join([f"'{cat}': '{val}%'" for cat, val in zip(categories, values, strict=True)])
+ "}[datum.label] + ')'",
labelExpr=f"datum.label + ' (' + {{{label_map}}}[datum.label] + '%)'",
),
),
tooltip=["category:N"],
opacity=alt.condition(legend_click, alt.value(1.0), alt.value(0.25)),
tooltip=[alt.Tooltip("category:N", title="Category")],
)
.add_params(legend_click)
.properties(
width=1600,
height=900,
width=430,
height=430,
background=PAGE_BG,
title=alt.Title(
"Budget Allocation · waffle-basic · altair · anyplot.ai", fontSize=28, anchor="middle", color=INK
title_text,
subtitle=subtitle_text,
fontSize=title_fontsize,
subtitleFontSize=round(title_fontsize * 0.55),
subtitleColor=INK_SOFT,
anchor="middle",
color=INK,
),
)
.configure_view(fill=PAGE_BG, stroke=INK_SOFT)
.configure_axis(
domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK
)
.configure_view(fill=PAGE_BG, stroke=None)
.configure_title(color=INK)
.configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)
)

# Save outputs
chart.save(f"plot-{THEME}.png", scale_factor=3.0)
chart.save(f"plot-{THEME}.png", scale_factor=4.0)
chart.save(f"plot-{THEME}.html")

# PAD-only to the canonical square target (2400x2400) — never crop, cropping
# would clip the title/subtitle/legend and trigger the AR-09 edge-clip check.
TW, TH = 2400, 2400
_img = Image.open(f"plot-{THEME}.png").convert("RGB")
_w, _h = _img.size
if _w > TW or _h > TH:
raise SystemExit(
f"altair vl-convert produced {_w}x{_h}, exceeds target {TW}x{TH}. "
f"Shrink chart .properties(width=, height=) values and re-render."
)
if _w < TW or _h < TH:
_canvas = Image.new("RGB", (TW, TH), PAGE_BG)
_canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))
_canvas.save(f"plot-{THEME}.png")
231 changes: 11 additions & 220 deletions plots/waffle-basic/metadata/python/altair.yaml
Original file line number Diff line number Diff line change
@@ -1,230 +1,21 @@
# Per-library metadata for altair implementation of waffle-basic
# Auto-generated by impl-generate.yml

library: altair
language: python
specification_id: waffle-basic
created: '2025-12-24T09:49:39Z'
updated: '2026-05-05T18:13:59Z'
generated_by: claude-haiku
workflow_run: 25393338380
updated: '2026-07-26T10:31:41Z'
generated_by: claude-sonnet
workflow_run: 30198249484
issue: 998
python_version: 3.13.13
library_version: 6.1.0
language_version: 3.13.14
library_version: 6.2.2
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waffle-basic/python/altair/plot-light.png
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waffle-basic/python/altair/plot-dark.png
preview_html_light: https://storage.googleapis.com/anyplot-images/plots/waffle-basic/python/altair/plot-light.html
preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/waffle-basic/python/altair/plot-dark.html
quality_score: 83
quality_score: null
review:
strengths:
- 'Perfect Visual Quality (29/30): Legible text with explicit sizing, no overlap,
all elements visible; flawless palette compliance and theme handling'
- 'Flawless Data Quality (15/15): Realistic, neutral budget allocation context with
correct proportions and full feature coverage'
- 'Clean Code (10/10): Simple structure, deterministic, proper output format'
- 'Proper Theme Adaptation: Both light and dark renders work perfectly; data colors
identical, only chrome adapts'
- 'Correct Waffle Implementation: 10x10 grid with proper proportions, clear square
definition via white borders'
weaknesses:
- 'Title Format (SC-04): Includes Budget Allocation prefix; should be just waffle-basic
· altair · anyplot.ai per spec requirement'
- 'Generic Design Excellence (10/20): Implementation is correct but lacks distinctive
aesthetic choices or visual sophistication'
- 'Limited Storytelling (DE-03): Displays data clearly but doesn''t create special
visual emphasis or hierarchy beyond the inherent proportional structure'
- 'No Distinctive Features (LM-02): Doesn''t use altair-specific techniques like
interactive selection or cross-filtering'
image_description: |-
Light render (plot-light.png):
Background: Warm off-white (#FAF8F1) - correct
Chrome: Title "Budget Allocation · waffle-basic · altair · anyplot.ai" visible in dark text; legend titled "Category" with category names and percentages; all text clearly readable
Data: Five categories shown as colored squares (100 total): Engineering green #009E73 (40 squares), Marketing orange #D55E00 (28), Operations blue #0072B2 (18), Design pink #CC79A7 (10), Legal yellow #E69F00 (4); white borders separate each square clearly; first series is correct #009E73
Legibility verdict: PASS - All text readable against light background; no light-on-light issues; square borders provide excellent definition

Dark render (plot-dark.png):
Background: Warm near-black (#1A1A17) - correct
Chrome: Title is light-colored; legend box has dark background with light text; all labels are light-colored; no dark-on-dark text failures
Data: Identical colors to light render - green #009E73, orange #D55E00, blue #0072B2, pink #CC79A7, yellow #E69F00 all present in same positions; white borders maintain visibility
Legibility verdict: PASS - All text clearly visible on dark background; brand green #009E73 clearly readable; no contrast failures; proper theme-adaptive chrome
criteria_checklist:
visual_quality:
score: 29
max: 30
items:
- id: VQ-01
name: Text Legibility
score: 8
max: 8
passed: true
comment: All text explicitly sized and readable in both themes
- id: VQ-02
name: No Overlap
score: 6
max: 6
passed: true
comment: Clean layout, legend positioned right, no text overlaps
- id: VQ-03
name: Element Visibility
score: 6
max: 6
passed: true
comment: All 100 waffle squares clearly visible with white borders
- id: VQ-04
name: Color Accessibility
score: 2
max: 2
passed: true
comment: Okabe-Ito palette is colorblind-safe with good contrast
- id: VQ-05
name: Layout & Canvas
score: 4
max: 4
passed: true
comment: Perfect layout with balanced margins, plot fills 60-70% of canvas
- id: VQ-06
name: Axis Labels & Title
score: 1
max: 2
passed: false
comment: Title present but format includes Budget Allocation when spec requires
waffle-basic format
- id: VQ-07
name: Palette Compliance
score: 2
max: 2
passed: true
comment: 'First series is #009E73, colors follow Okabe-Ito order, backgrounds
and chrome are theme-correct'
design_excellence:
score: 10
max: 20
items:
- id: DE-01
name: Aesthetic Sophistication
score: 4
max: 8
passed: false
comment: Well-configured defaults but lacks exceptional design or custom touches
- id: DE-02
name: Visual Refinement
score: 4
max: 6
passed: true
comment: Good refinement with white borders, legend styling, generous margins
- id: DE-03
name: Data Storytelling
score: 2
max: 6
passed: false
comment: Data displayed clearly but no special visual hierarchy or emphasis
spec_compliance:
score: 14
max: 15
items:
- id: SC-01
name: Plot Type
score: 5
max: 5
passed: true
comment: Correct waffle chart with 10x10 grid
- id: SC-02
name: Required Features
score: 4
max: 4
passed: true
comment: 'All features present: proportions, colors, legend with percentages'
- id: SC-03
name: Data Mapping
score: 3
max: 3
passed: true
comment: Categories to colors, values to squares (100 total)
- id: SC-04
name: Title & Legend
score: 2
max: 3
passed: false
comment: Legend correct but title format non-compliant
data_quality:
score: 15
max: 15
items:
- id: DQ-01
name: Feature Coverage
score: 6
max: 6
passed: true
comment: 'Shows all aspects: proportions, categories, colors, value range'
- id: DQ-02
name: Realistic Context
score: 5
max: 5
passed: true
comment: Budget allocation is real, realistic, neutral scenario
- id: DQ-03
name: Appropriate Scale
score: 4
max: 4
passed: true
comment: Percentages sum to 100, factually correct proportions
code_quality:
score: 10
max: 10
items:
- id: CQ-01
name: KISS Structure
score: 3
max: 3
passed: true
comment: Linear flow, no functions/classes, no over-engineering
- id: CQ-02
name: Reproducibility
score: 2
max: 2
passed: true
comment: Fixed data, deterministic output
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
comment: Only necessary imports, all used
- id: CQ-04
name: Code Elegance
score: 2
max: 2
passed: true
comment: Clean, Pythonic, no fake UI
- id: CQ-05
name: Output & API
score: 1
max: 1
passed: true
comment: 'Correct naming: plot-{THEME}.png and .html'
library_mastery:
score: 5
max: 10
items:
- id: LM-01
name: Idiomatic Usage
score: 4
max: 5
passed: true
comment: Good altair patterns with Chart.mark_rect(), proper encodings, theme-aware
config
- id: LM-02
name: Distinctive Features
score: 1
max: 5
passed: false
comment: Uses basic features, doesn't leverage interactive or distinctive
altair capabilities
verdict: APPROVED
impl_tags:
dependencies: []
techniques:
- custom-legend
patterns:
- data-generation
dataprep: []
styling:
- publication-ready
strengths: []
weaknesses: []