(
pos: Map<string, { x: number; y: number }>,
sizes: Map<string, number>,
assignments: Record<string, number>,
margin: number,
iterations: number,
)
| 172 | // Pushes overlapping nodes apart within each community. Runs after spacing. |
| 173 | |
| 174 | export function applyNoverlap( |
| 175 | pos: Map<string, { x: number; y: number }>, |
| 176 | sizes: Map<string, number>, |
| 177 | assignments: Record<string, number>, |
| 178 | margin: number, |
| 179 | iterations: number, |
| 180 | ): void { |
| 181 | // Group by community |
| 182 | const groups = new Map<number, string[]>(); |
| 183 | for (const [id] of pos) { |
| 184 | const cid = assignments[id]; |
| 185 | if (cid === undefined) continue; |
| 186 | let list = groups.get(cid); |
| 187 | if (!list) { |
| 188 | list = []; |
| 189 | groups.set(cid, list); |
| 190 | } |
| 191 | list.push(id); |
| 192 | } |
| 193 | |
| 194 | // Compute a scale factor from the position bounding box |
| 195 | let minX = Infinity, |
| 196 | maxX = -Infinity, |
| 197 | minY = Infinity, |
| 198 | maxY = -Infinity; |
| 199 | for (const [, p] of pos) { |
| 200 | if (p.x < minX) minX = p.x; |
| 201 | if (p.x > maxX) maxX = p.x; |
| 202 | if (p.y < minY) minY = p.y; |
| 203 | if (p.y > maxY) maxY = p.y; |
| 204 | } |
| 205 | const scale = Math.max(maxX - minX, maxY - minY, 1) / 4000; |
| 206 | const scaledMargin = margin * scale; |
| 207 | |
| 208 | const MAX_INLINE = 500; |
| 209 | |
| 210 | for (const [, ids] of groups) { |
| 211 | if (ids.length < 3 || ids.length > MAX_INLINE) continue; |
| 212 | |
| 213 | // Build local position/size array |
| 214 | const nodes = ids.map((id) => ({ |
| 215 | id, |
| 216 | x: pos.get(id)!.x, |
| 217 | y: pos.get(id)!.y, |
| 218 | size: (sizes.get(id) ?? 3) * scale, |
| 219 | })); |
| 220 | |
| 221 | for (let iter = 0; iter < iterations; iter++) { |
| 222 | for (let i = 0; i < nodes.length; i++) { |
| 223 | const a = nodes[i]; |
| 224 | for (let j = i + 1; j < nodes.length; j++) { |
| 225 | const b = nodes[j]; |
| 226 | const dx = b.x - a.x; |
| 227 | const dy = b.y - a.y; |
| 228 | const dist = Math.sqrt(dx * dx + dy * dy); |
| 229 | const minDist = a.size + b.size + scaledMargin; |
| 230 | if (dist < minDist && dist > 0.001) { |
| 231 | const push = (minDist - dist) * 0.5; |
no test coverage detected