(
allNodes: GraphNode[],
allLinks: GraphLink[],
communityData: CommunityData,
layoutConfig: LayoutConfig,
onTick: (
positions: Map<string, { x: number; y: number }>,
buffer?: Float64Array,
) => void,
initialLayoutMode: LayoutMode = 'spread',
)
| 89 | // ─── Hook ─────────────────────────────────────────────────────────────── |
| 90 | |
| 91 | export function usePixiLayout( |
| 92 | allNodes: GraphNode[], |
| 93 | allLinks: GraphLink[], |
| 94 | communityData: CommunityData, |
| 95 | layoutConfig: LayoutConfig, |
| 96 | onTick: ( |
| 97 | positions: Map<string, { x: number; y: number }>, |
| 98 | buffer?: Float64Array, |
| 99 | ) => void, |
| 100 | initialLayoutMode: LayoutMode = 'spread', |
| 101 | ): UsePixiLayoutResult { |
| 102 | const [layoutReady, setLayoutReady] = useState(false); |
| 103 | const [simRunning, setSimRunning] = useState(true); |
| 104 | const simRunningRef = useRef(true); |
| 105 | const workerRef = useRef<Worker | null>(null); |
| 106 | const unmountedRef = useRef(false); |
| 107 | const requestIdRef = useRef(0); |
| 108 | |
| 109 | // Stable refs for node order (set during init, used to decode Float64Array) |
| 110 | const nodeOrderRef = useRef<string[]>([]); |
| 111 | const breakpointRef = useRef<PixiScaleBreakpoint | null>(null); |
| 112 | const positionsRef = useRef<Map<string, { x: number; y: number }>>(new Map()); |
| 113 | const nodeSizesRef = useRef<Map<string, number>>(new Map()); |
| 114 | const onTickRef = useRef(onTick); |
| 115 | onTickRef.current = onTick; |
| 116 | |
| 117 | const flatMode = layoutConfig.flatMode ?? false; |
| 118 | const structuralTypes = new Set(flatMode ? [] : layoutConfig.structuralTypes); |
| 119 | |
| 120 | // Cleanup on unmount |
| 121 | useEffect(() => { |
| 122 | unmountedRef.current = false; |
| 123 | return () => { |
| 124 | unmountedRef.current = true; |
| 125 | workerRef.current?.terminate(); |
| 126 | workerRef.current = null; |
| 127 | }; |
| 128 | }, []); |
| 129 | |
| 130 | // Compute node sizes |
| 131 | useEffect(() => { |
| 132 | const degreeMap = new Map<string, number>(); |
| 133 | for (const link of allLinks) { |
| 134 | const s = endpointId(link.source); |
| 135 | const t = endpointId(link.target); |
| 136 | degreeMap.set(s, (degreeMap.get(s) || 0) + 1); |
| 137 | degreeMap.set(t, (degreeMap.get(t) || 0) + 1); |
| 138 | } |
| 139 | const sizes = new Map<string, number>(); |
| 140 | for (const node of allNodes) { |
| 141 | sizes.set( |
| 142 | node.id, |
| 143 | nodeSize(degreeMap.get(node.id) ?? 0, node.type, structuralTypes), |
| 144 | ); |
| 145 | } |
| 146 | nodeSizesRef.current = sizes; |
| 147 | // eslint-disable-next-line react-hooks/exhaustive-deps |
| 148 | }, [allNodes, allLinks]); |
no test coverage detected