{"spec_id":"frequency-polygon-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// frequency-polygon-basic: Frequency Polygon for Distribution Comparison\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: reaction times (ms) across three experimental conditions --------\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\n\nfunction normalSamples(rand, mean, std, n) {\n  const samples = [];\n  for (let i = 0; i < n; i++) {\n    const u1 = rand();\n    const u2 = rand();\n    const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n    samples.push(mean + z * std);\n  }\n  return samples;\n}\n\nconst rand = lcg(42);\nconst groups = [\n  { name: \"Placebo\", values: normalSamples(rand, 450, 60, 300) },\n  { name: \"Caffeine\", values: normalSamples(rand, 400, 50, 300) },\n  { name: \"Sleep-deprived\", values: normalSamples(rand, 520, 80, 300) },\n];\n\n// Shared bin edges across all groups so the polygons compare fairly.\nconst allValues = groups.flatMap((g) => g.values);\nconst dataMin = Math.min(...allValues);\nconst dataMax = Math.max(...allValues);\nconst binCount = 18;\nconst binWidth = (dataMax - dataMin) / binCount;\nconst midpoints = Array.from({ length: binCount }, (_, i) => dataMin + (i + 0.5) * binWidth);\n\nfunction toPolygon(values) {\n  const counts = new Array(binCount).fill(0);\n  values.forEach((v) => {\n    const idx = Math.min(binCount - 1, Math.floor((v - dataMin) / binWidth));\n    counts[idx] += 1;\n  });\n  // Extend to zero at both ends so the polygon closes.\n  const points = counts.map((c, i) => [midpoints[i], c]);\n  const peakIdx = counts.indexOf(Math.max(...counts));\n  const peak = [midpoints[peakIdx], counts[peakIdx]];\n  points.unshift([midpoints[0] - binWidth, 0]);\n  points.push([midpoints[midpoints.length - 1] + binWidth, 0]);\n  return { points, peak };\n}\n\nconst lineStyles = [\"solid\", \"dashed\", \"dotted\"];\nconst polygons = groups.map((g) => toPolygon(g.values));\n\n// --- Init --------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -------------------------------------------------------------------\nconst title = \"frequency-polygon-basic · javascript · echarts · anyplot.ai\";\nconst titleFontSize = Math.round(22 * Math.min(1, 67 / title.length));\n\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: title,\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 500 },\n  },\n  legend: {\n    data: groups.map((g) => g.name),\n    top: 76,\n    textStyle: { color: t.ink, fontSize: 16 },\n    itemWidth: 28,\n    itemHeight: 3,\n  },\n  tooltip: {\n    trigger: \"axis\",\n    backgroundColor: t.elevatedBg,\n    borderColor: t.grid,\n    textStyle: { color: t.ink },\n    formatter: (params) =>\n      [\n        `${Math.round(params[0].value[0])} ms`,\n        ...params.map((p) => `${p.marker} ${p.seriesName}: ${p.value[1]}`),\n      ].join(\"<br/>\"),\n  },\n  grid: { left: 100, right: 60, top: 150, bottom: 90 },\n  xAxis: {\n    type: \"value\",\n    name: \"Reaction Time (ms)\",\n    nameLocation: \"middle\",\n    nameGap: 45,\n    nameTextStyle: { color: t.ink, fontSize: 18 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Frequency (count)\",\n    nameLocation: \"middle\",\n    nameGap: 65,\n    nameTextStyle: { color: t.ink, fontSize: 18 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.grid, width: 1 } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: groups.map((g, i) => ({\n    name: g.name,\n    type: \"line\",\n    data: polygons[i].points,\n    symbol: \"circle\",\n    symbolSize: 7,\n    lineStyle: { width: 3, type: lineStyles[i], color: t.palette[i] },\n    itemStyle: { color: t.palette[i] },\n    areaStyle: { color: t.palette[i], opacity: 0.12 },\n    emphasis: { focus: \"series\", lineStyle: { width: 4 } },\n    markPoint: {\n      symbol: \"pin\",\n      symbolSize: 34,\n      itemStyle: { color: t.palette[i], borderColor: t.pageBg, borderWidth: 2 },\n      label: {\n        color: t.ink,\n        fontSize: 13,\n        fontWeight: 600,\n        position: \"top\",\n        distance: 6,\n        formatter: () => `${Math.round(polygons[i].peak[0])} ms`,\n      },\n      data: [{ coord: polygons[i].peak }],\n    },\n  })),\n});\n"}