{"spec_id":"voronoi-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// voronoi-basic: Voronoi Diagram for Spatial Partitioning\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\n//# anyplot-orientation: square\n// anyplot.ai\n// voronoi-basic: Voronoi Diagram for Spatial Partitioning\n// Library: echarts 6.1.0 | JavaScript 22\n// Quality: pending | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: retail store locations across a service region (km) -------------\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\nconst rand = lcg(42);\nconst DOMAIN = 100;\nconst N_STORES = 14;\nconst stores = Array.from({ length: N_STORES }, () => ({\n  x: 6 + rand() * (DOMAIN - 12),\n  y: 6 + rand() * (DOMAIN - 12),\n}));\n\n// --- Voronoi cells: clip the bounding box against each pairwise bisector ---\nfunction clipPolygon(poly, a, b, c) {\n  const eps = 1e-9;\n  const out = [];\n  for (let i = 0; i < poly.length; i++) {\n    const cur = poly[i];\n    const nxt = poly[(i + 1) % poly.length];\n    const fCur = a * cur[0] + b * cur[1] - c;\n    const fNxt = a * nxt[0] + b * nxt[1] - c;\n    const curIn = fCur <= eps;\n    const nxtIn = fNxt <= eps;\n    if (curIn) out.push(cur);\n    if (curIn !== nxtIn) {\n      const tt = fCur / (fCur - fNxt);\n      out.push([cur[0] + tt * (nxt[0] - cur[0]), cur[1] + tt * (nxt[1] - cur[1])]);\n    }\n  }\n  return out;\n}\n\nconst BOUNDS = [\n  [0, 0],\n  [DOMAIN, 0],\n  [DOMAIN, DOMAIN],\n  [0, DOMAIN],\n];\nconst cells = stores.map((p, i) => {\n  let poly = BOUNDS;\n  stores.forEach((q, j) => {\n    if (i === j) return;\n    const a = q.x - p.x;\n    const b = q.y - p.y;\n    const c = (q.x * q.x + q.y * q.y - p.x * p.x - p.y * p.y) / 2;\n    poly = clipPolygon(poly, a, b, c);\n  });\n  return poly;\n});\n\n// --- Init ---------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Title (fontsize scaled to the descriptive-prefix length) --------------\nconst title = \"Retail Store Service Areas · voronoi-basic · javascript · echarts · anyplot.ai\";\nconst titleFontSize = Math.max(15, Math.round(22 * Math.min(1, 67 / title.length)));\n\n// --- Option -----------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: title,\n    left: \"center\",\n    top: 36,\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 500 },\n  },\n  grid: { left: 150, right: 150, top: 180, bottom: 120 },\n  xAxis: {\n    type: \"value\",\n    min: 0,\n    max: DOMAIN,\n    name: \"X coordinate (km)\",\n    nameLocation: \"middle\",\n    nameGap: 46,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    min: 0,\n    max: DOMAIN,\n    name: \"Y coordinate (km)\",\n    nameLocation: \"middle\",\n    nameGap: 60,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  series: [\n    {\n      type: \"custom\",\n      coordinateSystem: \"cartesian2d\",\n      silent: true,\n      z: 1,\n      renderItem: (params, api) => {\n        const cell = cells[params.dataIndex];\n        const points = cell.map((pt) => api.coord(pt));\n        return {\n          type: \"polygon\",\n          shape: { points },\n          style: {\n            fill: t.palette[params.dataIndex % t.palette.length],\n            opacity: 0.62,\n            stroke: t.pageBg,\n            lineWidth: 3,\n          },\n        };\n      },\n      data: stores.map((p) => [p.x, p.y]),\n      encode: { x: 0, y: 1 },\n    },\n    {\n      type: \"scatter\",\n      data: stores.map((p) => [p.x, p.y]),\n      symbolSize: 18,\n      itemStyle: { color: t.ink, borderColor: t.pageBg, borderWidth: 2.5 },\n      z: 3,\n      silent: true,\n    },\n  ],\n});\n"}