{"spec_id":"histogram-capability","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// histogram-capability: Process Capability Plot with Specification Limits\n// Library: chartjs 4.4.7 | JavaScript 22.22.3\n// Quality: 88/100 | Created: 2026-06-20\n\nconst t = window.ANYPLOT_TOKENS;\n\n// ── Deterministic data generation (LCG + Box-Muller) ────────────────────────\nlet seed = 42;\nfunction lcg() {\n  seed = (seed * 1664525 + 1013904223) & 0xffffffff;\n  return (seed >>> 0) / 0x100000000;\n}\nfunction randn() {\n  return Math.sqrt(-2 * Math.log(lcg() || 1e-10)) * Math.cos(2 * Math.PI * lcg());\n}\n\n// Process parameters (shaft diameter, mm)\nconst LSL = 9.95, USL = 10.05, TARGET = 10.00;\nconst POP_MEAN = 10.005, POP_STD = 0.0125;\nconst N = 200;\n\nconst measurements = Array.from({ length: N }, () => POP_MEAN + POP_STD * randn());\n\n// Sample statistics — computed from data\nconst sampleMean = measurements.reduce((s, v) => s + v, 0) / N;\nconst sampleStd = Math.sqrt(measurements.reduce((s, v) => s + (v - sampleMean) ** 2, 0) / (N - 1));\n\n// Capability indices\nconst Cp  = (USL - LSL) / (6 * sampleStd);\nconst Cpk = Math.min((USL - sampleMean) / (3 * sampleStd), (sampleMean - LSL) / (3 * sampleStd));\n\n// ── Histogram bins ───────────────────────────────────────────────────────────\nconst BIN_COUNT = 20;\nconst X_MIN = 9.93, X_MAX = 10.08;\nconst binWidth = (X_MAX - X_MIN) / BIN_COUNT;\nconst binCenters = Array.from({ length: BIN_COUNT }, (_, i) => X_MIN + (i + 0.5) * binWidth);\nconst freqs = new Array(BIN_COUNT).fill(0);\nmeasurements.forEach(m => {\n  const idx = Math.min(Math.floor((m - X_MIN) / binWidth), BIN_COUNT - 1);\n  if (idx >= 0 && idx < BIN_COUNT) freqs[idx]++;\n});\n\n// ── Fitted normal distribution curve (scaled to histogram counts) ────────────\nconst CURVE_PTS = 150;\nconst normalData = Array.from({ length: CURVE_PTS }, (_, i) => {\n  const x = X_MIN + (i / (CURVE_PTS - 1)) * (X_MAX - X_MIN);\n  const z = (x - sampleMean) / sampleStd;\n  const y = (1 / (sampleStd * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * z * z) * N * binWidth;\n  return { x, y };\n});\n\nconst maxFreq = Math.max(...freqs, ...normalData.map(d => d.y));\nconst yMax = Math.ceil(maxFreq * 1.2 / 5) * 5;\n\n// ── Spec-lines + capability annotation plugin ────────────────────────────────\nconst specPlugin = {\n  id: 'specLines',\n  beforeDatasetsDraw(chart) {\n    const { ctx, chartArea, scales } = chart;\n    const xScale = scales.x;\n\n    // In-spec region background shading (behind histogram bars)\n    const xLSL = xScale.getPixelForValue(LSL);\n    const xUSL = xScale.getPixelForValue(USL);\n    ctx.save();\n    ctx.fillStyle = t.palette[0] + '20';\n    ctx.fillRect(xLSL, chartArea.top, xUSL - xLSL, chartArea.bottom - chartArea.top);\n    ctx.restore();\n  },\n  afterDatasetsDraw(chart) {\n    const { ctx, chartArea, scales } = chart;\n    const xScale = scales.x;\n\n    ctx.save();\n\n    // Vertical spec-limit and target lines\n    const lines = [\n      { value: LSL,    color: '#AE3030', dash: [10, 5], label: 'LSL',    labelSide: 'left'  },\n      { value: USL,    color: '#AE3030', dash: [10, 5], label: 'USL',    labelSide: 'right' },\n      { value: TARGET, color: t.inkSoft, dash: [6,  4], label: 'Target', labelSide: 'right' },\n    ];\n\n    lines.forEach(({ value, color, dash, label, labelSide }) => {\n      const px = xScale.getPixelForValue(value);\n\n      ctx.strokeStyle = color;\n      ctx.lineWidth = 3;\n      ctx.setLineDash(dash);\n      ctx.beginPath();\n      ctx.moveTo(px, chartArea.top);\n      ctx.lineTo(px, chartArea.bottom);\n      ctx.stroke();\n\n      ctx.setLineDash([]);\n      ctx.fillStyle = color;\n      ctx.font = 'bold 15px system-ui, sans-serif';\n      ctx.textAlign = labelSide === 'right' ? 'left' : 'right';\n      ctx.fillText(label, px + (labelSide === 'right' ? 7 : -7), chartArea.top + 26);\n    });\n\n    // Cp / Cpk annotation box (upper-right corner)\n    const bW = 200, bH = 96;\n    const bX = chartArea.right - bW - 12;\n    const bY = chartArea.top + 12;\n\n    ctx.fillStyle = t.elevatedBg;\n    ctx.strokeStyle = t.grid;\n    ctx.lineWidth = 1;\n    ctx.fillRect(bX, bY, bW, bH);\n    ctx.strokeRect(bX, bY, bW, bH);\n\n    ctx.fillStyle = t.ink;\n    ctx.font = 'bold 18px system-ui, sans-serif';\n    ctx.textAlign = 'left';\n    ctx.fillText(`Cp  = ${Cp.toFixed(2)}`,  bX + 20, bY + 36);\n    ctx.fillText(`Cpk = ${Cpk.toFixed(2)}`, bX + 20, bY + 70);\n\n    ctx.restore();\n  },\n};\n\n// ── Mount ────────────────────────────────────────────────────────────────────\nconst canvas = document.createElement('canvas');\ndocument.getElementById('container').appendChild(canvas);\n\n// ── Chart ────────────────────────────────────────────────────────────────────\nnew Chart(canvas, {\n  type: 'bar',\n  data: {\n    datasets: [\n      {\n        type: 'bar',\n        label: 'Frequency',\n        data: binCenters.map((c, i) => ({ x: c, y: freqs[i] })),\n        backgroundColor: t.palette[0] + 'bb',\n        borderColor: t.palette[0],\n        borderWidth: 1,\n        barPercentage: 1,\n        categoryPercentage: 1,\n        order: 2,\n      },\n      {\n        type: 'line',\n        label: 'Normal fit',\n        data: normalData,\n        borderColor: t.palette[1],\n        borderWidth: 3.5,\n        pointRadius: 0,\n        tension: 0.4,\n        fill: false,\n        order: 1,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: 'histogram-capability · javascript · chartjs · anyplot.ai',\n        color: t.ink,\n        font: { size: 22, weight: '600' },\n        padding: { top: 10, bottom: 20 },\n      },\n      legend: {\n        labels: {\n          color: t.ink,\n          font: { size: 16 },\n          boxWidth: 22,\n          padding: 20,\n        },\n      },\n    },\n    scales: {\n      x: {\n        type: 'linear',\n        min: X_MIN,\n        max: X_MAX,\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          maxTicksLimit: 11,\n          callback: v => v.toFixed(3),\n        },\n        grid: { color: t.grid },\n        border: { display: false },\n        title: {\n          display: true,\n          text: 'Shaft Diameter (mm)',\n          color: t.ink,\n          font: { size: 16 },\n          padding: { top: 10 },\n        },\n      },\n      y: {\n        type: 'linear',\n        beginAtZero: true,\n        max: yMax,\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          stepSize: 10,\n        },\n        grid: { color: t.grid },\n        border: { display: false },\n        title: {\n          display: true,\n          text: 'Frequency',\n          color: t.ink,\n          font: { size: 16 },\n          padding: { bottom: 10 },\n        },\n      },\n    },\n  },\n  plugins: [specPlugin],\n});\n"}