()
| 1225 | } |
| 1226 | |
| 1227 | private applyVisuals(): void { |
| 1228 | if (!this.app) return; |
| 1229 | const invScale = this.zoomInvScale(); |
| 1230 | |
| 1231 | // Pass 1: update sprites + determine which labels want to show |
| 1232 | // In 3D mode, skip sprite scale/alpha — the ticker's update3D() handles those. |
| 1233 | const wantLabel: PixiNode[] = []; |
| 1234 | for (const [id, node] of this.nodes) { |
| 1235 | if (!node.visible) continue; |
| 1236 | |
| 1237 | if (!this.mode3d) { |
| 1238 | if (this.hasHighlight) { |
| 1239 | const isHighlighted = this.highlightNodes.has(id); |
| 1240 | node.sprite.alpha = isHighlighted ? 1.0 : NODE_OPACITY_DIMMED; |
| 1241 | const s = isHighlighted |
| 1242 | ? node.size |
| 1243 | : node.size * NODE_SIZE_DIMMED_SCALE; |
| 1244 | node.sprite.scale.set((s / CIRCLE_RADIUS) * invScale); |
| 1245 | } else { |
| 1246 | node.sprite.alpha = 0.9; |
| 1247 | node.sprite.scale.set((node.size / CIRCLE_RADIUS) * invScale); |
| 1248 | } |
| 1249 | } |
| 1250 | |
| 1251 | if (!this.showAllLabels) { |
| 1252 | if (node.label) node.label.visible = false; |
| 1253 | } else if (this.hasHighlight) { |
| 1254 | if (this.highlightNodes.has(id)) { |
| 1255 | wantLabel.push(node); |
| 1256 | } else if (node.label) { |
| 1257 | node.label.visible = false; |
| 1258 | } |
| 1259 | } else { |
| 1260 | wantLabel.push(node); |
| 1261 | } |
| 1262 | } |
| 1263 | |
| 1264 | // Pass 2: cull overlapping labels (largest nodes first) |
| 1265 | this.applyLabelCulling(wantLabel, this.labelInvScale()); |
| 1266 | |
| 1267 | // In 3D mode, the ticker redraws edges; skip here to avoid 2D position flash |
| 1268 | if (!this.mode3d) { |
| 1269 | this.redrawAllEdges(); |
| 1270 | } |
| 1271 | } |
| 1272 | |
| 1273 | /** |
| 1274 | * Show labels for the given nodes, culling any that overlap a previously |
no test coverage detected