From 38e37dc99722fb275370237a83b56d5afd1dba0a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 11:54:10 +0000 Subject: [PATCH 1/5] feat(muix): implement polar-scatter Adopted from 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69), written by gemini-4-argon outside the pipeline. --- .../implementations/javascript/muix.tsx | 325 +++++++++++------- 1 file changed, 209 insertions(+), 116 deletions(-) diff --git a/plots/polar-scatter/implementations/javascript/muix.tsx b/plots/polar-scatter/implementations/javascript/muix.tsx index fb523255cf..2aab4a7a16 100644 --- a/plots/polar-scatter/implementations/javascript/muix.tsx +++ b/plots/polar-scatter/implementations/javascript/muix.tsx @@ -3,149 +3,243 @@ // Library: muix 7.29.1 | JavaScript 22.23.2 // Quality: 85/100 | Created: 2026-09-05 //# anyplot-orientation: square -import { ChartContainer } from "@mui/x-charts/ChartContainer"; + +import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { ScatterPlot } from "@mui/x-charts/ScatterChart"; -import { ChartsTooltip } from "@mui/x-charts/ChartsTooltip"; import { useXScale, useYScale } from "@mui/x-charts/hooks"; const t = window.ANYPLOT_TOKENS; -// --- Data (in-memory, deterministic LCG — the browser has no seeded RNG) ---- -let seed = 42; -function rand() { - seed = (seed * 1664525 + 1013904223) % 4294967296; - return seed / 4294967296; -} +// Constants +const TITLE = "polar-scatter · javascript · muix · anyplot.ai"; +const RING_COUNT = 4; +const SPOKE_STEP_DEG = 45; +const FULL_CIRCLE_DEG = 360; +const DEG_SYMBOL = "°"; +const FILL_OPACITY = 0.72; +const MARKER_SIZE = 8; +const TITLE_HEIGHT = 70; +const LEGEND_HEIGHT = 54; +const CHART_MARGIN = { top: 36, bottom: 36, left: 36, right: 36 }; +const LABEL_RADIUS_FACTOR = 1.13; +const DOMAIN_PAD_FACTOR = 1.26; -// Wind observations grouped by time of day. Coastal sites see a diurnal wind -// rotation (land breeze overnight/morning, sea breeze in the afternoon, with -// evening/night transitional flow), so each group's prevailing bearing is -// spaced ~90° apart around the full compass rather than clustered on one side. -const GROUPS = [ - { category: "Morning", bearing: 15, speed: 7, spread: 85, markerSize: 8 }, - { category: "Afternoon", bearing: 105, speed: 13, spread: 90, markerSize: 8 }, - { category: "Evening", bearing: 195, speed: 10, spread: 80, markerSize: 8 }, - { category: "Night", bearing: 285, speed: 4, spread: 75, markerSize: 6 }, -]; +// Columns: set these to your data's column names +const CATEGORY = "Time of Day"; // role: group +const BEARING = "Wind Bearing (°)"; // role: theta +const SPEED = "Wind Speed (m/s)"; // role: r + +// Data: replace this block with your own records const POINTS_PER_GROUP = 30; -const MAX_RADIUS = 20; // m/s -const FILL_OPACITY = 0.7; // keeps overlapping points distinguishable +const MIN_SPEED_FLOOR = 0.5; +const SPEED_CEIL_CAP = 20; +const GROUP_SEEDS = [ + { name: "Morning", meanBearing: 15, meanSpeed: 7, spread: 85 }, + { name: "Afternoon", meanBearing: 105, meanSpeed: 13, spread: 90 }, + { name: "Evening", meanBearing: 195, meanSpeed: 10, spread: 80 }, + { name: "Night", meanBearing: 285, meanSpeed: 4, spread: 75 }, +]; -function polarToXY(bearingDeg, radius) { - const rad = (bearingDeg * Math.PI) / 180; - return { x: radius * Math.sin(rad), y: radius * Math.cos(rad) }; +const data: Array> = []; +let lcgState = 42; +for (const cfg of GROUP_SEEDS) { + for (let idx = 0; idx < POINTS_PER_GROUP; idx += 1) { + lcgState = (lcgState * 1664525 + 1013904223) % 4294967296; + const u1 = lcgState / 4294967296; + lcgState = (lcgState * 1664525 + 1013904223) % 4294967296; + const u2 = lcgState / 4294967296; + const bearing = + (((cfg.meanBearing + (u1 - 0.5) * cfg.spread) % FULL_CIRCLE_DEG) + FULL_CIRCLE_DEG) % + FULL_CIRCLE_DEG; + const speed = Math.min( + SPEED_CEIL_CAP, + Math.max(MIN_SPEED_FLOOR, cfg.meanSpeed + (u2 - 0.5) * cfg.meanSpeed), + ); + data.push({ + [CATEGORY]: cfg.name, + [BEARING]: Math.round(bearing * 100) / 100, + [SPEED]: Math.round(speed * 100) / 100, + }); + } } -// `ScatterSeriesType` has no `fillOpacity` prop — the renderer paints markers -// with `fill: series.color` directly, so translucency is baked into the color. -function withAlpha(hex, alpha) { - const h = hex.replace("#", ""); - const r = parseInt(h.substring(0, 2), 16); - const g = parseInt(h.substring(2, 4), 16); - const b = parseInt(h.substring(4, 6), 16); - return `rgba(${r}, ${g}, ${b}, ${alpha})`; +// Plot: reads only data and the constants above +const categories: string[] = []; +for (const row of data) { + const cat = String(row[CATEGORY]); + if (!categories.includes(cat)) categories.push(cat); } -const series = GROUPS.map((group, i) => ({ - type: "scatter", - id: group.category, - label: group.category, - color: withAlpha(t.palette[i], FILL_OPACITY), - markerSize: group.markerSize, - data: Array.from({ length: POINTS_PER_GROUP }, (_, idx) => { - const bearing = (((group.bearing + (rand() - 0.5) * group.spread) % 360) + 360) % 360; - const radius = Math.min(MAX_RADIUS, Math.max(0.5, group.speed + (rand() - 0.5) * group.speed)); - const { x, y } = polarToXY(bearing, radius); - return { id: `${group.category}-${idx}`, x, y }; - }), -})); - -// --- Polar grid overlay — community `@mui/x-charts` has no native polar -// chart, so the radial/angular grid is drawn as an SVG layer that shares the -// same linear x/y scales as the scatter points (via useXScale/useYScale), -// keeping rings, spokes and the data perfectly aligned at any canvas size. -- -const RINGS = [5, 10, 15, 20]; -const SPOKE_DEGREES = [0, 45, 90, 135, 180, 225, 270, 315]; -const LABEL_RADIUS = MAX_RADIUS * 1.14; +const rawMaxSpeed = Math.max(...data.map((r) => Number(r[SPEED]))); +const rawStep = rawMaxSpeed / RING_COUNT; +const stepMag = 10 ** Math.floor(Math.log10(rawStep)); +const stepNorm = rawStep / stepMag; +const ringStep = (stepNorm <= 1 ? 1 : stepNorm <= 2 ? 2 : stepNorm <= 5 ? 5 : 10) * stepMag; +const ringDecimals = Math.max(0, -Math.floor(Math.log10(ringStep) + 1e-9)); +const maxRadius = ringStep * RING_COUNT; +const rings: number[] = []; +for (let k = 1; k <= RING_COUNT; k += 1) { + rings.push(Number((k * ringStep).toFixed(ringDecimals))); +} + +const spokeDegrees: number[] = []; +for (let deg = 0; deg < FULL_CIRCLE_DEG; deg += SPOKE_STEP_DEG) { + spokeDegrees.push(deg); +} + +// Choose ring-label bearing at the centre of the 45° sector with fewest points +const sectorCount = FULL_CIRCLE_DEG / SPOKE_STEP_DEG; +const sectorCounts = new Array(sectorCount).fill(0); +for (const row of data) { + const b = (((Number(row[BEARING]) % FULL_CIRCLE_DEG) + FULL_CIRCLE_DEG) % FULL_CIRCLE_DEG); + const sIdx = Math.min(sectorCount - 1, Math.floor(b / SPOKE_STEP_DEG)); + sectorCounts[sIdx] += 1; +} +let minSectorIdx = 0; +for (let s = 1; s < sectorCount; s += 1) { + if (sectorCounts[s] < sectorCounts[minSectorIdx]) { + minSectorIdx = s; + } +} +const tickSpokeDeg = minSectorIdx * SPOKE_STEP_DEG + SPOKE_STEP_DEG / 2; +const tickSpokeRad = (tickSpokeDeg * Math.PI) / 180; + +const speedUnitMatch = /\(([^)]+)\)/.exec(SPEED); +const speedUnit = speedUnitMatch ? speedUnitMatch[1] : ""; + +const labelRadius = maxRadius * LABEL_RADIUS_FACTOR; +const domainLimit = maxRadius * DOMAIN_PAD_FACTOR; + +const seriesGroups = categories.map((cat, i) => { + const catRows = data.filter((r) => String(r[CATEGORY]) === cat); + const baseHex = t.palette[i]; + const cleanHex = baseHex.replace("#", ""); + const rVal = parseInt(cleanHex.substring(0, 2), 16); + const gVal = parseInt(cleanHex.substring(2, 4), 16); + const bVal = parseInt(cleanHex.substring(4, 6), 16); + const rgbaColor = `rgba(${rVal}, ${gVal}, ${bVal}, ${FILL_OPACITY})`; + + const points = catRows.map((row, idx) => { + const bearingDeg = Number(row[BEARING]); + const radiusVal = Number(row[SPEED]); + const rad = (bearingDeg * Math.PI) / 180; + return { + id: `${cat}-${idx}`, + x: radiusVal * Math.sin(rad), + y: radiusVal * Math.cos(rad), + }; + }); + + return { + type: "scatter" as const, + id: cat, + label: cat, + baseColor: baseHex, + color: rgbaColor, + markerSize: MARKER_SIZE, + data: points, + }; +}); function PolarGrid() { const xScale = useXScale(); const yScale = useYScale(); - const cx = xScale(0); - const cy = yScale(0); + + const cx = Number(xScale(0)); + const cy = Number(yScale(0)); return ( - {RINGS.map((r) => ( + {rings.map((r) => ( ))} - {SPOKE_DEGREES.map((deg) => { - const { x, y } = polarToXY(deg, MAX_RADIUS); + {spokeDegrees.map((deg) => { + const rad = (deg * Math.PI) / 180; + const sx = maxRadius * Math.sin(rad); + const sy = maxRadius * Math.cos(rad); + const lx = labelRadius * Math.sin(rad); + const ly = labelRadius * Math.cos(rad); return ( - + + + + {`${deg}${DEG_SYMBOL}`} + + ); })} - {SPOKE_DEGREES.map((deg) => { - const { x, y } = polarToXY(deg, LABEL_RADIUS); + {rings.map((r) => { + const tx = Number(xScale(r * Math.sin(tickSpokeRad))); + const ty = Number(yScale(r * Math.cos(tickSpokeRad))); + const rStr = r.toFixed(ringDecimals); + const tickText = speedUnit ? `${rStr} ${speedUnit}` : rStr; + const pillWidth = tickText.length * 8.2 + 12; return ( - - {`${deg}°`} - + + + + {tickText} + + ); })} - {RINGS.map((r) => ( - - {`${r} m/s`} - - ))} ); } -const TITLE = "polar-scatter · javascript · muix · anyplot.ai"; -const TITLE_HEIGHT = 70; -const LEGEND_HEIGHT = 50; -const MARGIN = { top: 40, bottom: 40, left: 40, right: 40 }; -const DOMAIN = MAX_RADIUS * 1.3; - -// --- Chart (default-exported component — the harness mounts it) ------------- export default function Chart() { const { width, height } = window.ANYPLOT_SIZE; const chartSize = height - TITLE_HEIGHT - LEGEND_HEIGHT; return ( -
+
{TITLE}
- - - +
- {GROUPS.map((group, i) => ( -
+ {seriesGroups.map((group) => ( +
- {group.category} + {group.label}
))}
From 202e89c5505d1d652f51d9483e9219ffb0b78fc5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 11:54:12 +0000 Subject: [PATCH 2/5] chore(muix): add metadata for polar-scatter --- .../implementations/javascript/muix.tsx | 2 +- .../metadata/javascript/muix.yaml | 258 +----------------- 2 files changed, 13 insertions(+), 247 deletions(-) diff --git a/plots/polar-scatter/implementations/javascript/muix.tsx b/plots/polar-scatter/implementations/javascript/muix.tsx index 2aab4a7a16..ab25e5b295 100644 --- a/plots/polar-scatter/implementations/javascript/muix.tsx +++ b/plots/polar-scatter/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // polar-scatter: Polar Scatter Plot // Library: muix 7.29.1 | JavaScript 22.23.2 -// Quality: 85/100 | Created: 2026-09-05 +// Quality: pending | Created: 2026-09-05 //# anyplot-orientation: square import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; diff --git a/plots/polar-scatter/metadata/javascript/muix.yaml b/plots/polar-scatter/metadata/javascript/muix.yaml index d863376f97..ecad3e84ce 100644 --- a/plots/polar-scatter/metadata/javascript/muix.yaml +++ b/plots/polar-scatter/metadata/javascript/muix.yaml @@ -1,256 +1,22 @@ +# Per-library metadata for muix implementation of polar-scatter +# Auto-generated by impl-generate.yml + library: muix language: javascript specification_id: polar-scatter created: '2026-09-05T11:40:06Z' -updated: '2026-09-05T11:55:17Z' -generated_by: claude-sonnet -workflow_run: 33963668090 +updated: '2026-10-24T11:54:11Z' +generated_by: gemini-4-argon +adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) +workflow_run: 40091679090 issue: 2023 -language_version: 22.23.2 -library_version: 7.29.1 +language_version: 22.23.3 +library_version: 9.16.0 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-dark.html -quality_score: 85 +quality_score: null review: - strengths: - - Builds a genuine polar scatter on top of the community-only @mui/x-charts by drawing - a custom SVG grid (rings + spokes + labels) via useXScale/useYScale, so the overlay - stays perfectly aligned with the underlying linear scatter scale at any canvas - size — a clean way to work within the community-surface constraint instead of - reaching for Pro/Premium or another library. - - 'Deterministic, realistic wind-observation dataset (LCG-seeded) with a clear diurnal - wind-rotation narrative: Morning/Afternoon/Evening/Night bearings are spaced ~90° - apart, so the four categories visibly occupy distinct compass sectors.' - - 'Palette compliance is correct: first category (Morning) uses brand green, data - colors are identical between light and dark renders, and only chrome (background, - grid, text) flips per theme.' - - Marker size and alpha (0.7) are tuned per group to keep overlapping points distinguishable; - ChartsTooltip is wired for per-item detail in the interactive HTML output. - - 'Clean, idiomatic code: skipAnimation set, sizes from window.ANYPLOT_SIZE, no - unused imports, no fake interactivity.' - weaknesses: - - The '5 m/s' ring label sits directly against/behind a Morning-category marker - in both renders (visible at the inner ring along the 0° spoke) — move ring labels - to an unoccupied angular gap (e.g. between two spoke lines away from the densest - cluster) or give them a small background pill for contrast. - - The Night (amber) cluster near the center is dense enough that several markers - fully overlap even at alpha=0.7 — consider a slightly smaller markerSize for that - group or a touch more jitter/spread so individual observations stay distinguishable. - - Design excellence is solid but stops at functional refinement — no radial fade, - no emphasis/callout on one category, no secondary visual hierarchy beyond color - and position, which caps this a notch below top-tier polish. - image_description: |- - Light render (plot-light.png): - Background: Warm off-white, consistent with #FAF8F1, not pure white. - Chrome: Title "polar-scatter · javascript · muix · anyplot.ai" is dark ink, centered, fully legible. Radial ring labels (5/10/15/20 m/s) and angular spoke labels (0°, 45°, 90° ... 315°) are dark-gray and legible against the light background. Grid rings and spokes are thin, subtle gray lines — visible but not dominant. Bottom legend (Morning/Afternoon/Evening/Night) is clear with matching color swatches. - Data: Four color groups — Morning (brand green #009E73), Afternoon (light purple), Evening (indigo/blue), Night (amber) — each occupying a distinct ~90°-wide compass sector, consistent with a diurnal wind-rotation story. First categorical series correctly uses the brand green. - Legibility verdict: PASS, with one minor caveat — the "5 m/s" ring label sits directly against/behind a nearby Morning marker, a small text/marker overlap (not a legibility failure, all letters remain visible). - - Dark render (plot-dark.png): - Background: Warm near-black, consistent with #1A1A17, not pure black. - Chrome: Title, ring labels, and spoke labels flip to light gray/white text, fully legible against the dark background — no dark-on-dark text anywhere. Grid lines remain subtle but visible. Legend text is light and readable. - Data: Same four colors as the light render, confirming data colors are identical between themes and only chrome flips. - Legibility verdict: PASS, same minor "5 m/s" label/marker overlap as in the light render — no dark-on-dark or light-on-light issues. - 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 at appropriate sizes; minor crowding - near the '5 m/s' label from a nearby marker - - id: VQ-02 - name: No Overlap - score: 4 - max: 6 - passed: true - comment: '''5 m/s'' ring label overlaps a Morning marker in both renders; - some Night-cluster markers fully overlap each other' - - id: VQ-03 - name: Element Visibility - score: 5 - max: 6 - passed: true - comment: Marker size/alpha tuned per group for ~30-point clusters; dense Night - cluster still has some fully-hidden overlaps - - id: VQ-04 - name: Color Accessibility - score: 2 - max: 2 - passed: true - comment: Imprint palette hues are distinguishable by hue, not relying on red-green - alone - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: Square canvas gate passed, well-centered, generous padding, nothing - cropped - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: Radial labels carry units (m/s), angular labels in degrees at 45° - intervals - - id: VQ-07 - name: Palette Compliance - score: 2 - max: 2 - passed: true - comment: First series is brand green, data colors identical across themes, - correct backgrounds in both themes - design_excellence: - score: 12 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 5 - max: 8 - passed: true - comment: Custom polar overlay perfectly aligned to chart scale is a genuinely - sophisticated engineering solution; visual polish is functional rather than - showcase-level - - id: DE-02 - name: Visual Refinement - score: 4 - max: 6 - passed: true - comment: Subtle grid, generous whitespace, integrated custom title/legend - bars - - id: DE-03 - name: Data Storytelling - score: 3 - max: 6 - passed: true - comment: Diurnal wind-rotation pattern (4 sectors ~90° apart) is visible at - a glance, though no additional emphasis/callout beyond color and position - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: True polar scatter behavior (theta/radius mapping) despite no native - MUI X polar primitive - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: Radial gridlines, angular ticks at 45° intervals, category color - encoding, radius axis starts at 0 — all present - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: theta/radius correctly converted to x/y via polarToXY; full data - range shown within domain - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: Title matches required format; legend labels match category names - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: Angle, radius, and categorical color encoding all present, 120 points - across 4 groups - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: Plausible diurnal wind-direction/speed pattern, neutral subject matter - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: Wind speeds 0-20 m/s and bearings 0-360° are realistic ranges - code_quality: - score: 10 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: No unnecessary classes; plain functions - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: Fixed-seed LCG, fully deterministic - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: All imports (ChartContainer, ScatterPlot, ChartsTooltip, useXScale, - useYScale) are used - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: Appropriate complexity for the custom polar overlay; no fake UI/interactivity - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: 'Follows harness contract: default export, window.ANYPLOT_SIZE, skipAnimation' - library_mastery: - score: 7 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 4 - max: 5 - passed: true - comment: Composes ChartContainer + ScatterPlot correctly and stays within - the community API surface - - id: LM-02 - name: Distinctive Features - score: 3 - max: 5 - passed: true - comment: useXScale/useYScale hooks used to align a custom SVG overlay with - the chart's internal scale — a distinctive, non-obvious technique - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - polar-projection - - custom-legend - - manual-ticks - patterns: - - data-generation - - iteration-over-groups - dataprep: [] - styling: - - alpha-blending - - grid-styling + strengths: [] + weaknesses: [] From aaed424500ce707995c4831e035c1e9a3256b981 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 11:57:22 +0000 Subject: [PATCH 3/5] chore(muix): store the review for polar-scatter --- .../implementations/javascript/muix.tsx | 4 +- .../metadata/javascript/muix.yaml | 269 +++++++++++++++++- 2 files changed, 264 insertions(+), 9 deletions(-) diff --git a/plots/polar-scatter/implementations/javascript/muix.tsx b/plots/polar-scatter/implementations/javascript/muix.tsx index ab25e5b295..1aacd820e0 100644 --- a/plots/polar-scatter/implementations/javascript/muix.tsx +++ b/plots/polar-scatter/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // polar-scatter: Polar Scatter Plot -// Library: muix 7.29.1 | JavaScript 22.23.2 -// Quality: pending | Created: 2026-09-05 +// Library: muix 9.16.0 | JavaScript 22.23.3 +// Quality: 83/100 | Updated: 2026-10-24 //# anyplot-orientation: square import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; diff --git a/plots/polar-scatter/metadata/javascript/muix.yaml b/plots/polar-scatter/metadata/javascript/muix.yaml index ecad3e84ce..37add9d148 100644 --- a/plots/polar-scatter/metadata/javascript/muix.yaml +++ b/plots/polar-scatter/metadata/javascript/muix.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for muix implementation of polar-scatter -# Auto-generated by impl-generate.yml - library: muix language: javascript specification_id: polar-scatter created: '2026-09-05T11:40:06Z' -updated: '2026-10-24T11:54:11Z' +updated: '2026-10-24T11:57:22Z' generated_by: gemini-4-argon adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) workflow_run: 40091679090 @@ -16,7 +13,265 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/polar-sca preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-dark.html -quality_score: null +quality_score: 83 review: - strengths: [] - weaknesses: [] + strengths: + - 'A true polar layout composed in community MUI X: ChartsContainer with hidden + linear axes, and a custom layer that reads useXScale/useYScale so + rings, spokes and labels share the scatter''s coordinate system exactly.' + - Radius axis starts at 0 at the center, with nice 5 m/s rings computed from the + data up to 20 m/s; every marker sits inside the rim at its own bearing and speed + (sin/cos conversion from degrees via radians, as the Notes ask). + - Ring labels carry the unit ("10 m/s") and are placed in the computed least-populated + 45° sector, and the cardinal angles 0/90/180/270 are set in bold above the 45° + ticks, giving a clear tick hierarchy. + - 'Four time-of-day groups use the Imprint palette in canonical order (#009E73, + #C475FD, #4467A3, #BD8233) with 0.72 fill opacity, identical in both themes; the + data shows a plausible clockwise diurnal rotation of the prevailing wind.' + - Native MUI X scatter interactivity (hover tooltips and highlighting) carries over + to the HTML export, and skipAnimation is set. + weaknesses: + - 'VQ-02 (both): the "5 m/s" ring label sits under the Morning markers near bearing + 337°: several green dots are drawn over the text, so the label is half covered + → the ring labels must stay readable on top of every data mark. Likely cause: + (which draws the label pills) is rendered before + in ChartsContainer, so the scatter paints over it; render the ring-label group + after .' + - 'VQ-05 (both): the polar disc fills only about 66 % of the 2400 px canvas width, + with an empty band of about 230 px between the title and the 0° label and wide + side margins → the disc should reach about 80 % of the width (about +14 %). Likely + cause: DOMAIN_PAD_FACTOR = 1.26 combined with CHART_MARGIN of 36 px and a 70 px + title band; padding the domain only to fit the angular labels (about 1.15) would + fix it.' + - 'VQ-06 (both): the angular axis shows only degree values, so nothing says the + angle is the wind bearing (direction the wind comes from) → name it once, e.g. + a ''Wind bearing (°)'' caption or compass letters N/E/S/W next to 0°/90°/180°/270°. + Likely cause: the BEARING column label is never rendered.' + - 'CQ-04 (code): lines 115–120 parse each palette hex into r/g/b by hand to build + an rgba() string, and lines 268–291 build the legend as hand-written HTML → set + the opacity with sx={{ ''& .MuiScatterChart-marker'': { fillOpacity: 0.72 } }} + and use the series'' palette color, and render inside the composition. + Likely cause: hand-rolled replacements for the native styling and legend APIs.' + - 'Suggestion: a thin page-colored outline on the markers would separate the stacked + Night dots more clearly than translucency alone.' + image_description: |- + Light render (plot-light.png): + Background: warm off-white #FAF8F1, square 2400x2400 canvas. + Chrome: bold dark title "polar-scatter · javascript · muix · anyplot.ai" at the top; four concentric rings (5/10/15/20 m/s) and eight spokes in subtle grey; degree labels 0°-315° every 45° outside the rim (0/90/180/270 bold); ring labels "5 m/s"…"20 m/s" on pills along the ~337° bearing; HTML legend at the bottom (Morning, Afternoon, Evening, Night). All text dark and readable; the "5 m/s" label is partly covered by green Morning markers. + Data: 120 translucent circles: Morning #009E73 around 0-60° at 3-10 m/s, Afternoon lavender #C475FD around 60-150° at 7-20 m/s, Evening blue #4467A3 around 155-235°, Night ochre #BD8233 as a tight cluster near 285° at 2-6 m/s. First series is brand green. Disc fills about two thirds of the canvas width, leaving wide empty margins. + Legibility verdict: PASS + + Dark render (plot-dark.png): + Background: warm near-black #1A1A17. + Chrome: title in light ink, degree and ring labels in light grey, grid rings and spokes in subtle dark grey, legend text light; no dark-on-dark text. The ring-label pills switch to the dark page color, and the "5 m/s" label is again partly covered by Morning markers. + Data: identical positions and identical palette hues (#009E73, #C475FD, #4467A3, #BD8233) as the light render; only chrome flipped. Brand green clearly visible. + Legibility verdict: PASS + criteria_checklist: + visual_quality: + score: 27 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 8 + max: 8 + passed: true + comment: Title 22 px, ring/degree labels 14 px, legend 15 px CSS; all readable + in both themes and at thumbnail size + - id: VQ-02 + name: No Overlap + score: 5 + max: 6 + passed: false + comment: Morning markers are drawn over the "5 m/s" ring label (defect line) + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: 120 markers at size 8 with 0.72 opacity, well suited to the density; + legend dots visible in both themes + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Four distinct Imprint hue families, no red-green pairing + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: false + comment: Disc fills only about 66 % of the canvas width; large empty band + under the title (defect line) + - id: VQ-06 + name: Axis Labels & Title + score: 1 + max: 2 + passed: false + comment: Radius ticks carry m/s, but the angle is never named as wind bearing + (defect line) + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'Imprint positions 1-4 in order, first series #009E73; #FAF8F1 / + #1A1A17 backgrounds, chrome theme-correct' + design_excellence: + score: 11 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: false + comment: 'Above baseline: bold cardinal-angle ticks over regular 45° ticks, + ring labels with units on page-colored pills' + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: false + comment: Ring labels placed in the computed least-populated sector, and the + outer ring drawn heavier as the rim; the placement still lets the 5 m/s + label collide + - id: DE-03 + name: Data Storytelling + score: 3 + max: 6 + passed: false + comment: Data built so the prevailing direction rotates clockwise through + the day, which the four colored clouds make readable at a glance + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Polar scatter with bearing as angle and speed as radius + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Radial gridlines at 5 m/s, angular ticks every 45° in degrees, centered + disc, radius from 0, legend for the color encoding, translucency in dense + areas + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Marks at their exact bearing/speed; no jitter or nudging; all points + inside the 20 m/s rim + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title 'polar-scatter · javascript · muix · anyplot.ai'; legend entries + Morning/Afternoon/Evening/Night + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Prevailing-direction clusters, sparse sectors, varying speeds and + outliers near the rim + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Wind observations by time of day, neutral and plausible + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Speeds 0.5-19.5 m/s and bearings 0-360°, sensible for surface wind; + 120 points within the 100-150 range (no Check values in the spec) + code_quality: + score: 9 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Flat module; the one extra component (PolarGrid) is required to use + the scale hooks + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fixed-seed LCG + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: ChartsContainer, ScatterPlot, useXScale, useYScale all used + - id: CQ-04 + name: Code Elegance + score: 1 + max: 2 + passed: false + comment: Hand-parsed hex to rgba (lines 115-120) and a hand-built HTML legend + (lines 268-291) where native options exist (defect line) + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Default export, v9 ChartsContainer API, skipAnimation, sized from + ANYPLOT_SIZE + library_mastery: + score: 6 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 3 + max: 5 + passed: false + comment: Good v9 composition, but the legend and the opacity are hand-built + instead of ChartsLegend / sx styling + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: false + comment: 'Uses MUI X''s composition API: a custom SVG layer inside ChartsContainer + that reads the chart scales through useXScale/useYScale to draw the polar + grid' + verdict: REJECTED + model: claude-opus-5-5 + criteria_version: qc-899b86ffde.aqr-869a70087c.sg-c73b4641dd.lib-f7283eb626 + rendered_at: '2026-10-24T11:54:36Z' +impl_tags: + dependencies: [] + techniques: + - polar-projection + - layer-composition + - custom-legend + - manual-ticks + - hover-tooltips + - html-export + patterns: + - data-generation + - iteration-over-groups + dataprep: [] + styling: + - alpha-blending From 3869b3d4f2fad3ea84d15d48b90a79062cc7967f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 11:59:56 +0000 Subject: [PATCH 4/5] fix(muix): address review feedback for polar-scatter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempt 1/4 - fixes based on AI review - VQ-02: ring labels drawn in a layer after so they stay readable - VQ-05: domain pad 1.26 -> 1.11, tighter margins; disc now ~80% of canvas width - VQ-06: caption names the angle (Wind Bearing, clockwise from 0 at top) and radius from the column constants - CQ-04: series use palette colors with fillOpacity via sx on .MuiScatterChart-marker; native ChartsLegend in a ChartsDataProvider/ChartsWrapper composition replaces the hand-built HTML legend Declined: - "Suggestion: a thin page-colored outline on the markers" — suggestion, not taken Co-Authored-By: Claude Opus 5.5 --- .../implementations/javascript/muix.tsx | 87 +++++++++---------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/plots/polar-scatter/implementations/javascript/muix.tsx b/plots/polar-scatter/implementations/javascript/muix.tsx index 1aacd820e0..93726a6ea2 100644 --- a/plots/polar-scatter/implementations/javascript/muix.tsx +++ b/plots/polar-scatter/implementations/javascript/muix.tsx @@ -4,7 +4,10 @@ // Quality: 83/100 | Updated: 2026-10-24 //# anyplot-orientation: square -import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; +import { ChartsDataProvider } from "@mui/x-charts/ChartsDataProvider"; +import { ChartsWrapper } from "@mui/x-charts/ChartsWrapper"; +import { ChartsSurface } from "@mui/x-charts/ChartsSurface"; +import { ChartsLegend } from "@mui/x-charts/ChartsLegend"; import { ScatterPlot } from "@mui/x-charts/ScatterChart"; import { useXScale, useYScale } from "@mui/x-charts/hooks"; @@ -18,11 +21,11 @@ const FULL_CIRCLE_DEG = 360; const DEG_SYMBOL = "°"; const FILL_OPACITY = 0.72; const MARKER_SIZE = 8; -const TITLE_HEIGHT = 70; -const LEGEND_HEIGHT = 54; -const CHART_MARGIN = { top: 36, bottom: 36, left: 36, right: 36 }; -const LABEL_RADIUS_FACTOR = 1.13; -const DOMAIN_PAD_FACTOR = 1.26; +const TITLE_HEIGHT = 74; +const LEGEND_HEIGHT = 40; +const CHART_MARGIN = { top: 14, bottom: 14, left: 14, right: 14 }; +const LABEL_RADIUS_FACTOR = 1.065; +const DOMAIN_PAD_FACTOR = 1.11; // Columns: set these to your data's column names const CATEGORY = "Time of Day"; // role: group @@ -112,13 +115,6 @@ const domainLimit = maxRadius * DOMAIN_PAD_FACTOR; const seriesGroups = categories.map((cat, i) => { const catRows = data.filter((r) => String(r[CATEGORY]) === cat); - const baseHex = t.palette[i]; - const cleanHex = baseHex.replace("#", ""); - const rVal = parseInt(cleanHex.substring(0, 2), 16); - const gVal = parseInt(cleanHex.substring(2, 4), 16); - const bVal = parseInt(cleanHex.substring(4, 6), 16); - const rgbaColor = `rgba(${rVal}, ${gVal}, ${bVal}, ${FILL_OPACITY})`; - const points = catRows.map((row, idx) => { const bearingDeg = Number(row[BEARING]); const radiusVal = Number(row[SPEED]); @@ -134,8 +130,7 @@ const seriesGroups = categories.map((cat, i) => { type: "scatter" as const, id: cat, label: cat, - baseColor: baseHex, - color: rgbaColor, + color: t.palette[i], markerSize: MARKER_SIZE, data: points, }; @@ -191,6 +186,17 @@ function PolarGrid() { ); })} + + ); +} + +// Drawn after so the ring labels stay readable above the markers +function RingLabels() { + const xScale = useXScale(); + const yScale = useYScale(); + + return ( + {rings.map((r) => { const tx = Number(xScale(r * Math.sin(tickSpokeRad))); const ty = Number(yScale(r * Math.cos(tickSpokeRad))); @@ -244,16 +250,18 @@ export default function Chart() { style={{ height: TITLE_HEIGHT, display: "flex", + flexDirection: "column", alignItems: "center", justifyContent: "center", - fontSize: 22, - fontWeight: 600, - color: t.ink, + gap: 6, }} > - {TITLE} +
{TITLE}
+
+ {`Angle: ${BEARING}, clockwise from 0${DEG_SYMBOL} at top · Radius: ${SPEED}`} +
- - - - -
- {seriesGroups.map((group) => ( -
- - {group.label} -
- ))} -
+ + + + + + + + +
); } From 484f12b374fe1a193143feec3031cf8eb8cbf5e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 12:03:12 +0000 Subject: [PATCH 5/5] chore(muix): store the review for polar-scatter --- .../implementations/javascript/muix.tsx | 2 +- .../metadata/javascript/muix.yaml | 202 ++++++++---------- 2 files changed, 96 insertions(+), 108 deletions(-) diff --git a/plots/polar-scatter/implementations/javascript/muix.tsx b/plots/polar-scatter/implementations/javascript/muix.tsx index 93726a6ea2..c513759834 100644 --- a/plots/polar-scatter/implementations/javascript/muix.tsx +++ b/plots/polar-scatter/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // polar-scatter: Polar Scatter Plot // Library: muix 9.16.0 | JavaScript 22.23.3 -// Quality: 83/100 | Updated: 2026-10-24 +// Quality: 87/100 | Updated: 2026-10-24 //# anyplot-orientation: square import { ChartsDataProvider } from "@mui/x-charts/ChartsDataProvider"; diff --git a/plots/polar-scatter/metadata/javascript/muix.yaml b/plots/polar-scatter/metadata/javascript/muix.yaml index 37add9d148..5a00be777f 100644 --- a/plots/polar-scatter/metadata/javascript/muix.yaml +++ b/plots/polar-scatter/metadata/javascript/muix.yaml @@ -2,7 +2,7 @@ library: muix language: javascript specification_id: polar-scatter created: '2026-09-05T11:40:06Z' -updated: '2026-10-24T11:57:22Z' +updated: '2026-10-24T12:03:12Z' generated_by: gemini-4-argon adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) workflow_run: 40091679090 @@ -13,62 +13,49 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/polar-sca preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/polar-scatter/javascript/muix/plot-dark.html -quality_score: 83 +quality_score: 87 review: strengths: - - 'A true polar layout composed in community MUI X: ChartsContainer with hidden - linear axes, and a custom layer that reads useXScale/useYScale so - rings, spokes and labels share the scatter''s coordinate system exactly.' - - Radius axis starts at 0 at the center, with nice 5 m/s rings computed from the - data up to 20 m/s; every marker sits inside the rim at its own bearing and speed - (sin/cos conversion from degrees via radians, as the Notes ask). - - Ring labels carry the unit ("10 m/s") and are placed in the computed least-populated - 45° sector, and the cardinal angles 0/90/180/270 are set in bold above the 45° - ticks, giving a clear tick hierarchy. - - 'Four time-of-day groups use the Imprint palette in canonical order (#009E73, - #C475FD, #4467A3, #BD8233) with 0.72 fill opacity, identical in both themes; the - data shows a plausible clockwise diurnal rotation of the prevailing wind.' - - Native MUI X scatter interactivity (hover tooltips and highlighting) carries over - to the HTML export, and skipAnimation is set. + - 'Polar layout built on MUI X''s own composition API: a ScatterPlot inside ChartsSurface + with a custom SVG grid drawn from useXScale/useYScale, so native tooltips and + the HTML legend still work' + - Angular ticks every 45° in degrees with bold cardinals, 0° at top clockwise, and + a subtitle that states the convention and units + - Radius axis computed from the data to a nice 5 m/s step, starting at 0 and closing + past the 19 m/s maximum so every marker sits inside the rim + - Fill opacity 0.72 lets the dense Night cluster read darker while single outliers + stay visible + - Imprint palette in canonical order with identical data colors across themes; all + chrome uses theme tokens and reads cleanly in both renders + - 'Deterministic LCG data tells a clear diurnal story: the prevailing wind rotates + N → E → S → W through the day and calms at night' weaknesses: - - 'VQ-02 (both): the "5 m/s" ring label sits under the Morning markers near bearing - 337°: several green dots are drawn over the text, so the label is half covered - → the ring labels must stay readable on top of every data mark. Likely cause: - (which draws the label pills) is rendered before - in ChartsContainer, so the scatter paints over it; render the ring-label group - after .' - - 'VQ-05 (both): the polar disc fills only about 66 % of the 2400 px canvas width, - with an empty band of about 230 px between the title and the 0° label and wide - side margins → the disc should reach about 80 % of the width (about +14 %). Likely - cause: DOMAIN_PAD_FACTOR = 1.26 combined with CHART_MARGIN of 36 px and a 70 px - title band; padding the domain only to fit the angular labels (about 1.15) would - fix it.' - - 'VQ-06 (both): the angular axis shows only degree values, so nothing says the - angle is the wind bearing (direction the wind comes from) → name it once, e.g. - a ''Wind bearing (°)'' caption or compass letters N/E/S/W next to 0°/90°/180°/270°. - Likely cause: the BEARING column label is never rendered.' - - 'CQ-04 (code): lines 115–120 parse each palette hex into r/g/b by hand to build - an rgba() string, and lines 268–291 build the legend as hand-written HTML → set - the opacity with sx={{ ''& .MuiScatterChart-marker'': { fillOpacity: 0.72 } }} - and use the series'' palette color, and render inside the composition. - Likely cause: hand-rolled replacements for the native styling and legend APIs.' - - 'Suggestion: a thin page-colored outline on the markers would separate the stacked - Night dots more clearly than translucency alone.' + - 'VQ-02 (both): the opaque page-colored pills behind the ''5 m/s'' and ''10 m/s'' + ring labels sit on top of the scatter layer and hide parts of two Morning markers + near bearing 337° (about half of the marker at ~5.5 m/s and an edge of the one + at ~10 m/s) → no marker covered by a ring label. Likely cause: + rendered after with fill={t.pageBg}, and the ''emptiest sector'' + bearing (337.5°) still holds Morning points; draw the labels below the markers + or drop the opaque pill.' + - 'Suggestion: placing the ring labels on a bearing chosen by distance to the nearest + marker rather than by sector count would keep them clear without a pill.' + - 'Suggestion: a slightly larger angular label size (16 px) would hold up better + in the 400 px thumbnail.' image_description: |- Light render (plot-light.png): - Background: warm off-white #FAF8F1, square 2400x2400 canvas. - Chrome: bold dark title "polar-scatter · javascript · muix · anyplot.ai" at the top; four concentric rings (5/10/15/20 m/s) and eight spokes in subtle grey; degree labels 0°-315° every 45° outside the rim (0/90/180/270 bold); ring labels "5 m/s"…"20 m/s" on pills along the ~337° bearing; HTML legend at the bottom (Morning, Afternoon, Evening, Night). All text dark and readable; the "5 m/s" label is partly covered by green Morning markers. - Data: 120 translucent circles: Morning #009E73 around 0-60° at 3-10 m/s, Afternoon lavender #C475FD around 60-150° at 7-20 m/s, Evening blue #4467A3 around 155-235°, Night ochre #BD8233 as a tight cluster near 285° at 2-6 m/s. First series is brand green. Disc fills about two thirds of the canvas width, leaving wide empty margins. + Background: warm off-white #FAF8F1 across the whole 2400x2400 canvas. + Chrome: bold dark title "polar-scatter · javascript · muix · anyplot.ai" at top, grey subtitle "Angle: Wind Bearing (°), clockwise from 0° at top · Radius: Wind Speed (m/s)". Four concentric rings (5/10/15/20 m/s, outer rim slightly heavier) and eight spokes in subtle grey; angular labels 0°–315° every 45° outside the rim, cardinals bold; ring labels on page-colored pills along the ~337° bearing. Legend at the bottom: Morning, Afternoon, Evening, Night. All text readable dark-on-light. + Data: 120 translucent circles — Morning green #009E73 clustered N–NE at 5–10 m/s, Afternoon purple spread E–SE at 8–19 m/s, Evening blue S–SW at 5–15 m/s, Night ochre tightly clustered W–NW at 2–6 m/s. All inside the 20 m/s rim. The 5 m/s and 10 m/s pills cover parts of two Morning markers. Legibility verdict: PASS Dark render (plot-dark.png): Background: warm near-black #1A1A17. - Chrome: title in light ink, degree and ring labels in light grey, grid rings and spokes in subtle dark grey, legend text light; no dark-on-dark text. The ring-label pills switch to the dark page color, and the "5 m/s" label is again partly covered by Morning markers. - Data: identical positions and identical palette hues (#009E73, #C475FD, #4467A3, #BD8233) as the light render; only chrome flipped. Brand green clearly visible. + Chrome: title in light ink, subtitle, angular and ring labels in light grey; rings and spokes subtle dark grey, still visible; ring-label pills in the dark page color. Legend text light. No dark-on-dark text. + Data: same four hues as the light render (green first), same positions; translucency blends toward the dark page, so markers read slightly deeper but remain clearly distinguishable, brand green clearly visible. The same two Morning markers are partly covered by the ring-label pills. Legibility verdict: PASS criteria_checklist: visual_quality: - score: 27 + score: 29 max: 30 items: - id: VQ-01 @@ -76,48 +63,49 @@ review: score: 8 max: 8 passed: true - comment: Title 22 px, ring/degree labels 14 px, legend 15 px CSS; all readable - in both themes and at thumbnail size + comment: Explicit font sizes (title 22, subtitle/legend 15, angular and ring + labels 14 CSS px); all text readable in both themes - id: VQ-02 name: No Overlap score: 5 max: 6 - passed: false - comment: Morning markers are drawn over the "5 m/s" ring label (defect line) + passed: true + comment: The opaque ring-label pills for 5 m/s and 10 m/s are drawn above + the markers and cover parts of two Morning markers (defect line) - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: 120 markers at size 8 with 0.72 opacity, well suited to the density; - legend dots visible in both themes + comment: 120 markers at a prominent size with 0.72 fill opacity; legend dots + match the marks in both themes - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Four distinct Imprint hue families, no red-green pairing + comment: Green, purple, blue and ochre separate well; no red-green sole signal - id: VQ-05 name: Layout & Canvas - score: 3 + score: 4 max: 4 - passed: false - comment: Disc fills only about 66 % of the canvas width; large empty band - under the title (defect line) + passed: true + comment: 2400x2400 square, polar disc centered with balanced padding; title, + subtitle and legend fit cleanly - id: VQ-06 name: Axis Labels & Title - score: 1 + score: 2 max: 2 - passed: false - comment: Radius ticks carry m/s, but the angle is never named as wind bearing - (defect line) + passed: true + comment: Subtitle names both axes with units and the angle convention; ring + labels carry m/s - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'Imprint positions 1-4 in order, first series #009E73; #FAF8F1 / - #1A1A17 backgrounds, chrome theme-correct' + comment: 'Imprint palette in canonical order with first series #009E73; backgrounds + #FAF8F1 / #1A1A17; chrome theme-correct in both renders' design_excellence: score: 11 max: 20 @@ -126,24 +114,25 @@ review: name: Aesthetic Sophistication score: 5 max: 8 - passed: false - comment: 'Above baseline: bold cardinal-angle ticks over regular 45° ticks, - ring labels with units on page-colored pills' + passed: true + comment: Bold cardinal angles vs regular intercardinals, a heavier outer rim, + and a quiet subtitle line explaining the angle convention form a typographic + hierarchy beyond the baseline - id: DE-02 name: Visual Refinement score: 3 max: 6 - passed: false - comment: Ring labels placed in the computed least-populated sector, and the - outer ring drawn heavier as the rim; the placement still lets the 5 m/s - label collide + passed: true + comment: Ring labels placed along the bearing of the emptiest sector on page-colored + pills; the placement still lands on markers, so only partial credit - id: DE-03 name: Data Storytelling score: 3 max: 6 - passed: false - comment: Data built so the prevailing direction rotates clockwise through - the day, which the four colored clouds make readable at a glance + passed: true + comment: Data designed so the prevailing direction rotates clockwise through + the day (N morning, E afternoon, S evening, W night, calm at night) — a + readable diurnal story beyond the spec spec_compliance: score: 15 max: 15 @@ -153,29 +142,28 @@ review: score: 5 max: 5 passed: true - comment: Polar scatter with bearing as angle and speed as radius + comment: Polar scatter with color by the optional category column - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Radial gridlines at 5 m/s, angular ticks every 45° in degrees, centered - disc, radius from 0, legend for the color encoding, translucency in dense - areas + comment: Radians internally / degrees displayed, regular radial rings, angular + ticks every 45°, radius from 0 past the max, legend, translucent markers + — all Notes and 'A good version shows' bullets met - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Marks at their exact bearing/speed; no jitter or nudging; all points - inside the 20 m/s rim + comment: Markers at their computed (theta, r) positions, no jitter or nudging; + all points inside the 20 m/s rim - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title 'polar-scatter · javascript · muix · anyplot.ai'; legend entries - Morning/Afternoon/Evening/Night + comment: Title 'polar-scatter · javascript · muix · anyplot.ai'; legend Morning/Afternoon/Evening/Night data_quality: score: 15 max: 15 @@ -185,8 +173,8 @@ review: score: 6 max: 6 passed: true - comment: Prevailing-direction clusters, sparse sectors, varying speeds and - outliers near the rim + comment: Clusters around prevailing directions, a sparse WNW sector, outliers + near the rim, interleaving edges - id: DQ-02 name: Realistic Context score: 5 @@ -198,10 +186,10 @@ review: score: 4 max: 4 passed: true - comment: Speeds 0.5-19.5 m/s and bearings 0-360°, sensible for surface wind; - 120 points within the 100-150 range (no Check values in the spec) + comment: Speeds 0.5–20 m/s, 120 points (inside 100–150); no Check values in + the spec code_quality: - score: 9 + score: 10 max: 10 items: - id: CQ-01 @@ -209,63 +197,61 @@ review: score: 3 max: 3 passed: true - comment: Flat module; the one extra component (PolarGrid) is required to use - the scale hooks + comment: Data/plot zones at module level; PolarGrid and RingLabels are React + components required to call the useXScale/useYScale hooks - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Fixed-seed LCG + comment: Fixed-seed LCG (seed 42) - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: ChartsContainer, ScatterPlot, useXScale, useYScale all used + comment: All imports used - id: CQ-04 name: Code Elegance - score: 1 + score: 2 max: 2 - passed: false - comment: Hand-parsed hex to rgba (lines 115-120) and a hand-built HTML legend - (lines 268-291) where native options exist (defect line) + passed: true + comment: Nice-step ring computation and sector count are short and have no + available call equivalent - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Default export, v9 ChartsContainer API, skipAnimation, sized from - ANYPLOT_SIZE + comment: Default-exported component, skipAnimation, v9 composition API library_mastery: - score: 6 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 3 + score: 4 max: 5 - passed: false - comment: Good v9 composition, but the legend and the opacity are hand-built - instead of ChartsLegend / sx styling + passed: true + comment: 'v9 composition: ChartsDataProvider + ChartsWrapper + ChartsSurface + + ChartsLegend with ScatterPlot, styled via the .MuiScatterChart-marker + class' - id: LM-02 name: Distinctive Features score: 3 max: 5 - passed: false - comment: 'Uses MUI X''s composition API: a custom SVG layer inside ChartsContainer - that reads the chart scales through useXScale/useYScale to draw the polar - grid' - verdict: REJECTED + passed: true + comment: Custom SVG polar grid drawn from the chart's own useXScale/useYScale + hooks inside ChartsSurface, keeping native scatter tooltips and legend + verdict: APPROVED model: claude-opus-5-5 criteria_version: qc-899b86ffde.aqr-869a70087c.sg-c73b4641dd.lib-f7283eb626 - rendered_at: '2026-10-24T11:54:36Z' + rendered_at: '2026-10-24T12:00:20Z' impl_tags: dependencies: [] techniques: - polar-projection - layer-composition - - custom-legend - manual-ticks - hover-tooltips - html-export @@ -275,3 +261,5 @@ impl_tags: dataprep: [] styling: - alpha-blending + - grid-styling + - minimal-chrome