{"spec_id":"density-rug","library":"muix","language":"javascript","code":"// anyplot.ai\n// density-rug: Density Plot with Rug Marks\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-02\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { useXScale, useDrawingArea } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst TITLE = \"density-rug · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 56;\n\n// --- Data (in-memory, deterministic): petal lengths from two wildflower\n// populations surveyed in the same meadow, merged into one sample. -----------\n// Small LCG so results are reproducible without a seeded Math.random().\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\n\nconst rand = lcg(42);\n\nfunction randomNormal() {\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\nconst POP_A_SIZE = 75;\nconst POP_A_MEAN = 4.2;\nconst POP_A_SD = 0.4;\n\nconst POP_B_SIZE = 65;\nconst POP_B_MEAN = 6.6;\nconst POP_B_SD = 0.5;\n\nconst petalLengths = [\n  ...Array.from(\n    { length: POP_A_SIZE },\n    () => POP_A_MEAN + POP_A_SD * randomNormal(),\n  ),\n  ...Array.from(\n    { length: POP_B_SIZE },\n    () => POP_B_MEAN + POP_B_SD * randomNormal(),\n  ),\n];\n\n// --- Gaussian KDE -------------------------------------------------------\nfunction gaussianKernel(u) {\n  return Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);\n}\n\nconst n = petalLengths.length;\nconst sampleMean = petalLengths.reduce((sum, v) => sum + v, 0) / n;\nconst variance =\n  petalLengths.reduce((sum, v) => sum + (v - sampleMean) ** 2, 0) / (n - 1);\n// Narrower than Silverman's rule of thumb, otherwise the two source\n// populations blur into a single smoothed hump instead of staying distinct —\n// the rug marks below then confirm the resulting gap is real, not a KDE artifact.\nconst bandwidth = 0.55 * Math.sqrt(variance) * n ** (-1 / 5);\n\nconst dataMin = Math.min(...petalLengths);\nconst dataMax = Math.max(...petalLengths);\nconst GRID_POINTS = 200;\nconst gridStart = dataMin - 3 * bandwidth;\nconst gridEnd = dataMax + 3 * bandwidth;\nconst gridStep = (gridEnd - gridStart) / (GRID_POINTS - 1);\n\nconst grid = Array.from(\n  { length: GRID_POINTS },\n  (_, i) => gridStart + i * gridStep,\n);\nconst density = grid.map(\n  (x) =>\n    petalLengths.reduce(\n      (sum, xi) => sum + gaussianKernel((x - xi) / bandwidth),\n      0,\n    ) /\n    (n * bandwidth),\n);\n\n// --- Rug marks: one short tick per raw observation, anchored to the plot's\n// bottom edge via the chart's own x-scale and drawing-area geometry. ---------\nfunction RugMarks({ values, color }) {\n  const xScale = useXScale();\n  const { top, height } = useDrawingArea();\n  const axisY = top + height;\n  const tickLength = 18;\n\n  return (\n    <g>\n      {values.map((value, i) => (\n        <line\n          key={i}\n          x1={xScale(value)}\n          x2={xScale(value)}\n          y1={axisY}\n          y2={axisY - tickLength}\n          stroke={color}\n          strokeWidth={1.5}\n          strokeOpacity={0.55}\n        />\n      ))}\n    </g>\n  );\n}\n\nexport default function Chart() {\n  const chartHeight = window.ANYPLOT_SIZE.height - TITLE_HEIGHT;\n\n  return (\n    <div\n      style={{\n        width: window.ANYPLOT_SIZE.width,\n        height: window.ANYPLOT_SIZE.height,\n      }}\n    >\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          lineHeight: `${TITLE_HEIGHT}px`,\n          paddingLeft: 24,\n          fontSize: 22,\n          fontWeight: 600,\n          color: t.ink,\n        }}\n      >\n        {TITLE}\n      </div>\n      <LineChart\n        width={window.ANYPLOT_SIZE.width}\n        height={chartHeight}\n        margin={{ left: 110, right: 40, top: 20, bottom: 60 }}\n        skipAnimation\n        series={[\n          {\n            data: density,\n            label: \"Density\",\n            color: t.palette[0],\n            area: true,\n            curve: \"natural\",\n            showMark: false,\n          },\n        ]}\n        xAxis={[\n          {\n            data: grid,\n            scaleType: \"linear\",\n            label: \"Petal Length (cm)\",\n            labelStyle: { fontSize: 16 },\n            tickLabelStyle: { fontSize: 14 },\n            valueFormatter: (v) => v.toFixed(1),\n          },\n        ]}\n        yAxis={[\n          {\n            label: \"Density\",\n            labelStyle: { fontSize: 16 },\n            // tickFontSize only drives the axis-label offset (see ChartsYAxis\n            // labelRefPoint) — actual tick glyphs stay at tickLabelStyle's 14px.\n            tickFontSize: 40,\n            tickLabelStyle: { fontSize: 14 },\n            valueFormatter: (v) => v.toFixed(2),\n          },\n        ]}\n        grid={{ horizontal: true }}\n        slotProps={{ legend: { hidden: true } }}\n        sx={{\n          \"& .MuiAreaElement-root\": { fillOpacity: 0.3 },\n          \"& .MuiLineElement-root\": { strokeWidth: 3 },\n        }}\n      >\n        <RugMarks values={petalLengths} color={t.palette[0]} />\n      </LineChart>\n    </div>\n  );\n}\n"}