Counter-scale all sprites, labels, and edges for current viewport scale. * * `cull` controls whether the visible label set is re-evaluated. * - `true` (default) — call from user-zoom paths (wheel, ticker * zoom-change check, imperative setX, initial setData). Recomputes * which la
(cull = true)
| 856 | * put until a user actually zooms. |
| 857 | */ |
| 858 | private applyCounterScale(cull = true): void { |
| 859 | const invScale = this.zoomInvScale(); |
| 860 | const wantLabel: PixiNode[] = []; |
| 861 | |
| 862 | for (const node of this.nodes.values()) { |
| 863 | if (!node.visible) continue; |
| 864 | // In 3D mode, skip sprite scale — the ticker's update3D() handles it. |
| 865 | if (!this.mode3d) { |
| 866 | const s = |
| 867 | this.hasHighlight && !this.highlightNodes.has(node.id) |
| 868 | ? node.size * NODE_SIZE_DIMMED_SCALE |
| 869 | : node.size; |
| 870 | node.sprite.scale.set((s / CIRCLE_RADIUS) * invScale); |
| 871 | } |
| 872 | |
| 873 | if (!this.showAllLabels) { |
| 874 | if (node.label) node.label.visible = false; |
| 875 | } else if (this.hasHighlight) { |
| 876 | if (this.highlightNodes.has(node.id)) { |
| 877 | wantLabel.push(node); |
| 878 | } else if (node.label) { |
| 879 | node.label.visible = false; |
| 880 | } |
| 881 | } else { |
| 882 | wantLabel.push(node); |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | const lblInv = this.labelInvScale(); |
| 887 | if (cull) { |
| 888 | this.applyLabelCulling(wantLabel, lblInv); |
| 889 | } |
| 890 | |
| 891 | // Update scale + position for visible labels (skip in 3D — ticker handles it) |
| 892 | if (!this.mode3d) { |
| 893 | const lm = this.labelScaleMultiplier; |
| 894 | for (const node of wantLabel) { |
| 895 | if (!node.label?.visible) continue; |
| 896 | node.label.scale.set(lblInv * lm); |
| 897 | const gap = this.labelGap(node); |
| 898 | node.label.position.set( |
| 899 | node.sprite.position.x + gap, |
| 900 | node.sprite.position.y, |
| 901 | ); |
| 902 | } |
| 903 | } |
| 904 | |
| 905 | // Only redraw edges if scale actually changed (edge widths depend on zoom). |
| 906 | // Skip if this is just an exponent change at the same zoom level — |
| 907 | // edge geometry hasn't changed, only sprite sizes. |
| 908 | // |
| 909 | // Skip while edges are hidden for interaction (Fix #24). The wheel |
| 910 | // handler hides the edge layer for instant zoom response, but the |
| 911 | // ticker still drives `applyCounterScale` per frame the scale |
| 912 | // changes — and the original code redrew 15 k+ edges into an |
| 913 | // invisible Graphics object on each of those frames, blocking the |
| 914 | // main thread enough that the zoom itself stuttered. The 3D path |
| 915 | // already had this guard; the 2D path was missing it. The |
no test coverage detected