({
nodes,
edges,
highlightedIds,
opacity = 1.0,
brightness = 1.0,
targetNodes,
}: EdgeLinesProps)
| 52 | const DEFAULT_EDGE_COLOR = "#1C8585"; |
| 53 | |
| 54 | export function EdgeLines({ |
| 55 | nodes, |
| 56 | edges, |
| 57 | highlightedIds, |
| 58 | opacity = 1.0, |
| 59 | brightness = 1.0, |
| 60 | targetNodes, |
| 61 | }: EdgeLinesProps) { |
| 62 | const geometry = useMemo(() => { |
| 63 | /* Shrink per-edge glow as the edge count grows so the additively-blended |
| 64 | * center doesn't saturate to white; the user multiplier rides on top. */ |
| 65 | const densityScale = edgeIntensityScale(edges.length) * brightness; |
| 66 | const srcMap = new Map<number, number>(); |
| 67 | for (let i = 0; i < nodes.length; i++) { |
| 68 | srcMap.set(nodes[i].id, i); |
| 69 | } |
| 70 | const tgtArr = targetNodes ?? nodes; |
| 71 | const tgtMap = targetNodes ? new Map<number, number>() : srcMap; |
| 72 | if (targetNodes) { |
| 73 | for (let i = 0; i < targetNodes.length; i++) { |
| 74 | tgtMap.set(targetNodes[i].id, i); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | const hasHighlight = highlightedIds && highlightedIds.size > 0; |
| 79 | const positions = new Float32Array(edges.length * 6); |
| 80 | const colors = new Float32Array(edges.length * 6); |
| 81 | let validCount = 0; |
| 82 | |
| 83 | for (const edge of edges) { |
| 84 | const si = srcMap.get(edge.source); |
| 85 | const ti = tgtMap.get(edge.target); |
| 86 | if (si === undefined || ti === undefined) continue; |
| 87 | |
| 88 | const s = nodes[si]; |
| 89 | const t = tgtArr[ti]; |
| 90 | |
| 91 | const sHL = !hasHighlight || highlightedIds.has(s.id); |
| 92 | const tHL = !hasHighlight || highlightedIds.has(t.id); |
| 93 | if (hasHighlight && !sHL && !tHL) continue; |
| 94 | |
| 95 | const sameCluster = |
| 96 | getClusterKey(s.file_path) === getClusterKey(t.file_path); |
| 97 | |
| 98 | /* Intensity based on cluster membership and highlight. |
| 99 | * With additive blending + dark background, these glow nicely. */ |
| 100 | let intensity = sameCluster ? 0.25 : 0.06; |
| 101 | if (hasHighlight) { |
| 102 | /* A selection stays at full strength (never density-scaled) so it |
| 103 | * pops against the dimmed rest; only the un-selected bulk is scaled. */ |
| 104 | intensity = sHL && tHL ? 0.5 : 0.04 * densityScale; |
| 105 | } else { |
| 106 | intensity *= densityScale; |
| 107 | } |
| 108 | |
| 109 | const off = validCount * 6; |
| 110 | positions[off] = s.x; |
| 111 | positions[off + 1] = s.y; |
nothing calls this directly
no test coverage detected