From e8bf12b4f25600ec08e18bf85e3ae98c7c80ba47 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Aug 2026 15:21:02 +0000 Subject: [PATCH 1/7] feat(d3): implement ternary-basic --- .../implementations/javascript/d3.js | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 plots/ternary-basic/implementations/javascript/d3.js diff --git a/plots/ternary-basic/implementations/javascript/d3.js b/plots/ternary-basic/implementations/javascript/d3.js new file mode 100644 index 0000000000..4e881a5c10 --- /dev/null +++ b/plots/ternary-basic/implementations/javascript/d3.js @@ -0,0 +1,179 @@ +// anyplot.ai +// ternary-basic: Basic Ternary Plot +// Library: d3 7.9.0 | JavaScript 22 +// Quality: pending | Created: 2026-08-04 + +//# anyplot-orientation: square +const t = window.ANYPLOT_TOKENS; +const { width, height } = window.ANYPLOT_SIZE; +const margin = { top: 210, right: 110, bottom: 110, left: 110 }; +const iw = width - margin.left - margin.right; +const ih = height - margin.top - margin.bottom; + +// --- Data (in-memory, deterministic) ---------------------------------------- +// Soil samples: clay / sand / silt proportions (%), each triplet sums to 100. +function lcg(seed) { + let s = seed; + return () => { + s = (s * 1103515245 + 12345) & 0x7fffffff; + return s / 0x7fffffff; + }; +} +const rand = lcg(42123); +const samples = []; +for (let i = 0; i < 28; i++) { + const wClay = 0.15 + rand() * 0.85; + const wSand = 0.15 + rand() * 0.85; + const wSilt = 0.15 + rand() * 0.85; + const total = wClay + wSand + wSilt; + samples.push({ + clay: (wClay / total) * 100, + sand: (wSand / total) * 100, + silt: (wSilt / total) * 100, + }); +} + +// --- Triangle geometry -------------------------------------------------------- +// Apex = clay, bottom-left = sand, bottom-right = silt (classic soil-texture layout). +const side = iw; +const triHeight = (side * Math.sqrt(3)) / 2; +const apex = { x: margin.left + side / 2, y: margin.top }; +const left = { x: margin.left, y: margin.top + triHeight }; +const right = { x: margin.left + side, y: margin.top + triHeight }; +const centroid = { + x: (apex.x + left.x + right.x) / 3, + y: (apex.y + left.y + right.y) / 3, +}; + +// Barycentric (a=clay, b=sand, c=silt, each 0-1, a+b+c=1) -> pixel coordinates. +function toXY(a, b, c) { + return { + x: a * apex.x + b * left.x + c * right.x, + y: a * apex.y + b * left.y + c * right.y, + }; +} +function outward(p, dist) { + const dx = p.x - centroid.x; + const dy = p.y - centroid.y; + const len = Math.hypot(dx, dy) || 1; + return { x: p.x + (dx / len) * dist, y: p.y + (dy / len) * dist }; +} + +// --- SVG mount ---------------------------------------------------------------- +const svg = d3.select("#container").append("svg").attr("width", width).attr("height", height); + +// --- Grid lines (20% intervals, three families parallel to each edge) -------- +const levels = [0.2, 0.4, 0.6, 0.8]; +const gridSegments = []; +for (const lv of levels) { + gridSegments.push([toXY(lv, 1 - lv, 0), toXY(lv, 0, 1 - lv)]); // parallel to base + gridSegments.push([toXY(1 - lv, lv, 0), toXY(0, lv, 1 - lv)]); // parallel to apex-right + gridSegments.push([toXY(1 - lv, 0, lv), toXY(0, 1 - lv, lv)]); // parallel to apex-left +} +svg + .selectAll(".grid-line") + .data(gridSegments) + .join("line") + .attr("class", "grid-line") + .attr("x1", (d) => d[0].x) + .attr("y1", (d) => d[0].y) + .attr("x2", (d) => d[1].x) + .attr("y2", (d) => d[1].y) + .attr("stroke", t.grid) + .attr("stroke-width", 1.5); + +// --- Triangle border ------------------------------------------------------------ +svg + .append("path") + .attr("d", `M${apex.x},${apex.y} L${left.x},${left.y} L${right.x},${right.y} Z`) + .attr("fill", "none") + .attr("stroke", t.inkSoft) + .attr("stroke-width", 2.5); + +// --- Tick marks + labels (one axis per edge, 0-100 by 20%) ------------------- +const tickLevels = [0, 0.2, 0.4, 0.6, 0.8, 1]; +const ticks = []; +for (const lv of tickLevels) { + ticks.push(toXY(lv, 1 - lv, 0)); // clay axis, edge apex-left + ticks.push(toXY(1 - lv, 0, lv)); // silt axis, edge apex-right + ticks.push(toXY(0, lv, 1 - lv)); // sand axis, edge base +} +for (const p of ticks) { + const tickEnd = outward(p, 14); + const labelPos = outward(p, 34); + svg + .append("line") + .attr("x1", p.x) + .attr("y1", p.y) + .attr("x2", tickEnd.x) + .attr("y2", tickEnd.y) + .attr("stroke", t.inkSoft) + .attr("stroke-width", 1.5); +} +const tickLabels = []; +for (const lv of tickLevels) { + tickLabels.push({ p: outward(toXY(lv, 1 - lv, 0), 34), text: Math.round(lv * 100) }); + tickLabels.push({ p: outward(toXY(1 - lv, 0, lv), 34), text: Math.round(lv * 100) }); + tickLabels.push({ p: outward(toXY(0, lv, 1 - lv), 34), text: Math.round(lv * 100) }); +} +svg + .selectAll(".tick-label") + .data(tickLabels) + .join("text") + .attr("class", "tick-label") + .attr("x", (d) => d.p.x) + .attr("y", (d) => d.p.y) + .attr("text-anchor", "middle") + .attr("dominant-baseline", "middle") + .attr("fill", t.inkSoft) + .style("font-size", "14px") + .style("font-family", "sans-serif") + .text((d) => d.text); + +// --- Vertex labels -------------------------------------------------------------- +const vertexLabels = [ + { p: outward(apex, 78), text: "Clay" }, + { p: outward(left, 78), text: "Sand" }, + { p: outward(right, 78), text: "Silt" }, +]; +svg + .selectAll(".vertex-label") + .data(vertexLabels) + .join("text") + .attr("class", "vertex-label") + .attr("x", (d) => d.p.x) + .attr("y", (d) => d.p.y) + .attr("text-anchor", "middle") + .attr("dominant-baseline", "middle") + .attr("fill", t.ink) + .style("font-size", "20px") + .style("font-weight", "600") + .style("font-family", "sans-serif") + .text((d) => d.text); + +// --- Data points ------------------------------------------------------------ +const points = samples.map((s) => toXY(s.clay / 100, s.sand / 100, s.silt / 100)); +svg + .selectAll(".sample") + .data(points) + .join("circle") + .attr("class", "sample") + .attr("cx", (d) => d.x) + .attr("cy", (d) => d.y) + .attr("r", 9) + .attr("fill", t.palette[0]) + .attr("fill-opacity", 0.85) + .attr("stroke", t.pageBg) + .attr("stroke-width", 1.5); + +// --- Title ------------------------------------------------------------------ +svg + .append("text") + .attr("x", width / 2) + .attr("y", 56) + .attr("text-anchor", "middle") + .attr("fill", t.ink) + .style("font-size", "22px") + .style("font-weight", "600") + .style("font-family", "sans-serif") + .text("ternary-basic · javascript · d3 · anyplot.ai"); From 0306e11d2bfab16f0150acd886653c4e1c98abe7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Aug 2026 15:21:12 +0000 Subject: [PATCH 2/7] chore(d3): add metadata for ternary-basic --- .../ternary-basic/metadata/javascript/d3.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plots/ternary-basic/metadata/javascript/d3.yaml diff --git a/plots/ternary-basic/metadata/javascript/d3.yaml b/plots/ternary-basic/metadata/javascript/d3.yaml new file mode 100644 index 0000000000..3c73f0fab2 --- /dev/null +++ b/plots/ternary-basic/metadata/javascript/d3.yaml @@ -0,0 +1,21 @@ +# Per-library metadata for d3 implementation of ternary-basic +# Auto-generated by impl-generate.yml + +library: d3 +language: javascript +specification_id: ternary-basic +created: '2026-08-04T15:21:12Z' +updated: '2026-08-04T15:21:12Z' +generated_by: claude-sonnet +workflow_run: 30922911394 +issue: 1001 +language_version: 22.23.1 +library_version: 7.9.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 09ab8199ebd8dd00ff19ff95b9d7945c6bf0c125 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Aug 2026 15:28:32 +0000 Subject: [PATCH 3/7] chore(d3): update quality score 80 and review feedback for ternary-basic --- .../implementations/javascript/d3.js | 4 +- .../ternary-basic/metadata/javascript/d3.yaml | 258 +++++++++++++++++- 2 files changed, 253 insertions(+), 9 deletions(-) diff --git a/plots/ternary-basic/implementations/javascript/d3.js b/plots/ternary-basic/implementations/javascript/d3.js index 4e881a5c10..537eeb653a 100644 --- a/plots/ternary-basic/implementations/javascript/d3.js +++ b/plots/ternary-basic/implementations/javascript/d3.js @@ -1,7 +1,7 @@ // anyplot.ai // ternary-basic: Basic Ternary Plot -// Library: d3 7.9.0 | JavaScript 22 -// Quality: pending | Created: 2026-08-04 +// Library: d3 7.9.0 | JavaScript 22.23.1 +// Quality: 80/100 | Created: 2026-08-04 //# anyplot-orientation: square const t = window.ANYPLOT_TOKENS; diff --git a/plots/ternary-basic/metadata/javascript/d3.yaml b/plots/ternary-basic/metadata/javascript/d3.yaml index 3c73f0fab2..ca1df42efd 100644 --- a/plots/ternary-basic/metadata/javascript/d3.yaml +++ b/plots/ternary-basic/metadata/javascript/d3.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for d3 implementation of ternary-basic -# Auto-generated by impl-generate.yml - library: d3 language: javascript specification_id: ternary-basic created: '2026-08-04T15:21:12Z' -updated: '2026-08-04T15:21:12Z' +updated: '2026-08-04T15:28:31Z' generated_by: claude-sonnet workflow_run: 30922911394 issue: 1001 @@ -15,7 +12,254 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/ternary-b preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.html -quality_score: null +quality_score: 80 review: - strengths: [] - weaknesses: [] + strengths: + - Correct barycentric-coordinate math (toXY) drives grid lines, tick marks, and + data points from a single consistent transform — the three grid-line families + and tick axes all line up perfectly with the triangle edges + - Idiomatic D3 data-join pattern (selection.data().join()) used for grid lines, + tick labels, vertex labels, and sample points instead of manual DOM loops + - Deterministic LCG-seeded data generation, self-contained with no fetch/network + calls, matching the D3 reproducibility rule + - Marker sizing (r=9, fill-opacity 0.85, theme-adaptive stroke) is well matched + to the sparse 28-point dataset + - 'Correct Imprint palette usage: first series is #009E73, identical between light + and dark renders, with theme-correct #FAF8F1/#1A1A17 backgrounds and adaptive + ink tokens for all chrome' + weaknesses: + - 'At all three triangle vertices, two tick labels are rendered at the exact same + pixel position and overlap: the clay-axis ''0''/silt-axis ''0'' tick coincides + with the sand-axis ''100'' tick (and the equivalent pairs at the other two vertices), + because toXY() maps both tick''s (a,b,c) to the same vertex coordinate. Zooming + into the PNG shows a visibly doubled/bolder ''0'' fused into the second ''0'' + of ''100'' with chromatic-fringe ghosting. Fix: after building the `tickLabels` + array, dedupe entries whose pixel position (or (level, vertex) combination) coincides + — e.g. skip the sand-axis lv=1 label and the silt-axis lv=0 label (and their clay-axis + counterparts) since the shared vertex is already unambiguous from the two edges + meeting there, or nudge one of the two coincident labels tangentially so they + don''t stack exactly.' + - Vertex labels 'Clay', 'Sand', 'Silt' have no unit shown (no '%'), while the axes + clearly represent percentages (0-100 ticks); add '(%)' to each vertex label for + full VQ-06 credit, matching how the matplotlib sibling implementation labels 'Sand + (%)', 'Silt (%)', 'Clay (%)'. + - The synthetic data generator (wClay/wSand/wSilt = 0.15 + rand()*0.85, normalized + to sum 100) mathematically cannot produce any component below ~7% or above ~77%, + so the scatter never shows near-vertex (high-purity) or near-edge (single-component-dominant) + compositions — a real limitation of ternary data that a good example should demonstrate. + Widen the raw ranges (e.g. down to a lower floor near 0) so a few samples land + close to the vertices/edges. + - No visual hierarchy or storytelling element (e.g. a highlighted classification + region, an emphasized cluster, or a callout) — the plot is a plain uniform-color + scatter; consider highlighting a sub-region or a couple of representative points + to give the viewer a focal point. + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1 - not pure white. + Chrome: Bold dark title "ternary-basic · javascript · d3 · anyplot.ai" centered at top, clearly readable. Equilateral triangle outlined in a dark ink-soft stroke. Bold vertex labels "Clay" (top), "Sand" (bottom-left), "Silt" (bottom-right) in dark ink, clearly legible. Tick labels (0/20/40/60/80/100) along all three edges in a lighter ink-soft gray, readable — EXCEPT at the three vertices, where two tick labels from different axes are drawn at the identical pixel position, producing a visibly doubled/bolder "100" with faint color-fringe ghosting on close inspection (still reads as "100", not fully illegible, but is a real overlap defect). Grid lines at 20% intervals in three directions, subtle light gray, visible but not competing with data. + Data: 28 green (#009E73) circles with a light cream stroke for definition against the light background, fill-opacity 0.85, clustered mostly in the mid-lower portion of the triangle (no points close to the vertices). + Legibility verdict: PASS (all text readable; the vertex tick-label overlap is a genuine but minor defect, not a legibility failure) + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17 - not pure black. + Chrome: Title, vertex labels ("Clay"/"Sand"/"Silt"), and tick labels all rendered in light ink tokens, clearly visible against the dark background — no dark-on-dark failures anywhere. The same vertex tick-label doubling artifact from the light render reproduces identically here (expected, since it's a geometry bug in the tick computation, not a theme issue). + Data: Same #009E73 green circles, fill-opacity 0.85, now with a dark stroke (matching the dark page background) instead of the light-render's cream stroke — data hue is identical to the light render, only the marker's edge stroke color flips with the theme (intentional, theme-adaptive edge treatment). + Legibility verdict: PASS (no dark-on-dark text; all chrome correctly flipped) + criteria_checklist: + visual_quality: + score: 26 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All text readable in both themes; minor ghosting artifact on vertex + '100' labels from overlapping ticks + - id: VQ-02 + name: No Overlap + score: 4 + max: 6 + passed: true + comment: Two tick labels ('0' and '100' from different axes) render at the + identical pixel position at each of the 3 vertices - minimal but real overlap + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Marker size (r=9) and opacity (0.85) well matched to the sparse 28-point + dataset + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Single-series green with theme-adaptive stroke gives good contrast; + no reliance on hue-only distinctions + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Triangle occupies a well-balanced majority of the 2400x2400 canvas + with generous, even margins; nothing clipped + - id: VQ-06 + name: Axis Labels & Title + score: 1 + max: 2 + passed: true + comment: Vertex labels ('Clay'/'Sand'/'Silt') are descriptive but missing + the '%' unit that the 0-100 tick scale represents + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series is #009E73, identical across light/dark renders; backgrounds + and chrome are theme-correct' + design_excellence: + score: 10 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 4 + max: 8 + passed: false + comment: Well-configured single-color scatter; clean but not exceptional + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Subtle grid, generous whitespace, clean sans-serif typography hierarchy + - id: DE-03 + name: Data Storytelling + score: 2 + max: 6 + passed: false + comment: Plain uniform scatter with no highlighted region, focal point, or + emphasis + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: 'Correct ternary plot: triangle, labeled vertices, gridded' + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: 20% grid lines, labeled vertices, distinct markers, and edge tick + marks all present + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Barycentric mapping (clay/sand/silt) is internally consistent across + grid, ticks, and points + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title matches required format exactly; legend correctly omitted for + a single series + data_quality: + score: 12 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 4 + max: 6 + passed: false + comment: Generator's 0.15-1.0 raw range keeps every component within ~7-77%, + so no sample shows a near-vertex/high-purity composition + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Neutral, real-world soil-science scenario (clay/sand/silt texture + sampling) + - id: DQ-03 + name: Appropriate Scale + score: 3 + max: 4 + passed: true + comment: Values plausible for soil composition but the artificially narrow + achievable range is a mild realism gap + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Imports/data/geometry/draw flow; small geometry helper functions + (toXY, outward, lcg) are standard for this repo's D3 implementations + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fixed-seed LCG (42123) + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: No stray imports; only the global d3 + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Appropriately complex for custom ternary geometry, no fake UI + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Follows the mount-node contract correctly, no animation, single svg + append + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Proper selection.data().join() pattern throughout + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: Leverages D3's low-level SVG/data-binding flexibility to build a + bespoke barycentric coordinate system that higher-level chart libraries + can't express natively + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - manual-ticks + patterns: + - data-generation + dataprep: + - normalization + styling: + - alpha-blending From c31d88d11cbcd77b69eae11c57a8370516e746b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Aug 2026 15:34:46 +0000 Subject: [PATCH 4/7] fix(d3): address review feedback for ternary-basic Attempt 1/4 - fixes based on AI review --- .../implementations/javascript/d3.js | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/plots/ternary-basic/implementations/javascript/d3.js b/plots/ternary-basic/implementations/javascript/d3.js index 537eeb653a..ee9db09ff0 100644 --- a/plots/ternary-basic/implementations/javascript/d3.js +++ b/plots/ternary-basic/implementations/javascript/d3.js @@ -22,9 +22,9 @@ function lcg(seed) { const rand = lcg(42123); const samples = []; for (let i = 0; i < 28; i++) { - const wClay = 0.15 + rand() * 0.85; - const wSand = 0.15 + rand() * 0.85; - const wSilt = 0.15 + rand() * 0.85; + const wClay = 0.02 + rand() * 0.98; + const wSand = 0.02 + rand() * 0.98; + const wSilt = 0.02 + rand() * 0.98; const total = wClay + wSand + wSilt; samples.push({ clay: (wClay / total) * 100, @@ -110,8 +110,13 @@ for (const p of ticks) { .attr("stroke", t.inkSoft) .attr("stroke-width", 1.5); } +// Skip lv=0: each axis's "0" tick lands exactly on the vertex where a +// different axis's "100" tick already sits (e.g. the silt axis's 0 coincides +// with the clay axis's 100 at the apex) - drawing both stacks two labels on +// one pixel. The "100" from the owning axis is kept and is unambiguous. const tickLabels = []; for (const lv of tickLevels) { + if (lv === 0) continue; tickLabels.push({ p: outward(toXY(lv, 1 - lv, 0), 34), text: Math.round(lv * 100) }); tickLabels.push({ p: outward(toXY(1 - lv, 0, lv), 34), text: Math.round(lv * 100) }); tickLabels.push({ p: outward(toXY(0, lv, 1 - lv), 34), text: Math.round(lv * 100) }); @@ -132,9 +137,9 @@ svg // --- Vertex labels -------------------------------------------------------------- const vertexLabels = [ - { p: outward(apex, 78), text: "Clay" }, - { p: outward(left, 78), text: "Sand" }, - { p: outward(right, 78), text: "Silt" }, + { p: outward(apex, 78), text: "Clay (%)" }, + { p: outward(left, 78), text: "Sand (%)" }, + { p: outward(right, 78), text: "Silt (%)" }, ]; svg .selectAll(".vertex-label") @@ -151,8 +156,14 @@ svg .style("font-family", "sans-serif") .text((d) => d.text); -// --- Data points ------------------------------------------------------------ -const points = samples.map((s) => toXY(s.clay / 100, s.sand / 100, s.silt / 100)); +// --- Data points -------------------------------------------------------------- +// Marker radius scales with compositional purity (the dominant component's +// share) so the rarer near-vertex, high-purity samples read as a focal point +// against the more common balanced mixtures clustered mid-triangle. +const points = samples.map((s) => { + const purity = Math.max(s.clay, s.sand, s.silt) / 100; + return { ...toXY(s.clay / 100, s.sand / 100, s.silt / 100), r: 7 + purity * 7 }; +}); svg .selectAll(".sample") .data(points) @@ -160,7 +171,7 @@ svg .attr("class", "sample") .attr("cx", (d) => d.x) .attr("cy", (d) => d.y) - .attr("r", 9) + .attr("r", (d) => d.r) .attr("fill", t.palette[0]) .attr("fill-opacity", 0.85) .attr("stroke", t.pageBg) From c7b7039499f3d817ea847990b040cef788773673 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 5 Aug 2026 21:38:41 +0000 Subject: [PATCH 5/7] chore(d3): update quality score 0 and review feedback for ternary-basic --- .../implementations/javascript/d3.js | 2 +- .../ternary-basic/metadata/javascript/d3.yaml | 199 ++++++++---------- 2 files changed, 92 insertions(+), 109 deletions(-) diff --git a/plots/ternary-basic/implementations/javascript/d3.js b/plots/ternary-basic/implementations/javascript/d3.js index ee9db09ff0..7c152aaaf7 100644 --- a/plots/ternary-basic/implementations/javascript/d3.js +++ b/plots/ternary-basic/implementations/javascript/d3.js @@ -1,7 +1,7 @@ // anyplot.ai // ternary-basic: Basic Ternary Plot // Library: d3 7.9.0 | JavaScript 22.23.1 -// Quality: 80/100 | Created: 2026-08-04 +// Quality: 0/100 | Updated: 2026-08-05 //# anyplot-orientation: square const t = window.ANYPLOT_TOKENS; diff --git a/plots/ternary-basic/metadata/javascript/d3.yaml b/plots/ternary-basic/metadata/javascript/d3.yaml index ca1df42efd..829f58fe27 100644 --- a/plots/ternary-basic/metadata/javascript/d3.yaml +++ b/plots/ternary-basic/metadata/javascript/d3.yaml @@ -2,7 +2,7 @@ library: d3 language: javascript specification_id: ternary-basic created: '2026-08-04T15:21:12Z' -updated: '2026-08-04T15:28:31Z' +updated: '2026-08-05T21:38:41Z' generated_by: claude-sonnet workflow_run: 30922911394 issue: 1001 @@ -12,136 +12,127 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/ternary-b preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.html -quality_score: 80 +quality_score: 0 review: strengths: - - Correct barycentric-coordinate math (toXY) drives grid lines, tick marks, and - data points from a single consistent transform — the three grid-line families - and tick axes all line up perfectly with the triangle edges - - Idiomatic D3 data-join pattern (selection.data().join()) used for grid lines, - tick labels, vertex labels, and sample points instead of manual DOM loops - - Deterministic LCG-seeded data generation, self-contained with no fetch/network - calls, matching the D3 reproducibility rule - - Marker sizing (r=9, fill-opacity 0.85, theme-adaptive stroke) is well matched - to the sparse 28-point dataset - - 'Correct Imprint palette usage: first series is #009E73, identical between light - and dark renders, with theme-correct #FAF8F1/#1A1A17 backgrounds and adaptive - ink tokens for all chrome' + - Correct barycentric-coordinate ternary geometry with a proper three-family 20% + grid, tick marks, and tick labels along all three edges, all aligned + - Marker radius scales with each sample's dominant-component purity, giving the + plot a genuine visual-hierarchy focal point rather than uniform dots + - Wide compositional data spread (raw floor lowered to 0.02) gives good feature + coverage from near-vertex, high-purity samples to balanced mid-triangle mixtures, + in a realistic, neutral soil-science (clay/sand/silt) scenario summing to exactly + 100% + - Vertex tick-label doubling from the prior review was correctly deduped, and vertex + labels now correctly carry "(%)" units + - Clean idiomatic D3 data-join pattern for grid lines, ticks, labels, and points; + deterministic seeded LCG; correct Imprint palette/theme-adaptive chrome in both + renders weaknesses: - - 'At all three triangle vertices, two tick labels are rendered at the exact same - pixel position and overlap: the clay-axis ''0''/silt-axis ''0'' tick coincides - with the sand-axis ''100'' tick (and the equivalent pairs at the other two vertices), - because toXY() maps both tick''s (a,b,c) to the same vertex coordinate. Zooming - into the PNG shows a visibly doubled/bolder ''0'' fused into the second ''0'' - of ''100'' with chromatic-fringe ghosting. Fix: after building the `tickLabels` - array, dedupe entries whose pixel position (or (level, vertex) combination) coincides - — e.g. skip the sand-axis lv=1 label and the silt-axis lv=0 label (and their clay-axis - counterparts) since the shared vertex is already unambiguous from the two edges - meeting there, or nudge one of the two coincident labels tangentially so they - don''t stack exactly.' - - Vertex labels 'Clay', 'Sand', 'Silt' have no unit shown (no '%'), while the axes - clearly represent percentages (0-100 ticks); add '(%)' to each vertex label for - full VQ-06 credit, matching how the matplotlib sibling implementation labels 'Sand - (%)', 'Silt (%)', 'Clay (%)'. - - The synthetic data generator (wClay/wSand/wSilt = 0.15 + rand()*0.85, normalized - to sum 100) mathematically cannot produce any component below ~7% or above ~77%, - so the scatter never shows near-vertex (high-purity) or near-edge (single-component-dominant) - compositions — a real limitation of ternary data that a good example should demonstrate. - Widen the raw ranges (e.g. down to a lower floor near 0) so a few samples land - close to the vertices/edges. - - No visual hierarchy or storytelling element (e.g. a highlighted classification - region, an emphasized cluster, or a callout) — the plot is a plain uniform-color - scatter; consider highlighting a sub-region or a couple of representative points - to give the viewer a focal point. + - 'AR-09 EDGE CLIPPING (unresolved from prior review): the "Sand (%)" vertex label + is still clipped at the left canvas border in BOTH plot-light.png and plot-dark.png + -- the leading curve of the "S" is chopped off flat at x=0, missing pixels for + good. This is the exact same defect flagged in the previous review; the last fix + commit addressed the Attempt-1 feedback (tick dedup, "(%)" units, data-range widening, + purity-based sizing) but did not touch the vertex-label positioning that causes + the clipping.' + - 'Root cause: the label is positioned with outward(left, 78) and text-anchor="middle". + "Sand (%)" is the widest of the three vertex labels (wide a/n/d glyphs vs. the + narrower glyphs in "Clay (%)"/"Silt (%)"), so its horizontally-centered left edge + lands at a negative x coordinate before the 2x screenshot scale, pushing part + of the "S" off the 1200px CSS mount. Fix: switch the left vertex label to text-anchor="start" + (and the right vertex label to text-anchor="end"), or increase margin.left/margin.right + enough to clear the widest label at its outward-push distance.' + - Considerable unused whitespace remains on the left/right of the square canvas + outside the vertex labels; worth using more of the available area once the clipping + fix (which likely needs larger margins anyway) is in place. image_description: |- Light render (plot-light.png): - Background: Warm off-white, consistent with #FAF8F1 - not pure white. - Chrome: Bold dark title "ternary-basic · javascript · d3 · anyplot.ai" centered at top, clearly readable. Equilateral triangle outlined in a dark ink-soft stroke. Bold vertex labels "Clay" (top), "Sand" (bottom-left), "Silt" (bottom-right) in dark ink, clearly legible. Tick labels (0/20/40/60/80/100) along all three edges in a lighter ink-soft gray, readable — EXCEPT at the three vertices, where two tick labels from different axes are drawn at the identical pixel position, producing a visibly doubled/bolder "100" with faint color-fringe ghosting on close inspection (still reads as "100", not fully illegible, but is a real overlap defect). Grid lines at 20% intervals in three directions, subtle light gray, visible but not competing with data. - Data: 28 green (#009E73) circles with a light cream stroke for definition against the light background, fill-opacity 0.85, clustered mostly in the mid-lower portion of the triangle (no points close to the vertices). - Legibility verdict: PASS (all text readable; the vertex tick-label overlap is a genuine but minor defect, not a legibility failure) + Background: Warm off-white, matches #FAF8F1. + Chrome: Bold dark title fully visible at top. Triangle border + 20% grid subtle and correct. Tick labels (10-100) readable on all three edges, no doubled vertex ticks. "Clay (%)" and "Silt (%)" vertex labels fully clear of canvas edges. "Sand (%)" vertex label (bottom-left) is CLIPPED: leading curve of the "S" cut off flat at x=0, confirmed via 3x pixel-zoomed crop of the (0-300, 2100-2300) region -- missing pixels, not a tight margin. + Data: 28 green (#009E73) circles, radius scaled by compositional purity, clearly visible against background. + Legibility verdict: FAIL (Sand (%) label clipped at left canvas edge -- AR-09) Dark render (plot-dark.png): - Background: Warm near-black, consistent with #1A1A17 - not pure black. - Chrome: Title, vertex labels ("Clay"/"Sand"/"Silt"), and tick labels all rendered in light ink tokens, clearly visible against the dark background — no dark-on-dark failures anywhere. The same vertex tick-label doubling artifact from the light render reproduces identically here (expected, since it's a geometry bug in the tick computation, not a theme issue). - Data: Same #009E73 green circles, fill-opacity 0.85, now with a dark stroke (matching the dark page background) instead of the light-render's cream stroke — data hue is identical to the light render, only the marker's edge stroke color flips with the theme (intentional, theme-adaptive edge treatment). - Legibility verdict: PASS (no dark-on-dark text; all chrome correctly flipped) + Background: Warm near-black, matches #1A1A17. + Chrome: Title, grid, triangle border, and tick labels correctly flip to light ink tokens, fully legible, no dark-on-dark failures. + Data: Identical colors/positions/sizes to light render (#009E73), only marker stroke flips to match page background. + Same "Sand (%)" clipping defect reproduces identically in the equivalent pixel crop -- confirms this is a layout-math bug, not a per-theme rendering issue. + Legibility verdict: FAIL (Sand (%) label clipped at left canvas edge -- AR-09) criteria_checklist: visual_quality: - score: 26 + score: 22 max: 30 items: - id: VQ-01 name: Text Legibility - score: 7 + score: 4 max: 8 - passed: true - comment: All text readable in both themes; minor ghosting artifact on vertex - '100' labels from overlapping ticks + passed: false + comment: Explicit sizes, readable in both themes, but Sand (%) label's leading + S is physically clipped in both renders - id: VQ-02 name: No Overlap - score: 4 + score: 6 max: 6 passed: true - comment: Two tick labels ('0' and '100' from different axes) render at the - identical pixel position at each of the 3 vertices - minimal but real overlap + comment: Vertex tick-label dedup from the prior fix holds; no doubled labels + observed - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: Marker size (r=9) and opacity (0.85) well matched to the sparse 28-point - dataset + comment: Purity-scaled markers clearly visible, well-sized for the sparse + 28-point dataset - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Single-series green with theme-adaptive stroke gives good contrast; - no reliance on hue-only distinctions + comment: Good contrast, CVD-safe single-hue data - id: VQ-05 name: Layout & Canvas - score: 4 + score: 0 max: 4 - passed: true - comment: Triangle occupies a well-balanced majority of the 2400x2400 canvas - with generous, even margins; nothing clipped + passed: false + comment: 'AR-09 hard rule: clipped vertex label forces this to 0' - id: VQ-06 name: Axis Labels & Title - score: 1 + score: 2 max: 2 passed: true - comment: Vertex labels ('Clay'/'Sand'/'Silt') are descriptive but missing - the '%' unit that the 0-100 tick scale represents + comment: Vertex labels correctly carry (%) units now - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series is #009E73, identical across light/dark renders; backgrounds - and chrome are theme-correct' + comment: '#009E73 identical both themes; correct FAF8F1/1A1A17 backgrounds' design_excellence: - score: 10 + score: 13 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 4 + score: 5 max: 8 - passed: false - comment: Well-configured single-color scatter; clean but not exceptional + passed: true + comment: Custom triangle geometry + purity-scaled markers, above a bare default + but short of publication-ready polish - id: DE-02 name: Visual Refinement score: 4 max: 6 passed: true - comment: Subtle grid, generous whitespace, clean sans-serif typography hierarchy + comment: Subtle grid, clean chrome; held back by the clipping and moderate + canvas fill - id: DE-03 name: Data Storytelling - score: 2 + score: 4 max: 6 - passed: false - comment: Plain uniform scatter with no highlighted region, focal point, or - emphasis + passed: true + comment: Purity-scaled marker size creates a real visual-hierarchy focal point spec_compliance: score: 15 max: 15 @@ -151,53 +142,48 @@ review: score: 5 max: 5 passed: true - comment: 'Correct ternary plot: triangle, labeled vertices, gridded' + comment: Correct ternary triangle - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: 20% grid lines, labeled vertices, distinct markers, and edge tick - marks all present + comment: 20% grid, vertex labels, edge tick marks all present - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Barycentric mapping (clay/sand/silt) is internally consistent across - grid, ticks, and points + comment: Correct barycentric coordinates - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title matches required format exactly; legend correctly omitted for - a single series + comment: Title format exact; no legend needed for single series data_quality: - score: 12 + score: 14 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 4 + score: 5 max: 6 - passed: false - comment: Generator's 0.15-1.0 raw range keeps every component within ~7-77%, - so no sample shows a near-vertex/high-purity composition + passed: true + comment: Widened data range (0.02 floor) now shows near-vertex/high-purity + samples alongside balanced mixtures - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Neutral, real-world soil-science scenario (clay/sand/silt texture - sampling) + comment: Neutral soil-texture (clay/sand/silt) scenario - id: DQ-03 name: Appropriate Scale - score: 3 + score: 4 max: 4 passed: true - comment: Values plausible for soil composition but the artificially narrow - achievable range is a mild realism gap + comment: Every triplet normalized to sum to exactly 100% code_quality: score: 10 max: 10 @@ -207,35 +193,33 @@ review: score: 3 max: 3 passed: true - comment: Imports/data/geometry/draw flow; small geometry helper functions - (toXY, outward, lcg) are standard for this repo's D3 implementations + comment: No classes, flat data/plot flow - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Fixed-seed LCG (42123) + comment: Fixed-seed LCG - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: No stray imports; only the global d3 + comment: Only d3 global used - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Appropriately complex for custom ternary geometry, no fake UI + comment: No fake functionality - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Follows the mount-node contract correctly, no animation, single svg - append + comment: Correct mount-node contract usage library_mastery: - score: 7 + score: 6 max: 10 items: - id: LM-01 @@ -243,15 +227,13 @@ review: score: 4 max: 5 passed: true - comment: Proper selection.data().join() pattern throughout + comment: Clean selectAll().data().join() throughout - id: LM-02 name: Distinctive Features - score: 3 + score: 2 max: 5 - passed: true - comment: Leverages D3's low-level SVG/data-binding flexibility to build a - bespoke barycentric coordinate system that higher-level chart libraries - can't express natively + passed: false + comment: Custom barycentric math is D3-friendly but not D3-exclusive verdict: REJECTED impl_tags: dependencies: [] @@ -263,3 +245,4 @@ impl_tags: - normalization styling: - alpha-blending + - edge-highlighting From 3d9df15c1ebbc5b41be1e15c90b02e8504a50c5f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 5 Aug 2026 21:42:47 +0000 Subject: [PATCH 6/7] fix(d3): address review feedback for ternary-basic Attempt 2/4 - fixes based on AI review --- plots/ternary-basic/implementations/javascript/d3.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plots/ternary-basic/implementations/javascript/d3.js b/plots/ternary-basic/implementations/javascript/d3.js index 7c152aaaf7..3bc7628d1b 100644 --- a/plots/ternary-basic/implementations/javascript/d3.js +++ b/plots/ternary-basic/implementations/javascript/d3.js @@ -136,10 +136,14 @@ svg .text((d) => d.text); // --- Vertex labels -------------------------------------------------------------- +// The apex label stays centered (ample horizontal room either side), but the +// left/right vertex labels grow inward from their vertex ("start"/"end" +// anchors) instead of centering past the canvas edge - "Sand (%)" is wide +// enough that a centered anchor pushed its leading "S" off the left border. const vertexLabels = [ - { p: outward(apex, 78), text: "Clay (%)" }, - { p: outward(left, 78), text: "Sand (%)" }, - { p: outward(right, 78), text: "Silt (%)" }, + { p: outward(apex, 78), text: "Clay (%)", anchor: "middle" }, + { p: outward(left, 78), text: "Sand (%)", anchor: "start" }, + { p: outward(right, 78), text: "Silt (%)", anchor: "end" }, ]; svg .selectAll(".vertex-label") @@ -148,7 +152,7 @@ svg .attr("class", "vertex-label") .attr("x", (d) => d.p.x) .attr("y", (d) => d.p.y) - .attr("text-anchor", "middle") + .attr("text-anchor", (d) => d.anchor) .attr("dominant-baseline", "middle") .attr("fill", t.ink) .style("font-size", "20px") From 17f54093e9ae4f885382375cdde83bb86aa8f57b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 5 Aug 2026 21:47:22 +0000 Subject: [PATCH 7/7] chore(d3): update quality score 92 and review feedback for ternary-basic --- .../implementations/javascript/d3.js | 2 +- .../ternary-basic/metadata/javascript/d3.yaml | 168 +++++++++--------- 2 files changed, 81 insertions(+), 89 deletions(-) diff --git a/plots/ternary-basic/implementations/javascript/d3.js b/plots/ternary-basic/implementations/javascript/d3.js index 3bc7628d1b..ecca7659bd 100644 --- a/plots/ternary-basic/implementations/javascript/d3.js +++ b/plots/ternary-basic/implementations/javascript/d3.js @@ -1,7 +1,7 @@ // anyplot.ai // ternary-basic: Basic Ternary Plot // Library: d3 7.9.0 | JavaScript 22.23.1 -// Quality: 0/100 | Updated: 2026-08-05 +// Quality: 92/100 | Updated: 2026-08-05 //# anyplot-orientation: square const t = window.ANYPLOT_TOKENS; diff --git a/plots/ternary-basic/metadata/javascript/d3.yaml b/plots/ternary-basic/metadata/javascript/d3.yaml index 829f58fe27..91f3164ea6 100644 --- a/plots/ternary-basic/metadata/javascript/d3.yaml +++ b/plots/ternary-basic/metadata/javascript/d3.yaml @@ -2,7 +2,7 @@ library: d3 language: javascript specification_id: ternary-basic created: '2026-08-04T15:21:12Z' -updated: '2026-08-05T21:38:41Z' +updated: '2026-08-05T21:47:22Z' generated_by: claude-sonnet workflow_run: 30922911394 issue: 1001 @@ -12,105 +12,92 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/ternary-b preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/ternary-basic/javascript/d3/plot-dark.html -quality_score: 0 +quality_score: 92 review: strengths: - - Correct barycentric-coordinate ternary geometry with a proper three-family 20% - grid, tick marks, and tick labels along all three edges, all aligned - - Marker radius scales with each sample's dominant-component purity, giving the - plot a genuine visual-hierarchy focal point rather than uniform dots - - Wide compositional data spread (raw floor lowered to 0.02) gives good feature - coverage from near-vertex, high-purity samples to balanced mid-triangle mixtures, - in a realistic, neutral soil-science (clay/sand/silt) scenario summing to exactly - 100% - - Vertex tick-label doubling from the prior review was correctly deduped, and vertex - labels now correctly carry "(%)" units - - Clean idiomatic D3 data-join pattern for grid lines, ticks, labels, and points; - deterministic seeded LCG; correct Imprint palette/theme-adaptive chrome in both - renders + - Correct barycentric-to-pixel transform with three grid-line families and edge + tick marks at 20% intervals, matching the spec's 'regular interval gridlines' + and 'tick marks along each edge' requirements + - Marker radius encodes compositional purity (dominant component share), giving + the scatter a meaningful visual hierarchy without added chartjunk + - Thoughtful start/end text-anchor logic on the left/right vertex labels prevents + them from clipping at the canvas edges + - 'Fully theme-adaptive chrome — brand green #009E73 is identical and clearly legible + on both the light (#FAF8F1) and dark (#1A1A17) surfaces' + - Deterministic LCG-seeded data generation and idiomatic D3 `.data().join()` usage + throughout (grid lines, tick labels, vertex labels, sample points) weaknesses: - - 'AR-09 EDGE CLIPPING (unresolved from prior review): the "Sand (%)" vertex label - is still clipped at the left canvas border in BOTH plot-light.png and plot-dark.png - -- the leading curve of the "S" is chopped off flat at x=0, missing pixels for - good. This is the exact same defect flagged in the previous review; the last fix - commit addressed the Attempt-1 feedback (tick dedup, "(%)" units, data-range widening, - purity-based sizing) but did not touch the vertex-label positioning that causes - the clipping.' - - 'Root cause: the label is positioned with outward(left, 78) and text-anchor="middle". - "Sand (%)" is the widest of the three vertex labels (wide a/n/d glyphs vs. the - narrower glyphs in "Clay (%)"/"Silt (%)"), so its horizontally-centered left edge - lands at a negative x coordinate before the 2x screenshot scale, pushing part - of the "S" off the 1200px CSS mount. Fix: switch the left vertex label to text-anchor="start" - (and the right vertex label to text-anchor="end"), or increase margin.left/margin.right - enough to clear the widest label at its outward-push distance.' - - Considerable unused whitespace remains on the left/right of the square canvas - outside the vertex labels; worth using more of the available area once the clipping - fix (which likely needs larger margins anyway) is in place. + - Single accent color and uniform marker shape keep the design at 'strong' rather + than showcase-level — a secondary cue (e.g. subtle alpha/size gradient tied to + purity, or a couple of direct sample-group labels) would push the data storytelling + further + - No sample lands above ~65% clay, so coverage leans toward sand/silt-dominant mixtures + — a sample or two closer to the clay apex would balance feature coverage across + all three vertices image_description: |- Light render (plot-light.png): - Background: Warm off-white, matches #FAF8F1. - Chrome: Bold dark title fully visible at top. Triangle border + 20% grid subtle and correct. Tick labels (10-100) readable on all three edges, no doubled vertex ticks. "Clay (%)" and "Silt (%)" vertex labels fully clear of canvas edges. "Sand (%)" vertex label (bottom-left) is CLIPPED: leading curve of the "S" cut off flat at x=0, confirmed via 3x pixel-zoomed crop of the (0-300, 2100-2300) region -- missing pixels, not a tight margin. - Data: 28 green (#009E73) circles, radius scaled by compositional purity, clearly visible against background. - Legibility verdict: FAIL (Sand (%) label clipped at left canvas edge -- AR-09) + Background: Warm off-white, consistent with #FAF8F1 — not pure white. + Chrome: Title "ternary-basic · javascript · d3 · anyplot.ai" centered at top in dark bold text; vertex labels "Clay (%)" (top), "Sand (%)" (bottom-left), "Silt (%)" (bottom-right) in bold dark ink; tick labels (0/20/40/60/80/100) along each edge in a softer dark gray; triangular border and gridlines in subtle gray. All chrome text is clearly readable against the light background. + Data: 28 circular markers in brand green (#009E73), radius scaled by compositional purity, with a subtle pale stroke for edge definition against overlapping points. Points span a broad range of clay/sand/silt mixtures, concentrated toward sand- and silt-dominant compositions with a few near-vertex and mid-triangle samples. + Legibility verdict: PASS Dark render (plot-dark.png): - Background: Warm near-black, matches #1A1A17. - Chrome: Title, grid, triangle border, and tick labels correctly flip to light ink tokens, fully legible, no dark-on-dark failures. - Data: Identical colors/positions/sizes to light render (#009E73), only marker stroke flips to match page background. - Same "Sand (%)" clipping defect reproduces identically in the equivalent pixel crop -- confirms this is a layout-math bug, not a per-theme rendering issue. - Legibility verdict: FAIL (Sand (%) label clipped at left canvas edge -- AR-09) + Background: Warm near-black, consistent with #1A1A17 — not pure black. + Chrome: Same title and vertex labels now rendered in light/off-white text, tick labels in a lighter soft gray, triangular border and gridlines in a light, low-opacity gray — all fully legible against the dark surface, with no dark-on-dark text anywhere. + Data: Markers are identical #009E73 green to the light render — only the chrome (background, text, grid) flipped, confirming palette-token compliance. + Legibility verdict: PASS criteria_checklist: visual_quality: - score: 22 + score: 30 max: 30 items: - id: VQ-01 name: Text Legibility - score: 4 + score: 8 max: 8 - passed: false - comment: Explicit sizes, readable in both themes, but Sand (%) label's leading - S is physically clipped in both renders + passed: true + comment: All font sizes explicitly set (14px ticks, 20px vertex labels, 22px + title); readable in both themes, no overflow - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: Vertex tick-label dedup from the prior fix holds; no doubled labels - observed + comment: No text overlap; lv=0 duplicate-label case deliberately skipped - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: Purity-scaled markers clearly visible, well-sized for the sparse - 28-point dataset + comment: 28 sparse points with prominent 7-14px radius markers, purity-scaled - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Good contrast, CVD-safe single-hue data + comment: Single-series green with pale stroke for definition; no CVD risk - id: VQ-05 name: Layout & Canvas - score: 0 + score: 4 max: 4 - passed: false - comment: 'AR-09 hard rule: clipped vertex label forces this to 0' + passed: true + comment: Triangle occupies ~60-70% of canvas, balanced top/bottom margins, + nothing cut off - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Vertex labels correctly carry (%) units now + comment: 'Vertex labels descriptive with units: Clay/Sand/Silt (%)' - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: '#009E73 identical both themes; correct FAF8F1/1A1A17 backgrounds' + comment: 'First series #009E73, identical across themes, backgrounds and chrome + theme-correct' design_excellence: - score: 13 + score: 14 max: 20 items: - id: DE-01 @@ -118,21 +105,21 @@ review: score: 5 max: 8 passed: true - comment: Custom triangle geometry + purity-scaled markers, above a bare default - but short of publication-ready polish + comment: Clean, intentional, but single accent color keeps it below showcase-level - id: DE-02 name: Visual Refinement - score: 4 + score: 5 max: 6 passed: true - comment: Subtle grid, clean chrome; held back by the clipping and moderate - canvas fill + comment: Subtle gridlines, generous whitespace, custom triangular frame in + place of spines - id: DE-03 name: Data Storytelling score: 4 max: 6 passed: true - comment: Purity-scaled marker size creates a real visual-hierarchy focal point + comment: Purity-scaled marker size creates a real focal point, though no explicit + narrative beyond that spec_compliance: score: 15 max: 15 @@ -142,48 +129,50 @@ review: score: 5 max: 5 passed: true - comment: Correct ternary triangle + comment: Correct ternary/barycentric plot - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: 20% grid, vertex labels, edge tick marks all present + comment: Gridlines at regular intervals, labeled vertices, edge tick marks, + distinct markers all present - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Correct barycentric coordinates + comment: Barycentric transform correctly maps clay/sand/silt to all three + axes - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title format exact; no legend needed for single series + comment: Title format exact; single-series legend correctly omitted data_quality: - score: 14 + score: 15 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 5 + score: 6 max: 6 passed: true - comment: Widened data range (0.02 floor) now shows near-vertex/high-purity - samples alongside balanced mixtures + comment: Samples span near-vertex to balanced mid-triangle mixtures across + sand/silt/clay - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Neutral soil-texture (clay/sand/silt) scenario + comment: Soil-texture clay/sand/silt scenario — neutral, comprehensible, real-world - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Every triplet normalized to sum to exactly 100% + comment: Each triplet normalized to sum to 100%, plausible soil-science proportions code_quality: score: 10 max: 10 @@ -193,56 +182,59 @@ review: score: 3 max: 3 passed: true - comment: No classes, flat data/plot flow + comment: Flat top-to-bottom script; helper functions are minimal geometry + utilities, no classes - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Fixed-seed LCG + comment: Fixed-seed LCG (seed=42123) - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only d3 global used + comment: Only the d3 global, no unused imports - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: No fake functionality + comment: Appropriate complexity for ternary geometry, no fake functionality - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Correct mount-node contract usage + comment: Single svg sized from ANYPLOT_SIZE, correct mount-node contract, + no animation library_mastery: - score: 6 + score: 8 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 4 + score: 5 max: 5 passed: true - comment: Clean selectAll().data().join() throughout + comment: Consistent .data().join() pattern throughout; correct token/mount + usage - id: LM-02 name: Distinctive Features - score: 2 - max: 5 - passed: false - comment: Custom barycentric math is D3-friendly but not D3-exclusive - verdict: REJECTED + score: 3 + max: 3 + passed: true + comment: Hand-rolled barycentric transform, triangular grid families, and + radial outward-label placement — genuinely D3-specific low-level work + verdict: APPROVED impl_tags: dependencies: [] techniques: - manual-ticks patterns: - data-generation - dataprep: - - normalization + dataprep: [] styling: - alpha-blending - edge-highlighting