{"spec_id":"frequency-polygon-basic","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// frequency-polygon-basic: Frequency Polygon for Distribution Comparison\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 88/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Tiny fixed-seed LCG — the browser has no seeded RNG.\nlet seed = 42;\nfunction lcg() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\nfunction randNormal(mean, sd) {\n  const u1 = 1 - lcg();\n  const u2 = lcg();\n  const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n  return mean + z * sd;\n}\n\nconst groups = [\n  { name: \"Placebo\", mean: 620, sd: 90, n: 400 },\n  { name: \"Low dose\", mean: 540, sd: 80, n: 400 },\n  { name: \"High dose\", mean: 470, sd: 70, n: 400 },\n];\n\n// Shared bin edges across all groups so the polygons compare fairly.\nconst BIN_WIDTH = 40;\nconst BIN_MIN = 250;\nconst BIN_MAX = 850;\nconst binEdges = [];\nfor (let edge = BIN_MIN; edge <= BIN_MAX; edge += BIN_WIDTH) binEdges.push(edge);\nconst binCount = binEdges.length - 1;\nconst binMidpoints = Array.from(\n  { length: binCount },\n  (_, i) => (binEdges[i] + binEdges[i + 1]) / 2,\n);\n\nfunction frequencyPolygon(mean, sd, n) {\n  const counts = new Array(binCount).fill(0);\n  for (let i = 0; i < n; i++) {\n    const v = randNormal(mean, sd);\n    const bin = Math.floor((v - BIN_MIN) / BIN_WIDTH);\n    if (bin >= 0 && bin < binCount) counts[bin] += 1;\n  }\n  // Extend the polygon to zero at both ends by padding one empty bin on each side.\n  const midpoints = [binMidpoints[0] - BIN_WIDTH, ...binMidpoints, binMidpoints[binCount - 1] + BIN_WIDTH];\n  const values = [0, ...counts, 0];\n  return midpoints.map((x, i) => [x, values[i]]);\n}\n\n// Redundant color+style encoding (spec: \"distinct line colors and/or styles\")\n// so overlapping fills near ~530-590ms remain distinguishable by stroke alone.\nconst DASH_STYLES = [\"Solid\", \"ShortDash\", \"ShortDot\"];\n\nconst series = groups.map((g, i) => ({\n  name: g.name,\n  data: frequencyPolygon(g.mean, g.sd, g.n),\n  color: t.palette[i],\n  lineWidth: 3,\n  dashStyle: DASH_STYLES[i],\n  marker: { enabled: true, radius: 4, symbol: \"circle\", fillColor: t.palette[i], lineWidth: 0 },\n  fillOpacity: 0.08,\n}));\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"area\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"frequency-polygon-basic · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  xAxis: {\n    title: { text: \"Reaction Time (ms)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    min: BIN_MIN - BIN_WIDTH,\n    max: BIN_MAX + BIN_WIDTH,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    // Distinctive Highcharts touch: mark each group's mean reaction time.\n    plotLines: groups.map((g, i) => ({\n      value: g.mean,\n      color: t.palette[i],\n      dashStyle: \"Dot\",\n      width: 1.5,\n      zIndex: 5,\n      label: {\n        text: `${g.name} mean`,\n        rotation: 90,\n        align: \"left\",\n        x: 4,\n        y: 8,\n        style: { color: t.inkSoft, fontSize: \"11px\" },\n      },\n    })),\n  },\n  yAxis: {\n    title: { text: \"Frequency\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    gridLineColor: t.grid,\n    min: 0,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  plotOptions: {\n    series: { animation: false },\n    area: { marker: { enabled: true, radius: 4 }, lineWidth: 3, fillOpacity: 0.08 },\n  },\n  tooltip: { enabled: false },\n  series,\n});\n"}