{"spec_id":"scatter-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// scatter-basic: Basic Scatter Plot\n// Library: muix 7.29.1 | JavaScript 22.22.3\n// Quality: 88/100 | Created: 2026-06-25\n//# anyplot-orientation: landscape\n// anyplot.ai\n// scatter-basic: Basic Scatter Plot\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-06-25\n\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ScatterPlot } from \"@mui/x-charts/ScatterChart\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { ChartsGrid } from \"@mui/x-charts/ChartsGrid\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\n\n// Deterministic LCG (seed 42) — no Math.random() in browser harness\nlet _seed = 42;\nfunction lcg() {\n  _seed = (1664525 * _seed + 1013904223) >>> 0;\n  return _seed / 4294967296;\n}\nfunction randn() {\n  const u = lcg(), v = lcg();\n  return Math.sqrt(-2 * Math.log(u + 1e-15)) * Math.cos(2 * Math.PI * v);\n}\n\n// Data: Weekly creative project hours vs employee innovation score\n// Scenario: 120 tech-sector employees; moderate positive correlation (r ≈ 0.72)\nconst N = 120;\nconst scatterData = Array.from({ length: N }, (_, id) => {\n  const hours = 2 + lcg() * 36;\n  const score = 28 + (hours / 40) * 58 + randn() * 11;\n  return {\n    id,\n    x: Math.round(hours * 10) / 10,\n    y: Math.min(98, Math.max(12, Math.round(score * 10) / 10)),\n  };\n});\n\n// OLS linear regression for trend line overlay\nconst sumX  = scatterData.reduce((s, p) => s + p.x, 0);\nconst sumY  = scatterData.reduce((s, p) => s + p.y, 0);\nconst sumXY = scatterData.reduce((s, p) => s + p.x * p.y, 0);\nconst sumX2 = scatterData.reduce((s, p) => s + p.x * p.x, 0);\nconst sumY2 = scatterData.reduce((s, p) => s + p.y * p.y, 0);\nconst meanX = sumX / N;\nconst meanY = sumY / N;\nconst slope     = (sumXY - N * meanX * meanY) / (sumX2 - N * meanX * meanX);\nconst intercept = meanY - slope * meanX;\nconst r = (N * sumXY - sumX * sumY) /\n  Math.sqrt((N * sumX2 - sumX * sumX) * (N * sumY2 - sumY * sumY));\n\n// Pixel-space helpers — CSS px in the 1600×900 mount\nconst MARGIN = { top: 72, right: 52, bottom: 90, left: 100 };\nconst X_MIN = 0, X_MAX = 42, Y_MIN = 8, Y_MAX = 105;\nconst PLOT_W = width  - MARGIN.left - MARGIN.right;\nconst PLOT_H = height - MARGIN.top  - MARGIN.bottom;\nconst xPx = (x) => MARGIN.left + ((x - X_MIN) / (X_MAX - X_MIN)) * PLOT_W;\nconst yPx = (y) => height - MARGIN.bottom - ((y - Y_MIN) / (Y_MAX - Y_MIN)) * PLOT_H;\n\n// Trend line endpoints (clamped to y-axis domain)\nconst trendY0   = Math.max(Y_MIN, Math.min(Y_MAX, intercept));\nconst trendY42  = Math.max(Y_MIN, Math.min(Y_MAX, slope * X_MAX + intercept));\n\n// Brand green at 72% opacity — satisfies \"alpha ~0.7\" spec requirement\nconst BRAND_RGBA = \"rgba(0, 158, 115, 0.72)\";\nconst TITLE = \"scatter-basic · javascript · muix · anyplot.ai\";\n\nexport default function Chart() {\n  return (\n    <ChartContainer\n      width={width}\n      height={height}\n      margin={MARGIN}\n      series={[{\n        type: \"scatter\",\n        id: \"employees\",\n        label: \"Employee\",\n        color: BRAND_RGBA,\n        markerSize: 7,\n        data: scatterData,\n        valueFormatter: (v) => v ? `${v.x} h/wk · score ${v.y}` : \"\",\n      }]}\n      xAxis={[{\n        scaleType: \"linear\",\n        min: X_MIN,\n        max: X_MAX,\n        label: \"Weekly Hours on Creative Projects\",\n        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n        labelStyle: { fontSize: 16, fill: t.ink },\n      }]}\n      yAxis={[{\n        scaleType: \"linear\",\n        min: Y_MIN,\n        max: Y_MAX,\n        label: \"Innovation Score\",\n        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n        labelStyle: { fontSize: 16, fill: t.ink },\n      }]}\n    >\n      <ChartsGrid horizontal vertical />\n      <ScatterPlot skipAnimation />\n\n      {/* OLS trend line — dashed SVG path, highlights the positive correlation */}\n      <line\n        x1={xPx(X_MIN)} y1={yPx(trendY0)}\n        x2={xPx(X_MAX)} y2={yPx(trendY42)}\n        stroke={t.inkSoft}\n        strokeWidth={2}\n        strokeDasharray=\"6 4\"\n        opacity={0.55}\n      />\n\n      {/* MUI X ChartsReferenceLine — vertical marker at mean creative hours */}\n      <ChartsReferenceLine\n        x={Math.round(meanX)}\n        label={`Avg ${Math.round(meanX)} h/wk`}\n        labelAlign=\"end\"\n        lineStyle={{ stroke: t.inkSoft, opacity: 0.4 }}\n        labelStyle={{ fontSize: 12, fill: t.inkSoft }}\n      />\n\n      <ChartsXAxis />\n      <ChartsYAxis />\n\n      {/* L-frame: suppress top and right axis lines for clean L-shape */}\n      <line\n        x1={MARGIN.left - 1} y1={MARGIN.top}\n        x2={width - MARGIN.right + 1} y2={MARGIN.top}\n        stroke={t.pageBg}\n        strokeWidth={3}\n      />\n      <line\n        x1={width - MARGIN.right} y1={MARGIN.top - 1}\n        x2={width - MARGIN.right} y2={height - MARGIN.bottom + 1}\n        stroke={t.pageBg}\n        strokeWidth={3}\n      />\n\n      {/* Title */}\n      <text\n        x={width / 2}\n        y={38}\n        textAnchor=\"middle\"\n        fontSize={22}\n        fontWeight={600}\n        fill={t.ink}\n      >\n        {TITLE}\n      </text>\n\n      {/* Correlation annotation — focal point for the storytelling */}\n      <text\n        x={width - MARGIN.right - 10}\n        y={MARGIN.top + 22}\n        textAnchor=\"end\"\n        fontSize={13}\n        fill={t.inkSoft}\n      >\n        {`r = ${r.toFixed(2)}`}\n      </text>\n    </ChartContainer>\n  );\n}\n"}