(
pos: Map<string, { x: number; y: number }>,
assignments: Record<string, number>,
radiusScale: number,
gap: number,
maxIterations: number,
pushFactor: number,
)
| 63 | // on the main thread before the first render frame. |
| 64 | |
| 65 | export function applySpacing( |
| 66 | pos: Map<string, { x: number; y: number }>, |
| 67 | assignments: Record<string, number>, |
| 68 | radiusScale: number, |
| 69 | gap: number, |
| 70 | maxIterations: number, |
| 71 | pushFactor: number, |
| 72 | ): void { |
| 73 | // Group by community |
| 74 | const groups = new Map<number, string[]>(); |
| 75 | for (const [id] of pos) { |
| 76 | const cid = assignments[id]; |
| 77 | if (cid === undefined) continue; |
| 78 | let list = groups.get(cid); |
| 79 | if (!list) { |
| 80 | list = []; |
| 81 | groups.set(cid, list); |
| 82 | } |
| 83 | list.push(id); |
| 84 | } |
| 85 | |
| 86 | if (groups.size < 2) return; |
| 87 | |
| 88 | // Build community centroids and radii |
| 89 | const comms: { |
| 90 | nodeIds: string[]; |
| 91 | cx: number; |
| 92 | cy: number; |
| 93 | radius: number; |
| 94 | }[] = []; |
| 95 | for (const [, ids] of groups) { |
| 96 | let cx = 0, |
| 97 | cy = 0; |
| 98 | for (const id of ids) { |
| 99 | const p = pos.get(id)!; |
| 100 | cx += p.x; |
| 101 | cy += p.y; |
| 102 | } |
| 103 | cx /= ids.length; |
| 104 | cy /= ids.length; |
| 105 | comms.push({ |
| 106 | nodeIds: ids, |
| 107 | cx, |
| 108 | cy, |
| 109 | radius: Math.sqrt(ids.length) * radiusScale, |
| 110 | }); |
| 111 | } |
| 112 | |
| 113 | for (let iter = 0; iter < maxIterations; iter++) { |
| 114 | const pushX = new Float64Array(comms.length); |
| 115 | const pushY = new Float64Array(comms.length); |
| 116 | let maxOverlap = 0; |
| 117 | |
| 118 | for (let i = 0; i < comms.length; i++) { |
| 119 | for (let j = i + 1; j < comms.length; j++) { |
| 120 | const a = comms[i]; |
| 121 | const b = comms[j]; |
| 122 | const dx = b.cx - a.cx; |
no test coverage detected