{"spec_id":"heatmap-adjacency","library":"echarts","language":"javascript","code":"// anyplot.ai\n// heatmap-adjacency: Network Adjacency Matrix Heatmap\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-05\n\n//# anyplot-orientation: square\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: employee collaboration network, grouped by department -----------\n// Node order is fixed by department membership so the matrix exposes\n// block-diagonal structure (dense intra-department collaboration) without\n// any extra clustering step.\nconst departments = [\n  { name: \"Engineering\", size: 12 },\n  { name: \"Design\", size: 8 },\n  { name: \"Marketing\", size: 10 },\n];\n\nconst nodeNames = [];\nconst nodeDept = [];\ndepartments.forEach((dept, deptIndex) => {\n  for (let i = 1; i <= dept.size; i++) {\n    nodeNames.push(`${dept.name.slice(0, 3).toUpperCase()}-${i}`);\n    nodeDept.push(deptIndex);\n  }\n});\nconst n = nodeNames.length;\n\n// Boundary label: show the department name once, centered under its block.\nconst boundaryLabel = {};\nlet cursor = 0;\ndepartments.forEach((dept) => {\n  boundaryLabel[cursor + Math.floor((dept.size - 1) / 2)] = dept.name;\n  cursor += dept.size;\n});\nconst axisLabelFormatter = (value, index) => boundaryLabel[index] ?? \"\";\n\n// Fixed-seed LCG so the matrix is reproducible without a browser RNG.\nconst makeRng = (seed) => {\n  let state = seed >>> 0;\n  return () => {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n};\nconst rng = makeRng(42);\n\n// Symmetric weight matrix: dense within a department, sparse across.\nconst weights = Array.from({ length: n }, () => new Array(n).fill(0));\nfor (let i = 0; i < n; i++) {\n  for (let j = i + 1; j < n; j++) {\n    const sameDept = nodeDept[i] === nodeDept[j];\n    let w = 0;\n    if (sameDept && rng() > 0.12) {\n      w = 0.35 + rng() * 0.6;\n    } else if (!sameDept && rng() > 0.75) {\n      w = 0.05 + rng() * 0.3;\n    }\n    w = Math.round(w * 100) / 100;\n    weights[i][j] = w;\n    weights[j][i] = w;\n  }\n}\n\n// Heatmap cells: [colIndex, rowIndex, weight]. The diagonal (self) is\n// omitted entirely; absent edges (weight 0) get an explicit near-background\n// fill so they read as \"no connection\" rather than the low end of the scale.\nconst cells = [];\nfor (let row = 0; row < n; row++) {\n  for (let col = 0; col < n; col++) {\n    if (row === col) continue;\n    const w = weights[row][col];\n    cells.push({\n      value: [col, row, w],\n      itemStyle:\n        w === 0\n          ? { color: t.elevatedBg, borderColor: t.pageBg, borderWidth: 1 }\n          : { borderColor: t.pageBg, borderWidth: 1 },\n    });\n  }\n}\n\n// Notable structural exceptions: the strongest cross-department ties that\n// bridge otherwise-separate blocks. Called out with a ring marker so the\n// viewer's eye is drawn past the block-diagonal pattern to the few\n// collaborations that cross department lines.\nconst bridges = [];\nfor (let i = 0; i < n; i++) {\n  for (let j = i + 1; j < n; j++) {\n    if (nodeDept[i] !== nodeDept[j] && weights[i][j] > 0) {\n      bridges.push([i, j, weights[i][j]]);\n    }\n  }\n}\nbridges.sort((a, b) => b[2] - a[2]);\nconst bridgeMarkers = [];\nbridges.slice(0, 3).forEach(([i, j]) => {\n  bridgeMarkers.push([j, i]);\n  bridgeMarkers.push([i, j]);\n});\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: \"Employee Collaboration Network · heatmap-adjacency · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 16,\n    textStyle: { color: t.ink, fontSize: 17 },\n  },\n  tooltip: {\n    formatter: (p) =>\n      p.value[2] > 0\n        ? `${nodeNames[p.value[1]]} ↔ ${nodeNames[p.value[0]]}<br/>strength: ${p.value[2].toFixed(2)}`\n        : `${nodeNames[p.value[1]]} ↔ ${nodeNames[p.value[0]]}<br/>no connection`,\n  },\n  grid: { left: 110, right: 170, top: 110, bottom: 90 },\n  xAxis: {\n    type: \"category\",\n    data: nodeNames,\n    position: \"bottom\",\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n    axisLabel: { color: t.inkSoft, fontSize: 14, interval: 0, formatter: axisLabelFormatter },\n    // Boundary labels keep the 30-node axis readable; hovering near the axis\n    // still reveals the individual node name via the axis pointer.\n    axisPointer: { show: true, type: \"line\", label: { show: true, backgroundColor: t.ink, color: t.pageBg, fontSize: 12 } },\n  },\n  yAxis: {\n    type: \"category\",\n    data: nodeNames,\n    inverse: true,\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n    axisLabel: { color: t.inkSoft, fontSize: 14, interval: 0, formatter: axisLabelFormatter },\n    axisPointer: { show: true, type: \"line\", label: { show: true, backgroundColor: t.ink, color: t.pageBg, fontSize: 12 } },\n  },\n  // Lets viewers zoom into a department block to inspect individual node\n  // pairs on the 30x30 matrix without cluttering the static view with labels.\n  dataZoom: [\n    { type: \"inside\", xAxisIndex: 0, filterMode: \"none\" },\n    { type: \"inside\", yAxisIndex: 0, filterMode: \"none\" },\n  ],\n  visualMap: {\n    type: \"continuous\",\n    min: 0.05,\n    max: 1,\n    calculable: false,\n    orient: \"vertical\",\n    right: 16,\n    top: \"middle\",\n    itemWidth: 22,\n    itemHeight: 260,\n    text: [\"Strong\", \"Weak\"],\n    textStyle: { color: t.inkSoft, fontSize: 14 },\n    inRange: { color: t.seq },\n  },\n  series: [\n    {\n      type: \"heatmap\",\n      data: cells,\n      itemStyle: { color: t.elevatedBg },\n    },\n    {\n      type: \"scatter\",\n      name: \"Notable cross-department bridge\",\n      data: bridgeMarkers,\n      symbol: \"circle\",\n      symbolSize: 14,\n      itemStyle: { color: \"transparent\", borderColor: t.ink, borderWidth: 2 },\n      tooltip: {\n        formatter: (p) =>\n          `${nodeNames[p.value[1]]} ↔ ${nodeNames[p.value[0]]}<br/>notable cross-department bridge (strength ${weights[p.value[1]][p.value[0]].toFixed(2)})`,\n      },\n      z: 10,\n    },\n  ],\n});\n"}