{"spec_id":"venn-basic","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// venn-basic: Venn Diagram\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 88/100 | Created: 2026-09-09\n//# anyplot-orientation: square\n// anyplot.ai\n// venn-basic: Venn Diagram\n// Library: Highcharts 12.6.0 | Node 22\n// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license)\n// Quality: pending | Created: 2026-09-09\n\nconst t = window.ANYPLOT_TOKENS;\nconst mount = window.ANYPLOT_SIZE;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Outdoor-activity survey: which activities 225 respondents said they enjoy.\n// Circle area scales with sqrt(set size), so area stays proportional to size.\nconst setLabels = [\"Hiking\", \"Cycling\", \"Swimming\"];\nconst onlyHiking = 70;\nconst onlyCycling = 50;\nconst onlySwimming = 45;\nconst hikingCycling = 25; // hiking & cycling, not swimming\nconst hikingSwimming = 15; // hiking & swimming, not cycling\nconst cyclingSwimming = 10; // cycling & swimming, not hiking\nconst allThree = 10;\n\nconst sizeHiking = onlyHiking + hikingCycling + hikingSwimming + allThree; // 120\nconst sizeCycling = onlyCycling + hikingCycling + cyclingSwimming + allThree; // 95\nconst sizeSwimming = onlySwimming + hikingSwimming + cyclingSwimming + allThree; // 80\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n    events: {\n      load() {\n        drawVennDiagram(this);\n      },\n    },\n  },\n  credits: { enabled: false },\n  title: {\n    text: \"venn-basic · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  xAxis: { visible: false },\n  yAxis: { visible: false },\n  legend: { enabled: false },\n  series: [],\n  plotOptions: { series: { animation: false } },\n});\n\nfunction drawVennDiagram(chart) {\n  const renderer = chart.renderer;\n  const cx = mount.width / 2;\n  const cy = mount.height * 0.52;\n\n  // Radius ~ sqrt(set size): circle area stays proportional to set size.\n  const scale = 27;\n  const rHiking = Math.sqrt(sizeHiking) * scale;\n  const rCycling = Math.sqrt(sizeCycling) * scale;\n  const rSwimming = Math.sqrt(sizeSwimming) * scale;\n  const avgR = (rHiking + rCycling + rSwimming) / 3;\n\n  // Classic symmetric 3-circle layout: centers at the corners of an\n  // upward-pointing triangle so every pairwise + triple region is visible.\n  const spread = avgR * 0.62;\n  const centerHiking = { x: cx - spread, y: cy - spread * 0.58 };\n  const centerCycling = { x: cx + spread, y: cy - spread * 0.58 };\n  const centerSwimming = { x: cx, y: cy + spread * 0.78 };\n\n  const circles = [\n    { center: centerHiking, r: rHiking, color: t.palette[0] },\n    { center: centerCycling, r: rCycling, color: t.palette[1] },\n    { center: centerSwimming, r: rSwimming, color: t.palette[2] },\n  ];\n\n  circles.forEach(({ center, r, color }) => {\n    renderer\n      .circle(center.x, center.y, r)\n      .attr({\n        fill: Highcharts.color(color).setOpacity(0.55).get(\"rgba\"),\n        stroke: color,\n        \"stroke-width\": 2,\n      })\n      .add();\n  });\n\n  // Set-name labels, positioned just outside each circle. Rendered in the\n  // theme ink color (not the raw palette hue) so all three stay above WCAG\n  // large-text contrast in light mode; the outer circle stroke already\n  // carries the color-coded set identity.\n  const nameLabels = [\n    { text: setLabels[0], x: centerHiking.x, y: centerHiking.y - rHiking - 16 },\n    {\n      text: setLabels[1],\n      x: centerCycling.x,\n      y: centerCycling.y - rCycling - 16,\n    },\n    {\n      text: setLabels[2],\n      x: centerSwimming.x,\n      y: centerSwimming.y + rSwimming + 34,\n    },\n  ];\n  nameLabels.forEach(({ text, x, y }) => {\n    renderer\n      .text(text, x, y)\n      .attr({ align: \"center\" })\n      .css({\n        color: t.ink,\n        fontSize: \"18px\",\n        fontWeight: \"600\",\n        fontFamily: \"inherit\",\n      })\n      .add();\n  });\n\n  // Region count labels — one per exclusive slice, plus the triple overlap.\n  const regionLabels = [\n    {\n      value: onlyHiking,\n      x: centerHiking.x - rHiking * 0.45,\n      y: centerHiking.y - rHiking * 0.3,\n    },\n    {\n      value: onlyCycling,\n      x: centerCycling.x + rCycling * 0.45,\n      y: centerCycling.y - rCycling * 0.3,\n    },\n    {\n      value: onlySwimming,\n      x: centerSwimming.x,\n      y: centerSwimming.y + rSwimming * 0.5,\n    },\n    {\n      value: hikingCycling,\n      x: (centerHiking.x + centerCycling.x) / 2,\n      y: (centerHiking.y + centerCycling.y) / 2 - avgR * 0.25,\n    },\n    {\n      value: hikingSwimming,\n      x: (centerHiking.x + centerSwimming.x) / 2 - avgR * 0.14,\n      y: (centerHiking.y + centerSwimming.y) / 2 + avgR * 0.1,\n    },\n    {\n      value: cyclingSwimming,\n      x: (centerCycling.x + centerSwimming.x) / 2 + avgR * 0.14,\n      y: (centerCycling.y + centerSwimming.y) / 2 + avgR * 0.1,\n    },\n    { value: allThree, x: cx, y: cy + avgR * 0.05 },\n  ];\n  regionLabels.forEach(({ value, x, y }) => {\n    renderer\n      .text(String(value), x, y)\n      .attr({ align: \"center\" })\n      .css({\n        color: t.ink,\n        fontSize: \"20px\",\n        fontWeight: \"600\",\n        fontFamily: \"inherit\",\n      })\n      .add();\n  });\n}\n"}