From 221390f1ede9660ee3d8f89d8ddef8a9a1ff87b9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Sep 2026 06:29:27 +0000 Subject: [PATCH 1/7] feat(plotnine): implement wireframe-3d-basic --- .../implementations/python/plotnine.py | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 plots/wireframe-3d-basic/implementations/python/plotnine.py diff --git a/plots/wireframe-3d-basic/implementations/python/plotnine.py b/plots/wireframe-3d-basic/implementations/python/plotnine.py new file mode 100644 index 00000000000..2629f39d37b --- /dev/null +++ b/plots/wireframe-3d-basic/implementations/python/plotnine.py @@ -0,0 +1,145 @@ +"""anyplot.ai +wireframe-3d-basic: Basic 3D Wireframe Plot +Library: plotnine 0.15.8 | Python 3.13.13 +Quality: pending | Created: 2026-09-10 +""" + +import os + +import numpy as np +import pandas as pd +from plotnine import ( + aes, + coord_fixed, + element_rect, + element_text, + geom_path, + geom_segment, + geom_text, + ggplot, + labs, + theme, + theme_void, +) + + +# Theme tokens +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +BRAND = "#009E73" # Imprint palette position 1 — ALWAYS first series + +# Camera: orthographic projection at elevation 30 deg / azimuth 45 deg, per spec. +# plotnine has no 3D grammar, so the mesh is projected to 2D screen coordinates +# ourselves (the same technique any static 3D renderer uses under the hood), +# then drawn with plotnine's own geom_path / geom_segment / geom_text. +elev = np.radians(30) +azim = np.radians(45) + +view_dir = np.array([np.cos(elev) * np.cos(azim), np.cos(elev) * np.sin(azim), np.sin(elev)]) +world_up = np.array([0.0, 0.0, 1.0]) +right_axis = np.cross(view_dir, world_up) +right_axis /= np.linalg.norm(right_axis) +up_axis = np.cross(right_axis, view_dir) + +Z_LIFT = 3.2 # visual height exaggeration so the shallow membrane displacement reads clearly + + +def project(x, y, z): + px = x * right_axis[0] + y * right_axis[1] + z * Z_LIFT * right_axis[2] + py = x * up_axis[0] + y * up_axis[1] + z * Z_LIFT * up_axis[2] + return px, py + + +# Data — circular drumhead vibration mode: displacement z = sin(sqrt(x^2 + y^2)) +np.random.seed(42) +grid_n = 26 +x_vals = np.linspace(-6, 6, grid_n) +y_vals = np.linspace(-6, 6, grid_n) +grid_x, grid_y = np.meshgrid(x_vals, y_vals) +grid_z = np.sin(np.sqrt(grid_x**2 + grid_y**2)) + +z_min, z_max = float(grid_z.min()), float(grid_z.max()) +floor_z = z_min - 0.3 +ceil_z = z_max + 0.3 + +# Wireframe mesh lines running in both x and y directions (per spec) +mesh_rows = [] +for j in range(grid_n): + px, py = project(grid_x[:, j], grid_y[:, j], grid_z[:, j]) + for i in range(grid_n): + mesh_rows.append({"px": px[i], "py": py[i], "line": f"col_{j}"}) +for i in range(grid_n): + px, py = project(grid_x[i, :], grid_y[i, :], grid_z[i, :]) + for j in range(grid_n): + mesh_rows.append({"px": px[j], "py": py[j], "line": f"row_{i}"}) +mesh = pd.DataFrame(mesh_rows) +mesh_cols = mesh[mesh["line"].str.startswith("col_")] +mesh_rows_df = mesh[mesh["line"].str.startswith("row_")] + +# Axis box: three edges meeting at the front-left-bottom corner +axis_lines = pd.DataFrame( + { + "x": [-6, -6, -6], + "y": [-6, -6, -6], + "z": [floor_z, floor_z, floor_z], + "xend": [6, -6, -6], + "yend": [-6, 6, -6], + "zend": [floor_z, floor_z, ceil_z], + } +) +axis_lines["px"], axis_lines["py"] = project(axis_lines["x"], axis_lines["y"], axis_lines["z"]) +axis_lines["pxend"], axis_lines["pyend"] = project(axis_lines["xend"], axis_lines["yend"], axis_lines["zend"]) + +x_breaks = np.array([-6, -3, 0, 3, 6]) +y_breaks = np.array([-6, -3, 0, 3, 6]) +z_breaks = np.array([-1, 0, 1]) + +ticks = pd.concat( + [ + pd.DataFrame({"x": x_breaks, "y": -9.6, "z": floor_z, "label": [f"{v:g}" for v in x_breaks]}), + pd.DataFrame({"x": -9.6, "y": y_breaks, "z": floor_z, "label": [f"{v:g}" for v in y_breaks]}), + ], + ignore_index=True, +) +ticks["px"], ticks["py"] = project(ticks["x"], ticks["y"], ticks["z"]) + +# Z ticks sit on the vertical axis line itself; nudge the label text (not the +# axis line) sideways past the Y-axis tick column so the two groups don't merge. +z_ticks = pd.DataFrame({"x": -6, "y": -6, "z": z_breaks, "label": [f"{v:g}" for v in z_breaks]}) +z_px, z_py = project(z_ticks["x"], z_ticks["y"], z_ticks["z"]) +z_ticks["px"] = z_px - 13 +z_ticks["py"] = z_py + +axis_labels = pd.DataFrame( + { + "x": [9.4, -6, -6], + "y": [-6, 9.4, -6], + "z": [floor_z, floor_z, ceil_z + 1.0], + "label": ["X (cm)", "Y (cm)", "Z (mm)"], + } +) +axis_labels["px"], axis_labels["py"] = project(axis_labels["x"], axis_labels["y"], axis_labels["z"]) + +# Plot +plot = ( + ggplot() + + geom_path(aes("px", "py", group="line"), mesh_cols, color=BRAND, size=0.3, alpha=0.35) + + geom_path(aes("px", "py", group="line"), mesh_rows_df, color=BRAND, size=0.3, alpha=0.35) + + geom_segment(aes(x="px", y="py", xend="pxend", yend="pyend"), axis_lines, color=INK_SOFT, size=0.6) + + geom_text(aes("px", "py", label="label"), ticks, color=INK_SOFT, size=3.3) + + geom_text(aes("px", "py", label="label"), z_ticks, color=INK_SOFT, size=3.3) + + geom_text(aes("px", "py", label="label"), axis_labels, color=INK, size=3.6, fontweight="bold") + + labs(title="wireframe-3d-basic · python · plotnine · anyplot.ai") + + coord_fixed(ratio=1) + + theme_void(base_size=7) + + theme( + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), + panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), + plot_title=element_text(color=INK, size=12, ha="center"), + figure_size=(8, 4.5), + ) +) + +plot.save(f"plot-{THEME}.png", dpi=400, width=8, height=4.5, units="in") From e1bfc0f3080af1d18387bbbfdbb7ffcc5e4080d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Sep 2026 06:29:36 +0000 Subject: [PATCH 2/7] chore(plotnine): add metadata for wireframe-3d-basic --- .../metadata/python/plotnine.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plots/wireframe-3d-basic/metadata/python/plotnine.yaml diff --git a/plots/wireframe-3d-basic/metadata/python/plotnine.yaml b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml new file mode 100644 index 00000000000..03d7b996198 --- /dev/null +++ b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml @@ -0,0 +1,21 @@ +# Per-library metadata for plotnine implementation of wireframe-3d-basic +# Auto-generated by impl-generate.yml + +library: plotnine +language: python +specification_id: wireframe-3d-basic +created: '2026-09-10T06:29:36Z' +updated: '2026-09-10T06:29:36Z' +generated_by: claude-sonnet +workflow_run: 34445071277 +issue: 1015 +language_version: 3.13.15 +library_version: 0.15.8 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/wireframe-3d-basic/python/plotnine/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/wireframe-3d-basic/python/plotnine/plot-dark.png +preview_html_light: null +preview_html_dark: null +quality_score: null +review: + strengths: [] + weaknesses: [] From 130f1caa5f736e87146f883d4f3fb063d36d1517 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Sep 2026 06:33:59 +0000 Subject: [PATCH 3/7] chore(plotnine): update quality score 72 and review feedback for wireframe-3d-basic --- .../implementations/python/plotnine.py | 6 +- .../metadata/python/plotnine.yaml | 265 +++++++++++++++++- 2 files changed, 261 insertions(+), 10 deletions(-) diff --git a/plots/wireframe-3d-basic/implementations/python/plotnine.py b/plots/wireframe-3d-basic/implementations/python/plotnine.py index 2629f39d37b..5663b6dd8af 100644 --- a/plots/wireframe-3d-basic/implementations/python/plotnine.py +++ b/plots/wireframe-3d-basic/implementations/python/plotnine.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai wireframe-3d-basic: Basic 3D Wireframe Plot -Library: plotnine 0.15.8 | Python 3.13.13 -Quality: pending | Created: 2026-09-10 +Library: plotnine 0.15.8 | Python 3.13.15 +Quality: 72/100 | Created: 2026-09-10 """ import os diff --git a/plots/wireframe-3d-basic/metadata/python/plotnine.yaml b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml index 03d7b996198..e82b16f6a7d 100644 --- a/plots/wireframe-3d-basic/metadata/python/plotnine.yaml +++ b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for plotnine implementation of wireframe-3d-basic -# Auto-generated by impl-generate.yml - library: plotnine language: python specification_id: wireframe-3d-basic created: '2026-09-10T06:29:36Z' -updated: '2026-09-10T06:29:36Z' +updated: '2026-09-10T06:33:59Z' generated_by: claude-sonnet workflow_run: 34445071277 issue: 1015 @@ -15,7 +12,261 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/wireframe preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/wireframe-3d-basic/python/plotnine/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: null +quality_score: 72 review: - strengths: [] - weaknesses: [] + strengths: + - 'Correct Imprint theme-adaptive chrome in both renders: page background matches + #FAF8F1 (light) / #1A1A17 (dark), ink tokens flip correctly, and the mesh stays + #009E73 (brand green) unchanged between themes with no dark-on-dark or light-on-light + legibility failures.' + - 'Title matches the mandated format exactly: ''wireframe-3d-basic · python · plotnine + · anyplot.ai''.' + - 'Respects the plotnine ''no workarounds'' rule: since plotnine has no native 3D + grammar, the implementation builds its own orthographic projection (elevation + 30°/azimuth 45°) and draws the result with plotnine''s own geom_path/geom_segment/geom_text + primitives instead of falling back to matplotlib.' + - All three axes are labeled with units (X/Y in cm, Z in mm) and tick marks, and + wireframe mesh lines run in both x and y directions per the spec's 'Notes' section. + - 'Clean top-to-bottom script: no functions/classes, all imports used, correct plot.save(...) + API and filename pattern.' + weaknesses: + - 'No depth/hidden-line handling: every mesh line (near side and far side of the + surface) is drawn at the same fixed alpha=0.35 with no z-depth-based ordering + or fading, so the front and back of the ripple surface are fully superimposed. + This produces a dense, tangled cluster of crossing lines near the plot center/lower + dome that reads as visual noise rather than a legible 3D structure. Fix: fade + or thin lines by depth (e.g. scale alpha/linewidth by the projected view-direction + distance, or draw back-facing rows/cols first at lower alpha and front-facing + ones last at higher alpha) to approximate hidden-line suppression, and/or reduce + grid_n slightly (e.g. 20-22) to reduce line density.' + - 'Z-axis tick labels (''1'', ''0'', ''-1'') are pushed 13 units left of their true + projected position (z_ticks[''px''] = z_px - 13) to avoid merging with the Y-axis + tick column, but this leaves them visually stranded far from the vertical Z-axis + line at top-center, with no tick marks or leader connecting them to that axis + — a viewer can easily mistake them for an unrelated fourth axis. Fix: move the + Z tick labels closer to the Z-axis line (smaller offset) or add short tick marks/leader + segments connecting each Z label back to the axis.' + - The combination of the two issues above works against the spec's stated goal that + a wireframe should 'reveal the underlying structure' of the surface — the current + render is harder to parse as a coherent 3D ripple than it should be, hurting overall + storytelling/data clarity even though the underlying math and data range are correct. + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1 — not pure white, not dark. + Chrome: Title "wireframe-3d-basic · python · plotnine · anyplot.ai" centered at top in dark ink, fully visible, not clipped. Bold axis titles "X (cm)", "Y (cm)", "Z (mm)" placed at the ends of the three projected axis edges. Tick labels (-6, -3, 0, 3, 6 for X/Y; 1, 0, -1 for Z) rendered in a softer gray-ink tone. All text is clearly readable against the light background. + Data: A green (#009E73) wireframe mesh forming two nested dome/ripple shapes (matching sin(sqrt(x^2+y^2)) over the given domain) with upturned petal-like edges. The mesh lines run in both grid directions as required. However, the near and far surface lines are fully superimposed with no depth ordering, creating a busy tangle of crossings near the lower-center of the shape that obscures the underlying structure. + Legibility verdict: PASS (all text readable; the data-density/tangle issue is a clarity weakness, not a text legibility failure). + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17 — not pure black, not light. + Chrome: Same title, axis titles, and tick labels as the light render, now in light ink (#F0EFE8 for bold axis titles, softer light-gray for tick labels and axis lines) against the dark background. No dark-on-dark issues — all chrome text is clearly legible. + Data: Identical #009E73 green mesh geometry and color to the light render — only the chrome (background, axis line color, text color) flips between themes, confirming correct theme-adaptive implementation. The same near/far mesh superimposition and tangled center region is present, as expected since it's a data/geometry issue independent of theme. + Legibility verdict: PASS (all text readable in both themes; data colors identical between renders). + criteria_checklist: + visual_quality: + score: 20 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 6 + max: 8 + passed: true + comment: All text readable in both themes at appropriate sizes; docked slightly + because the Z-tick labels read ambiguously given their placement (see VQ-06). + - id: VQ-02 + name: No Overlap + score: 3 + max: 6 + passed: false + comment: No text-on-text collisions, but the wireframe mesh lines (near and + far surface) are fully superimposed with no depth ordering, creating a dense + tangle of crossings near the lower-center of the dome. + - id: VQ-03 + name: Element Visibility + score: 3 + max: 6 + passed: false + comment: Individual mesh lines are visible, but the lack of hidden-line/depth + handling significantly reduces the legibility of the overall 3D structure. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Single-hue green mesh on high-contrast off-white/near-black backgrounds; + no red-green reliance. + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: Title proportion and canvas use are fine, no clipping; docked 1 for + the disconnected Z-tick label placement. + - id: VQ-06 + name: Axis Labels & Title + score: 1 + max: 2 + passed: false + comment: Axes labeled with units, but Z tick labels (1, 0, -1) are shifted + 13 units away from the Z axis line with no leader/tick connecting them, + making their association ambiguous. + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series is #009E73, backgrounds match #FAF8F1/#1A1A17, both + themes correct and consistent.' + design_excellence: + score: 10 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Creative, technically sophisticated approach (manual orthographic + projection drawn via native plotnine grammar) raised above the generic-styling + default, though execution has clarity flaws. + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: true + comment: theme_void removes chrome cleanly and lines are subtle/thin, but + the tangled overlap undercuts overall refinement. + - id: DE-03 + name: Data Storytelling + score: 2 + max: 6 + passed: false + comment: The mesh tangle and disconnected Z-tick labels work against a clear + focal point/readable structure; kept at default. + spec_compliance: + score: 14 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 4 + max: 5 + passed: true + comment: Recognizable as a 3D wireframe via axis box + mesh, though the visual + clutter slightly undercuts the read. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Grid lines in both x/y directions, elevation 30/azimuth 45 perspective, + all three axes labeled with ticks. + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: x/y/z mapped correctly; axes show the full data range. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title matches the mandated format exactly; no legend needed for single + series. + data_quality: + score: 13 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 4 + max: 6 + passed: true + comment: Shows the grid mesh and ripple surface, but the tangle reduces how + well it 'reveals the underlying structure' per the spec's stated purpose. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Circular drumhead vibration mode (sin(sqrt(x^2+y^2))) matches the + spec's suggested ripple example, plausible and neutral. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: grid_n=26 within the 20-50 recommended range; domain and units (cm/mm) + are sensible. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: No functions/classes, linear top-to-bottom script. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Deterministic output; np.random.seed present though unused since + no randomness drives the data. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: Only used imports (numpy, pandas, plotnine components). + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Well-organized projection math with clear comments explaining the + technique; no fake UI/interactivity. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves as plot-{THEME}.png via plot.save(dpi=400, width=8, height=4.5, + units='in') matching the library's canvas contract. + library_mastery: + score: 5 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 3 + max: 5 + passed: true + comment: Uses plotnine's own grammar (geom_path, geom_segment, geom_text, + coord_fixed, theme_void) as idiomatically as possible given plotnine has + no native 3D support. + - id: LM-02 + name: Distinctive Features + score: 2 + max: 5 + passed: true + comment: Creative manual orthographic-projection technique to simulate 3D + within plotnine's 2D grammar, raised above the generic-usage default. + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - 3d-projection + - manual-ticks + - annotations + patterns: + - data-generation + - matrix-construction + dataprep: [] + styling: + - minimal-chrome + - alpha-blending From f150a211f64e22551d6d2fafb96c35c58142ea69 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Sep 2026 06:39:04 +0000 Subject: [PATCH 4/7] fix(plotnine): address review feedback for wireframe-3d-basic Attempt 1/4 - fixes based on AI review --- .../implementations/python/plotnine.py | 90 ++++++++++++++----- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/plots/wireframe-3d-basic/implementations/python/plotnine.py b/plots/wireframe-3d-basic/implementations/python/plotnine.py index 5663b6dd8af..cac2c1dbfb4 100644 --- a/plots/wireframe-3d-basic/implementations/python/plotnine.py +++ b/plots/wireframe-3d-basic/implementations/python/plotnine.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai wireframe-3d-basic: Basic 3D Wireframe Plot Library: plotnine 0.15.8 | Python 3.13.15 Quality: 72/100 | Created: 2026-09-10 @@ -13,11 +13,11 @@ coord_fixed, element_rect, element_text, - geom_path, geom_segment, geom_text, ggplot, labs, + scale_alpha_continuous, theme, theme_void, ) @@ -52,9 +52,17 @@ def project(x, y, z): return px, py +def depth(x, y, z): + """Distance along the camera's view direction — larger means closer to + the viewer, so it doubles as a painter's-algorithm draw-order key and as + the source for depth-based alpha (approximates hidden-line suppression + since plotnine has no real depth buffer).""" + return x * view_dir[0] + y * view_dir[1] + z * Z_LIFT * view_dir[2] + + # Data — circular drumhead vibration mode: displacement z = sin(sqrt(x^2 + y^2)) np.random.seed(42) -grid_n = 26 +grid_n = 21 # kept modest (20-22) so depth-faded lines stay legible, not a tangle x_vals = np.linspace(-6, 6, grid_n) y_vals = np.linspace(-6, 6, grid_n) grid_x, grid_y = np.meshgrid(x_vals, y_vals) @@ -64,19 +72,39 @@ def project(x, y, z): floor_z = z_min - 0.3 ceil_z = z_max + 0.3 -# Wireframe mesh lines running in both x and y directions (per spec) -mesh_rows = [] -for j in range(grid_n): - px, py = project(grid_x[:, j], grid_y[:, j], grid_z[:, j]) - for i in range(grid_n): - mesh_rows.append({"px": px[i], "py": py[i], "line": f"col_{j}"}) +grid_px, grid_py = project(grid_x, grid_y, grid_z) +grid_depth = depth(grid_x, grid_y, grid_z) + +# Wireframe mesh as individual edges (not whole rows/columns) so each edge can +# carry its own depth-based alpha: far-side edges fade low, near-side edges +# stay opaque, which reads as an approximate hidden-line-suppressed surface +# instead of a flat tangle of fully superimposed lines. +edges = [] for i in range(grid_n): - px, py = project(grid_x[i, :], grid_y[i, :], grid_z[i, :]) - for j in range(grid_n): - mesh_rows.append({"px": px[j], "py": py[j], "line": f"row_{i}"}) -mesh = pd.DataFrame(mesh_rows) -mesh_cols = mesh[mesh["line"].str.startswith("col_")] -mesh_rows_df = mesh[mesh["line"].str.startswith("row_")] + for j in range(grid_n - 1): + edges.append( + { + "px": grid_px[i, j], + "py": grid_py[i, j], + "pxend": grid_px[i, j + 1], + "pyend": grid_py[i, j + 1], + "edge_depth": (grid_depth[i, j] + grid_depth[i, j + 1]) / 2, + } + ) +for j in range(grid_n): + for i in range(grid_n - 1): + edges.append( + { + "px": grid_px[i, j], + "py": grid_py[i, j], + "pxend": grid_px[i + 1, j], + "pyend": grid_py[i + 1, j], + "edge_depth": (grid_depth[i, j] + grid_depth[i + 1, j]) / 2, + } + ) +# Sort back-to-front so later (nearer, higher-alpha) edges paint over earlier +# (farther, lower-alpha) ones — plotnine draws geom_segment rows in data order. +mesh_edges = pd.DataFrame(edges).sort_values("edge_depth", ignore_index=True) # Axis box: three edges meeting at the front-left-bottom corner axis_lines = pd.DataFrame( @@ -105,12 +133,19 @@ def project(x, y, z): ) ticks["px"], ticks["py"] = project(ticks["x"], ticks["y"], ticks["z"]) -# Z ticks sit on the vertical axis line itself; nudge the label text (not the -# axis line) sideways past the Y-axis tick column so the two groups don't merge. -z_ticks = pd.DataFrame({"x": -6, "y": -6, "z": z_breaks, "label": [f"{v:g}" for v in z_breaks]}) -z_px, z_py = project(z_ticks["x"], z_ticks["y"], z_ticks["z"]) -z_ticks["px"] = z_px - 13 -z_ticks["py"] = z_py +# Z ticks sit on the vertical axis line itself. A short leader segment (tick +# mark) connects each label back to the axis line so it reads as belonging to +# the Z axis rather than as a stray fourth axis (previously offset -13 with +# no connector, leaving the labels visually stranded). +Z_TICK_LEADER = 1.2 +Z_TICK_LABEL_GAP = 0.6 +z_axis_px, z_axis_py = project(-6, -6, z_breaks) +z_ticks = pd.DataFrame( + {"px": z_axis_px - Z_TICK_LEADER - Z_TICK_LABEL_GAP, "py": z_axis_py, "label": [f"{v:g}" for v in z_breaks]} +) +z_tick_leaders = pd.DataFrame( + {"px": z_axis_px, "py": z_axis_py, "pxend": z_axis_px - Z_TICK_LEADER, "pyend": z_axis_py} +) axis_labels = pd.DataFrame( { @@ -125,11 +160,18 @@ def project(x, y, z): # Plot plot = ( ggplot() - + geom_path(aes("px", "py", group="line"), mesh_cols, color=BRAND, size=0.3, alpha=0.35) - + geom_path(aes("px", "py", group="line"), mesh_rows_df, color=BRAND, size=0.3, alpha=0.35) + + geom_segment( + aes(x="px", y="py", xend="pxend", yend="pyend", alpha="edge_depth"), + mesh_edges, + color=BRAND, + size=0.3, + show_legend=False, + ) + + scale_alpha_continuous(range=(0.12, 0.6)) + geom_segment(aes(x="px", y="py", xend="pxend", yend="pyend"), axis_lines, color=INK_SOFT, size=0.6) + + geom_segment(aes(x="px", y="py", xend="pxend", yend="pyend"), z_tick_leaders, color=INK_SOFT, size=0.6) + geom_text(aes("px", "py", label="label"), ticks, color=INK_SOFT, size=3.3) - + geom_text(aes("px", "py", label="label"), z_ticks, color=INK_SOFT, size=3.3) + + geom_text(aes("px", "py", label="label"), z_ticks, color=INK_SOFT, size=3.3, ha="right") + geom_text(aes("px", "py", label="label"), axis_labels, color=INK, size=3.6, fontweight="bold") + labs(title="wireframe-3d-basic · python · plotnine · anyplot.ai") + coord_fixed(ratio=1) From 762e8ef289b7617bd73ecbb7f6665a2ade5fd261 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Sep 2026 06:43:56 +0000 Subject: [PATCH 5/7] chore(plotnine): update quality score 77 and review feedback for wireframe-3d-basic --- .../implementations/python/plotnine.py | 4 +- .../metadata/python/plotnine.yaml | 219 +++++++++--------- 2 files changed, 106 insertions(+), 117 deletions(-) diff --git a/plots/wireframe-3d-basic/implementations/python/plotnine.py b/plots/wireframe-3d-basic/implementations/python/plotnine.py index cac2c1dbfb4..cae9dafbe90 100644 --- a/plots/wireframe-3d-basic/implementations/python/plotnine.py +++ b/plots/wireframe-3d-basic/implementations/python/plotnine.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai wireframe-3d-basic: Basic 3D Wireframe Plot Library: plotnine 0.15.8 | Python 3.13.15 -Quality: 72/100 | Created: 2026-09-10 +Quality: 77/100 | Created: 2026-09-10 """ import os diff --git a/plots/wireframe-3d-basic/metadata/python/plotnine.yaml b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml index e82b16f6a7d..296db920627 100644 --- a/plots/wireframe-3d-basic/metadata/python/plotnine.yaml +++ b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml @@ -2,7 +2,7 @@ library: plotnine language: python specification_id: wireframe-3d-basic created: '2026-09-10T06:29:36Z' -updated: '2026-09-10T06:33:59Z' +updated: '2026-09-10T06:43:56Z' generated_by: claude-sonnet workflow_run: 34445071277 issue: 1015 @@ -12,231 +12,220 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/wireframe preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/wireframe-3d-basic/python/plotnine/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 72 +quality_score: 77 review: strengths: - - 'Correct Imprint theme-adaptive chrome in both renders: page background matches - #FAF8F1 (light) / #1A1A17 (dark), ink tokens flip correctly, and the mesh stays - #009E73 (brand green) unchanged between themes with no dark-on-dark or light-on-light - legibility failures.' - - 'Title matches the mandated format exactly: ''wireframe-3d-basic · python · plotnine - · anyplot.ai''.' - - 'Respects the plotnine ''no workarounds'' rule: since plotnine has no native 3D - grammar, the implementation builds its own orthographic projection (elevation - 30°/azimuth 45°) and draws the result with plotnine''s own geom_path/geom_segment/geom_text - primitives instead of falling back to matplotlib.' - - All three axes are labeled with units (X/Y in cm, Z in mm) and tick marks, and - wireframe mesh lines run in both x and y directions per the spec's 'Notes' section. - - 'Clean top-to-bottom script: no functions/classes, all imports used, correct plot.save(...) - API and filename pattern.' + - Technically sophisticated hand-rolled 3D-to-2D projection (view/right/up basis + vectors, painter's-algorithm depth sort, depth-based alpha for approximate hidden-line + suppression) built entirely from plotnine's native geom_segment/geom_text — respects + the library's no-matplotlib-workaround rule. + - Z-axis tick labels now have leader segments connecting them back to the vertical + axis line, fixing the previous attempt's visually stranded floating-label issue. + - 'Correct Imprint palette usage (BRAND #009E73 for the single data series), correct + theme-adaptive PAGE_BG/INK/INK_SOFT tokens, and all chrome text is legible in + both themes with no dark-on-dark or light-on-light failures.' + - Title format, three labeled axes with units and tick marks, and a deterministic + ripple dataset (grid_n=21, seeded) all match the specification. weaknesses: - - 'No depth/hidden-line handling: every mesh line (near side and far side of the - surface) is drawn at the same fixed alpha=0.35 with no z-depth-based ordering - or fading, so the front and back of the ripple surface are fully superimposed. - This produces a dense, tangled cluster of crossing lines near the plot center/lower - dome that reads as visual noise rather than a legible 3D structure. Fix: fade - or thin lines by depth (e.g. scale alpha/linewidth by the projected view-direction - distance, or draw back-facing rows/cols first at lower alpha and front-facing - ones last at higher alpha) to approximate hidden-line suppression, and/or reduce - grid_n slightly (e.g. 20-22) to reduce line density.' - - 'Z-axis tick labels (''1'', ''0'', ''-1'') are pushed 13 units left of their true - projected position (z_ticks[''px''] = z_px - 13) to avoid merging with the Y-axis - tick column, but this leaves them visually stranded far from the vertical Z-axis - line at top-center, with no tick marks or leader connecting them to that axis - — a viewer can easily mistake them for an unrelated fourth axis. Fix: move the - Z tick labels closer to the Z-axis line (smaller offset) or add short tick marks/leader - segments connecting each Z label back to the axis.' - - The combination of the two issues above works against the spec's stated goal that - a wireframe should 'reveal the underlying structure' of the surface — the current - render is harder to parse as a coherent 3D ripple than it should be, hurting overall - storytelling/data clarity even though the underlying math and data range are correct. + - The wireframe reads as a dense, self-crossing tangle (a 'crown'/basket-weave pattern) + rather than a legible rippled surface — Z_LIFT=3.2 combined with the multi-period + sin(sqrt(x^2+y^2)) ripple over a 21x21 grid makes adjacent rings of the surface + overlap heavily once projected to 2D. Reduce Z_LIFT to roughly 1.5-2.0 and/or + slightly reduce grid_n so the concentric ripple structure stays traceable by eye, + and consider widening the depth-alpha range (currently 0.12-0.6) so near-side + rings visually separate more clearly from far-side ones. + - np.random.seed(42) is set but never used — grid_x/grid_y/grid_z are built purely + from np.linspace/np.meshgrid/np.sin with no call into np.random. Remove the dead + seed call (the data is already fully deterministic without it). + - The X/Y/Z axis lines all converge at a point that sits visually in the middle + of the wireframe mass, so the eye is drawn straight through the densest part of + the crossing mesh lines instead of tracing the axis frame from a clear corner + — consider nudging the axis-box corner or lightening mesh alpha near the convergence + point so the axis reads as a distinct frame rather than cutting through the data. image_description: |- Light render (plot-light.png): - Background: Warm off-white, consistent with #FAF8F1 — not pure white, not dark. - Chrome: Title "wireframe-3d-basic · python · plotnine · anyplot.ai" centered at top in dark ink, fully visible, not clipped. Bold axis titles "X (cm)", "Y (cm)", "Z (mm)" placed at the ends of the three projected axis edges. Tick labels (-6, -3, 0, 3, 6 for X/Y; 1, 0, -1 for Z) rendered in a softer gray-ink tone. All text is clearly readable against the light background. - Data: A green (#009E73) wireframe mesh forming two nested dome/ripple shapes (matching sin(sqrt(x^2+y^2)) over the given domain) with upturned petal-like edges. The mesh lines run in both grid directions as required. However, the near and far surface lines are fully superimposed with no depth ordering, creating a busy tangle of crossings near the lower-center of the shape that obscures the underlying structure. - Legibility verdict: PASS (all text readable; the data-density/tangle issue is a clarity weakness, not a text legibility failure). + Background: Warm off-white, matches #FAF8F1 — not pure white, not dark. + Chrome: Title "wireframe-3d-basic · python · plotnine · anyplot.ai" centered at top in dark ink, clearly readable. Three axis labels ("X (cm)", "Y (cm)", "Z (mm)") in bold dark text at the plot corners. Tick labels (-6,-3,0,3,6 on X/Y; -1,0,1 on Z with short leader segments back to the Z axis line) all in a soft dark gray, all legible against the light background. A three-edge axis "box" (dark gray lines) meets at a single point roughly in the middle of the composition. + Data: A dense mesh of thin green (#009E73) segments forming a radially-symmetric ripple surface (sin(sqrt(x^2+y^2))) projected in pseudo-3D via depth-weighted alpha (fainter = farther, more opaque = nearer). The overall silhouette reads as a crown/flower shape with two upturned "wing" lobes at the sides and a domed peak in the center, but the interior is a dense tangle of crossing diagonal lines rather than a cleanly separable set of concentric rings. + Legibility verdict: PASS (all title/axis/tick text is clearly readable against the light background; no light-on-light issues). Element visibility of the data mesh itself is weak due to overlap density (see weaknesses). Dark render (plot-dark.png): - Background: Warm near-black, consistent with #1A1A17 — not pure black, not light. - Chrome: Same title, axis titles, and tick labels as the light render, now in light ink (#F0EFE8 for bold axis titles, softer light-gray for tick labels and axis lines) against the dark background. No dark-on-dark issues — all chrome text is clearly legible. - Data: Identical #009E73 green mesh geometry and color to the light render — only the chrome (background, axis line color, text color) flips between themes, confirming correct theme-adaptive implementation. The same near/far mesh superimposition and tangled center region is present, as expected since it's a data/geometry issue independent of theme. - Legibility verdict: PASS (all text readable in both themes; data colors identical between renders). + Background: Warm near-black, matches #1A1A17 — not pure black, not light. + Chrome: Same title, axis labels, and tick labels as the light render, all rendered in light ink (#F0EFE8) / soft light gray (#B8B7B0), clearly legible against the dark background. No dark-on-dark failures anywhere — axis lines, tick labels, and axis titles all show good contrast. + Data: Data colors are identical to the light render — the same #009E73 green mesh with the same depth-alpha treatment, confirming chrome-only theme flip as required. Same crown/tangle silhouette and same interior density issue as the light render. + Legibility verdict: PASS (all chrome text readable, brand green clearly visible against the dark surface; no dark-on-dark). Element visibility of the mesh itself is the same weakness as light. criteria_checklist: visual_quality: - score: 20 + score: 23 max: 30 items: - id: VQ-01 name: Text Legibility - score: 6 + score: 7 max: 8 passed: true - comment: All text readable in both themes at appropriate sizes; docked slightly - because the Z-tick labels read ambiguously given their placement (see VQ-06). + comment: Title, axis labels, and all tick labels are clearly readable in both + themes; no dark-on-dark or light-on-light failures. - id: VQ-02 name: No Overlap - score: 3 + score: 5 max: 6 - passed: false - comment: No text-on-text collisions, but the wireframe mesh lines (near and - far surface) are fully superimposed with no depth ordering, creating a dense - tangle of crossings near the lower-center of the dome. + passed: true + comment: No text-on-text or text-on-data collisions, though tick labels sit + close to the busiest part of the mesh. - id: VQ-03 name: Element Visibility - score: 3 + score: 2 max: 6 passed: false - comment: Individual mesh lines are visible, but the lack of hidden-line/depth - handling significantly reduces the legibility of the overall 3D structure. + comment: Wireframe mesh reads as a dense self-crossing tangle rather than + a legible surface; Z_LIFT=3.2 over-exaggerates the multi-period ripple, + causing rings to overlap heavily in projection. - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Single-hue green mesh on high-contrast off-white/near-black backgrounds; - no red-green reliance. + comment: Single-hue mesh with high contrast against both backgrounds; no red-green + reliance. - id: VQ-05 name: Layout & Canvas score: 3 max: 4 passed: true - comment: Title proportion and canvas use are fine, no clipping; docked 1 for - the disconnected Z-tick label placement. + comment: Good use of the 3200x1800 canvas, nothing clipped, but axis lines + converge through the densest part of the data mass rather than framing it + from a clear corner. - id: VQ-06 name: Axis Labels & Title - score: 1 + score: 2 max: 2 - passed: false - comment: Axes labeled with units, but Z tick labels (1, 0, -1) are shifted - 13 units away from the Z axis line with no leader/tick connecting them, - making their association ambiguous. + passed: true + comment: Descriptive axis labels with units (cm, mm). - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series is #009E73, backgrounds match #FAF8F1/#1A1A17, both - themes correct and consistent.' + comment: 'First/only series is #009E73; both renders use the correct theme-adaptive + #FAF8F1/#1A1A17 backgrounds.' design_excellence: - score: 10 + score: 11 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 5 + score: 4 max: 8 - passed: true - comment: Creative, technically sophisticated approach (manual orthographic - projection drawn via native plotnine grammar) raised above the generic-styling - default, though execution has clarity flaws. + passed: false + comment: Technically sophisticated hand-rolled projection and depth-alpha + hidden-line approximation, but the resulting visual is busy and hard to + parse. - id: DE-02 name: Visual Refinement - score: 3 + score: 4 max: 6 passed: true - comment: theme_void removes chrome cleanly and lines are subtle/thin, but - the tangled overlap undercuts overall refinement. + comment: theme_void removes spines/grid, generous whitespace, minimal chrome. - id: DE-03 name: Data Storytelling - score: 2 + score: 3 max: 6 passed: false - comment: The mesh tangle and disconnected Z-tick labels work against a clear - focal point/readable structure; kept at default. + comment: Depth-based alpha attempts a near/far hierarchy but the overall crown-shaped + tangle doesn't clearly guide the viewer through the surface topology. spec_compliance: - score: 14 + score: 15 max: 15 items: - id: SC-01 name: Plot Type - score: 4 + score: 5 max: 5 passed: true - comment: Recognizable as a 3D wireframe via axis box + mesh, though the visual - clutter slightly undercuts the read. + comment: Hand-projected 3D wireframe built from plotnine's own geoms, per + the library's no-matplotlib-workaround rule. - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Grid lines in both x/y directions, elevation 30/azimuth 45 perspective, - all three axes labeled with ticks. + comment: Grid lines in both x and y directions, elevation-30/azimuth-45 perspective, + labeled X/Y/Z axes with ticks. - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: x/y/z mapped correctly; axes show the full data range. + comment: x/y/z correctly mapped and shown. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title matches the mandated format exactly; no legend needed for single - series. + comment: Title matches required format; single series needs no legend. data_quality: - score: 13 + score: 15 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 4 + score: 6 max: 6 passed: true - comment: Shows the grid mesh and ripple surface, but the tangle reduces how - well it 'reveals the underlying structure' per the spec's stated purpose. + comment: Shows grid mesh, axis box, tick marks, and perspective — all plot-type + aspects covered. - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Circular drumhead vibration mode (sin(sqrt(x^2+y^2))) matches the - spec's suggested ripple example, plausible and neutral. + comment: Circular drumhead vibration mode with plausible cm/mm units, neutral + subject matter. - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: grid_n=26 within the 20-50 recommended range; domain and units (cm/mm) - are sensible. + comment: Axis tick values reflect true data range; visual height exaggeration + (Z_LIFT) does not distort the labeled data. code_quality: - score: 10 + score: 8 max: 10 items: - id: CQ-01 name: KISS Structure - score: 3 + score: 2 max: 3 passed: true - comment: No functions/classes, linear top-to-bottom script. + comment: Two small helper functions (project, depth) are used, justified by + reused projection math, but deviate from a pure no-functions script. - id: CQ-02 name: Reproducibility - score: 2 + score: 1 max: 2 - passed: true - comment: Deterministic output; np.random.seed present though unused since - no randomness drives the data. + passed: false + comment: np.random.seed(42) is set but never used — all data is deterministic + via linspace/meshgrid/sin; dead code. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only used imports (numpy, pandas, plotnine components). + comment: Every imported name is used. - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Well-organized projection math with clear comments explaining the - technique; no fake UI/interactivity. + comment: Appropriate complexity for the projection workaround; no fake UI. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot-{THEME}.png via plot.save(dpi=400, width=8, height=4.5, - units='in') matching the library's canvas contract. + comment: Saves plot-{THEME}.png at width=8,height=4.5,dpi=400 matching the + landscape canvas rule. library_mastery: score: 5 max: 10 @@ -246,23 +235,23 @@ review: score: 3 max: 5 passed: true - comment: Uses plotnine's own grammar (geom_path, geom_segment, geom_text, - coord_fixed, theme_void) as idiomatically as possible given plotnine has - no native 3D support. + comment: Uses plotnine's own geom_segment/geom_text/theme grammar throughout, + though the overall approach is necessarily a workaround rather than plotnine's + native idiom. - id: LM-02 name: Distinctive Features score: 2 max: 5 - passed: true - comment: Creative manual orthographic-projection technique to simulate 3D - within plotnine's 2D grammar, raised above the generic-usage default. + passed: false + comment: Creative custom 3D projection and painter's-algorithm depth sort + go beyond generic usage, but execution quality (the tangle) limits the payoff. verdict: REJECTED impl_tags: dependencies: [] techniques: - 3d-projection - manual-ticks - - annotations + - layer-composition patterns: - data-generation - matrix-construction From ac8cb6580e5feda00208788e871511495e4afcbf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Sep 2026 06:49:28 +0000 Subject: [PATCH 6/7] fix(plotnine): address review feedback for wireframe-3d-basic Attempt 2/4 - fixes based on AI review --- .../implementations/python/plotnine.py | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/plots/wireframe-3d-basic/implementations/python/plotnine.py b/plots/wireframe-3d-basic/implementations/python/plotnine.py index cae9dafbe90..4bc637c3900 100644 --- a/plots/wireframe-3d-basic/implementations/python/plotnine.py +++ b/plots/wireframe-3d-basic/implementations/python/plotnine.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai wireframe-3d-basic: Basic 3D Wireframe Plot Library: plotnine 0.15.8 | Python 3.13.15 Quality: 77/100 | Created: 2026-09-10 @@ -43,7 +43,7 @@ right_axis /= np.linalg.norm(right_axis) up_axis = np.cross(right_axis, view_dir) -Z_LIFT = 3.2 # visual height exaggeration so the shallow membrane displacement reads clearly +Z_LIFT = 1.8 # visual height exaggeration so the shallow membrane displacement reads clearly def project(x, y, z): @@ -61,7 +61,6 @@ def depth(x, y, z): # Data — circular drumhead vibration mode: displacement z = sin(sqrt(x^2 + y^2)) -np.random.seed(42) grid_n = 21 # kept modest (20-22) so depth-faded lines stay legible, not a tangle x_vals = np.linspace(-6, 6, grid_n) y_vals = np.linspace(-6, 6, grid_n) @@ -106,13 +105,17 @@ def depth(x, y, z): # (farther, lower-alpha) ones — plotnine draws geom_segment rows in data order. mesh_edges = pd.DataFrame(edges).sort_values("edge_depth", ignore_index=True) -# Axis box: three edges meeting at the front-left-bottom corner +# Axis box: three edges meeting at the (x=6, y=-6) corner. This corner sits off +# the camera's azimuth-45 view axis (unlike the diagonally opposite (-6, -6) +# corner, which projects to dead screen-center and would drag the axis frame +# straight through the densest part of the mesh), so the frame reads as a +# distinct side reference instead of cutting through the data. axis_lines = pd.DataFrame( { - "x": [-6, -6, -6], + "x": [6, 6, 6], "y": [-6, -6, -6], "z": [floor_z, floor_z, floor_z], - "xend": [6, -6, -6], + "xend": [-6, 6, 6], "yend": [-6, 6, -6], "zend": [floor_z, floor_z, ceil_z], } @@ -127,7 +130,7 @@ def depth(x, y, z): ticks = pd.concat( [ pd.DataFrame({"x": x_breaks, "y": -9.6, "z": floor_z, "label": [f"{v:g}" for v in x_breaks]}), - pd.DataFrame({"x": -9.6, "y": y_breaks, "z": floor_z, "label": [f"{v:g}" for v in y_breaks]}), + pd.DataFrame({"x": 9.6, "y": y_breaks, "z": floor_z, "label": [f"{v:g}" for v in y_breaks]}), ], ignore_index=True, ) @@ -139,7 +142,7 @@ def depth(x, y, z): # no connector, leaving the labels visually stranded). Z_TICK_LEADER = 1.2 Z_TICK_LABEL_GAP = 0.6 -z_axis_px, z_axis_py = project(-6, -6, z_breaks) +z_axis_px, z_axis_py = project(6, -6, z_breaks) z_ticks = pd.DataFrame( {"px": z_axis_px - Z_TICK_LEADER - Z_TICK_LABEL_GAP, "py": z_axis_py, "label": [f"{v:g}" for v in z_breaks]} ) @@ -149,7 +152,7 @@ def depth(x, y, z): axis_labels = pd.DataFrame( { - "x": [9.4, -6, -6], + "x": [-9.4, 6, 6], "y": [-6, 9.4, -6], "z": [floor_z, floor_z, ceil_z + 1.0], "label": ["X (cm)", "Y (cm)", "Z (mm)"], @@ -167,7 +170,7 @@ def depth(x, y, z): size=0.3, show_legend=False, ) - + scale_alpha_continuous(range=(0.12, 0.6)) + + scale_alpha_continuous(range=(0.08, 0.85)) + geom_segment(aes(x="px", y="py", xend="pxend", yend="pyend"), axis_lines, color=INK_SOFT, size=0.6) + geom_segment(aes(x="px", y="py", xend="pxend", yend="pyend"), z_tick_leaders, color=INK_SOFT, size=0.6) + geom_text(aes("px", "py", label="label"), ticks, color=INK_SOFT, size=3.3) From 6085145689548b7e9df3a6c9bc6550afd66c91a3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 10 Sep 2026 06:55:43 +0000 Subject: [PATCH 7/7] chore(plotnine): update quality score 85 and review feedback for wireframe-3d-basic --- .../implementations/python/plotnine.py | 4 +- .../metadata/python/plotnine.yaml | 200 +++++++++--------- 2 files changed, 100 insertions(+), 104 deletions(-) diff --git a/plots/wireframe-3d-basic/implementations/python/plotnine.py b/plots/wireframe-3d-basic/implementations/python/plotnine.py index 4bc637c3900..bb089e52a53 100644 --- a/plots/wireframe-3d-basic/implementations/python/plotnine.py +++ b/plots/wireframe-3d-basic/implementations/python/plotnine.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai wireframe-3d-basic: Basic 3D Wireframe Plot Library: plotnine 0.15.8 | Python 3.13.15 -Quality: 77/100 | Created: 2026-09-10 +Quality: 85/100 | Created: 2026-09-10 """ import os diff --git a/plots/wireframe-3d-basic/metadata/python/plotnine.yaml b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml index 296db920627..ab8377247d1 100644 --- a/plots/wireframe-3d-basic/metadata/python/plotnine.yaml +++ b/plots/wireframe-3d-basic/metadata/python/plotnine.yaml @@ -2,7 +2,7 @@ library: plotnine language: python specification_id: wireframe-3d-basic created: '2026-09-10T06:29:36Z' -updated: '2026-09-10T06:43:56Z' +updated: '2026-09-10T06:55:43Z' generated_by: claude-sonnet workflow_run: 34445071277 issue: 1015 @@ -12,48 +12,49 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/wireframe preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/wireframe-3d-basic/python/plotnine/plot-dark.png preview_html_light: null preview_html_dark: null -quality_score: 77 +quality_score: 85 review: strengths: - - Technically sophisticated hand-rolled 3D-to-2D projection (view/right/up basis - vectors, painter's-algorithm depth sort, depth-based alpha for approximate hidden-line - suppression) built entirely from plotnine's native geom_segment/geom_text — respects - the library's no-matplotlib-workaround rule. - - Z-axis tick labels now have leader segments connecting them back to the vertical - axis line, fixing the previous attempt's visually stranded floating-label issue. - - 'Correct Imprint palette usage (BRAND #009E73 for the single data series), correct - theme-adaptive PAGE_BG/INK/INK_SOFT tokens, and all chrome text is legible in - both themes with no dark-on-dark or light-on-light failures.' - - Title format, three labeled axes with units and tick marks, and a deterministic - ripple dataset (grid_n=21, seeded) all match the specification. + - Faithful adherence to plotnine's "no 3D workaround" rule — implements genuine + camera-projection math (elevation 30°/azimuth 45°) instead of faking 3D or falling + back to matplotlib, exactly as the library rules require. + - 'Depth-based alpha combined with painter''s-algorithm edge sorting creates a convincing + hidden-line-suppression effect: near-side mesh reads crisp, far-side mesh recedes, + giving real visual hierarchy.' + - 'Correct theme-adaptive chrome: identical brand-green mesh color across light/dark + renders, INK/INK_SOFT correctly applied to axis lines, ticks and labels, both + plot backgrounds match spec exactly (#FAF8F1 / #1A1A17).' + - Deterministic, well-commented code that explains non-obvious choices, e.g. picking + the (x=6, y=-6) axis corner specifically because it stays off the camera's view + axis instead of cutting through the densest part of the mesh. + - Physically plausible "drumhead vibration" framing with sensible cm/mm units and + a well-chosen 21x21 grid density that stays legible instead of turning into a + tangle. weaknesses: - - The wireframe reads as a dense, self-crossing tangle (a 'crown'/basket-weave pattern) - rather than a legible rippled surface — Z_LIFT=3.2 combined with the multi-period - sin(sqrt(x^2+y^2)) ripple over a 21x21 grid makes adjacent rings of the surface - overlap heavily once projected to 2D. Reduce Z_LIFT to roughly 1.5-2.0 and/or - slightly reduce grid_n so the concentric ripple structure stays traceable by eye, - and consider widening the depth-alpha range (currently 0.12-0.6) so near-side - rings visually separate more clearly from far-side ones. - - np.random.seed(42) is set but never used — grid_x/grid_y/grid_z are built purely - from np.linspace/np.meshgrid/np.sin with no call into np.random. Remove the dead - seed call (the data is already fully deterministic without it). - - The X/Y/Z axis lines all converge at a point that sits visually in the middle - of the wireframe mass, so the eye is drawn straight through the densest part of - the crossing mesh lines instead of tracing the axis frame from a clear corner - — consider nudging the axis-box corner or lightening mesh alpha near the convergence - point so the axis reads as a distinct frame rather than cutting through the data. + - In the upper-right region, the X-axis tick label '0' (and to a lesser extent '3') + lands very close to the Z-axis tick labels ('1'/'0'), crowding that area — increase + the separation between the X-tick offset (currently y=-9.6) and the Z-axis tick/leader + cluster, e.g. by pushing X-ticks further out or nudging Z-tick leaders away from + the axis corner. + - The far/top ridge of the wireframe fades to alpha≈0.08 in the depth cue, verging + on invisible in both renders — raise the alpha floor in scale_alpha_continuous + (e.g. to ~0.18-0.20) so the distant surface stays faintly legible instead of nearly + disappearing. + - Two small top-level helper functions (project, depth) deviate from the CQ-01 'no + functions' default — justified here by the manual 3D-projection math, but keep + them minimal if further edits are made. image_description: |- Light render (plot-light.png): - Background: Warm off-white, matches #FAF8F1 — not pure white, not dark. - Chrome: Title "wireframe-3d-basic · python · plotnine · anyplot.ai" centered at top in dark ink, clearly readable. Three axis labels ("X (cm)", "Y (cm)", "Z (mm)") in bold dark text at the plot corners. Tick labels (-6,-3,0,3,6 on X/Y; -1,0,1 on Z with short leader segments back to the Z axis line) all in a soft dark gray, all legible against the light background. A three-edge axis "box" (dark gray lines) meets at a single point roughly in the middle of the composition. - Data: A dense mesh of thin green (#009E73) segments forming a radially-symmetric ripple surface (sin(sqrt(x^2+y^2))) projected in pseudo-3D via depth-weighted alpha (fainter = farther, more opaque = nearer). The overall silhouette reads as a crown/flower shape with two upturned "wing" lobes at the sides and a domed peak in the center, but the interior is a dense tangle of crossing diagonal lines rather than a cleanly separable set of concentric rings. - Legibility verdict: PASS (all title/axis/tick text is clearly readable against the light background; no light-on-light issues). Element visibility of the data mesh itself is weak due to overlap density (see weaknesses). + Background: Warm off-white (#FAF8F1), correct light-theme surface — not pure white. + Chrome: Title "wireframe-3d-basic · python · plotnine · anyplot.ai" centered at top in dark ink, clearly legible. Axis labels "X (cm)", "Y (cm)", "Z (mm)" in bold dark ink at the ends of their respective axis lines. Tick labels (-6..6 on X and Y, -1/0/1 on Z) rendered in soft grey ink; all are individually legible, though the X=0 and X=3 ticks sit close to the Z-axis tick cluster in the upper-right, creating a crowded (but not fully overlapping) area. + Data: Wireframe mesh in brand green (#009E73), forming a rippled "drumhead" bowl shape with grid lines in both x and y directions. Depth-based alpha makes near-side lines fully opaque and far-side (top ridge) lines fade to near-invisibility (~0.08 alpha) — a deliberate depth cue, though the top ridge borders on too faint. + Legibility verdict: PASS (all text readable; minor tick crowding noted as a weakness, not a legibility failure). Dark render (plot-dark.png): - Background: Warm near-black, matches #1A1A17 — not pure black, not light. - Chrome: Same title, axis labels, and tick labels as the light render, all rendered in light ink (#F0EFE8) / soft light gray (#B8B7B0), clearly legible against the dark background. No dark-on-dark failures anywhere — axis lines, tick labels, and axis titles all show good contrast. - Data: Data colors are identical to the light render — the same #009E73 green mesh with the same depth-alpha treatment, confirming chrome-only theme flip as required. Same crown/tangle silhouette and same interior density issue as the light render. - Legibility verdict: PASS (all chrome text readable, brand green clearly visible against the dark surface; no dark-on-dark). Element visibility of the mesh itself is the same weakness as light. + Background: Warm near-black (#1A1A17), correct dark-theme surface — not pure black. + Chrome: Title, axis labels, and tick labels flip to light ink (#F0EFE8 for labels, #B8B7B0 for axis lines/ticks) and remain clearly readable against the dark background — no dark-on-dark failures observed. + Data: Mesh color is identical brand green (#009E73) to the light render — only chrome (background, ink) flipped, as required. Same depth-fade behavior and same faint top ridge as in the light render. + Legibility verdict: PASS (all text readable against the dark background; same minor tick-crowding note as light render). criteria_checklist: visual_quality: score: 23 @@ -61,79 +62,76 @@ review: items: - id: VQ-01 name: Text Legibility - score: 7 + score: 6 max: 8 passed: true - comment: Title, axis labels, and all tick labels are clearly readable in both - themes; no dark-on-dark or light-on-light failures. + comment: All text readable in both themes; X/Z tick labels crowd together + in the upper-right region - id: VQ-02 name: No Overlap - score: 5 + score: 4 max: 6 passed: true - comment: No text-on-text or text-on-data collisions, though tick labels sit - close to the busiest part of the mesh. + comment: X-axis tick '0' sits very close to Z-axis tick '1', near-collision + but not a full overlap - id: VQ-03 name: Element Visibility - score: 2 + score: 4 max: 6 - passed: false - comment: Wireframe mesh reads as a dense self-crossing tangle rather than - a legible surface; Z_LIFT=3.2 over-exaggerates the multi-period ripple, - causing rings to overlap heavily in projection. + passed: true + comment: Depth-fade alpha floor (~0.08) makes the far/top ridge of the mesh + nearly invisible in both renders - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Single-hue mesh with high contrast against both backgrounds; no red-green - reliance. + comment: Single-hue mesh against ink chrome, strong contrast, no red-green + reliance - id: VQ-05 name: Layout & Canvas score: 3 max: 4 passed: true - comment: Good use of the 3200x1800 canvas, nothing clipped, but axis lines - converge through the densest part of the data mass rather than framing it - from a clear corner. + comment: Canvas dimension gate passed; good overall proportions; minor crowding + in tick region - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Descriptive axis labels with units (cm, mm). + comment: X (cm), Y (cm), Z (mm) all descriptive with units - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First/only series is #009E73; both renders use the correct theme-adaptive - #FAF8F1/#1A1A17 backgrounds.' + comment: 'Single series uses #009E73, identical across themes; backgrounds + match #FAF8F1 / #1A1A17 exactly' design_excellence: - score: 11 + score: 16 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 4 + score: 6 max: 8 - passed: false - comment: Technically sophisticated hand-rolled projection and depth-alpha - hidden-line approximation, but the resulting visual is busy and hard to - parse. + passed: true + comment: Genuine camera-projection math, painter's-algorithm depth sorting, + deliberate axis-corner placement — well above generic defaults - id: DE-02 name: Visual Refinement - score: 4 + score: 5 max: 6 passed: true - comment: theme_void removes spines/grid, generous whitespace, minimal chrome. + comment: theme_void, minimal partial axis frame, generous whitespace, no chart + junk - id: DE-03 name: Data Storytelling - score: 3 + score: 5 max: 6 - passed: false - comment: Depth-based alpha attempts a near/far hierarchy but the overall crown-shaped - tangle doesn't clearly guide the viewer through the surface topology. + passed: true + comment: Depth-fade creates clear near/far hierarchy and a legible focal point spec_compliance: score: 15 max: 15 @@ -143,54 +141,55 @@ review: score: 5 max: 5 passed: true - comment: Hand-projected 3D wireframe built from plotnine's own geoms, per - the library's no-matplotlib-workaround rule. + comment: Correct wireframe representation via legitimate manual 3D-to-2D projection, + per plotnine's no-3D-native-support rule - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Grid lines in both x and y directions, elevation-30/azimuth-45 perspective, - labeled X/Y/Z axes with ticks. + comment: Grid lines in both x/y directions, elevation 30/azimuth 45 camera, + all three axes labeled with ticks - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: x/y/z correctly mapped and shown. + comment: X/Y/Z correctly mapped and shown across full data range - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title matches required format; single series needs no legend. + comment: Title matches mandated format exactly; no legend needed for single + series 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 grid mesh, axis box, tick marks, and perspective — all plot-type - aspects covered. + comment: Covers grid mesh, camera projection, axis frame and ticks; only a + partial (not full-box) axis frame - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Circular drumhead vibration mode with plausible cm/mm units, neutral - subject matter. + comment: Plausible, neutral 'drumhead vibration' framing with sensible cm/mm + units - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Axis tick values reflect true data range; visual height exaggeration - (Z_LIFT) does not distort the labeled data. + comment: X/Y range -6..6 cm, Z displacement ~-1..1 mm, physically sensible + for the described context code_quality: - score: 8 + score: 9 max: 10 items: - id: CQ-01 @@ -198,54 +197,51 @@ review: score: 2 max: 3 passed: true - comment: Two small helper functions (project, depth) are used, justified by - reused projection math, but deviate from a pure no-functions script. + comment: Two small helper functions (project, depth) used, justified by the + manual projection math - id: CQ-02 name: Reproducibility - score: 1 + score: 2 max: 2 - passed: false - comment: np.random.seed(42) is set but never used — all data is deterministic - via linspace/meshgrid/sin; dead code. + passed: true + comment: Fully deterministic linspace/meshgrid, no randomness - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Every imported name is used. + comment: All imports used - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Appropriate complexity for the projection workaround; no fake UI. + comment: Appropriate complexity for a manual 3D projection, no fake UI - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot-{THEME}.png at width=8,height=4.5,dpi=400 matching the - landscape canvas rule. + comment: Saves plot-{THEME}.png at correct landscape dimensions library_mastery: - score: 5 + score: 8 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 3 + score: 4 max: 5 passed: true - comment: Uses plotnine's own geom_segment/geom_text/theme grammar throughout, - though the overall approach is necessarily a workaround rather than plotnine's - native idiom. + comment: Faithful use of plotnine's own geoms (geom_segment, geom_text, theme_void) + to build the projection rather than any workaround - id: LM-02 name: Distinctive Features - score: 2 + score: 4 max: 5 - passed: false - comment: Creative custom 3D projection and painter's-algorithm depth sort - go beyond generic usage, but execution quality (the tangle) limits the payoff. - verdict: REJECTED + passed: true + comment: Custom camera-projection + depth-sorted alpha painter's algorithm + is a genuinely distinctive technique for a library with no native 3D support + verdict: APPROVED impl_tags: dependencies: [] techniques: