From 46159f4edcf3984c48af7245c338db4cf7d39470 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 May 2026 08:46:38 +0000 Subject: [PATCH 1/4] chore(letsplot): add metadata for bar-3d-categorical --- .../implementations/python/letsplot.py | 74 +++++++++++++++++++ .../metadata/python/letsplot.yaml | 21 ++++++ 2 files changed, 95 insertions(+) create mode 100644 plots/bar-3d-categorical/implementations/python/letsplot.py create mode 100644 plots/bar-3d-categorical/metadata/python/letsplot.yaml diff --git a/plots/bar-3d-categorical/implementations/python/letsplot.py b/plots/bar-3d-categorical/implementations/python/letsplot.py new file mode 100644 index 0000000000..a23d09b37f --- /dev/null +++ b/plots/bar-3d-categorical/implementations/python/letsplot.py @@ -0,0 +1,74 @@ +"""anyplot.ai +bar-3d-categorical: 3D Bar Chart for Categorical Comparison +Library: letsplot | Python 3.13 +Quality: pending | Created: 2026-05-15 +""" + +import os + +import numpy as np +import pandas as pd +from lets_plot import * + + +LetsPlot.setup_html() + +# Theme tokens +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" +GRID_COLOR = "#D0CFC8" if THEME == "light" else "#333330" + +OKABE_ITO = {"High School": "#009E73", "Bachelor's": "#D55E00", "Graduate": "#0072B2"} + +# Data — job satisfaction scores by age group and education level +np.random.seed(42) +age_groups = ["18–34", "35–49", "50–64", "65+"] +edu_levels = ["High School", "Bachelor's", "Graduate"] + +base = {"High School": [6.1, 5.7, 5.4, 5.8], "Bachelor's": [7.0, 7.3, 6.8, 7.1], "Graduate": [7.7, 8.0, 7.5, 7.8]} + +rows = [] +for edu, scores in base.items(): + for age, score in zip(age_groups, scores, strict=False): + noise = np.random.uniform(-0.2, 0.2) + val = round(score + noise, 1) + rows.append({"Age Group": age, "Education": edu, "Satisfaction": val, "Label": f"{val:.1f}"}) + +df = pd.DataFrame(rows) + +# Plot +plot = ( + ggplot(df, aes(x="Age Group", y="Satisfaction", fill="Education")) + + geom_bar(stat="identity", position="dodge", width=0.75, color=PAGE_BG, size=0.4) + + geom_text(aes(label="Label"), position=position_dodge(0.75), vjust=-0.5, size=11, color=INK_SOFT) + + scale_fill_manual(values=OKABE_ITO) + + scale_x_discrete(limits=age_groups) + + scale_y_continuous(limits=[0, 10.5]) + + labs( + x="Age Group", + y="Satisfaction Score (0–10)", + title="bar-3d-categorical · letsplot · anyplot.ai", + fill="Education", + ) + + theme_classic() + + theme( + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), + panel_background=element_rect(fill=PAGE_BG), + panel_grid_major_y=element_line(color=GRID_COLOR, size=0.5), + axis_title=element_text(color=INK, size=20), + axis_text=element_text(color=INK_SOFT, size=16), + axis_line=element_line(color=INK_SOFT), + plot_title=element_text(color=INK, size=24), + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), + legend_text=element_text(color=INK_SOFT, size=16), + legend_title=element_text(color=INK, size=16), + ) + + ggsize(1600, 900) +) + +# Save +ggsave(plot, f"plot-{THEME}.png", path=".", scale=3) +ggsave(plot, f"plot-{THEME}.html", path=".") diff --git a/plots/bar-3d-categorical/metadata/python/letsplot.yaml b/plots/bar-3d-categorical/metadata/python/letsplot.yaml new file mode 100644 index 0000000000..4bdaac3e6a --- /dev/null +++ b/plots/bar-3d-categorical/metadata/python/letsplot.yaml @@ -0,0 +1,21 @@ +# Per-library metadata for letsplot implementation of bar-3d-categorical +# Auto-generated by impl-generate.yml + +library: letsplot +language: python +specification_id: bar-3d-categorical +created: '2026-05-15T08:46:38Z' +updated: '2026-05-15T08:46:38Z' +generated_by: claude-sonnet +workflow_run: 25908551462 +issue: 5248 +python_version: 3.13.13 +library_version: 4.9.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-dark.html +quality_score: null +review: + strengths: [] + weaknesses: [] From e5c6a17a36bb1977f58ae17fe13d433ba90431b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 May 2026 08:53:46 +0000 Subject: [PATCH 2/4] chore(letsplot): update quality score 40 and review feedback for bar-3d-categorical --- .../implementations/python/letsplot.py | 6 +- .../metadata/python/letsplot.yaml | 258 +++++++++++++++++- 2 files changed, 254 insertions(+), 10 deletions(-) diff --git a/plots/bar-3d-categorical/implementations/python/letsplot.py b/plots/bar-3d-categorical/implementations/python/letsplot.py index a23d09b37f..09b4a587aa 100644 --- a/plots/bar-3d-categorical/implementations/python/letsplot.py +++ b/plots/bar-3d-categorical/implementations/python/letsplot.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai bar-3d-categorical: 3D Bar Chart for Categorical Comparison -Library: letsplot | Python 3.13 -Quality: pending | Created: 2026-05-15 +Library: letsplot 4.9.0 | Python 3.13.13 +Quality: 40/100 | Created: 2026-05-15 """ import os diff --git a/plots/bar-3d-categorical/metadata/python/letsplot.yaml b/plots/bar-3d-categorical/metadata/python/letsplot.yaml index 4bdaac3e6a..35ad2f3903 100644 --- a/plots/bar-3d-categorical/metadata/python/letsplot.yaml +++ b/plots/bar-3d-categorical/metadata/python/letsplot.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for letsplot implementation of bar-3d-categorical -# Auto-generated by impl-generate.yml - library: letsplot language: python specification_id: bar-3d-categorical created: '2026-05-15T08:46:38Z' -updated: '2026-05-15T08:46:38Z' +updated: '2026-05-15T08:53:46Z' generated_by: claude-sonnet workflow_run: 25908551462 issue: 5248 @@ -15,7 +12,254 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-3d-ca preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-dark.html -quality_score: null +quality_score: 40 review: - strengths: [] - weaknesses: [] + strengths: + - 'Perfect visual quality: explicit font sizing (title=24pt, axes=20pt, ticks=16pt), + clean adaptive theming in both light and dark renders' + - 'Correct Okabe-Ito palette in canonical order (#009E73 first, #D55E00 second, + #0072B2 third) with theme-correct backgrounds (#FAF8F1 light / #1A1A17 dark)' + - Code is clean, reproducible (np.random.seed(42)), and idiomatic; theme tokens + fully applied to all chrome elements + - Realistic, neutral dataset (job satisfaction by age group x education level) with + appropriate scale and plausible value variation + - Title format 'bar-3d-categorical · letsplot · anyplot.ai' is correct; legend labels + match data + weaknesses: + - 'CRITICAL SC-01=0: Wrong chart type — spec requires a 3D bar chart with two categorical + spatial axes and a vertical height axis; letsplot cannot render 3D projections + (analogous to AR-06 not-feasible); implementation fell back to a 2D grouped bar + chart' + - 'SC-02 LOW: 3D-specific features entirely absent — no base-plane grid, no depth/perspective, + no two positional categorical axes, no viewing angle; only minor spec features + (value labels, bar spacing) are present' + - 'DE-01/DE-03: No visual emphasis or storytelling; the ''Graduate satisfaction + is consistently highest'' pattern is discoverable but never visually emphasized + through color contrast, size variation, or annotation' + - 'DE-02: Axis tick marks and legend border box could be removed for a cleaner look' + - 'LM-02: No distinctively letsplot-specific features used — basic ggplot2 grammar + patterns with no HTML interactivity leverage, no tooltips, no letsplot-exclusive + geoms' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct theme surface, not pure white + Chrome: Title "bar-3d-categorical · letsplot · anyplot.ai" in dark ink, clearly readable. X-axis label "Age Group" and Y-axis label "Satisfaction Score (0-10)" in dark ink, readable. Tick labels (18-34, 35-49, 50-64, 65+; 0-11 on Y) in muted dark ink (#4A4A44), readable. Value labels above bars (6.0, 6.9, 7.7, etc.) in muted ink, readable. Legend title "Education" and labels (High School, Bachelor's, Graduate) readable. + Data: Three bars per age group in green (#009E73 / High School), orange (#D55E00 / Bachelor's), blue (#0072B2 / Graduate). First series is #009E73 as required. Bars clearly visible, well-separated, with subtle PAGE_BG edge color. + Legibility verdict: PASS — all text fully readable against warm off-white background + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct dark theme surface, not pure black + Chrome: Title in off-white (#F0EFE8), clearly readable against dark background. Axis labels in off-white, readable. Tick labels in light muted tone (#B8B7B0), readable. Value labels above bars in light muted tone, readable. Legend text in light muted tone, readable. No dark-on-dark failures observed — all text is light-colored and legible. + Data: Bar colors are IDENTICAL to light render — green (#009E73), orange (#D55E00), blue (#0072B2). Only chrome elements (background, text, grid, legend frame) have flipped. + Legibility verdict: PASS — all text fully readable against near-black background; no dark-on-dark failures + + CRITICAL NOTE (both renders): The chart is a 2D grouped bar chart, NOT a 3D bar chart. The spec requires bars rising from a 2D categorical grid with two categorical spatial axes and a vertical height axis. letsplot cannot render true 3D projections — this is an AR-06 not-feasible situation. SC-01=0, score capped at 40/100. + criteria_checklist: + visual_quality: + score: 30 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 8 + max: 8 + passed: true + comment: 'All font sizes explicitly set: title=24pt, axis_title=20pt, axis_text=16pt, + legend=16pt; fully readable in both themes' + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: Value labels, tick labels, and legend are well-separated; position_dodge(0.75) + matches bar width correctly + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Bars clearly visible with subtle PAGE_BG edge color for definition; + good sizing + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Okabe-Ito palette is CVD-safe; three bars easily distinguishable + by both hue and luminance + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Plot fills canvas well; y-axis headroom for value labels is intentional; + balanced margins + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Y-axis includes units '(0-10)'; title correct format + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: '#009E73 first series, #D55E00 second, #0072B2 third; correct Okabe-Ito + order; backgrounds #FAF8F1/#1A1A17; chrome fully adaptive' + design_excellence: + score: 10 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 4 + max: 8 + passed: false + comment: 'Well-configured library default: theme_classic + adaptive chrome + + Okabe-Ito + value labels. Clean but not exceptional — no visual hierarchy + lifts it above ''configured ggplot''' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: false + comment: theme_classic removes top/right spines; custom GRID_COLOR for subtle + y-only grid; all chrome colors set. Tick marks and legend border could be + removed for further polish + - id: DE-03 + name: Data Storytelling + score: 2 + max: 6 + passed: false + comment: Data displayed clearly but no visual emphasis highlights the 'Graduate + consistently highest' pattern. Data shown without interpretation. + spec_compliance: + score: 6 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 0 + max: 5 + passed: false + comment: WRONG CHART TYPE. Spec requires 3D bar chart with two categorical + spatial axes and Z-height axis. Implementation is a 2D grouped bar chart. + letsplot cannot do 3D (analogous to AR-06 not-feasible). + - id: SC-02 + name: Required Features + score: 1 + max: 4 + passed: false + comment: 'Only minor spec features present: value labels on top ✓, bar spacing + ✓. Missing: 3D base-plane grid, two positional categorical axes, depth/perspective, + viewing angle, color encoding of value magnitude' + - id: SC-03 + name: Data Mapping + score: 2 + max: 3 + passed: false + comment: 'Within 2D frame: Age Group→X and Satisfaction→Y correct. Education + encoded via fill/dodge (only 2D option). Second categorical axis is not + a spatial axis as spec requires.' + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title 'bar-3d-categorical · letsplot · anyplot.ai' correct format; + legend labels match data categories + data_quality: + score: 13 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 4 + max: 6 + passed: false + comment: Shows variation across both dimensions; meaningful differences present. + However 3D spec features (grid of 9-100 bars on 2D base plane) not represented. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: 'Job satisfaction by age group and education level: realistic, comprehensible, + neutral scenario' + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 0-10 satisfaction scale with values 5.5-8.1 appropriate; higher education + correlating with higher satisfaction is factually plausible + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Imports → tokens → data → plot → save; no functions or classes + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: np.random.seed(42) set + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: os, numpy, pandas, lets_plot — all used + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, Pythonic; dict-mapped scale_fill_manual is idiomatic; position_dodge(0.75) + matches bar width correctly + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot-{THEME}.png and plot-{THEME}.html; current API with scale=3 + for PNG + library_mastery: + score: 6 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: false + comment: Correct ggplot2 grammar with layered geoms, named-dict scale_fill_manual, + theme_classic() + layered theme() override. Good; minor opportunity to use + scale_y_continuous(expand=...) for tighter layout. + - id: LM-02 + name: Distinctive Features + score: 2 + max: 5 + passed: false + comment: position_dodge with matching geom_text position is a standard ggplot2 + pattern. No distinctively letsplot-specific features (no HTML tooltip leverage, + no letsplot-exclusive geoms). + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - annotations + - layer-composition + patterns: + - data-generation + - iteration-over-groups + dataprep: [] + styling: + - edge-highlighting + - grid-styling From e4c5316812289bafb04d0b59c8d94c39b95310d3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 May 2026 09:20:31 +0000 Subject: [PATCH 3/4] fix(letsplot): address review feedback for bar-3d-categorical Attempt 1/3 - fixes based on AI review --- .../implementations/python/letsplot.py | 206 ++++++++++++++---- 1 file changed, 167 insertions(+), 39 deletions(-) diff --git a/plots/bar-3d-categorical/implementations/python/letsplot.py b/plots/bar-3d-categorical/implementations/python/letsplot.py index 09b4a587aa..2f237f5a7b 100644 --- a/plots/bar-3d-categorical/implementations/python/letsplot.py +++ b/plots/bar-3d-categorical/implementations/python/letsplot.py @@ -1,7 +1,6 @@ -""" anyplot.ai +"""anyplot.ai bar-3d-categorical: 3D Bar Chart for Categorical Comparison Library: letsplot 4.9.0 | Python 3.13.13 -Quality: 40/100 | Created: 2026-05-15 """ import os @@ -13,62 +12,191 @@ LetsPlot.setup_html() -# Theme tokens 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" -GRID_COLOR = "#D0CFC8" if THEME == "light" else "#333330" +GRID_COLOR = "#C8C7C0" if THEME == "light" else "#3A3A35" + +OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] + + +def shade(col, f): + """Adjust hex color brightness by factor f.""" + r = min(255, max(0, round(int(col[1:3], 16) * f))) + g = min(255, max(0, round(int(col[3:5], 16) * f))) + b = min(255, max(0, round(int(col[5:7], 16) * f))) + return f"#{r:02X}{g:02X}{b:02X}" -OKABE_ITO = {"High School": "#009E73", "Bachelor's": "#D55E00", "Graduate": "#0072B2"} -# Data — job satisfaction scores by age group and education level np.random.seed(42) -age_groups = ["18–34", "35–49", "50–64", "65+"] -edu_levels = ["High School", "Bachelor's", "Graduate"] +X_CATS = ["18–34", "35–49", "50–64", "65+"] +Y_CATS = ["High School", "Bachelor's", "Graduate"] # index 0 = furthest back + +BASE = {"High School": [6.1, 5.7, 5.4, 5.8], "Bachelor's": [7.0, 7.3, 6.8, 7.1], "Graduate": [7.7, 8.0, 7.5, 7.8]} + +NX = len(X_CATS) +NY = len(Y_CATS) + +vals = { + (ai, ei): round(BASE[edu][ai] + np.random.uniform(-0.15, 0.15), 1) + for ei, edu in enumerate(Y_CATS) + for ai in range(NX) +} + +# Cabinet projection: x3=age-group axis, y3=depth axis, z3=height +BW = 0.78 # bar width (x3 units; gap = 1 - BW) +BD = 0.65 # bar depth (y3 units) +DX = 0.52 # x2 shift per unit of y3 depth +DZ = 0.32 # z2 (upward) shift per unit of y3 depth + + +def prj(x3, y3, z3): + return (x3 + y3 * DX, z3 + y3 * DZ) + + +# ── Base-plane grid ────────────────────────────────────────────────── +seg_rows = [] + + +def seg(x1, y1, x2, y2): + seg_rows.append({"x": x1, "y": y1, "xend": x2, "yend": y2}) + + +# Lines along the depth direction (x edges of the grid) +for xi in range(NX + 1): + p0 = prj(xi, 0, 0) + p1 = prj(xi, NY - 1 + BD, 0) + seg(p0[0], p0[1], p1[0], p1[1]) + +# Lines along the x direction (depth edges of the grid) +for yi_f in [0.0, 1.0, 2.0, NY - 1 + BD]: + p0 = prj(0, yi_f, 0) + p1 = prj(NX, yi_f, 0) + seg(p0[0], p0[1], p1[0], p1[1]) + +df_seg = pd.DataFrame(seg_rows) + +# ── Z-axis (left-front vertical) ──────────────────────────────────── +z_segs = [] +ztick_rows = [] +Z_TICKS = [0, 2, 4, 6, 8, 10] + +ax0 = prj(0, 0, 0) +ax1 = prj(0, 0, 10) +z_segs.append({"x": ax0[0], "y": ax0[1], "xend": ax1[0], "yend": ax1[1]}) + +for zt in Z_TICKS: + pt = prj(0, 0, zt) + z_segs.append({"x": pt[0], "y": pt[1], "xend": pt[0] - 0.18, "yend": pt[1]}) + ztick_rows.append({"x": pt[0] - 0.25, "y": pt[1], "label": str(zt)}) + +df_zsegs = pd.DataFrame(z_segs) +df_zticks = pd.DataFrame(ztick_rows) + +# ── Category axis labels ───────────────────────────────────────────── +x_lbl = [] +for ai, age in enumerate(X_CATS): + pt = prj(ai + BW / 2, 0, 0) + x_lbl.append({"x": pt[0], "y": pt[1] - 0.55, "label": age}) +df_xlbl = pd.DataFrame(x_lbl) + +# ── Bar polygons (painter's order: back → front) ───────────────────── +poly_rows = [] +gc = [0] + + +def face(pts, fill_col): + gid = str(gc[0]) + gc[0] += 1 + for px, py in pts: + poly_rows.append({"x": px, "y": py, "g": gid, "fill": fill_col}) + + +for ei in range(NY - 1, -1, -1): # draw back→front; ei=0 (High School) drawn last = in front + c0 = OKABE_ITO[ei] + c_front = c0 + c_right = shade(c0, 0.60) + c_top = shade(c0, 1.32) + + for ai in range(NX): + h = vals[(ai, ei)] + x0, x1 = ai, ai + BW + y0, y1 = ei, ei + BD + + C = { + k: prj(*v) + for k, v in { + "bfl": (x0, y0, 0), + "bfr": (x1, y0, 0), + "bbl": (x0, y1, 0), + "bbr": (x1, y1, 0), + "tfl": (x0, y0, h), + "tfr": (x1, y0, h), + "tbl": (x0, y1, h), + "tbr": (x1, y1, h), + }.items() + } + + face([C["bfl"], C["bfr"], C["tfr"], C["tfl"]], c_front) # front + face([C["bfr"], C["bbr"], C["tbr"], C["tfr"]], c_right) # right + face([C["tfl"], C["tfr"], C["tbr"], C["tbl"]], c_top) # top + +df_poly = pd.DataFrame(poly_rows) + +# ── Value labels (centre-top of each bar) ─────────────────────────── +val_rows = [] +for ei in range(NY): + for ai in range(NX): + h = vals[(ai, ei)] + pt = prj(ai + BW / 2, ei + BD / 2, h) + val_rows.append({"x": pt[0], "y": pt[1] + 0.28, "label": f"{h:.1f}"}) +df_vals = pd.DataFrame(val_rows) -base = {"High School": [6.1, 5.7, 5.4, 5.8], "Bachelor's": [7.0, 7.3, 6.8, 7.1], "Graduate": [7.7, 8.0, 7.5, 7.8]} +# ── Manual legend inside chart (upper-left area, above bars) ───────── +LEG_X = 0.15 +LEG_Y0 = 10.8 +LEG_DY = 0.65 +leg_title = [{"x": LEG_X, "y": LEG_Y0 + 0.4, "label": "Education Level"}] +leg_rect = [] +leg_text = [] +for i, edu in enumerate(Y_CATS): + lx, ly = LEG_X, LEG_Y0 - i * LEG_DY + leg_rect.append({"xmin": lx, "xmax": lx + 0.32, "ymin": ly - 0.20, "ymax": ly + 0.20, "fill": OKABE_ITO[i]}) + leg_text.append({"x": lx + 0.42, "y": ly, "label": edu}) -rows = [] -for edu, scores in base.items(): - for age, score in zip(age_groups, scores, strict=False): - noise = np.random.uniform(-0.2, 0.2) - val = round(score + noise, 1) - rows.append({"Age Group": age, "Education": edu, "Satisfaction": val, "Label": f"{val:.1f}"}) +df_ltitle = pd.DataFrame(leg_title) +df_lrect = pd.DataFrame(leg_rect) +df_ltxt = pd.DataFrame(leg_text) -df = pd.DataFrame(rows) +# unique fill → itself (scale_fill_manual with identity mapping) +fill_vals = {c: c for c in df_poly["fill"].unique()} -# Plot +# ── Assemble plot ───────────────────────────────────────────────────── plot = ( - ggplot(df, aes(x="Age Group", y="Satisfaction", fill="Education")) - + geom_bar(stat="identity", position="dodge", width=0.75, color=PAGE_BG, size=0.4) - + geom_text(aes(label="Label"), position=position_dodge(0.75), vjust=-0.5, size=11, color=INK_SOFT) - + scale_fill_manual(values=OKABE_ITO) - + scale_x_discrete(limits=age_groups) - + scale_y_continuous(limits=[0, 10.5]) - + labs( - x="Age Group", - y="Satisfaction Score (0–10)", - title="bar-3d-categorical · letsplot · anyplot.ai", - fill="Education", + ggplot() + + geom_segment(aes(x="x", y="y", xend="xend", yend="yend"), data=df_seg, color=GRID_COLOR, size=0.8) + + geom_segment(aes(x="x", y="y", xend="xend", yend="yend"), data=df_zsegs, color=INK_SOFT, size=0.7) + + geom_polygon(aes(x="x", y="y", group="g", fill="fill"), data=df_poly, color=PAGE_BG, size=0.3) + + scale_fill_manual(values=fill_vals, guide="none") + + geom_text(aes(x="x", y="y", label="label"), data=df_zticks, size=11, color=INK_SOFT, hjust=1) + + geom_text(aes(x="x", y="y", label="label"), data=df_xlbl, size=12, color=INK_SOFT, vjust=1) + + geom_text(aes(x="x", y="y", label="label"), data=df_vals, size=10, color=INK) + + geom_text(aes(x="x", y="y", label="label"), data=df_ltitle, size=14, color=INK, hjust=0, fontface="bold") + + geom_rect( + aes(xmin="xmin", xmax="xmax", ymin="ymin", ymax="ymax", fill="fill"), data=df_lrect, color=INK_SOFT, size=0.3 ) - + theme_classic() + + geom_text(aes(x="x", y="y", label="label"), data=df_ltxt, size=13, color=INK_SOFT, hjust=0) + + labs(title="bar-3d-categorical · letsplot · anyplot.ai") + + theme_void() + theme( plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), - panel_background=element_rect(fill=PAGE_BG), - panel_grid_major_y=element_line(color=GRID_COLOR, size=0.5), - axis_title=element_text(color=INK, size=20), - axis_text=element_text(color=INK_SOFT, size=16), - axis_line=element_line(color=INK_SOFT), - plot_title=element_text(color=INK, size=24), - legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), - legend_text=element_text(color=INK_SOFT, size=16), - legend_title=element_text(color=INK, size=16), + plot_title=element_text(color=INK, size=24, hjust=0.5), + plot_margin=[30, 40, 30, 40], ) + ggsize(1600, 900) ) -# Save ggsave(plot, f"plot-{THEME}.png", path=".", scale=3) ggsave(plot, f"plot-{THEME}.html", path=".") From b72c55ef73f05d5caca3d574774fa510b252b636 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 May 2026 09:27:02 +0000 Subject: [PATCH 4/4] chore(letsplot): update quality score 83 and review feedback for bar-3d-categorical --- .../implementations/python/letsplot.py | 3 +- .../metadata/python/letsplot.yaml | 226 +++++++++--------- 2 files changed, 110 insertions(+), 119 deletions(-) diff --git a/plots/bar-3d-categorical/implementations/python/letsplot.py b/plots/bar-3d-categorical/implementations/python/letsplot.py index 2f237f5a7b..656e2593d1 100644 --- a/plots/bar-3d-categorical/implementations/python/letsplot.py +++ b/plots/bar-3d-categorical/implementations/python/letsplot.py @@ -1,6 +1,7 @@ -"""anyplot.ai +""" anyplot.ai bar-3d-categorical: 3D Bar Chart for Categorical Comparison Library: letsplot 4.9.0 | Python 3.13.13 +Quality: 83/100 | Created: 2026-05-15 """ import os diff --git a/plots/bar-3d-categorical/metadata/python/letsplot.yaml b/plots/bar-3d-categorical/metadata/python/letsplot.yaml index 35ad2f3903..8e6b041446 100644 --- a/plots/bar-3d-categorical/metadata/python/letsplot.yaml +++ b/plots/bar-3d-categorical/metadata/python/letsplot.yaml @@ -2,7 +2,7 @@ library: letsplot language: python specification_id: bar-3d-categorical created: '2026-05-15T08:46:38Z' -updated: '2026-05-15T08:53:46Z' +updated: '2026-05-15T09:27:01Z' generated_by: claude-sonnet workflow_run: 25908551462 issue: 5248 @@ -12,188 +12,176 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-3d-ca preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/bar-3d-categorical/python/letsplot/plot-dark.html -quality_score: 40 +quality_score: 83 review: strengths: - - 'Perfect visual quality: explicit font sizing (title=24pt, axes=20pt, ticks=16pt), - clean adaptive theming in both light and dark renders' - - 'Correct Okabe-Ito palette in canonical order (#009E73 first, #D55E00 second, - #0072B2 third) with theme-correct backgrounds (#FAF8F1 light / #1A1A17 dark)' - - Code is clean, reproducible (np.random.seed(42)), and idiomatic; theme tokens - fully applied to all chrome elements - - Realistic, neutral dataset (job satisfaction by age group x education level) with - appropriate scale and plausible value variation - - Title format 'bar-3d-categorical · letsplot · anyplot.ai' is correct; legend labels - match data + - Sophisticated cabinet projection with per-face shading (front/right/top) creates + convincing 3D depth in a 2D library, demonstrating real visualization engineering + - 'Perfect spec compliance 15/15: correct plot type, all required features, accurate + data mapping, correct title and legend format' + - 'Full data quality 15/15: realistic survey context (education x age x satisfaction), + appropriate 0-10 scale, all 12 bars present' + - 'Perfect code quality 10/10: reproducible seed, clean imports, organized sections, + correct PNG+HTML outputs' + - Theme-adaptive chrome correctly applied in both renders — INK/INK_SOFT tokens + thread through to all geom_text color parameters, no dark-on-dark failures weaknesses: - - 'CRITICAL SC-01=0: Wrong chart type — spec requires a 3D bar chart with two categorical - spatial axes and a vertical height axis; letsplot cannot render 3D projections - (analogous to AR-06 not-feasible); implementation fell back to a 2D grouped bar - chart' - - 'SC-02 LOW: 3D-specific features entirely absent — no base-plane grid, no depth/perspective, - no two positional categorical axes, no viewing angle; only minor spec features - (value labels, bar spacing) are present' - - 'DE-01/DE-03: No visual emphasis or storytelling; the ''Graduate satisfaction - is consistently highest'' pattern is discoverable but never visually emphasized - through color contrast, size variation, or annotation' - - 'DE-02: Axis tick marks and legend border box could be removed for a cleaner look' - - 'LM-02: No distinctively letsplot-specific features used — basic ggplot2 grammar - patterns with no HTML interactivity leverage, no tooltips, no letsplot-exclusive - geoms' + - 'Design excellence (11/20) is the primary gap: functionally solid but lacks visual + polish — no legend background fill, tight right edge for 65+/Graduate bars' + - 'No descriptive axis titles: z-axis lacks ''Satisfaction Score'' label, x-axis + lacks ''Age Group'' label — viewers must infer from context' + - Value labels (size=10) and z-tick labels (size=11) are small for the 4800x2700 + canvas; slightly larger sizes would improve immediate legibility + - Rightmost bars (65+/Graduate) are very close to the right canvas edge — adjust + DX or canvas width for breathing room image_description: |- Light render (plot-light.png): - Background: Warm off-white #FAF8F1 — correct theme surface, not pure white - Chrome: Title "bar-3d-categorical · letsplot · anyplot.ai" in dark ink, clearly readable. X-axis label "Age Group" and Y-axis label "Satisfaction Score (0-10)" in dark ink, readable. Tick labels (18-34, 35-49, 50-64, 65+; 0-11 on Y) in muted dark ink (#4A4A44), readable. Value labels above bars (6.0, 6.9, 7.7, etc.) in muted ink, readable. Legend title "Education" and labels (High School, Bachelor's, Graduate) readable. - Data: Three bars per age group in green (#009E73 / High School), orange (#D55E00 / Bachelor's), blue (#0072B2 / Graduate). First series is #009E73 as required. Bars clearly visible, well-separated, with subtle PAGE_BG edge color. - Legibility verdict: PASS — all text fully readable against warm off-white background + Background: Warm off-white #FAF8F1 — correct, not pure white + Chrome: Title "bar-3d-categorical · letsplot · anyplot.ai" in dark ink at top center — clearly readable. Z-axis tick labels (0,2,4,6,8,10) in INK_SOFT dark. Age group labels (18-34, 35-49, 50-64, 65+) in INK_SOFT below bars. Legend title "Education Level" in bold INK. All text readable against light background. + Data: Three Okabe-Ito bar groups — High School (#009E73 green, front), Bachelor's (#D55E00 orange, middle), Graduate (#0072B2 blue, back). Cabinet projection shading: front/right/top faces. 12 value labels (5.5-8.1) above bar tops. Base-plane grid lines in GRID_COLOR. + Legibility verdict: PASS Dark render (plot-dark.png): - Background: Warm near-black #1A1A17 — correct dark theme surface, not pure black - Chrome: Title in off-white (#F0EFE8), clearly readable against dark background. Axis labels in off-white, readable. Tick labels in light muted tone (#B8B7B0), readable. Value labels above bars in light muted tone, readable. Legend text in light muted tone, readable. No dark-on-dark failures observed — all text is light-colored and legible. - Data: Bar colors are IDENTICAL to light render — green (#009E73), orange (#D55E00), blue (#0072B2). Only chrome elements (background, text, grid, legend frame) have flipped. - Legibility verdict: PASS — all text fully readable against near-black background; no dark-on-dark failures - - CRITICAL NOTE (both renders): The chart is a 2D grouped bar chart, NOT a 3D bar chart. The spec requires bars rising from a 2D categorical grid with two categorical spatial axes and a vertical height axis. letsplot cannot render true 3D projections — this is an AR-06 not-feasible situation. SC-01=0, score capped at 40/100. + Background: Warm near-black #1A1A17 — correct, not pure black + Chrome: Title and all labels rendered in light/near-white tones (#F0EFE8 for primary, #B8B7B0 for secondary). Title, z-tick labels, age labels, legend text all clearly readable against dark background. No dark-on-dark failures — INK token flips to #F0EFE8, INK_SOFT to #B8B7B0, correctly applied via geom_text color parameters. + Data: Bar colors identical to light render — green, orange, blue Okabe-Ito colors unchanged. Value labels switch to light #F0EFE8 and are fully readable on dark background. + Legibility verdict: PASS criteria_checklist: visual_quality: - score: 30 + score: 25 max: 30 items: - id: VQ-01 name: Text Legibility - score: 8 + score: 7 max: 8 passed: true - comment: 'All font sizes explicitly set: title=24pt, axis_title=20pt, axis_text=16pt, - legend=16pt; fully readable in both themes' + comment: All text readable in both themes; value labels (size=10) and z-tick + labels (size=11) are small but legible at 4800x2700 - id: VQ-02 name: No Overlap - score: 6 + score: 5 max: 6 passed: true - comment: Value labels, tick labels, and legend are well-separated; position_dodge(0.75) - matches bar width correctly + comment: Minor overlap between back-row value labels and foreground bars in + 3D perspective; labels still readable as topmost layer - id: VQ-03 name: Element Visibility - score: 6 + score: 5 max: 6 passed: true - comment: Bars clearly visible with subtle PAGE_BG edge color for definition; - good sizing + comment: All 12 bars visible with distinct colors and shaded faces; 65+/Graduate + bar tight at right edge but contained - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Okabe-Ito palette is CVD-safe; three bars easily distinguishable - by both hue and luminance + comment: Okabe-Ito palette, CVD-safe, no red-green sole signal - id: VQ-05 name: Layout & Canvas - score: 4 + score: 3 max: 4 passed: true - comment: Plot fills canvas well; y-axis headroom for value labels is intentional; - balanced margins + comment: Good 16:9 layout; 65+/Graduate bar's right face is very close to + canvas edge with little margin - id: VQ-06 name: Axis Labels & Title - score: 2 + score: 1 max: 2 - passed: true - comment: Y-axis includes units '(0-10)'; title correct format + passed: false + comment: Title correct; z-axis ticks and x-axis age labels present, but no + descriptive axis titles (Satisfaction Score, Age Group) - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: '#009E73 first series, #D55E00 second, #0072B2 third; correct Okabe-Ito - order; backgrounds #FAF8F1/#1A1A17; chrome fully adaptive' + comment: 'First series #009E73 correct; Okabe-Ito order maintained; backgrounds + #FAF8F1/#1A1A17 correct; colors identical between themes' design_excellence: - score: 10 + score: 11 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 4 + score: 5 max: 8 - passed: false - comment: 'Well-configured library default: theme_classic + adaptive chrome - + Okabe-Ito + value labels. Clean but not exceptional — no visual hierarchy - lifts it above ''configured ggplot''' + passed: true + comment: 'Above default: cabinet projection with per-face shading creates + convincing 3D depth; intentional color hierarchy; functional rather than + elegantly polished' - id: DE-02 name: Visual Refinement - score: 4 + score: 3 max: 6 - passed: false - comment: theme_classic removes top/right spines; custom GRID_COLOR for subtle - y-only grid; all chrome colors set. Tick marks and legend border could be - removed for further polish + passed: true + comment: 'Above default: theme_void() cleans canvas; bar edges use PAGE_BG; + manually drawn grid and z-axis are tidy; no legend background fill' - id: DE-03 name: Data Storytelling - score: 2 + score: 3 max: 6 - passed: false - comment: Data displayed clearly but no visual emphasis highlights the 'Graduate - consistently highest' pattern. Data shown without interpretation. + passed: true + comment: 'Above default: clear hierarchy (Graduate > Bachelor > HS) immediately + visible; value labels enable precise comparison; no focal emphasis' spec_compliance: - score: 6 + score: 15 max: 15 items: - id: SC-01 name: Plot Type - score: 0 + score: 5 max: 5 - passed: false - comment: WRONG CHART TYPE. Spec requires 3D bar chart with two categorical - spatial axes and Z-height axis. Implementation is a 2D grouped bar chart. - letsplot cannot do 3D (analogous to AR-06 not-feasible). + passed: true + comment: Correct 3D bar chart with two categorical axes and height-encoded + values via cabinet projection - id: SC-02 name: Required Features - score: 1 + score: 4 max: 4 - passed: false - comment: 'Only minor spec features present: value labels on top ✓, bar spacing - ✓. Missing: 3D base-plane grid, two positional categorical axes, depth/perspective, - viewing angle, color encoding of value magnitude' + passed: true + comment: 'All spec features: 2 categorical dims, height encoding, bar spacing, + color encoding, base-plane grid, value labels, viewing angle, legend' - id: SC-03 name: Data Mapping - score: 2 + score: 3 max: 3 - passed: false - comment: 'Within 2D frame: Age Group→X and Satisfaction→Y correct. Education - encoded via fill/dodge (only 2D option). Second categorical axis is not - a spatial axis as spec requires.' + passed: true + comment: 'X: age groups (4); depth: education level (3); Z: satisfaction score; + all axes fully populated' - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title 'bar-3d-categorical · letsplot · anyplot.ai' correct format; - legend labels match data categories + comment: Title 'bar-3d-categorical · letsplot · anyplot.ai' correct; manual + legend with Education Level title and correct labels data_quality: - score: 13 + score: 15 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 4 + score: 6 max: 6 - passed: false - comment: Shows variation across both dimensions; meaningful differences present. - However 3D spec features (grid of 9-100 bars on 2D base plane) not represented. + passed: true + comment: All 12 bars (4x3 grid); front/right/top faces; grid, z-axis, value + labels, and legend all present - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: 'Job satisfaction by age group and education level: realistic, comprehensible, - neutral scenario' + comment: Survey satisfaction scores by age group x education level; plausible + 5.5-8.1 range on 0-10 scale; neutral data - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: 0-10 satisfaction scale with values 5.5-8.1 appropriate; higher education - correlating with higher satisfaction is factually plausible + comment: Values 5.5-8.1 on 0-10 axis; ticks 0,2,4,6,8,10 sensible; 4x3 grid + within spec's 3-10 per axis requirement code_quality: score: 10 max: 10 @@ -203,63 +191,65 @@ review: score: 3 max: 3 passed: true - comment: Imports → tokens → data → plot → save; no functions or classes + comment: Small utility helpers (prj, shade, face, seg) justified by 3D complexity; + flat linear script otherwise - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: np.random.seed(42) set + comment: np.random.seed(42) used - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: os, numpy, pandas, lets_plot — all used + comment: os, numpy, pandas, lets_plot — all used, none redundant - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean, Pythonic; dict-mapped scale_fill_manual is idiomatic; position_dodge(0.75) - matches bar width correctly + comment: Well-organized sections; complexity matches task requirements; no + fake UI - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot-{THEME}.png and plot-{THEME}.html; current API with scale=3 - for PNG + comment: ggsave scale=3 PNG + HTML, plot-{THEME}.png/html paths correct library_mastery: - score: 6 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage score: 4 max: 5 - passed: false - comment: Correct ggplot2 grammar with layered geoms, named-dict scale_fill_manual, - theme_classic() + layered theme() override. Good; minor opportunity to use - scale_y_continuous(expand=...) for tighter layout. + passed: true + comment: Correct ggplot grammar, aes mappings, geom layers, scale_fill_manual + with identity mapping is idiomatic letsplot - id: LM-02 name: Distinctive Features - score: 2 + score: 3 max: 5 - passed: false - comment: position_dodge with matching geom_text position is a standard ggplot2 - pattern. No distinctively letsplot-specific features (no HTML tooltip leverage, - no letsplot-exclusive geoms). - verdict: REJECTED + passed: true + comment: Creative use of geom_polygon for 3D faces with painter's-order z-sorting; + custom fill identity scale; theme_void for fully hand-crafted canvas; HTML+PNG + outputs + verdict: APPROVED impl_tags: dependencies: [] techniques: - - annotations - layer-composition + - custom-legend + - manual-ticks + - 3d-projection + - html-export patterns: - data-generation - iteration-over-groups dataprep: [] styling: + - minimal-chrome - edge-highlighting - - grid-styling