{"spec_id":"spc-xbar-r","library":"muix","language":"javascript","code":"// anyplot.ai\n// spc-xbar-r: Statistical Process Control Chart (X-bar/R)\n// Library: muix 7.29.1 | JavaScript 22.22.3\n// Quality: 92/100 | Created: 2026-06-20\n\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { LinePlot } from \"@mui/x-charts/LineChart\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\nimport { useDrawingArea } from \"@mui/x-charts/hooks\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst FONT = \"system-ui, -apple-system, sans-serif\";\n\n// SPC control chart constants for subgroup size n=5\nconst A2 = 0.577;\nconst D3 = 0.000;\nconst D4 = 2.115;\n\nconst N = 25;             // number of subgroups\nconst XBAR_BAR = 25.000; // process grand mean (CNC shaft diameter, mm)\nconst R_BAR = 0.120;     // average range (mm)\n\nconst XBAR_UCL = XBAR_BAR + A2 * R_BAR;            // ≈ 25.069\nconst XBAR_LCL = XBAR_BAR - A2 * R_BAR;            // ≈ 24.931\nconst XBAR_UWL = XBAR_BAR + (2 / 3) * A2 * R_BAR;  // ≈ 25.046\nconst XBAR_LWL = XBAR_BAR - (2 / 3) * A2 * R_BAR;  // ≈ 24.954\n\nconst R_UCL = D4 * R_BAR;                           // ≈ 0.254\nconst R_LCL = D3 * R_BAR;                           // = 0.000\nconst R_UWL = (2 / 3) * D4 * R_BAR;                // ≈ 0.169\n\n// 25 synthetic subgroup means — OOC at indices 7 (25.082), 15 (24.918), 21 (25.075)\nconst sampleIds = Array.from({ length: N }, (_, i) => i + 1);\nconst sampleMeans = [\n  25.012, 24.978, 25.003, 24.995, 25.021,\n  24.963, 25.008, 25.082, 25.034, 24.987,\n  24.991, 25.018, 24.955, 25.007, 24.982,\n  24.918, 25.044, 24.976, 25.013, 25.001,\n  24.968, 25.075, 24.993, 25.006, 24.974,\n];\n\n// 25 synthetic subgroup ranges — OOC at index 11 (0.278)\nconst sampleRanges = [\n  0.098, 0.115, 0.087, 0.132, 0.121,\n  0.108, 0.094, 0.141, 0.119, 0.127,\n  0.103, 0.278, 0.091, 0.138, 0.116,\n  0.143, 0.088, 0.112, 0.097, 0.125,\n  0.136, 0.109, 0.145, 0.102, 0.118,\n];\n\n// x-axis domain with half-unit padding on each side\nconst X_MIN = 0.5;\nconst X_MAX = N + 0.5;\n\n// Custom SVG layer: renders green dots for in-control points, larger red for OOC\nfunction OOCMarkers({ data, yMin, yMax, UCL, LCL }) {\n  const { left, top, width, height } = useDrawingArea();\n  const toX = (i) => left + (i + 1 - X_MIN) / (X_MAX - X_MIN) * width;\n  const toY = (v) => top + (yMax - v) / (yMax - yMin) * height;\n\n  return (\n    <g>\n      {data.map((v, i) =>\n        v > UCL || v < LCL ? null : (\n          <circle key={i} cx={toX(i)} cy={toY(v)} r={5}\n            fill={t.palette[0]} opacity={0.9} />\n        )\n      )}\n      {data.map((v, i) =>\n        v <= UCL && v >= LCL ? null : (\n          <circle key={`ooc${i}`} cx={toX(i)} cy={toY(v)} r={8}\n            fill={t.palette[4]} stroke={t.pageBg} strokeWidth={2.5} />\n        )\n      )}\n    </g>\n  );\n}\n\nconst ML = { left: 112, right: 90, top: 28, bottom: 12 };\nconst MR = { left: 112, right: 90, top: 10, bottom: 72 };\n\nfunction XBarPanel({ width, height }) {\n  const yMin = 24.88;\n  const yMax = 25.12;\n\n  return (\n    <ChartContainer\n      width={width} height={height} margin={ML}\n      series={[{ type: \"line\", data: sampleMeans, color: t.palette[0], showMark: false, curve: \"linear\" }]}\n      xAxis={[{ data: sampleIds, scaleType: \"linear\", min: X_MIN, max: X_MAX }]}\n      yAxis={[{ min: yMin, max: yMax, tickNumber: 5 }]}\n    >\n      <ChartsReferenceLine y={XBAR_BAR}\n        lineStyle={{ stroke: t.inkSoft, strokeWidth: 2.5 }}\n        label=\"X̄\" labelAlign=\"end\"\n        labelStyle={{ fill: t.inkSoft, fontSize: 14, fontFamily: FONT }} />\n      <ChartsReferenceLine y={XBAR_UWL}\n        lineStyle={{ stroke: t.amber, strokeWidth: 1.5, strokeDasharray: \"5 4\", opacity: 0.8 }}\n        label=\"+2σ\" labelAlign=\"end\"\n        labelStyle={{ fill: t.amber, fontSize: 12, fontFamily: FONT }} />\n      <ChartsReferenceLine y={XBAR_LWL}\n        lineStyle={{ stroke: t.amber, strokeWidth: 1.5, strokeDasharray: \"5 4\", opacity: 0.8 }}\n        label=\"−2σ\" labelAlign=\"end\"\n        labelStyle={{ fill: t.amber, fontSize: 12, fontFamily: FONT }} />\n      <ChartsReferenceLine y={XBAR_UCL}\n        lineStyle={{ stroke: t.palette[4], strokeWidth: 2, strokeDasharray: \"9 5\" }}\n        label=\"UCL\" labelAlign=\"end\"\n        labelStyle={{ fill: t.palette[4], fontSize: 14, fontWeight: \"600\", fontFamily: FONT }} />\n      <ChartsReferenceLine y={XBAR_LCL}\n        lineStyle={{ stroke: t.palette[4], strokeWidth: 2, strokeDasharray: \"9 5\" }}\n        label=\"LCL\" labelAlign=\"end\"\n        labelStyle={{ fill: t.palette[4], fontSize: 14, fontWeight: \"600\", fontFamily: FONT }} />\n      <LinePlot skipAnimation />\n      <OOCMarkers data={sampleMeans} yMin={yMin} yMax={yMax} UCL={XBAR_UCL} LCL={XBAR_LCL} />\n      <ChartsYAxis\n        label=\"Sample Mean (mm)\"\n        tickLabelStyle={{ fontSize: 13, fill: t.inkSoft, fontFamily: FONT }}\n        labelStyle={{ fontSize: 15, fill: t.ink, fontFamily: FONT }} />\n    </ChartContainer>\n  );\n}\n\nfunction RPanel({ width, height }) {\n  const yMin = 0;\n  const yMax = 0.32;\n\n  return (\n    <ChartContainer\n      width={width} height={height} margin={MR}\n      series={[{ type: \"line\", data: sampleRanges, color: t.palette[0], showMark: false, curve: \"linear\" }]}\n      xAxis={[{ data: sampleIds, scaleType: \"linear\", min: X_MIN, max: X_MAX }]}\n      yAxis={[{ min: yMin, max: yMax, tickNumber: 5 }]}\n    >\n      <ChartsReferenceLine y={R_BAR}\n        lineStyle={{ stroke: t.inkSoft, strokeWidth: 2.5 }}\n        label=\"R̄\" labelAlign=\"end\"\n        labelStyle={{ fill: t.inkSoft, fontSize: 14, fontFamily: FONT }} />\n      <ChartsReferenceLine y={R_UWL}\n        lineStyle={{ stroke: t.amber, strokeWidth: 1.5, strokeDasharray: \"5 4\", opacity: 0.8 }}\n        label=\"+2σ\" labelAlign=\"end\"\n        labelStyle={{ fill: t.amber, fontSize: 12, fontFamily: FONT }} />\n      <ChartsReferenceLine y={R_UCL}\n        lineStyle={{ stroke: t.palette[4], strokeWidth: 2, strokeDasharray: \"9 5\" }}\n        label=\"UCL\" labelAlign=\"end\"\n        labelStyle={{ fill: t.palette[4], fontSize: 14, fontWeight: \"600\", fontFamily: FONT }} />\n      <LinePlot skipAnimation />\n      <OOCMarkers data={sampleRanges} yMin={yMin} yMax={yMax} UCL={R_UCL} LCL={R_LCL} />\n      <ChartsXAxis\n        label=\"Sample Number\"\n        tickLabelStyle={{ fontSize: 13, fill: t.inkSoft, fontFamily: FONT }}\n        labelStyle={{ fontSize: 15, fill: t.ink, fontFamily: FONT }} />\n      <ChartsYAxis\n        label=\"Range (mm)\"\n        tickLabelStyle={{ fontSize: 13, fill: t.inkSoft, fontFamily: FONT }}\n        labelStyle={{ fontSize: 15, fill: t.ink, fontFamily: FONT }} />\n    </ChartContainer>\n  );\n}\n\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const TITLE_H = 56;\n  const CHART_H = height - TITLE_H;\n  const XBAR_H = Math.round(CHART_H * 0.55);\n  const R_H = CHART_H - XBAR_H;\n\n  return (\n    <Box sx={{\n      width, height,\n      bgcolor: t.pageBg,\n      display: \"flex\",\n      flexDirection: \"column\",\n      pt: \"18px\",\n      boxSizing: \"border-box\",\n    }}>\n      <Typography sx={{\n        color: t.ink,\n        fontSize: 22,\n        fontWeight: 500,\n        textAlign: \"center\",\n        fontFamily: FONT,\n        lineHeight: 1,\n        mb: \"4px\",\n      }}>\n        CNC Shaft Diameter · spc-xbar-r · javascript · muix · anyplot.ai\n      </Typography>\n      <XBarPanel width={width} height={XBAR_H} />\n      <RPanel width={width} height={R_H} />\n    </Box>\n  );\n}\n"}