{"spec_id":"arc-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// arc-basic: Basic Arc Diagram\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-08-24\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Stages of a data pipeline, ordered by typical execution sequence\nconst stages = [\n  \"Ingest\", \"Validate\", \"Dedupe\", \"Enrich\", \"Normalize\", \"Transform\",\n  \"Aggregate\", \"Score\", \"Rank\", \"Filter\", \"Cache\", \"Publish\",\n];\n\n// [sourceIndex, targetIndex, weight] — weight ~ relative call frequency (1-9)\nconst dependencies = [\n  [0, 1, 9], [1, 2, 8], [2, 3, 7], [3, 4, 6], [4, 5, 6], [5, 6, 5],\n  [6, 7, 5], [7, 8, 4], [8, 9, 4], [9, 11, 7], [0, 2, 3], [1, 4, 2],\n  [3, 6, 3], [5, 10, 4], [6, 10, 3], [10, 11, 6], [2, 9, 2], [0, 11, 1],\n  [4, 8, 2], [7, 10, 3],\n];\n\nconst degree = stages.map(\n  (_, i) => dependencies.filter(([s, tgt]) => s === i || tgt === i).length,\n);\nconst maxDistance = Math.max(...dependencies.map(([s, tgt]) => tgt - s));\n// Lift nodes above y=0 so they clear the rotated axis-label row beneath them\nconst baseline = maxDistance * 0.12;\n\n// --- Init --------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option --------------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"arc-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n  },\n  grid: { left: 90, right: 90, top: 140, bottom: 140 },\n  xAxis: {\n    type: \"category\",\n    data: stages,\n    axisLabel: { color: t.inkSoft, fontSize: 15, rotate: 30, margin: 16 },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  // The custom series draws a *quadratic* bezier (single control point), whose\n  // rendered peak sits at the midpoint between baseline and control height —\n  // peak = baseline + (targetIdx - sourceIdx) / 2, not the full control height.\n  // Size the axis off that actual peak so the tallest arc reaches near the top.\n  yAxis: {\n    type: \"value\",\n    min: 0,\n    max: baseline + (maxDistance / 2) * 1.15,\n    show: false,\n  },\n  series: [\n    {\n      type: \"custom\",\n      coordinateSystem: \"cartesian2d\",\n      silent: true,\n      z: 2,\n      data: dependencies,\n      renderItem: (params, api) => {\n        const sourceIdx = api.value(0);\n        const targetIdx = api.value(1);\n        const weight = api.value(2);\n        const start = api.coord([sourceIdx, baseline]);\n        const end = api.coord([targetIdx, baseline]);\n        const control = api.coord([\n          (sourceIdx + targetIdx) / 2,\n          baseline + (targetIdx - sourceIdx),\n        ]);\n        return {\n          type: \"bezierCurve\",\n          shape: {\n            x1: start[0], y1: start[1],\n            x2: end[0], y2: end[1],\n            cpx1: control[0], cpy1: control[1],\n          },\n          style: {\n            stroke: t.palette[0],\n            fill: \"none\",\n            lineWidth: 1.5 + (weight / 9) * 4,\n            opacity: 0.32 + (weight / 9) * 0.4,\n          },\n        };\n      },\n    },\n    {\n      type: \"scatter\",\n      silent: true,\n      z: 3,\n      data: stages.map((_, i) => ({\n        value: [i, baseline],\n        symbolSize: 15 + degree[i] * 3,\n      })),\n      itemStyle: { color: t.ink, borderColor: t.pageBg, borderWidth: 2 },\n    },\n  ],\n});\n"}