Skip to content

feat(letsplot): implement bar-3d-categorical#6793

Merged
MarkusNeusinger merged 5 commits into
mainfrom
implementation/bar-3d-categorical/letsplot
May 15, 2026
Merged

feat(letsplot): implement bar-3d-categorical#6793
MarkusNeusinger merged 5 commits into
mainfrom
implementation/bar-3d-categorical/letsplot

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Implementation: bar-3d-categorical - python/letsplot

Implements the python/letsplot version of bar-3d-categorical.

File: plots/bar-3d-categorical/implementations/python/letsplot.py

Parent Issue: #5248


🤖 impl-generate workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 15, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): A 2D grouped (dodged) bar chart on a warm off-white #FAF8F1 background. Four age groups (18–34, 35–49, 50–64, 65+) appear on the X-axis; Satisfaction Score (0–10) on the Y-axis. Each age group has three bars side-by-side: High School (green #009E73), Bachelor's (orange #D55E00), and Graduate (blue #0072B2) — the first three Okabe-Ito positions in canonical order. Value labels (e.g., "6.0", "6.9", "7.7") float above each bar in a muted ink color. The legend sits to the right. Title reads "bar-3d-categorical · letsplot · anyplot.ai". All text (title, axis labels, tick labels, value labels, legend) is clearly readable against the warm off-white background. Grid lines are subtle horizontal rules. The overall render is clean, professional, and fully legible.

Dark render (plot-dark.png): The same chart on a near-black #1A1A17 background. Title, axis labels, tick labels, value labels, and legend text all flip to light tones (off-white/light gray), consistent with the adaptive chrome tokens. Data bar colors are identical to the light render — green, orange, and blue bars are visually indistinguishable from their light-theme counterparts. No dark-on-dark failures observed: all text is clearly readable against the dark surface. Subtle horizontal grid lines remain visible but unobtrusive.

Critical issue — both renders: The chart is a standard 2D grouped bar chart, not the 3D bar chart specified. There is no third spatial axis, no 2D categorical grid on a base plane, no depth or perspective. The spec (bar-3d-categorical) explicitly requires bars rising from a 2D categorical grid with X-category, Y-category, and Z-height axes. letsplot is a 2D grammar-of-graphics library and cannot render true 3D projections.

Score: 40/100

Category Score Max
Visual Quality 30 30
Design Excellence 10 20
Spec Compliance 6 15
Data Quality 13 15
Code Quality 10 10
Library Mastery 6 10
Raw Total 75 100
Cap Applied (SC-01=0) 40 100

Visual Quality (30/30)

  • VQ-01: Text Legibility (8/8) — title=24pt, axis labels=20pt, tick labels=16pt, legend=16pt all explicitly set and fully readable in both themes
  • VQ-02: No Overlap (6/6) — value labels, tick labels, legend all well-separated; position_dodge matches bar width
  • VQ-03: Element Visibility (6/6) — bars are clearly sized with subtle edge highlighting via PAGE_BG color border
  • VQ-04: Color Accessibility (2/2) — Okabe-Ito palette is CVD-safe; bars are easily distinguishable
  • VQ-05: Layout & Canvas (4/4) — plot fills canvas well; y-axis headroom for value labels is intentional and appropriate
  • VQ-06: Axis Labels & Title (2/2) — "Age Group" and "Satisfaction Score (0–10)" include units
  • VQ-07: Palette Compliance (2/2) — #009E73 first, #D55E00 second, #0072B2 third; backgrounds #FAF8F1 / #1A1A17; chrome fully adaptive

Design Excellence (10/20)

  • DE-01: Aesthetic Sophistication (4/8) — Well-configured library default: theme_classic() with custom adaptive chrome, explicit Okabe-Ito colors, and value labels. Clean but not exceptional — no visual hierarchy or emphasis that lifts it above "configured ggplot."
  • DE-02: Visual Refinement (4/6) — theme_classic() removes top/right spines; custom GRID_COLOR for subtle y-only major grid; all chrome colors explicitly set. Good refinement, but axis tick marks remain and the legend has a border box that could be removed.
  • DE-03: Data Storytelling (2/6) — Data is displayed clearly but no visual emphasis guides the reader. The pattern (Graduate satisfaction consistently highest) is discoverable but never highlighted through color contrast, size variation, or annotation.

Spec Compliance (6/15)

  • SC-01: Plot Type (0/5) — WRONG CHART TYPE. Spec requires a 3D bar chart with two categorical axes on a base plane and a vertical height axis. The implementation is a standard 2D grouped bar chart. letsplot is a 2D library and cannot project into 3D space. This is analogous to the AR-06 example ("pygal cannot do 3D") and should be flagged accordingly.
  • SC-02: Required Features (1/4) — Only minor spec features are present: value labels on top of bars ✓, spacing between bars ✓. Missing: 3D base plane grid, two categorical axes forming a 2D grid, depth/perspective view, adjustable viewing angle, color encoding of value magnitude (color encodes category instead).
  • SC-03: Data Mapping (2/3) — Within the 2D frame, Age Group → X and Satisfaction → Y are correct. The second categorical dimension (Education) is encoded via fill/dodge rather than a second spatial axis, which is the only feasible mapping in 2D.
  • SC-04: Title & Legend (3/3) — Title "bar-3d-categorical · letsplot · anyplot.ai" matches required format exactly; legend labels (High School, Bachelor's, Graduate) match data.

Data Quality (13/15)

  • DQ-01: Feature Coverage (4/6) — Shows variation across both categorical dimensions (age and education) with meaningful differences. However, the 3D aspect of the spec (demonstrating a 9–100 bar grid on a 2D base plane) is not represented at all. Deducting for missing dimensional depth.
  • DQ-02: Realistic Context (5/5) — Job satisfaction by age group and education level is a real, comprehensible, neutral scenario. Values align with plausible survey data.
  • DQ-03: Appropriate Scale (4/4) — 0–10 satisfaction scale with values 5.5–8.1 is appropriate; higher education correlating with higher scores is factually plausible.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Imports → tokens → data → plot → save; no functions or classes
  • CQ-02: Reproducibility (2/2) — np.random.seed(42) set
  • CQ-03: Clean Imports (2/2) — os, numpy, pandas, lets_plot; all used
  • CQ-04: Code Elegance (2/2) — Clean, Pythonic; dict-mapped scale_fill_manual is idiomatic; position_dodge(0.75) matches bar width correctly
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html; current API

Library Mastery (6/10)

  • LM-01: Idiomatic Usage (4/5) — Correct ggplot2 grammar with layered geoms, scale_fill_manual with named dict mapping, theme_classic() + layered theme() override. Good idiomatic usage; minor opportunity to use scale_y_continuous(expand=...) for tighter layout.
  • LM-02: Distinctive Features (2/5) — position_dodge with matching position_dodge in geom_text is a well-known ggplot2 pattern. No distinctively letsplot-specific features are used (e.g., no tooltip, no HTML interactivity leverage, no letsplot-specific geoms).

Score Caps Applied

  • SC-01 = 0 → max score capped at 40. Raw score 75 reduced to 40.

Strengths

  • Perfect visual quality: explicit font sizing, clean adaptive theming in both light and dark, correct Okabe-Ito palette in canonical order
  • Code is clean, reproducible, and idiomatic; theme tokens fully applied to all chrome elements
  • Realistic, neutral dataset (job satisfaction by age × education) with appropriate scale and value variation
  • Title format and legend labels are correct

Weaknesses

  • Critical: Wrong chart type (SC-01=0). The spec requires a 3D bar chart (two categorical spatial axes + vertical height axis). letsplot cannot render 3D projections — this is essentially an AR-06 situation. The implementation fell back to a 2D grouped bar chart, which does not satisfy the spec.
  • SC-02: The 3D-specific features (base-plane grid, depth, two positional categorical axes, viewing angle) are entirely absent. Value labels are present but the spec's core structural requirements are not met.
  • DE-01/DE-03: No visual emphasis or storytelling. The "Graduate satisfaction is consistently highest" insight is discoverable but never visually emphasized through color contrast, size, or annotation.
  • DE-02: Axis tick marks and legend border box could be removed for a cleaner look.
  • LM-02: No distinctively letsplot features used (HTML interactivity, tooltips, etc.).

Issues Found

  1. SC-01 CRITICAL: 2D grouped bar chart instead of 3D bar chart
    • Fix: letsplot cannot do true 3D projections. Consider: (a) flag as AR-06 not-feasible for this library, or (b) attempt a creative isometric/pseudo-3D approach using layered geom_rect/geom_polygon patches to simulate 3D-looking bars with perspective — though this would be complex and might still not satisfy the spec intent.
  2. DE-03 LOW: No visual storytelling or emphasis
    • Fix: Add alpha variation or outline emphasis on the highest bar per age group; or use geom_text with a highlight color on the maximum value per group to draw attention to the "Graduate" pattern.
  3. LM-02 LOW: Generic ggplot2 usage with no letsplot-distinctive features
    • Fix: Leverage letsplot's built-in HTML interactivity (tooltips are automatic in HTML output) as a storytelling differentiator.

AI Feedback for Next Attempt

The fundamental problem is chart type: letsplot cannot render 3D spatial projections, so a true 3D bar chart is not feasible with this library. If repair is attempted: (1) Either accept AR-06 not-feasible, or (2) attempt a creative pseudo-3D isometric bar chart using layered geom_polygon or geom_rect shapes to draw 3D-looking bars from a tilted perspective — map x_category and y_category to grid positions, draw each bar as a stack of parallelograms to simulate depth. This is complex but would satisfy the spec intent. If going 2D, consider a geom_tile heatmap as the closest honest 2D alternative (it displays both categorical axes on X and Y and color-encodes the value). For design: add visual emphasis to the maximum per row/column, remove legend border, remove tick marks.

Verdict: REJECTED

@github-actions github-actions Bot added quality:40 Quality score 40/100 ai-rejected Quality not OK, triggers update quality-poor Score < 50, needs fundamental fixes labels May 15, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels May 15, 2026
@github-actions
Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

Status: Repair completed, re-triggering review...


🤖 impl-repair

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 15, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): The plot shows a 3D bar chart using cabinet projection on a warm off-white (#FAF8F1) background. Three education levels (High School = #009E73 green, Bachelor's = #D55E00 orange, Graduate = #0072B2 blue) are rendered as 3D bars with shaded front, right, and top faces across four age groups (18–34, 35–49, 50–64, 65+). A vertical z-axis (0–10) with tick marks is drawn on the left. All 12 bars have numeric value labels (5.5–8.1) at their tops. A manual legend sits in the upper-left with "Education Level" as bold title. Title "bar-3d-categorical · letsplot · anyplot.ai" sits at top center. All text — title, z-axis ticks, x-axis age labels, value labels, and legend — is clearly readable in dark ink against the light background. Legibility verdict: PASS.

Dark render (plot-dark.png): The same plot is rendered on a warm near-black (#1A1A17) background. Data colors (green, orange, blue bars) are identical to the light render. Title, z-axis labels, x-axis labels, value labels, and legend text all appear in light/near-white tones that are clearly readable against the dark background. No dark-on-dark failures observed — the INK/INK_SOFT tokens are correctly applied through the geom_text color parameters. The value labels switch to #F0EFE8 in dark mode, remaining fully readable. Legibility verdict: PASS.

Both paragraphs are required. A review that only describes one render is invalid.

Score: 83/100

Category Score Max
Visual Quality 25 30
Design Excellence 11 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 10 10
Library Mastery 7 10
Total 83 100

Visual Quality (25/30)

  • VQ-01: Text Legibility (7/8) — All text readable in both themes; value labels (size=10) and z-tick labels (size=11) are on the smaller side for a 4800×2700 render but remain legible
  • VQ-02: No Overlap (5/6) — Minor overlap between some back-row (High School) value labels and the foreground Bachelor's bar faces due to 3D perspective; labels are still readable as they are the topmost layer
  • VQ-03: Element Visibility (5/6) — All 12 bars clearly visible with distinct colors and shaded faces; rightmost (65+/Graduate) bar is tight at the right canvas edge but contained
  • VQ-04: Color Accessibility (2/2) — Okabe-Ito palette, CVD-safe, no red-green sole signal
  • VQ-05: Layout & Canvas (3/4) — Good 16:9 layout; the 65+/Graduate bar's right face is very close to the canvas edge with little margin; manual plot_margin is modest
  • VQ-06: Axis Labels & Title (1/2) — Title correct; z-axis tick values and x-axis age labels present, but no descriptive axis titles (e.g., "Age Group", "Education Level", "Satisfaction Score")
  • VQ-07: Palette Compliance (2/2) — First series (High School) = #009E73 ✓; Okabe-Ito order maintained; backgrounds #FAF8F1 / #1A1A17 correct; data colors identical between themes ✓

Design Excellence (11/20)

  • DE-01: Aesthetic Sophistication (5/8) — Above default: cabinet projection with per-face shading (shade factor 0.60 for right face, 1.32 for top face) creates convincing 3D depth; intentional color hierarchy and manual legend are well-executed; overall feel is functional rather than elegantly polished
  • DE-02: Visual Refinement (3/6) — Above default: theme_void() removes all chart chrome leaving a clean canvas; bar edges use PAGE_BG for subtle separation; manually drawn grid and z-axis are tidy; minimal whitespace beyond the plot_margin
  • DE-03: Data Storytelling (3/6) — Above default: clear visual hierarchy (Graduate > Bachelor's > High School) is immediately apparent; value labels enable precise comparisons; the 3D layout naturally groups bars for cross-category reading; no focal emphasis beyond the basic structure

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct 3D bar chart with two categorical axes and height-encoded values via cabinet projection
  • SC-02: Required Features (4/4) — All spec features present: 2 categorical dims, height encoding, spacing between bars, color encoding (education level), base-plane grid, value labels (12 bars < 25 threshold), viewing angle equivalent, legend
  • SC-03: Data Mapping (3/3) — X: age groups (4); depth: education level (3); Z (height): satisfaction score; all axes fully populated
  • SC-04: Title & Legend (3/3) — Title "bar-3d-categorical · letsplot · anyplot.ai" ✓; manual legend with "Education Level" title and correct category labels ✓

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — All 12 bars rendered (4×3 grid); front/right/top faces shown; grid, z-axis, value labels, and legend all present
  • DQ-02: Realistic Context (5/5) — Survey data: satisfaction score by age group × education level; plausible values (5.5–8.1 on 0–10 scale); neutral and non-controversial
  • DQ-03: Appropriate Scale (4/4) — Values in 5.5–8.1 range on 0–10 axis; tick marks at 0,2,4,6,8,10 are sensible; 4×3 grid is within spec's 3–10 per axis requirement

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Small utility helpers (prj, shade, face, seg) justified by 3D complexity; otherwise flat linear script
  • CQ-02: Reproducibility (2/2) — np.random.seed(42) ✓
  • CQ-03: Clean Imports (2/2) — os, numpy, pandas, lets_plot — all used, none redundant
  • CQ-04: Code Elegance (2/2) — Well-organized sections (grid, z-axis, labels, bars, legend); code complexity matches task requirements; no fake UI
  • CQ-05: Output & API (1/1) — ggsave with scale=3 for PNG + HTML, paths correct, plot-{THEME}.png / plot-{THEME}.html

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5) — Correct ggplot() grammar, aes() mappings, geom layers, scale_fill_manual, theme composition; scale_fill_manual with identity mapping is an idiomatic letsplot technique
  • LM-02: Distinctive Features (3/5) — Creative use of geom_polygon for 3D bar faces with painter's-order z-sorting; custom fill identity scale; theme_void for a fully hand-crafted canvas; generates both HTML and PNG outputs

Score Caps Applied

  • None

Strengths

  • 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: all 15/15 — correct plot type, all required features, accurate data mapping, correct title format and legend
  • Full data quality 15/15: realistic survey context, appropriate scale, all 12 bars present
  • Perfect code quality 10/10: reproducible, clean, organized, correct outputs
  • Theme-adaptive chrome correctly applied in both renders — all text tokens (INK, INK_SOFT) thread through to geom_text color parameters, no dark-on-dark failures

Weaknesses

  • Design excellence (11/20) is the primary gap: the plot is functionally solid but lacks visual polish — no subtle background for the legend area, no depth-to-perspective gradient, tightly packed right edge for 65+/Graduate bars
  • Axis labels missing: no descriptive title for x-axis ("Age Group"), z-axis ("Satisfaction Score"), or a subtitle explaining the data context — viewers must infer from context
  • Value labels (size=10) and z-tick labels (size=11) are among the smallest text elements on the 4800×2700 canvas; slightly larger sizes would improve immediate legibility
  • The rightmost bars (65+/Graduate) are very close to the right canvas edge — a slightly wider canvas or adjusted projection parameters would add breathing room

Issues Found

  1. DE-02 MEDIUM: Legend area has no background fill — on both themes the legend swatches and text float directly on the plot background with no elevated surface to group them
    • Fix: Add a semi-transparent or ELEVATED_BG rectangle behind the legend items to anchor the grouping visually
  2. VQ-06 LOW: No axis titles present — z-axis has numeric ticks but no "Satisfaction Score" label; x-axis has category labels but no "Age Group" label
    • Fix: Add geom_text labels for the z-axis title (rotated, left of tick labels) and x-axis title (below the category labels)
  3. VQ-05 MINOR: 65+/Graduate bar's right face is tight against the right canvas edge
    • Fix: Add a right-side expansion (e.g., reduce DX slightly or clip the x range with an explicit xlim)

AI Feedback for Next Attempt

The core 3D construction is excellent — keep the cabinet projection, painter ordering, and per-face shading. To raise the score: (1) add a subtle ELEVATED_BG rectangle behind the legend to anchor it visually; (2) add axis title annotations ("Satisfaction Score" on z-axis, "Age Group" on x-axis) to bring VQ-06 to full marks; (3) increase value label size from 10 to 12 and z-tick label size from 11 to 13; (4) either reduce DX from 0.52 to 0.45 or set an explicit xlim to give the rightmost bars more breathing room.

Verdict: APPROVED

@github-actions github-actions Bot added quality:83 Quality score 83/100 ai-approved Quality OK, ready for merge and removed quality:40 Quality score 40/100 labels May 15, 2026
@MarkusNeusinger MarkusNeusinger merged commit 176e1a4 into main May 15, 2026
3 checks passed
@MarkusNeusinger MarkusNeusinger deleted the implementation/bar-3d-categorical/letsplot branch May 15, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-approved Quality OK, ready for merge ai-attempt-1 First repair attempt quality:83 Quality score 83/100 quality-poor Score < 50, needs fundamental fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant