{"spec_id":"contour-density","library":"muix","language":"javascript","code":"// anyplot.ai\n// contour-density: Density Contour Plot\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-09-04\n//# anyplot-orientation: square\n// anyplot.ai\n// contour-density: Density Contour 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-09-04\n\n// MUI X community has no native contour/isoline chart. We compose one on top\n// of ScatterChart: a raw sample overlay (native ScatterPlot) plus a custom\n// child that draws real marching-squares contour paths from a real 2D KDE,\n// using the chart's own useXScale/useYScale — the documented composition\n// API, not a workaround.\nimport { ScatterChart } from \"@mui/x-charts/ScatterChart\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n// ANYPLOT_TOKENS has no tertiary \"muted\" anchor — derive it the same way the\n// style guide defines it (theme-adaptive, used for context/behind-the-data marks).\nconst INK_MUTED = window.ANYPLOT_THEME === \"dark\" ? \"#A8A79F\" : \"#6B6A63\";\n\n// --- Data: bearing QC measurements from two production batches ------------\n// Batch A sits on-spec; batch B drifted high on both dimensions — a common\n// quality-control pattern where a density contour reveals two overlapping\n// process clusters that a plain scatter would bury in overplotting.\nfunction createRng(seed: number) {\n  let state = seed >>> 0;\n  return () => {\n    state = (1664525 * state + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = createRng(42);\n\nfunction randNormal() {\n  const u1 = Math.max(rand(), 1e-9);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\nfunction correlatedPoint(meanX: number, sdX: number, meanY: number, sdY: number, rho: number) {\n  const z1 = randNormal();\n  const z2 = randNormal();\n  return {\n    x: meanX + sdX * z1,\n    y: meanY + sdY * (rho * z1 + Math.sqrt(1 - rho * rho) * z2),\n  };\n}\n\nconst points: { id: number; x: number; y: number }[] = [];\nfor (let i = 0; i < 900; i += 1) {\n  const p = correlatedPoint(23.92, 0.13, 45.6, 0.7, 0.5);\n  points.push({ id: points.length, x: p.x, y: p.y });\n}\nfor (let i = 0; i < 500; i += 1) {\n  const p = correlatedPoint(24.58, 0.12, 48.4, 0.65, 0.45);\n  points.push({ id: points.length, x: p.x, y: p.y });\n}\n\nconst diameters = points.map((p) => p.x);\nconst weights = points.map((p) => p.y);\nconst diameterMin = Math.min(...diameters);\nconst diameterMax = Math.max(...diameters);\nconst weightMin = Math.min(...weights);\nconst weightMax = Math.max(...weights);\nconst padX = (diameterMax - diameterMin) * 0.12;\nconst padY = (weightMax - weightMin) * 0.12;\nconst domainXMin = diameterMin - padX;\nconst domainXMax = diameterMax + padX;\nconst domainYMin = weightMin - padY;\nconst domainYMax = weightMax + padY;\n\n// --- 2D kernel density estimate on a grid ----------------------------------\nfunction silvermanBandwidth(values: number[]) {\n  const n = values.length;\n  const mean = values.reduce((a, b) => a + b, 0) / n;\n  const variance = values.reduce((a, b) => a + (b - mean) ** 2, 0) / (n - 1);\n  return 1.06 * Math.sqrt(variance) * n ** (-1 / 5);\n}\n\nconst GRID_N = 70;\nconst gridX = Array.from(\n  { length: GRID_N },\n  (_, i) => domainXMin + (i * (domainXMax - domainXMin)) / (GRID_N - 1),\n);\nconst gridY = Array.from(\n  { length: GRID_N },\n  (_, i) => domainYMin + (i * (domainYMax - domainYMin)) / (GRID_N - 1),\n);\nconst bandwidthX = silvermanBandwidth(diameters);\nconst bandwidthY = silvermanBandwidth(weights);\n\nconst densityGrid = gridY.map((yv) =>\n  gridX.map((xv) => {\n    let sum = 0;\n    for (let k = 0; k < points.length; k += 1) {\n      const dx = (xv - points[k].x) / bandwidthX;\n      const dy = (yv - points[k].y) / bandwidthY;\n      sum += Math.exp(-0.5 * (dx * dx + dy * dy));\n    }\n    return sum / (points.length * bandwidthX * bandwidthY);\n  }),\n);\nconst maxDensity = Math.max(...densityGrid.map((row) => Math.max(...row)));\n\n// --- Marching squares: extract iso-density line segments -------------------\nfunction lerp(a: number, b: number, va: number, vb: number, threshold: number) {\n  if (vb === va) return a;\n  return a + ((b - a) * (threshold - va)) / (vb - va);\n}\n\nconst CASE_EDGES: Record<number, [string, string][]> = {\n  1: [[\"L\", \"B\"]],\n  2: [[\"B\", \"R\"]],\n  3: [[\"L\", \"R\"]],\n  4: [[\"T\", \"R\"]],\n  6: [[\"T\", \"B\"]],\n  7: [[\"T\", \"L\"]],\n  8: [[\"T\", \"L\"]],\n  9: [[\"T\", \"B\"]],\n  11: [[\"T\", \"R\"]],\n  12: [[\"L\", \"R\"]],\n  13: [[\"B\", \"R\"]],\n  14: [[\"L\", \"B\"]],\n};\n\nfunction marchingSquares(xs: number[], ys: number[], grid: number[][], threshold: number) {\n  const segments: [number, number, number, number][] = [];\n  for (let i = 0; i < ys.length - 1; i += 1) {\n    for (let j = 0; j < xs.length - 1; j += 1) {\n      const x0 = xs[j];\n      const x1 = xs[j + 1];\n      const y0 = ys[i];\n      const y1 = ys[i + 1];\n      const v00 = grid[i][j];\n      const v10 = grid[i][j + 1];\n      const v01 = grid[i + 1][j];\n      const v11 = grid[i + 1][j + 1];\n\n      const idx =\n        (v00 >= threshold ? 8 : 0) |\n        (v10 >= threshold ? 4 : 0) |\n        (v11 >= threshold ? 2 : 0) |\n        (v01 >= threshold ? 1 : 0);\n      if (idx === 0 || idx === 15) continue;\n\n      const edgePoint = (edge: string): [number, number] => {\n        if (edge === \"T\") return [lerp(x0, x1, v00, v10, threshold), y0];\n        if (edge === \"R\") return [x1, lerp(y0, y1, v10, v11, threshold)];\n        if (edge === \"B\") return [lerp(x0, x1, v01, v11, threshold), y1];\n        return [x0, lerp(y0, y1, v00, v01, threshold)];\n      };\n\n      // Saddle cases (5, 10): resolve via the cell's average value so the\n      // contour topology stays consistent with the smooth underlying KDE.\n      const avg = (v00 + v10 + v01 + v11) / 4;\n      let pairs: [string, string][];\n      if (idx === 5) pairs = avg >= threshold ? [[\"T\", \"L\"], [\"B\", \"R\"]] : [[\"T\", \"R\"], [\"L\", \"B\"]];\n      else if (idx === 10) pairs = avg >= threshold ? [[\"T\", \"R\"], [\"L\", \"B\"]] : [[\"T\", \"L\"], [\"B\", \"R\"]];\n      else pairs = CASE_EDGES[idx];\n\n      pairs.forEach(([e1, e2]) => {\n        const [ax, ay] = edgePoint(e1);\n        const [bx, by] = edgePoint(e2);\n        segments.push([ax, ay, bx, by]);\n      });\n    }\n  }\n  return segments;\n}\n\nconst LEVEL_FRACTIONS = [0.12, 0.28, 0.46, 0.65, 0.85];\nconst CONTOURS = LEVEL_FRACTIONS.map((fraction) => ({\n  fraction,\n  segments: marchingSquares(gridX, gridY, densityGrid, fraction * maxDensity),\n}));\n\n// --- Imprint sequential ramp (brand green → blue) --------------------------\nfunction mixHex(hexA: string, hexB: string, ratio: number) {\n  const a = parseInt(hexA.slice(1), 16);\n  const b = parseInt(hexB.slice(1), 16);\n  const channel = (shift: number) => {\n    const av = (a >> shift) & 255;\n    const bv = (b >> shift) & 255;\n    return Math.round(av + (bv - av) * ratio).toString(16).padStart(2, \"0\");\n  };\n  return `#${[16, 8, 0].map(channel).join(\"\")}`;\n}\n\nfunction hexToRgba(hex: string, alpha: number) {\n  const n = parseInt(hex.slice(1), 16);\n  return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;\n}\n\n// Custom mark: draws the precomputed iso-density paths in the chart's own\n// pixel space via its native scale hooks — this is MUI X's documented\n// composition pattern for chart types the community surface lacks natively.\nfunction ContourLines() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  if (!xScale || !yScale) return null;\n\n  return (\n    <g>\n      {CONTOURS.map(({ fraction, segments }, level) => {\n        if (segments.length === 0) return null;\n        const d = segments\n          .map(([x1, y1, x2, y2]) => `M ${xScale(x1)} ${yScale(y1)} L ${xScale(x2)} ${yScale(y2)}`)\n          .join(\" \");\n        const color = mixHex(t.seq[0], t.seq[1], level / (LEVEL_FRACTIONS.length - 1));\n        return (\n          <path\n            key={fraction}\n            d={d}\n            fill=\"none\"\n            stroke={color}\n            strokeWidth={2.5 + level * 0.7}\n            strokeOpacity={0.92}\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n          />\n        );\n      })}\n    </g>\n  );\n}\n\nfunction DensityLegend({ height }: { height: number }) {\n  const rows = LEVEL_FRACTIONS.map((fraction, level) => ({\n    fraction,\n    color: mixHex(t.seq[0], t.seq[1], level / (LEVEL_FRACTIONS.length - 1)),\n  })).reverse();\n  return (\n    <Box sx={{ width: 168, height, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", pl: \"18px\" }}>\n      <Typography sx={{ color: t.inkSoft, fontSize: 14, fontWeight: 600, mb: \"10px\", fontFamily: \"inherit\" }}>\n        KDE density\n      </Typography>\n      {rows.map(({ fraction, color }, i) => (\n        <Box key={color} sx={{ display: \"flex\", alignItems: \"center\", mb: \"8px\" }}>\n          <Box sx={{ width: 22, height: 6, borderRadius: \"3px\", bgcolor: color, mr: \"10px\", flexShrink: 0 }} />\n          <Typography sx={{ color: t.inkSoft, fontSize: 13, fontFamily: \"inherit\" }}>\n            {i === 0 ? \"Highest\" : i === rows.length - 1 ? \"Lowest\" : `~${Math.round(fraction * 100)}%`}\n          </Typography>\n        </Box>\n      ))}\n    </Box>\n  );\n}\n\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const TITLE_H = 100;\n  const LEGEND_W = 168;\n  const chartWidth = width - LEGEND_W;\n  const chartHeight = height - TITLE_H;\n\n  const title = \"Bearing QC: Diameter vs. Weight · contour-density · javascript · muix · anyplot.ai\";\n  const titleSize = title.length > 67 ? Math.round(22 * (67 / title.length)) : 22;\n  const contextColor = hexToRgba(INK_MUTED, 0.55);\n\n  return (\n    <Box sx={{ width, height, bgcolor: t.pageBg, display: \"flex\", flexDirection: \"column\" }}>\n      <Box sx={{ height: TITLE_H, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", px: \"40px\" }}>\n        <Typography sx={{ color: t.ink, fontSize: titleSize, fontWeight: 600, lineHeight: 1.25, fontFamily: \"inherit\" }}>\n          {title}\n        </Typography>\n        <Typography sx={{ color: t.inkSoft, fontSize: 16, fontStyle: \"italic\", lineHeight: 1.3, fontFamily: \"inherit\", mt: \"4px\" }}>\n          Contours trace KDE density across two production batches (n = 1,400 parts)\n        </Typography>\n      </Box>\n      <Box sx={{ flex: 1, display: \"flex\", flexDirection: \"row\" }}>\n        <ScatterChart\n          width={chartWidth}\n          height={chartHeight}\n          skipAnimation\n          disableVoronoi\n          series={[\n            {\n              id: \"sample\",\n              type: \"scatter\",\n              data: points,\n              color: contextColor,\n              markerSize: 3.5,\n              label: \"Sampled parts\",\n            },\n          ]}\n          xAxis={[\n            {\n              scaleType: \"linear\",\n              min: domainXMin,\n              max: domainXMax,\n              label: \"Outer Diameter (mm)\",\n              labelStyle: { fontSize: 16, fill: t.inkSoft },\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            },\n          ]}\n          yAxis={[\n            {\n              scaleType: \"linear\",\n              min: domainYMin,\n              max: domainYMax,\n              label: \"Weight (g)\",\n              labelStyle: { fontSize: 16, fill: t.inkSoft },\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n              // MUI X positions the rotated axis label at a fixed offset of\n              // `tickFontSize + tickSize + 10` from the axis line — that offset\n              // uses this deprecated prop, NOT `tickLabelStyle.fontSize` above,\n              // so it must be set wide enough on its own to clear the actual\n              // (wider) rendered tick-label text at every tick position.\n              tickFontSize: 46,\n            },\n          ]}\n          grid={{ horizontal: true, vertical: true }}\n          margin={{ top: 20, right: 40, bottom: 90, left: 150 }}\n          sx={{\n            \"& .MuiChartsGrid-line\": { stroke: t.grid, strokeWidth: 1 },\n          }}\n          slotProps={{ legend: { hidden: true } }}\n        >\n          <ContourLines />\n        </ScatterChart>\n        <DensityLegend height={chartHeight} />\n      </Box>\n    </Box>\n  );\n}\n"}