@opendata-ai/openchart-vanilla 8.5.1 → 8.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WOLX5EZT.js → chunk-273EHRBC.js} +22 -18
- package/dist/chunk-273EHRBC.js.map +1 -0
- package/dist/{chunk-UAK6HWOC.js → chunk-CPULCRVF.js} +2 -2
- package/dist/{chunk-XGII4ZRT.js → chunk-MDK6AILK.js} +4 -1
- package/dist/chunk-MDK6AILK.js.map +1 -0
- package/dist/{chunk-5KV3ZBBL.js → chunk-W3WPGHYV.js} +36 -3
- package/dist/chunk-W3WPGHYV.js.map +1 -0
- package/dist/graph-3d/index.d.ts +5 -4
- package/dist/graph-3d/index.js +214 -27
- package/dist/graph-3d/index.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/static.js +2 -2
- package/dist/story/index.js +2 -2
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-5KV3ZBBL.js.map +0 -1
- package/dist/chunk-WOLX5EZT.js.map +0 -1
- package/dist/chunk-XGII4ZRT.js.map +0 -1
- /package/dist/{chunk-UAK6HWOC.js.map → chunk-CPULCRVF.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/graph-3d/mount.ts","../../src/graph-3d/emphasis.ts","../../src/graph-3d/entrance.ts","../../src/graph-3d/fit.ts","../../src/graph-3d/forces.ts","../../src/graph-3d/labels.ts","../../src/graph-3d/links.ts","../../src/graph-3d/nodes.ts","../../src/graph-3d/index.ts"],"sourcesContent":["/**\n * The 3D graph renderer: a `GraphInstance` backed by `3d-force-graph`.\n *\n * Registered against `dimensions: 3` by `./index`. `createGraph()` builds the\n * shared shell (wrapper, chrome, legend slot, tooltip manager, resize wiring)\n * and hands it here; this module owns the WebGL scene, the d3-force-3d\n * simulation the library drives, the per-node and per-link three.js objects,\n * emphasis state, the label budget, tooltip anchoring, and disposal.\n *\n * Three library facts shape almost everything below and are easy to get wrong:\n *\n * 1. `nodeOpacity` and `linkOpacity` are GLOBAL numbers, not accessors. Every\n * per-object opacity (dim, highlight, entrance, search) therefore needs a\n * custom object with its own material. See `nodes.ts` / `links.ts`.\n * 2. `refresh()` rebuilds every node and link object. It must never run on\n * hover; the hover path only writes `material.opacity`.\n * 3. `rendererConfig` and `controlType` are CONSTRUCTOR options, not chained\n * setters.\n *\n * Differences from 2D that are deliberate and documented in RFC 27: no\n * keyboard navigation, no node drag, no cursor repulsion or springy drag, no\n * wall-clock warmup budget, and a structural `update()` reheats globally\n * (`graphData()` restarts the layout at alpha 1) instead of applying 2D's local\n * impulse.\n */\n\nimport type { GraphSpec, ThemeConfig, TooltipContent } from '@opendata-ai/openchart-core';\nimport type { CompiledGraphNode } from '@opendata-ai/openchart-engine';\nimport { buildEdgeTooltip } from '@opendata-ai/openchart-engine';\nimport ForceGraph3D from '3d-force-graph';\nimport type { Object3D } from 'three';\nimport { Raycaster, Vector2 } from 'three';\nimport type { CameraFlightOptions } from '../graph/camera';\nimport { ENTRANCE_STAGGER_MAX_NODES, popAlpha, popScale } from '../graph/entrance';\nimport {\n composeStandingFocus,\n type FocusSnapshot,\n layerHoverFocus,\n} from '../graph/focus-transition';\nimport {\n categoryHighlightSet as categoryHighlightIds,\n resolveHighlightTarget as resolveHighlightTargetIds,\n} from '../graph/highlight';\nimport { createGraphLegend, type GraphLegendController } from '../graph/legend';\nimport { createTween, linear, prefersReducedMotion, resolveEase } from '../graph/motion';\nimport type { GraphRendererContext } from '../graph/renderer-registry';\nimport { AnimationScheduler, type GraphAnimation } from '../graph/scheduler';\nimport { GraphSearchManager } from '../graph/search';\nimport { seedNodePositions } from '../graph/seed';\nimport type { GraphCamera, GraphFlyTarget, PositionedEdge, PositionedNode } from '../graph/types';\nimport { diffGraphUpdate } from '../graph/update-diff';\nimport type { SimNode } from '../graph/worker-protocol';\nimport type {\n GraphHighlightTarget,\n GraphInstance,\n GraphLegendData,\n GraphTooltipFormatter,\n} from '../graph-mount';\nimport { resolvedSurface } from '../theme-tokens';\nimport { resolveEmphasis } from './emphasis';\nimport { ENTRANCE_CAMERA_PULLBACK, elementProgress, planEntrance } from './entrance';\nimport { computeFit, normalize } from './fit';\nimport { applySimulationConfig } from './forces';\nimport { LABEL_BUDGET_3D, type LabelBox, labelBox, overlaps, resolveVisibleLabels } from './labels';\nimport {\n applyLinkVisuals,\n createLinkObject,\n disposeLinkObject,\n LINK_WIDTH_MAX_EDGES,\n type LinkObject3D,\n linkShapeMatches,\n updateLinkPosition,\n widthAsAlpha,\n} from './links';\nimport {\n applyNodeVisuals,\n createNodeObject,\n disposeNodeObject,\n ensureLabelSprite,\n LABEL_TEXT_HEIGHT,\n type NodeObject3D,\n} from './nodes';\nimport type { Graph3D, Link3D, Node3D } from './types';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * The camera distance that `getCamera().k === 1` refers to. 3D has no zoom\n * scalar, so `k` is expressed as `FIT_DISTANCE / cameraDistance`: a `k` of 2 is\n * twice as close as the reference framing, matching the 2D sign convention.\n */\nexport const FIT_DISTANCE = 1000;\n\n/** Default breathing room around a fit, in pixels of viewport height. */\nconst FIT_PADDING_PX = 40;\n\n/** Target on-screen cap height for a node label, in CSS pixels. */\nconst LABEL_SCREEN_PX = 11;\n\n/** Bounds on the perspective correction, so a label never vanishes or fills the view. */\nconst LABEL_SCALE_MIN = 0.4;\nconst LABEL_SCALE_MAX = 8;\n\n/** The library's own clamp on devicePixelRatio; kept in step on resize. */\nconst MAX_PIXEL_RATIO = 2;\n\n/** Fallback when the camera has not reported its own field of view yet. */\nconst DEFAULT_FOV_DEG = 50;\n\n/** Throttle for the settle-phase auto re-fit (ms). */\nconst AUTO_FIT_INTERVAL_MS = 250;\n/** Camera standoff from a node for `zoomToNode`, in scene units. */\nexport const NODE_FOCUS_DISTANCE = 120;\n/** Fallback flight duration when `animation.camera.duration` is `'auto'`. */\nconst AUTO_FLIGHT_MS = 800;\n/**\n * The scale a node holds before its pop starts. Not 0: a zero scale collapses\n * the sphere's bounding box, and three skips frustum culling maths on a\n * degenerate box in ways that vary by build.\n */\nconst ENTRANCE_MIN_SCALE = 0.01;\n/** Entrance alpha below which a node's label stays hidden. */\nconst LABEL_ENTRANCE_MIN = 0.4;\n/** Label re-rank throttle while orbiting. */\nconst LABEL_RERANK_MS = 100;\n/** `k` clamp, mirroring 2D's `clampK`. */\nconst K_MIN = 0.05;\nconst K_MAX = 40;\n/** Second seed stream for the z axis (golden-ratio salt, as in `seed.ts`). */\nconst Z_SEED_SALT = 0x9e3779b9;\n\n/**\n * A camera pose: the shared `GraphCamera` with the pose fields narrowed to\n * required, since the 3D renderer always reports them.\n */\nexport interface Camera3D extends GraphCamera {\n position: { x: number; y: number; z: number };\n target: { x: number; y: number; z: number };\n}\n\n/** `flyTo` accepts either the 2D shape or a full 3D pose from `getCamera()`. */\nexport type FlyTarget3D = GraphFlyTarget;\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nexport function createGraph3DRenderer(ctx: GraphRendererContext): GraphInstance {\n const { shell, options } = ctx;\n let currentSpec = ctx.spec;\n let compilation = ctx.compilation;\n let destroyed = false;\n\n // -- Scene ---------------------------------------------------------------\n\n const surfaceEl = document.createElement('div');\n surfaceEl.className = 'oc-graph-3d';\n surfaceEl.setAttribute('role', 'img');\n if (compilation.a11y?.altText) surfaceEl.setAttribute('aria-label', compilation.a11y.altText);\n // Absolute inside the (relative) wrapper, not just width/height 100%.\n // three's `setSize` writes the drawing-buffer size onto the canvas as an\n // inline pixel width, which would otherwise become the min-content width of\n // every flex ancestor: the container could then never shrink below whatever\n // it happened to measure on the first frame. Taking the surface out of flow\n // breaks that feedback loop, and it still fills the wrapper.\n surfaceEl.style.position = 'absolute';\n surfaceEl.style.inset = '0';\n shell.mountSurface(surfaceEl);\n\n // The library's declaration is not generic at the constructor, so the datum\n // types are asserted here rather than parameterized.\n const graph = new ForceGraph3D(surfaceEl, {\n controlType: 'trackball',\n // `alpha` is what lets a transparent theme show the host page through the\n // canvas; both are constructor-only in 3d-force-graph.\n rendererConfig: { alpha: true, antialias: true },\n }) as unknown as Graph3D;\n\n // -- Scene object registries ---------------------------------------------\n\n const nodeObjects = new Map<string, NodeObject3D>();\n /** Keyed by index into `compilation.edges` so parallel edges stay distinct. */\n const linkObjects = new Map<number, LinkObject3D>();\n let nodeData: Node3D[] = [];\n let nodeById = new Map<string, Node3D>();\n let linkData: Link3D[] = [];\n\n // -- Derived data --------------------------------------------------------\n\n let adjacency = new Map<string, Set<string>>();\n let nodeDataMap = new Map<string, Record<string, unknown>>();\n let nodeCategory = new Map<string, string>();\n let seedIds = new Set<string>();\n let edgeBaseAlpha: Map<number, number> | undefined;\n let useLinkWidth = false;\n\n // -- Interaction state ---------------------------------------------------\n\n const searchManager = new GraphSearchManager();\n let legendController: GraphLegendController | null = null;\n let hoveredNodeId: string | null = null;\n let hoveredLinkIndex: number | null = null;\n let selectedNodeIds = new Set<string>();\n let activeCategories = new Set<string>();\n let transientHighlight: Set<string> | null = null;\n let transientTarget: GraphHighlightTarget | null = null;\n let highlightSet: Set<string> | null = null;\n let highlightDimOpacity: number | null = null;\n\n // -- Opacity state -------------------------------------------------------\n // `display*` is what the emphasis model says each element should show;\n // `entrance*` is a per-element multiplier on top of it, so the entrance and\n // the emphasis crossfade compose instead of fighting over `material.opacity`.\n // While `entranceActive` is false the multipliers are skipped entirely, which\n // keeps the steady-state paint a plain map read.\n\n const displayNodeAlpha = new Map<string, number>();\n const displayEdgeAlpha = new Map<number, number>();\n let entranceActive = false;\n const entranceNodeAlpha = new Map<string, number>();\n const entranceNodeScale = new Map<string, number>();\n const entranceEdgeAlpha = new Map<number, number>();\n /** Standoff multiplier the fit is scaled by while the camera flies in. */\n let entranceCameraPull = 1;\n let emphasisTween: GraphAnimation | null = null;\n\n // -- Frame loop ----------------------------------------------------------\n // The library runs its own render loop; this scheduler only drives our tweens.\n\n let pumpId: number | null = null;\n const scheduler = new AnimationScheduler(() => pump());\n\n function pump(): void {\n if (pumpId !== null || destroyed) return;\n pumpId = requestAnimationFrame((now) => {\n pumpId = null;\n if (destroyed) return;\n scheduler.tick(now);\n if (scheduler.active) pump();\n });\n }\n\n // -- Camera / label bookkeeping ------------------------------------------\n\n // The layout starts collapsed near the origin, so a single fit on the first\n // tick frames a blob and leaves the camera inside the cloud once it expands.\n // Re-fit on a throttle while the simulation settles, and stop the moment the\n // viewer takes the camera themselves.\n let autoFit = true;\n let lastAutoFit = 0;\n let cameraChangeQueued = false;\n /** Ids whose label won a slot in the last rank. `paint()` reads it so the\n * entrance can hide a label whose node has not popped in yet. */\n const labelShown = new Set<string>();\n let lastLabelRank = 0;\n let labelRankQueued = false;\n let controlsListener: (() => void) | null = null;\n\n // -- Tooltip state -------------------------------------------------------\n\n let openTooltip: { kind: 'node'; id: string } | { kind: 'edge'; index: number } | null = null;\n /**\n * The target whose content is currently rendered into the tooltip element.\n * `anchorTooltip()` runs on every engine tick and every camera change, so\n * re-running the host formatter and rewriting the DOM each time would be a\n * per-frame cost for content that only changes when the target does.\n */\n let renderedTooltip: string | null = null;\n\n // =========================================================================\n // Derived-data builders\n // =========================================================================\n\n function rebuildDerived(): void {\n adjacency = new Map();\n for (const e of compilation.edges) {\n if (!adjacency.has(e.source)) adjacency.set(e.source, new Set());\n if (!adjacency.has(e.target)) adjacency.set(e.target, new Set());\n adjacency.get(e.source)!.add(e.target);\n adjacency.get(e.target)!.add(e.source);\n }\n nodeDataMap = new Map(compilation.nodes.map((n) => [n.id, n.data ?? {}]));\n nodeCategory = new Map();\n const field = compilation.legendField;\n if (field) {\n for (const n of compilation.nodes) {\n const v = n.data?.[field];\n if (v != null) nodeCategory.set(n.id, String(v));\n }\n }\n seedIds = new Set(compilation.seedNodeIds);\n\n // Cylinders are only affordable below the edge threshold; above it the\n // width encoding becomes a resting-opacity ramp on plain lines.\n const widthEncoded = currentSpec.encoding?.edgeWidth != null;\n useLinkWidth = widthEncoded && compilation.edges.length <= LINK_WIDTH_MAX_EDGES;\n edgeBaseAlpha = widthEncoded && !useLinkWidth ? widthAsAlpha(compilation.edges) : undefined;\n }\n\n /** Label sprite color: the theme's text ink, as in 2D's canvas label layer. */\n function labelColor(): string {\n return compilation.theme.colors.text;\n }\n\n // =========================================================================\n // Seeding\n // =========================================================================\n\n /**\n * Deterministic 3D start positions. x/y come from the shared 2D seeder so a\n * spec settles to a recognisably similar shape in both dimensions; z reuses\n * the same disc placement under a salted seed and takes its x component.\n */\n function seedPositions(nodes: CompiledGraphNode[]): Map<string, [number, number, number]> {\n const xy: SimNode[] = nodes.map((n) => ({\n id: n.id,\n radius: n.radius,\n community: n.community,\n }));\n const seed = compilation.simulationConfig.seed ?? 0;\n seedNodePositions(xy, seed);\n const z: SimNode[] = nodes.map((n) => ({\n id: n.id,\n radius: n.radius,\n community: n.community,\n }));\n seedNodePositions(z, seed ^ Z_SEED_SALT);\n\n const out = new Map<string, [number, number, number]>();\n for (let i = 0; i < nodes.length; i++) {\n out.set(nodes[i].id, [xy[i].x ?? 0, xy[i].y ?? 0, z[i].x ?? 0]);\n }\n return out;\n }\n\n /** The z a single entering node should spawn at (same stream as `seedPositions`). */\n function seedZ(id: string, radius: number, community: string | undefined): number {\n const one: SimNode[] = [{ id, radius, community }];\n seedNodePositions(one, (compilation.simulationConfig.seed ?? 0) ^ Z_SEED_SALT);\n return one[0].x ?? 0;\n }\n\n // =========================================================================\n // Scene data\n // =========================================================================\n\n function buildGraphData(positions: Map<string, [number, number, number]>): void {\n nodeData = compilation.nodes.map((node) => {\n const p = positions.get(node.id) ?? [0, 0, 0];\n return { id: node.id, node, x: p[0], y: p[1], z: p[2] };\n });\n linkData = compilation.edges.map((edge, edgeIndex) => ({\n source: edge.source,\n target: edge.target,\n edge,\n edgeIndex,\n }));\n nodeById = new Map(nodeData.map((d) => [d.id, d]));\n graph.graphData({ nodes: nodeData, links: linkData });\n }\n\n function nodeObjectFor(datum: Node3D): NodeObject3D {\n let obj = nodeObjects.get(datum.id);\n if (!obj) {\n obj = createNodeObject(datum.node, labelColor());\n obj.group.userData.nodeId = datum.id;\n nodeObjects.set(datum.id, obj);\n const base = displayNodeAlpha.get(datum.id) ?? datum.node.opacity;\n if (entranceActive) {\n obj.material.opacity = base * (entranceNodeAlpha.get(datum.id) ?? 0);\n obj.mesh.scale.setScalar(entranceNodeScale.get(datum.id) ?? ENTRANCE_MIN_SCALE);\n } else {\n obj.material.opacity = base;\n }\n }\n return obj;\n }\n\n /**\n * The `linkThreeObject` accessor, hoisted out of the boot chain so the\n * shape-class flip in `update()` can re-set the same function: that prop\n * changing is what makes three-forcegraph rebuild link objects on their own.\n */\n const linkThreeObject = (datum: Link3D): LinkObject3D['object'] => linkObjectFor(datum).object;\n\n /**\n * Same identity contract as {@link nodeObjectFor}: whenever the link datums\n * are rebuilt, `linkObjects` must be cleared first, so the digest never hands\n * a cached object to a new datum while the old datum's remove hook is still\n * pending on it. The structural `update()` clears the map for exactly this\n * reason (the edge renumbering makes it necessary anyway).\n */\n function linkObjectFor(datum: Link3D): LinkObject3D {\n let obj = linkObjects.get(datum.edgeIndex);\n if (!obj) {\n const resting = edgeBaseAlpha?.get(datum.edgeIndex) ?? 0.3;\n obj = createLinkObject(datum.edge, useLinkWidth, resting);\n linkObjects.set(datum.edgeIndex, obj);\n const base = displayEdgeAlpha.get(datum.edgeIndex) ?? resting;\n obj.material.opacity = entranceActive\n ? base * (entranceEdgeAlpha.get(datum.edgeIndex) ?? 0)\n : base;\n }\n return obj;\n }\n\n // =========================================================================\n // Emphasis\n // =========================================================================\n\n function hoverConnectedSet(nodeId: string | null): Set<string> | null {\n if (nodeId === null) return null;\n if (compilation.interaction.hoverMode === 'none') return null;\n if (compilation.interaction.hoverMode === 'category') {\n const cat = nodeCategory.get(nodeId);\n const set = new Set<string>([nodeId]);\n if (cat !== undefined) for (const [id, c] of nodeCategory) if (c === cat) set.add(id);\n return set;\n }\n if (compilation.interaction.hoverMode === 'node') return new Set([nodeId]);\n const set = new Set<string>([nodeId]);\n const neighbors = adjacency.get(nodeId);\n if (neighbors) for (const nid of neighbors) set.add(nid);\n return set;\n }\n\n function currentFocus(): FocusSnapshot {\n const standing = composeStandingFocus(\n highlightSet,\n searchManager.getMatches(),\n selectedNodeIds,\n adjacency,\n );\n return layerHoverFocus(standing, hoveredNodeId, hoverConnectedSet(hoveredNodeId));\n }\n\n function effectiveDimOpacity(): number {\n const custom = transientHighlight !== null ? highlightDimOpacity : null;\n return custom ?? compilation.interaction.dimOpacity;\n }\n\n /**\n * Push the composed opacity (and, mid-entrance, the pop scale) onto every\n * material. Called from both the emphasis tween and the entrance tween, so it\n * allocates nothing: every map it reads is preallocated and reused.\n */\n function paint(): void {\n for (const [id, obj] of nodeObjects) {\n const a = displayNodeAlpha.get(id);\n if (a === undefined) continue;\n if (entranceActive) {\n const factor = entranceNodeAlpha.get(id) ?? 0;\n obj.material.opacity = a * factor;\n obj.mesh.scale.setScalar(entranceNodeScale.get(id) ?? ENTRANCE_MIN_SCALE);\n // SpriteText has no opacity channel of its own, so a label whose node\n // has not popped yet would otherwise float over empty space.\n if (obj.sprite) obj.sprite.visible = labelShown.has(id) && factor > LABEL_ENTRANCE_MIN;\n } else {\n obj.material.opacity = a;\n }\n }\n for (const [index, obj] of linkObjects) {\n const a = displayEdgeAlpha.get(index);\n if (a === undefined) continue;\n obj.material.opacity = entranceActive ? a * (entranceEdgeAlpha.get(index) ?? 0) : a;\n }\n }\n\n /**\n * Retarget every material toward the composed emphasis state.\n *\n * Never calls `refresh()`: that rebuilds every node and link object, which at\n * hover rate would be a full scene teardown per pointer move.\n */\n function armEmphasis(): void {\n const targets = resolveEmphasis({\n nodes: compilation.nodes,\n edges: compilation.edges,\n focus: currentFocus(),\n exemptIds: seedIds,\n dimOpacity: effectiveDimOpacity(),\n edgeBaseAlpha,\n });\n\n if (emphasisTween) {\n scheduler.remove(emphasisTween);\n emphasisTween = null;\n }\n\n const hoverCfg = compilation.animation?.hover ?? null;\n const duration = hoverCfg && !prefersReducedMotion() ? hoverCfg.duration : 0;\n\n if (duration <= 0) {\n for (const [id, a] of targets.nodes) displayNodeAlpha.set(id, a);\n for (const [i, a] of targets.edges) displayEdgeAlpha.set(i, a);\n paint();\n refreshLabels();\n return;\n }\n\n const fromNodes = new Map(displayNodeAlpha);\n const fromEdges = new Map(displayEdgeAlpha);\n const tween = createTween({\n duration,\n ease: resolveEase(hoverCfg?.ease ?? 'smooth'),\n apply: (t) => {\n for (const [id, to] of targets.nodes) {\n const from = fromNodes.get(id) ?? to;\n displayNodeAlpha.set(id, from + (to - from) * t);\n }\n for (const [i, to] of targets.edges) {\n const from = fromEdges.get(i) ?? to;\n displayEdgeAlpha.set(i, from + (to - from) * t);\n }\n paint();\n },\n onDone: () => {\n emphasisTween = null;\n },\n });\n emphasisTween = tween;\n scheduler.add(tween);\n refreshLabels();\n }\n\n // =========================================================================\n // Highlight composition (mirrors graph-mount's two-layer model)\n // =========================================================================\n\n function resolveHighlightTarget(target: GraphHighlightTarget): Set<string> {\n return resolveHighlightTargetIds(target, compilation.nodes, adjacency);\n }\n\n function categoryHighlightSet(): Set<string> | null {\n return categoryHighlightIds(activeCategories, nodeCategory);\n }\n\n function recomputeHighlight(): void {\n const filter = categoryHighlightSet();\n const transient =\n transientHighlight !== null && transientHighlight.size > 0 ? transientHighlight : null;\n if (filter !== null && transient !== null) {\n const inter = new Set<string>();\n for (const id of transient) if (filter.has(id)) inter.add(id);\n highlightSet = inter.size > 0 ? inter : transient;\n return;\n }\n highlightSet = filter ?? transient;\n }\n\n function refreshTransientHighlight(): void {\n if (transientTarget === null) {\n transientHighlight = null;\n return;\n }\n const nextIds = new Set(compilation.nodes.map((n) => n.id));\n const resolved = new Set(\n [...resolveHighlightTarget(transientTarget)].filter((id) => nextIds.has(id)),\n );\n transientHighlight = resolved.size > 0 ? resolved : null;\n }\n\n function emitHighlightChange(): void {\n options?.onHighlightChange?.(highlightSet ? [...highlightSet] : null);\n }\n\n // =========================================================================\n // Labels\n // =========================================================================\n\n /** Labels that are always shown, regardless of the distance budget. */\n function forcedLabelIds(): Set<string> {\n const forced = new Set<string>();\n for (const id of seedIds) forced.add(id);\n for (const [id, override] of Object.entries(currentSpec.nodeOverrides ?? {})) {\n if (override?.alwaysShowLabel) forced.add(id);\n }\n if (hoveredNodeId) forced.add(hoveredNodeId);\n for (const id of selectedNodeIds) forced.add(id);\n const matches = searchManager.getMatches();\n if (matches) for (const id of matches) forced.add(id);\n return forced;\n }\n\n function refreshLabels(): void {\n if (destroyed || nodeObjects.size === 0) return;\n const camera = graph.camera();\n const cameraPos = camera?.position ?? { x: 0, y: 0, z: FIT_DISTANCE };\n const forced = forcedLabelIds();\n const ranked = resolveVisibleLabels(\n // Only nodes that actually compiled a label are candidates. Filtering on\n // the datum rather than `nodeObjects` matters: the library's digest is\n // deferred, so on the first rank the objects may not exist yet and an\n // object-side filter would let unlabelled nodes eat budget slots.\n nodeData\n .filter((d) => d.node.label)\n .map((d) => ({\n id: d.id,\n priority: d.node.labelPriority,\n x: d.x ?? 0,\n y: d.y ?? 0,\n z: d.z ?? 0,\n })),\n forced,\n cameraPos,\n LABEL_BUDGET_3D,\n );\n\n // Sprites are attenuated by perspective, so a label 2,000 units out is\n // unreadable while one at 200 shouts. Counter it: scale each label so it\n // lands at roughly LABEL_SCREEN_PX on screen whatever the distance. That is\n // also what makes a screen-space declutter pass meaningful — the boxes only\n // mean something once every label is the same size.\n const fov =\n ((camera as { fov?: number } | undefined)?.fov ?? DEFAULT_FOV_DEG) * (Math.PI / 180);\n const viewportHeight = shell.getSize().height || 1;\n const worldPerPixel = (2 * Math.tan(fov / 2)) / viewportHeight;\n\n labelShown.clear();\n const placed: LabelBox[] = [];\n for (const id of ranked) {\n const obj = nodeObjects.get(id);\n const datum = nodeById.get(id);\n if (!obj || !datum || !obj.labelText) continue;\n\n const dist = Math.hypot(\n (datum.x ?? 0) - cameraPos.x,\n (datum.y ?? 0) - cameraPos.y,\n (datum.z ?? 0) - cameraPos.z,\n );\n const desiredHeight = LABEL_SCREEN_PX * worldPerPixel * dist;\n const factor = Math.min(\n LABEL_SCALE_MAX,\n Math.max(LABEL_SCALE_MIN, desiredHeight / LABEL_TEXT_HEIGHT),\n );\n\n // Same declutter contract as 2D's `drawLabels`: forced labels reserve\n // their box unconditionally, everything else takes the first free slot.\n const projected = graph.graph2ScreenCoords(datum.x ?? 0, datum.y ?? 0, datum.z ?? 0);\n const box = labelBox(projected, obj.labelText, LABEL_SCREEN_PX * factor);\n if (box && !forced.has(id) && placed.some((other) => overlaps(box, other))) continue;\n if (box) placed.push(box);\n\n // Only a label that actually won a slot is worth a sprite.\n const sprite = ensureLabelSprite(obj);\n if (!sprite) continue;\n labelShown.add(id);\n if (obj.labelBaseScale) {\n sprite.scale.set(obj.labelBaseScale.x * factor, obj.labelBaseScale.y * factor, 0);\n }\n }\n\n for (const [id, obj] of nodeObjects) {\n if (obj.sprite) {\n obj.sprite.visible =\n labelShown.has(id) &&\n (!entranceActive || (entranceNodeAlpha.get(id) ?? 0) > LABEL_ENTRANCE_MIN);\n }\n }\n lastLabelRank = performance.now();\n }\n\n /** Throttled re-rank, driven by the controls `change` event while orbiting. */\n function scheduleLabelRank(): void {\n if (labelRankQueued || destroyed) return;\n const wait = Math.max(0, LABEL_RERANK_MS - (performance.now() - lastLabelRank));\n labelRankQueued = true;\n setTimeout(() => {\n labelRankQueued = false;\n if (!destroyed) refreshLabels();\n }, wait);\n }\n\n // =========================================================================\n // Camera\n // =========================================================================\n\n function controlsTarget(): { x: number; y: number; z: number } {\n const t = (graph.controls() as { target?: { x: number; y: number; z: number } }).target;\n return t ? { x: t.x, y: t.y, z: t.z } : { x: 0, y: 0, z: 0 };\n }\n\n function getCamera(): Camera3D {\n const pos = graph.cameraPosition();\n const target = controlsTarget();\n const dist = Math.hypot(pos.x - target.x, pos.y - target.y, pos.z - target.z) || FIT_DISTANCE;\n return {\n x: target.x,\n y: target.y,\n k: FIT_DISTANCE / dist,\n position: { x: pos.x, y: pos.y, z: pos.z },\n target,\n };\n }\n\n /** Flight length in ms; 0 snaps (reduced motion, `animation: false`, `duration: 0`). */\n function flightMs(opts?: CameraFlightOptions): number {\n const cfg = compilation.animation?.camera ?? null;\n if (cfg === null || prefersReducedMotion() || opts?.duration === 0) return 0;\n const requested = opts?.duration ?? cfg.duration;\n return requested === 'auto' ? AUTO_FLIGHT_MS : requested;\n }\n\n function clampK(k: number): number {\n return Math.min(K_MAX, Math.max(K_MIN, k));\n }\n\n function flyTo(target: FlyTarget3D, opts?: CameraFlightOptions): void {\n if (destroyed) return;\n autoFit = false;\n const ms = flightMs(opts);\n if (target.position) {\n graph.cameraPosition(\n target.position,\n target.target ?? { x: target.x, y: target.y, z: 0 },\n ms,\n );\n queueCameraChange();\n return;\n }\n const look = { x: target.x, y: target.y, z: 0 };\n const cam = getCamera();\n const dist = FIT_DISTANCE / clampK(target.k ?? cam.k);\n // Keep the current viewing direction so a `{ x, y }` recentre orbits nothing.\n const dir = normalize({\n x: cam.position.x - cam.target.x,\n y: cam.position.y - cam.target.y,\n z: cam.position.z - cam.target.z,\n });\n graph.cameraPosition(\n { x: look.x + dir.x * dist, y: look.y + dir.y * dist, z: look.z + dir.z * dist },\n look,\n ms,\n );\n queueCameraChange();\n }\n\n /** Frame every node from the current viewing angle. See `computeFit`. */\n function zoomToFit(opts?: CameraFlightOptions & { padding?: number }): void {\n // An explicit fit is the host taking the camera, so the settle-phase\n // auto-fit stops second-guessing it.\n autoFit = false;\n fitNow(opts);\n }\n\n function fitNow(opts?: CameraFlightOptions & { padding?: number }): void {\n if (destroyed || nodeData.length === 0) return;\n const camera = graph.camera() as { fov?: number; aspect?: number } | undefined;\n const fit = computeFit(\n nodeData.map((d) => ({\n x: d.x ?? 0,\n y: d.y ?? 0,\n z: d.z ?? 0,\n radius: d.node.radius ?? 0,\n })),\n {\n cameraPos: getCamera().position,\n fovDeg: camera?.fov ?? DEFAULT_FOV_DEG,\n aspect: camera?.aspect ?? 1,\n viewportHeight: shell.getSize().height,\n paddingPx: opts?.padding ?? FIT_PADDING_PX,\n },\n );\n if (!fit) return;\n // Mid-entrance the camera stands `entranceCameraPull ×` further back than\n // the true fit, easing to 1. See `startEntrance`.\n const distance = fit.distance * entranceCameraPull;\n graph.cameraPosition(\n {\n x: fit.center.x + fit.dir.x * distance,\n y: fit.center.y + fit.dir.y * distance,\n z: fit.center.z + fit.dir.z * distance,\n },\n fit.center,\n flightMs(opts),\n );\n queueCameraChange();\n }\n\n function zoomToNode(nodeId: string, opts?: CameraFlightOptions & { scale?: number }): void {\n if (destroyed) return;\n autoFit = false;\n const datum = nodeById.get(nodeId);\n if (!datum) return;\n const at = { x: datum.x ?? 0, y: datum.y ?? 0, z: datum.z ?? 0 };\n // Approach along the node's own radial direction so the camera ends up\n // outside the cloud rather than inside it.\n const dir = normalize(at);\n const dist = opts?.scale ? NODE_FOCUS_DISTANCE / clampK(opts.scale) : NODE_FOCUS_DISTANCE;\n graph.cameraPosition(\n { x: at.x + dir.x * dist, y: at.y + dir.y * dist, z: at.z + dir.z * dist },\n at,\n flightMs(opts),\n );\n queueCameraChange();\n }\n\n function queueCameraChange(): void {\n if (cameraChangeQueued || destroyed || !options?.onCameraChange) return;\n cameraChangeQueued = true;\n requestAnimationFrame(() => {\n cameraChangeQueued = false;\n if (destroyed) return;\n options.onCameraChange?.(getCamera());\n });\n }\n\n // =========================================================================\n // Tooltips\n // =========================================================================\n\n function tooltipFormatter(): GraphTooltipFormatter | null {\n const t = options?.tooltip;\n return t && typeof t === 'object' ? (t.formatter ?? null) : null;\n }\n\n /**\n * Project a scene point into wrapper-relative pixels. `graph2ScreenCoords` is\n * canvas-relative, and the tooltip manager positions against the wrapper, so\n * the surface's offset inside the wrapper is added here.\n */\n function toWrapperXY(p: { x: number; y: number; z: number }): { x: number; y: number } | null {\n const screen = graph.graph2ScreenCoords(p.x, p.y, p.z);\n if (!screen || !Number.isFinite(screen.x) || !Number.isFinite(screen.y)) return null;\n const s = surfaceEl.getBoundingClientRect();\n const w = shell.wrapper.getBoundingClientRect();\n return { x: screen.x + (s.left - w.left), y: screen.y + (s.top - w.top) };\n }\n\n function applyFormatterResult(\n result: TooltipContent | string | HTMLElement | null,\n x: number,\n y: number,\n ): void {\n const tm = shell.tooltipManager;\n if (!tm) return;\n // A suppressed item has to clear `openTooltip`/`renderedTooltip` too:\n // leaving them set would send the next `anchorTooltip()` down the\n // reposition fast path, which re-shows the previous item's content.\n if (result === null) hideTooltip();\n else if (typeof result === 'string') tm.show({ text: result }, x, y);\n else if (result instanceof HTMLElement) tm.show({ element: result }, x, y);\n else tm.show(result, x, y);\n }\n\n /**\n * Re-anchor whatever tooltip is open, rendering its content only when the\n * open target has changed since the last render. Everything else is a\n * reposition, which is what the tick and orbit paths actually need.\n */\n function anchorTooltip(): void {\n const tm = shell.tooltipManager;\n if (!tm || openTooltip === null) return;\n const formatter = tooltipFormatter();\n const key =\n openTooltip.kind === 'node' ? `node:${openTooltip.id}` : `edge:${openTooltip.index}`;\n\n if (openTooltip.kind === 'node') {\n const id = openTooltip.id;\n const datum = nodeById.get(id);\n const defaults = compilation.tooltipDescriptors.get(id);\n if (!datum || !defaults) return;\n const at = toWrapperXY({ x: datum.x ?? 0, y: datum.y ?? 0, z: datum.z ?? 0 });\n if (!at) return;\n if (key === renderedTooltip) {\n tm.move(at.x, at.y);\n return;\n }\n renderedTooltip = key;\n if (!formatter) {\n tm.show(defaults, at.x, at.y);\n return;\n }\n applyFormatterResult(\n formatter({ kind: 'node', data: nodeDataMap.get(id) ?? {} }, defaults),\n at.x,\n at.y,\n );\n return;\n }\n\n const link = linkData[openTooltip.index];\n if (!link) return;\n const a = endpointPos(link.source);\n const b = endpointPos(link.target);\n const at = toWrapperXY({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 });\n if (!at) return;\n if (key === renderedTooltip) {\n tm.move(at.x, at.y);\n return;\n }\n renderedTooltip = key;\n const defaults = buildEdgeTooltip(link.edge);\n if (!formatter) {\n tm.show(defaults, at.x, at.y);\n return;\n }\n applyFormatterResult(\n formatter({ kind: 'edge', data: link.edge.data ?? {} }, defaults),\n at.x,\n at.y,\n );\n }\n\n function endpointPos(endpoint: string | Node3D): { x: number; y: number; z: number } {\n if (typeof endpoint !== 'string') {\n return { x: endpoint.x ?? 0, y: endpoint.y ?? 0, z: endpoint.z ?? 0 };\n }\n const datum = nodeById.get(endpoint);\n return { x: datum?.x ?? 0, y: datum?.y ?? 0, z: datum?.z ?? 0 };\n }\n\n function hideTooltip(): void {\n openTooltip = null;\n renderedTooltip = null;\n shell.tooltipManager?.hide();\n }\n\n // =========================================================================\n // Legend\n // =========================================================================\n\n function legendSetting(): boolean | { interactive?: boolean; counts?: boolean } | undefined {\n return options?.legend ?? currentSpec.legend;\n }\n\n function legendConfig(): { interactive: boolean; counts: boolean } {\n const l = legendSetting();\n if (l && typeof l === 'object') {\n return { interactive: l.interactive ?? true, counts: l.counts ?? true };\n }\n return { interactive: true, counts: true };\n }\n\n function getLegend(): GraphLegendData {\n const nodeEntries = 'entries' in compilation.legend ? compilation.legend.entries : [];\n return {\n field: compilation.legendField,\n nodes: nodeEntries\n .filter((e) => !e.overflow)\n .map((e) => ({\n label: e.label,\n color: e.color,\n count: e.count,\n active: activeCategories.size === 0 || activeCategories.has(e.label),\n })),\n edges: (compilation.edgeLegend ?? []).map((e) => ({\n label: e.label,\n color: e.color,\n count: e.count,\n })),\n };\n }\n\n function renderLegend(): void {\n if (!shell.legendEl) return;\n const view = { nodes: getLegend().nodes, edges: getLegend().edges };\n if (!legendController) {\n const cfg = legendConfig();\n legendController = createGraphLegend(shell.legendEl, view, {\n interactive: cfg.interactive,\n counts: cfg.counts,\n onToggle: (value) => toggleLegendCategory(value),\n onHover: (value) => {\n const field = compilation.legendField;\n options?.onLegendHover?.(value !== null && field ? { field, value } : null);\n },\n });\n } else {\n legendController.update(view);\n }\n shell.syncChromeInset();\n }\n\n function toggleLegendCategory(value: string): void {\n if (activeCategories.has(value)) activeCategories.delete(value);\n else activeCategories.add(value);\n recomputeHighlight();\n renderLegend();\n armEmphasis();\n options?.onLegendToggle?.([...activeCategories]);\n emitHighlightChange();\n }\n\n // =========================================================================\n // Interaction wiring\n // =========================================================================\n\n function handleNodeClick(nodeId: string, shiftKey: boolean): void {\n if (shiftKey) {\n if (selectedNodeIds.has(nodeId)) selectedNodeIds.delete(nodeId);\n else selectedNodeIds.add(nodeId);\n } else {\n selectedNodeIds = new Set([nodeId]);\n }\n armEmphasis();\n options?.onSelectionChange?.([...selectedNodeIds]);\n options?.onNodeClick?.(nodeDataMap.get(nodeId) ?? {});\n }\n\n const raycaster = new Raycaster();\n const pointer = new Vector2();\n\n /** Node id under a mouse event, via a raycast against the node groups. */\n function pickNode(event: MouseEvent): string | null {\n const canvas = graph.renderer()?.domElement;\n const camera = graph.camera();\n if (!canvas || !camera) return null;\n const rect = canvas.getBoundingClientRect();\n pointer.x = ((event.clientX - rect.left) / (rect.width || 1)) * 2 - 1;\n pointer.y = -((event.clientY - rect.top) / (rect.height || 1)) * 2 + 1;\n raycaster.setFromCamera(pointer, camera);\n const groups = [...nodeObjects.values()].map((o) => o.group);\n const hits = raycaster.intersectObjects(groups, true);\n for (const hit of hits) {\n // The hit is the sphere mesh; the node id lives on its parent group.\n let obj: Object3D | null = hit.object;\n while (obj) {\n const id = obj.userData?.nodeId;\n if (typeof id === 'string') return id;\n obj = obj.parent;\n }\n }\n return null;\n }\n\n const onCanvasCameraInput = (): void => {\n autoFit = false;\n };\n\n const onCanvasDblClick = (event: MouseEvent): void => {\n const id = pickNode(event);\n // Matches 2D exactly: the two clicks already fired, and the double-click\n // fires IN ADDITION rather than suppressing them.\n if (id) options?.onNodeDoubleClick?.(nodeDataMap.get(id) ?? {});\n };\n\n // three's own `onContextRestore` rebuilds the GL resources for every object\n // already in the scene, so there is nothing left for us to rebuild. Calling\n // `refresh()` here would tear the scene down and re-add it, and resume the\n // engine for a spurious tick.\n const onContextLost = (event: Event): void => {\n event.preventDefault();\n };\n\n // =========================================================================\n // Entrance\n // =========================================================================\n\n /** Drop the entrance multipliers and restore every node to full scale. */\n function endEntrance(): void {\n entranceActive = false;\n entranceCameraPull = 1;\n for (const obj of nodeObjects.values()) obj.mesh.scale.setScalar(1);\n paint();\n }\n\n /**\n * Nodes pop in (scale 0.01 → 1 with an overshoot, opacity 0 → their emphasis\n * alpha), staggered by `entranceOrder`; links fade in a beat after the later\n * of their endpoints; the camera starts pulled back to\n * `ENTRANCE_CAMERA_PULLBACK ×` the fit and flies in over the same window.\n *\n * The pull-in is expressed as a MULTIPLIER on the fit distance rather than as\n * its own camera flight. The settle-phase auto-fit re-frames the cloud every\n * `AUTO_FIT_INTERVAL_MS` while the layout expands from its seeded blob, and a\n * flight to a distance computed at t=0 would be re-targeting a framing that is\n * already stale. As a multiplier the two compose: the auto-fit keeps deciding\n * WHAT to frame, the entrance only decides how far back to stand.\n *\n * Runs once per mount. `update()` never calls it.\n */\n function startEntrance(): void {\n const enter = compilation.animation?.enter ?? null;\n if (options?.suppressEntrance || !enter || prefersReducedMotion()) {\n endEntrance();\n return;\n }\n\n // Same gate as 2D: thousands of distinct start times read as noise, so\n // above the cap the whole graph reveals as one.\n const stagger = enter.stagger && compilation.nodes.length <= ENTRANCE_STAGGER_MAX_NODES;\n const plan = planEntrance(\n nodeData.map((d) => ({ id: d.id, x: d.x ?? 0, y: d.y ?? 0 })),\n compilation.edges,\n enter.duration,\n stagger,\n );\n const ease = resolveEase(enter.ease);\n\n entranceActive = true;\n entranceCameraPull = enter.cameraFit ? ENTRANCE_CAMERA_PULLBACK : 1;\n for (const id of plan.nodeOffset.keys()) {\n entranceNodeAlpha.set(id, 0);\n entranceNodeScale.set(id, ENTRANCE_MIN_SCALE);\n }\n for (const index of plan.linkOffset.keys()) entranceEdgeAlpha.set(index, 0);\n paint();\n\n // The driver runs linear over the whole span so each element can apply the\n // spec's ease across its OWN window; easing the global clock instead would\n // squeeze the stagger rather than shape each pop.\n const tween = createTween({\n duration: plan.span,\n ease: linear,\n apply: (t) => {\n const elapsed = t * plan.span;\n for (const [id, offset] of plan.nodeOffset) {\n const p = elementProgress(elapsed, offset, enter.duration, ease);\n entranceNodeAlpha.set(id, popAlpha(p));\n entranceNodeScale.set(id, Math.max(ENTRANCE_MIN_SCALE, popScale(p)));\n }\n for (const [index, offset] of plan.linkOffset) {\n entranceEdgeAlpha.set(index, elementProgress(elapsed, offset, enter.duration, ease));\n }\n if (enter.cameraFit) {\n entranceCameraPull = ENTRANCE_CAMERA_PULLBACK + (1 - ENTRANCE_CAMERA_PULLBACK) * ease(t);\n // The auto-fit throttle is too coarse to carry a flight, so the\n // entrance re-fits every frame while it owns the standoff.\n if (autoFit && options?.fitOnLoad !== false) fitNow({ duration: 0 });\n }\n paint();\n },\n onDone: endEntrance,\n });\n scheduler.add(tween);\n }\n\n // =========================================================================\n // Public handle\n // =========================================================================\n\n function search(query: string): void {\n if (destroyed) return;\n searchManager.search(query, compilation.nodes);\n armEmphasis();\n }\n\n function clearSearch(): void {\n if (destroyed) return;\n searchManager.clearSearch();\n armEmphasis();\n }\n\n function selectNode(nodeId: string, opts?: { fly?: boolean } & CameraFlightOptions): void {\n if (destroyed) return;\n selectedNodeIds = new Set([nodeId]);\n armEmphasis();\n options?.onSelectionChange?.([nodeId]);\n const shouldFly = opts?.fly ?? compilation.interaction.selectFlyTo;\n if (shouldFly) zoomToNode(nodeId, opts);\n }\n\n function highlight(target: GraphHighlightTarget, opts?: { dimOpacity?: number }): void {\n if (destroyed) return;\n transientTarget = target;\n refreshTransientHighlight();\n highlightDimOpacity = opts?.dimOpacity ?? null;\n recomputeHighlight();\n armEmphasis();\n emitHighlightChange();\n }\n\n function clearHighlight(): void {\n if (destroyed) return;\n transientTarget = null;\n transientHighlight = null;\n highlightDimOpacity = null;\n recomputeHighlight();\n armEmphasis();\n emitHighlightChange();\n }\n\n function setActiveCategories(values: string[]): void {\n if (destroyed) return;\n activeCategories = new Set(values);\n recomputeHighlight();\n renderLegend();\n armEmphasis();\n emitHighlightChange();\n }\n\n function doResize(): void {\n if (destroyed) return;\n // three-render-objects reads devicePixelRatio once, at construction, so a\n // window dragged onto a retina display (or a browser zoom change) would\n // stay soft until remount. Re-apply it before the size, which is what\n // actually reallocates the drawing buffer.\n const renderer = graph.renderer() as\n | { getPixelRatio?(): number; setPixelRatio?(v: number): void }\n | undefined;\n const dpr = Math.min(MAX_PIXEL_RATIO, globalThis.devicePixelRatio || 1);\n if (renderer?.getPixelRatio?.() !== dpr) renderer?.setPixelRatio?.(dpr);\n const { width, height } = shell.getSize();\n graph.width(width).height(height);\n shell.syncChromeInset();\n }\n\n /**\n * Unified data update.\n *\n * `diffGraphUpdate` classifies the change exactly as in 2D. A visual-only\n * change mutates materials and geometry in place and never touches\n * `graphData()`. A structural change reuses the surviving node datums (so\n * their positions, velocities AND their scene-object bindings all carry\n * over), spawns z for entering nodes from the seeded stream, and then calls\n * `graphData()`.\n *\n * 3D difference: `graphData()` reheats the layout globally (alpha 1). 2D\n * applies a local impulse instead (`initialAlpha` + `suppressCenter`), which\n * 3d-force-graph gives us no seam for. Carried positions stop survivors from\n * respawning, but they do drift; `d3AlphaDecay` from the compiled `settle`\n * bounds how far.\n */\n function update(newSpec: GraphSpec): void {\n if (destroyed) return;\n\n const next = ctx.compile(newSpec);\n if (next.numDimensions !== 3) {\n shell.warn('createGraph: update() cannot change dimensions; remount the graph');\n return;\n }\n\n scheduler.finishAll();\n\n const prevNodes: PositionedNode[] = nodeData.map((d, index) => ({\n ...d.node,\n x: d.x ?? 0,\n y: d.y ?? 0,\n index,\n }));\n const prevEdges: PositionedEdge[] = compilation.edges.map((edge) => {\n const a = endpointPos(edge.source);\n const b = endpointPos(edge.target);\n return { ...edge, sourceX: a.x, sourceY: a.y, targetX: b.x, targetY: b.y };\n });\n const prevConfig = compilation.simulationConfig;\n const prevUseLinkWidth = useLinkWidth;\n\n currentSpec = newSpec;\n compilation = next;\n // The compiled descriptors behind an open tooltip may have changed, so the\n // next anchor must re-render rather than take the reposition fast path.\n renderedTooltip = null;\n\n const diff = diffGraphUpdate(\n prevNodes,\n prevEdges,\n compilation,\n prevConfig,\n prevConfig.seed ?? 0,\n );\n\n rebuildDerived();\n shell.renderChrome(compilation);\n renderLegend();\n refreshTransientHighlight();\n recomputeHighlight();\n reRunSearch();\n\n // An `edgeWidth` encoding appearing or disappearing swaps every link\n // between a cylinder mesh and a line, which cannot be done in place: the\n // scene binds objects to datums by identity, so a replacement object never\n // reaches the scene on its own.\n const shapeClassChanged =\n useLinkWidth !== prevUseLinkWidth ||\n [...linkObjects.values()].some((obj) => !linkShapeMatches(obj, useLinkWidth));\n\n // Lines are thin hit targets compared with a cylinder, so the precision\n // has to follow the shape class rather than stay at its boot value.\n if (useLinkWidth !== prevUseLinkWidth) graph.linkHoverPrecision(useLinkWidth ? 4 : 8);\n\n if (diff.visualOnly) {\n // Same ids in the same order, so the datum's compiled node/edge can be\n // swapped under the existing scene objects with no layout restart.\n for (let i = 0; i < compilation.nodes.length; i++) {\n const node = compilation.nodes[i];\n nodeData[i].node = node;\n const obj = nodeObjects.get(node.id);\n if (obj) applyNodeVisuals(obj, node, labelColor());\n }\n for (let i = 0; i < compilation.edges.length; i++) {\n const edge = compilation.edges[i];\n linkData[i].edge = edge;\n if (shapeClassChanged) continue;\n const obj = linkObjects.get(i);\n // Width, dash pattern and color all move here: color alone would leave\n // a cylinder at its old radius and a line at its old dash geometry.\n if (obj) applyLinkVisuals(obj, edge, useLinkWidth);\n }\n if (shapeClassChanged) {\n // Re-setting `linkThreeObject` makes three-forcegraph clear its link\n // mapper and re-digest the links alone, so every link object is\n // rebuilt from the cleared cache without `graphData()` reheating the\n // layout at alpha 1.\n for (const obj of linkObjects.values()) disposeLinkObject(obj);\n linkObjects.clear();\n graph.linkThreeObject(linkThreeObject);\n }\n pruneInteractionState();\n armEmphasis();\n return;\n }\n\n // Structural: dispose the scene objects for everything that left, then\n // rebuild the data arrays with carried-over kinematics.\n const nextNodeIds = new Set(compilation.nodes.map((n) => n.id));\n for (const [id, obj] of nodeObjects) {\n if (!nextNodeIds.has(id)) {\n disposeNodeObject(obj);\n nodeObjects.delete(id);\n displayNodeAlpha.delete(id);\n entranceNodeAlpha.delete(id);\n entranceNodeScale.delete(id);\n }\n }\n // Link objects are keyed by index into the edge list, which the new\n // compilation renumbers, so they all go.\n for (const obj of linkObjects.values()) disposeLinkObject(obj);\n linkObjects.clear();\n displayEdgeAlpha.clear();\n entranceEdgeAlpha.clear();\n\n // Survivors keep their EXISTING datum OBJECT, mutated in place.\n //\n // This is not a micro-optimisation, it is the only correct thing to do.\n // `graphData()` runs a `DataBindMapper` digest whose id accessor is\n // identity (`d => d`); three-forcegraph never sets one, and `graphData()`\n // does not clear the node mapper. Handing the digest a fresh literal for a\n // node whose cached group `nodeThreeObject` still returns therefore binds\n // that group to the NEW datum, and then the OLD datum -- absent from the\n // new set -- runs the remove hook on the SAME group: `scene.remove`,\n // `_deallocate`, and the binding deleted. Every survivor silently leaves\n // the scene and the tick loop. Reusing the datum keeps the binding, and it\n // already carries the x/y/z and vx/vy/vz that used to be copied out into\n // carry-over maps.\n const survivors = new Map(nodeById);\n nodeData = compilation.nodes.map((node) => {\n const existing = survivors.get(node.id);\n if (existing) {\n existing.node = node;\n // The cached group was built from the OLD compiled node, so a\n // structural update that also recolored or resized a survivor would\n // otherwise keep painting the stale fill, radius and label.\n const obj = nodeObjects.get(node.id);\n if (obj) applyNodeVisuals(obj, node, labelColor());\n return existing;\n }\n const spawn = diff.spawnPositions.get(node.id) ?? { x: 0, y: 0 };\n return {\n id: node.id,\n node,\n x: spawn.x,\n y: spawn.y,\n z: seedZ(node.id, node.radius, node.community),\n };\n });\n linkData = compilation.edges.map((edge, edgeIndex) => ({\n source: edge.source,\n target: edge.target,\n edge,\n edgeIndex,\n }));\n\n nodeById = new Map(nodeData.map((d) => [d.id, d]));\n applySimulationConfig(graph, compilation.simulationConfig, nodeData.length);\n graph.graphData({ nodes: nodeData, links: linkData });\n pruneInteractionState();\n armEmphasis();\n }\n\n function pruneInteractionState(): void {\n const ids = new Set(compilation.nodes.map((n) => n.id));\n if (hoveredNodeId && !ids.has(hoveredNodeId)) hoveredNodeId = null;\n // Edge indices are renumbered by every update, so the hover always drops.\n // Tell the host: a listener that opened something on hover would otherwise\n // never get the close.\n const hadEdgeHover = hoveredLinkIndex !== null;\n hoveredLinkIndex = null;\n if (hadEdgeHover) options?.onEdgeHover?.(null);\n selectedNodeIds = new Set([...selectedNodeIds].filter((id) => ids.has(id)));\n if (openTooltip?.kind === 'node' && !ids.has(openTooltip.id)) hideTooltip();\n if (openTooltip?.kind === 'edge') hideTooltip();\n }\n\n function reRunSearch(): void {\n const q = searchManager.getQuery();\n if (q !== null) searchManager.search(q, compilation.nodes);\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n scheduler.cancelAll();\n if (pumpId !== null) {\n cancelAnimationFrame(pumpId);\n pumpId = null;\n }\n\n const canvas = graph.renderer()?.domElement;\n canvas?.removeEventListener('dblclick', onCanvasDblClick);\n canvas?.removeEventListener('pointerdown', onCanvasCameraInput);\n canvas?.removeEventListener('wheel', onCanvasCameraInput);\n canvas?.removeEventListener('webglcontextlost', onContextLost);\n if (controlsListener) {\n (\n graph.controls() as { removeEventListener?(t: string, f: () => void): void }\n ).removeEventListener?.('change', controlsListener);\n controlsListener = null;\n }\n disconnectResize();\n\n // Grab the renderer BEFORE _destructor(): it tears down the render loop and\n // the DOM, after which the accessor is no longer reliable.\n const renderer = graph.renderer();\n graph._destructor();\n\n for (const obj of nodeObjects.values()) disposeNodeObject(obj);\n nodeObjects.clear();\n for (const obj of linkObjects.values()) disposeLinkObject(obj);\n linkObjects.clear();\n\n // Browsers cap live WebGL contexts (~16). `dispose()` alone does not free\n // one; the context has to be explicitly lost.\n renderer?.dispose();\n (renderer as { forceContextLoss?(): void })?.forceContextLoss?.();\n\n legendController?.destroy();\n legendController = null;\n\n shell.destroy();\n }\n\n // =========================================================================\n // Boot\n // =========================================================================\n\n rebuildDerived();\n shell.renderChrome(compilation);\n renderLegend();\n\n const initial = compilation.initialHighlight;\n if (initial) {\n activeCategories = new Set(initial.values);\n recomputeHighlight();\n }\n\n // Read the SPEC's background, not the resolved theme's: the default resolved\n // theme also reports 'transparent', so keying off the compilation would make\n // every 3D graph see-through. 2D resolves transparent to an opaque surface\n // because it paints on a canvas; 3D honours it only when a host asked for it.\n const requestedBackground = themeBackground(options?.theme) ?? themeBackground(currentSpec.theme);\n if (requestedBackground === 'transparent') {\n graph.backgroundColor('rgba(0,0,0,0)');\n // The shared wrapper paints `--oc-bg` behind the canvas, which would defeat\n // the transparent renderer clear color.\n shell.wrapper.style.background = 'transparent';\n } else {\n graph.backgroundColor(resolvedSurface(compilation.theme));\n }\n\n graph\n .showNavInfo(false)\n .enableNodeDrag(false)\n .nodeId('id')\n // Our own DOM tooltip owns hover text; the library's built-in label would\n // render a second, unstyled one on top of it.\n .nodeLabel('')\n .linkLabel('')\n .nodeVal((d: Node3D) => d.node.radius)\n .nodeColor((d: Node3D) => d.node.fill)\n .nodeThreeObject((d: Node3D) => nodeObjectFor(d).group)\n .linkColor((d: Link3D) => d.edge.stroke)\n .linkWidth((d: Link3D) => (useLinkWidth ? d.edge.strokeWidth : 0))\n .linkThreeObject(linkThreeObject)\n .linkPositionUpdate((_obj, coords, d: Link3D) => {\n const link = linkObjects.get(d.edgeIndex);\n // Returning false hands cylinder meshes back to the library, which\n // already positions them correctly (translate + scale.z + lookAt).\n return link ? updateLinkPosition(link, coords.start, coords.end) : false;\n })\n // Lines are thin hit targets compared with a 2D 5px hit band.\n .linkHoverPrecision(useLinkWidth ? 4 : 8);\n\n const { width, height } = shell.getSize();\n graph.width(width).height(height);\n\n applySimulationConfig(graph, compilation.simulationConfig, compilation.nodes.length);\n buildGraphData(seedPositions(compilation.nodes));\n\n // Resting opacities before the first frame, so nothing flashes at full alpha.\n for (const n of compilation.nodes) displayNodeAlpha.set(n.id, n.opacity);\n for (let i = 0; i < compilation.edges.length; i++) {\n displayEdgeAlpha.set(i, edgeBaseAlpha?.get(i) ?? 0.3);\n }\n armEmphasis();\n startEntrance();\n\n graph\n .onNodeHover((node) => {\n const id = node ? node.id : null;\n if (id === hoveredNodeId) return;\n hoveredNodeId = id;\n if (id && hoveredLinkIndex !== null) {\n hoveredLinkIndex = null;\n options?.onEdgeHover?.(null);\n }\n armEmphasis();\n options?.onNodeHover?.(id ? (nodeDataMap.get(id) ?? {}) : null);\n if (id && shell.tooltipManager) {\n openTooltip = { kind: 'node', id };\n anchorTooltip();\n } else {\n hideTooltip();\n }\n })\n .onLinkHover((link) => {\n const index = link ? link.edgeIndex : null;\n if (index === hoveredLinkIndex) return;\n // A live node hover owns the tooltip; don't let an edge steal it.\n if (hoveredNodeId !== null) return;\n hoveredLinkIndex = index;\n options?.onEdgeHover?.(link ? (link.edge.data ?? {}) : null);\n if (index !== null && shell.tooltipManager) {\n openTooltip = { kind: 'edge', index };\n anchorTooltip();\n } else {\n hideTooltip();\n }\n })\n .onNodeClick((node, event) => {\n handleNodeClick(node.id, Boolean(event?.shiftKey));\n })\n .onBackgroundClick(() => {\n if (selectedNodeIds.size === 0) return;\n selectedNodeIds = new Set();\n armEmphasis();\n options?.onSelectionChange?.([]);\n })\n .onEngineTick(() => {\n if (destroyed) return;\n // The node moves under a still pointer while the layout settles, so an\n // open tooltip has to follow it.\n anchorTooltip();\n if (autoFit && options?.fitOnLoad !== false) {\n const now = performance.now();\n if (now - lastAutoFit > AUTO_FIT_INTERVAL_MS) {\n lastAutoFit = now;\n fitNow({ duration: 0 });\n refreshLabels();\n }\n }\n })\n .onEngineStop(() => {\n if (destroyed) return;\n if (autoFit && options?.fitOnLoad !== false) {\n autoFit = false;\n fitNow({ duration: 0 });\n }\n refreshLabels();\n });\n\n // Orbiting changes both the label ranking and the tooltip anchor.\n controlsListener = (): void => {\n if (destroyed) return;\n scheduleLabelRank();\n anchorTooltip();\n queueCameraChange();\n };\n (graph.controls() as { addEventListener?(t: string, f: () => void): void }).addEventListener?.(\n 'change',\n controlsListener,\n );\n\n const canvasEl = graph.renderer()?.domElement;\n canvasEl?.addEventListener('dblclick', onCanvasDblClick);\n canvasEl?.addEventListener('pointerdown', onCanvasCameraInput);\n canvasEl?.addEventListener('wheel', onCanvasCameraInput, { passive: true });\n canvasEl?.addEventListener('webglcontextlost', onContextLost);\n\n const disconnectResize = shell.observeResize(() => doResize());\n\n return {\n update,\n // `update()` already routes an id-set-preserving change through the\n // material-mutation path, which never touches `graphData()`.\n updateVisuals: update,\n search,\n clearSearch,\n zoomToFit,\n zoomToNode,\n flyTo,\n centerAt: (x, y, opts) => flyTo({ x, y }, opts),\n getCamera,\n selectNode,\n getSelectedNodes: () => [...selectedNodeIds],\n getSearchMatches: () => [...(searchManager.getMatches() ?? [])],\n highlight,\n clearHighlight,\n getHighlight: () => (highlightSet ? [...highlightSet] : null),\n getLegend,\n setActiveCategories,\n getActiveCategories: () => [...activeCategories],\n resize: doResize,\n destroy,\n };\n}\n\n/**\n * The background a spec or mount option explicitly asked for.\n *\n * `ThemeConfig.colors` also accepts a bare palette array, which carries no\n * background, hence the shape guard.\n */\nfunction themeBackground(theme: ThemeConfig | undefined): string | undefined {\n const colors = theme?.colors;\n if (!colors || Array.isArray(colors)) return undefined;\n return typeof colors.background === 'string' ? colors.background : undefined;\n}\n","/**\n * Emphasis composition for the 3D renderer: turns the focus model into a\n * per-node and per-edge target opacity.\n *\n * The composition rules are NOT re-derived here. `composeStandingFocus` and\n * `layerHoverFocus` from `../graph/focus-transition` are the single source of\n * truth for \"highlight ∩ search, hover on top\" and are reused verbatim; this\n * module only maps the resulting {@link FocusSnapshot} onto opacity numbers,\n * mirroring the tier rules the 2D canvas renderer applies\n * (`graph/canvas-renderer.ts` `nodeTier` / `edgeTier` / `*TierAlpha`).\n *\n * The one 3D-specific input is `edgeBaseAlpha`: above `LINK_WIDTH_MAX_EDGES`\n * the renderer draws plain lines and maps `edgeWidth` to a resting alpha\n * instead of a cylinder radius, so the resting tier is per-edge rather than a\n * single constant.\n *\n * Pure: no three.js, no DOM.\n */\n\nimport type { FocusSnapshot } from '../graph/focus-transition';\n\n/** Resting edge alpha when nothing is emphasized (matches the 2D light tier). */\nexport const EDGE_ALPHA_DEFAULT = 0.3;\n/** Edge alpha when both endpoints are in the connected set. */\nexport const EDGE_ALPHA_CONNECTED = 1;\n/** Multiplier applied to nodes/edges outside an active search. */\nexport const SEARCH_NON_MATCH_ALPHA = 0.25;\n\n/** Minimal node shape the composition needs. */\nexport interface EmphasisNode {\n id: string;\n /** Compiled `nodeOpacity` encoding value; multiplies into the result. */\n opacity: number;\n}\n\n/** Minimal edge shape the composition needs. Index is the link object key. */\nexport interface EmphasisEdge {\n source: string;\n target: string;\n}\n\nexport interface EmphasisInput {\n nodes: EmphasisNode[];\n edges: EmphasisEdge[];\n /** The composed focus snapshot (standing state with the hover layer on top). */\n focus: FocusSnapshot;\n /** Ids that never dim (the spec's `seedNode`). */\n exemptIds: Set<string>;\n /** Resolved `interaction.hover.dimOpacity`. */\n dimOpacity: number;\n /** Per-edge resting alpha keyed by edge index; defaults to EDGE_ALPHA_DEFAULT. */\n edgeBaseAlpha?: Map<number, number>;\n}\n\nexport interface EmphasisTargets {\n /** Target material opacity by node id. */\n nodes: Map<string, number>;\n /** Target material opacity by edge index. */\n edges: Map<number, number>;\n}\n\n/**\n * Compose the focus snapshot into target opacities.\n *\n * Node rule (mirrors `nodeTier` + `nodeTierAlpha`): nothing active → 1; an\n * exempt id → 1; in the connected set → 1; otherwise `dimOpacity`. An active\n * search multiplies non-matching nodes by {@link SEARCH_NON_MATCH_ALPHA}, and\n * exemption deliberately does NOT apply to search (a seed that doesn't match\n * the query should not pretend to). The compiled `nodeOpacity` multiplies last.\n *\n * Edge rule (mirrors `edgeTier` + `edgeTierAlpha`): nothing active → the edge's\n * resting alpha; both endpoints connected → 1; otherwise `dimOpacity / 3`,\n * which preserves the node-to-edge dim ratio that keeps dense hairballs quiet.\n * Edges are NOT exempted by `exemptIds`: the seed stays lit while its edges dim\n * with everything else.\n */\nexport function resolveEmphasis(input: EmphasisInput): EmphasisTargets {\n const { focus, exemptIds, dimOpacity, edgeBaseAlpha } = input;\n const search = focus.searchMatches;\n\n const nodes = new Map<string, number>();\n for (const n of input.nodes) {\n let alpha = 1;\n if (focus.hasActive && !exemptIds.has(n.id) && !focus.connected.has(n.id)) {\n alpha = dimOpacity;\n }\n if (search !== null && !search.has(n.id)) alpha *= SEARCH_NON_MATCH_ALPHA;\n nodes.set(n.id, alpha * n.opacity);\n }\n\n const edges = new Map<number, number>();\n for (let i = 0; i < input.edges.length; i++) {\n const e = input.edges[i];\n const resting = edgeBaseAlpha?.get(i) ?? EDGE_ALPHA_DEFAULT;\n let alpha: number;\n if (!focus.hasActive) {\n alpha = resting;\n } else if (focus.connected.has(e.source) && focus.connected.has(e.target)) {\n alpha = EDGE_ALPHA_CONNECTED;\n } else {\n alpha = dimOpacity / 3;\n }\n if (search !== null && !search.has(e.source) && !search.has(e.target)) {\n alpha *= SEARCH_NON_MATCH_ALPHA;\n }\n edges.set(i, alpha);\n }\n\n return { nodes, edges };\n}\n","/**\n * Entrance choreography timing for the 3D renderer.\n *\n * Pure scheduling math: which node starts revealing when, and how far along its\n * own pop a node is at a given wall-clock offset. The mount owns the tween, the\n * materials and the camera; this module owns nothing but numbers, so the\n * stagger can be tested without a WebGL scene.\n *\n * The stagger rank comes from 2D's `entranceOrder` (a deterministic hash\n * shuffle of the node ids), so a spec reveals in the same order in both\n * dimensions. 2D's companion `entranceOffsets` deliberately does NOT port:\n * three-forcegraph writes each node group's position from its datum on every\n * engine tick, so a convergence drift we wrote into `group.position` would be\n * clobbered before it was ever painted. 3D pops in scale and opacity only.\n */\n\nimport { entranceOrder } from '../graph/entrance';\n\n/**\n * How much of `enter.duration` the stagger spreads start times across. The last\n * node therefore begins at `0.5 · duration` and finishes at `1.5 · duration`.\n * Wider reads as a slow drip on a hundred-node graph; narrower stops reading as\n * a stagger at all.\n */\nexport const ENTRANCE_STAGGER_FRACTION = 0.5;\n\n/**\n * How long after the later of its two endpoints a link starts fading in, as a\n * fraction of `enter.duration`. An edge that arrives with its endpoints reads\n * as a mesh snapping into place; a beat later reads as the structure being\n * drawn on.\n */\nexport const LINK_ENTRANCE_DELAY = 0.25;\n\n/** The camera's starting standoff, as a multiple of the fit distance. */\nexport const ENTRANCE_CAMERA_PULLBACK = 1.6;\n\n/** Start times for every node and link, plus the total wall time they span. */\nexport interface EntrancePlan {\n /** ms from entrance start at which each node begins its pop. */\n nodeOffset: Map<string, number>;\n /** ms from entrance start at which each link begins its fade, by edge index. */\n linkOffset: Map<number, number>;\n /** Total choreography length: `duration + max(offset)`. */\n span: number;\n}\n\n/**\n * Plan the reveal.\n *\n * With `stagger` off every offset is 0 and the whole graph fades as one, which\n * is also what happens above `ENTRANCE_STAGGER_MAX_NODES` (the caller applies\n * that gate, mirroring 2D).\n */\nexport function planEntrance(\n nodes: Array<{ id: string; x: number; y: number }>,\n edges: Array<{ source: string; target: string }>,\n duration: number,\n stagger: boolean,\n): EntrancePlan {\n const nodeOffset = new Map<string, number>();\n const linkOffset = new Map<number, number>();\n\n if (!stagger || nodes.length === 0) {\n for (const n of nodes) nodeOffset.set(n.id, 0);\n for (let i = 0; i < edges.length; i++) linkOffset.set(i, duration * LINK_ENTRANCE_DELAY);\n return { nodeOffset, linkOffset, span: duration * (1 + LINK_ENTRANCE_DELAY) };\n }\n\n const rank = entranceOrder(nodes);\n const last = Math.max(1, nodes.length - 1);\n const spread = duration * ENTRANCE_STAGGER_FRACTION;\n let maxOffset = 0;\n for (const n of nodes) {\n const offset = ((rank.get(n.id) ?? 0) / last) * spread;\n nodeOffset.set(n.id, offset);\n if (offset > maxOffset) maxOffset = offset;\n }\n\n // A link waits for the later of its endpoints, then a beat.\n const delay = duration * LINK_ENTRANCE_DELAY;\n for (let i = 0; i < edges.length; i++) {\n const a = nodeOffset.get(edges[i].source) ?? 0;\n const b = nodeOffset.get(edges[i].target) ?? 0;\n const offset = Math.max(a, b) + delay;\n linkOffset.set(i, offset);\n if (offset > maxOffset) maxOffset = offset;\n }\n\n return { nodeOffset, linkOffset, span: duration + maxOffset };\n}\n\n/**\n * A single element's eased progress through its own window at `elapsed` ms.\n *\n * Returns 0 before the element's start and 1 once its `duration` has run, so a\n * caller can read every element from one global clock.\n */\nexport function elementProgress(\n elapsed: number,\n offset: number,\n duration: number,\n ease: (t: number) => number,\n): number {\n if (duration <= 0) return elapsed >= offset ? 1 : 0;\n const local = (elapsed - offset) / duration;\n if (local <= 0) return 0;\n if (local >= 1) return 1;\n return ease(local);\n}\n","/**\n * Camera framing math for the 3D renderer.\n *\n * Split out of `mount.ts` because it is pure vector arithmetic over positions\n * the mount already has: no three.js, no library instance, no DOM. The mount\n * reads the camera and hands the numbers in.\n */\n\n/** A point or direction in scene space. */\nexport interface Vec3 {\n x: number;\n y: number;\n z: number;\n}\n\n/** A node as the fit sees it: a position and the radius to keep in frame. */\nexport interface FitPoint {\n x: number;\n y: number;\n z: number;\n radius: number;\n}\n\n/** Where the camera should sit, and what it should look at. */\nexport interface FitResult {\n center: Vec3;\n /** Unit vector from `center` towards the camera. */\n dir: Vec3;\n distance: number;\n}\n\n/** Inputs the mount reads off the live camera and shell. */\nexport interface FitView {\n cameraPos: Vec3;\n /** Vertical field of view, in degrees. */\n fovDeg: number;\n /** Viewport width / height. */\n aspect: number;\n /** Viewport height in pixels, used to turn pixel padding into a ratio. */\n viewportHeight: number;\n /** Breathing room around the cloud, in pixels of viewport height. */\n paddingPx: number;\n}\n\n/** Floor on the fit distance so a single-node graph does not sit on the camera. */\nconst MIN_FIT_DISTANCE = 60;\n\nexport function normalize(v: Vec3): Vec3 {\n const len = Math.hypot(v.x, v.y, v.z);\n // A degenerate direction (camera sitting on its target) falls back to +z,\n // the library's own default framing axis.\n if (!(len > 1e-6)) return { x: 0, y: 0, z: 1 };\n return { x: v.x / len, y: v.y / len, z: v.z / len };\n}\n\nexport function cross(a: Vec3, b: Vec3): Vec3 {\n return {\n x: a.y * b.z - a.z * b.y,\n y: a.z * b.x - a.x * b.z,\n z: a.x * b.y - a.y * b.x,\n };\n}\n\n/**\n * Camera placement that frames every point from the current viewing angle.\n *\n * Deliberately not `graph.zoomToFit()`: three-render-objects computes its\n * distance as `maxBoxSide / Math.atan(fov)` where the geometry calls for\n * `maxBoxSide / (2 * Math.tan(fov / 2))`, so it lands the camera about twice as\n * far out as it needs to be, and it frames the bbox as a cube centred on the\n * world origin, so depth inflates the height and an off-centre layout drifts\n * out of frame. This measures the cloud in the camera's own basis instead: how\n * wide and tall it actually appears, plus how much of it sits in front.\n *\n * Returns null for an empty or non-finite cloud.\n */\nexport function computeFit(points: FitPoint[], view: FitView): FitResult | null {\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let minZ = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n let maxZ = Number.NEGATIVE_INFINITY;\n for (const p of points) {\n if (p.x < minX) minX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.z < minZ) minZ = p.z;\n if (p.x > maxX) maxX = p.x;\n if (p.y > maxY) maxY = p.y;\n if (p.z > maxZ) maxZ = p.z;\n }\n // All six extents, not just minX: an empty cloud leaves every one at its\n // sentinel, but a single NaN or Infinity coordinate poisons only the extents\n // it touches, and any one of them would carry through into center/distance.\n if (\n !Number.isFinite(minX) ||\n !Number.isFinite(minY) ||\n !Number.isFinite(minZ) ||\n !Number.isFinite(maxX) ||\n !Number.isFinite(maxY) ||\n !Number.isFinite(maxZ)\n ) {\n return null;\n }\n\n const center = { x: (minX + maxX) / 2, y: (minY + maxY) / 2, z: (minZ + maxZ) / 2 };\n // Keep the viewing angle the viewer already chose; only the distance moves.\n const dir = normalize({\n x: view.cameraPos.x - center.x,\n y: view.cameraPos.y - center.y,\n z: view.cameraPos.z - center.z,\n });\n // Screen right/up for that direction. World +y is the camera's up axis unless\n // the view is looking straight down it, where +z takes over.\n const worldUp = Math.abs(dir.y) > 0.999 ? { x: 0, y: 0, z: 1 } : { x: 0, y: 1, z: 0 };\n const right = normalize(cross(worldUp, dir));\n const up = cross(dir, right);\n\n let halfRight = 0;\n let halfUp = 0;\n let nearDepth = 0;\n for (const p of points) {\n const dx = p.x - center.x;\n const dy = p.y - center.y;\n const dz = p.z - center.z;\n halfRight = Math.max(\n halfRight,\n Math.abs(dx * right.x + dy * right.y + dz * right.z) + p.radius,\n );\n halfUp = Math.max(halfUp, Math.abs(dx * up.x + dy * up.y + dz * up.z) + p.radius);\n // Only depth towards the camera pushes it back; the far side is free.\n nearDepth = Math.max(nearDepth, dx * dir.x + dy * dir.y + dz * dir.z + p.radius);\n }\n\n const vFov = (view.fovDeg * Math.PI) / 180;\n const hFov = 2 * Math.atan(Math.tan(vFov / 2) * view.aspect);\n // Padding is quoted in pixels; express it as a share of the viewport so the\n // margin holds at any canvas size.\n const slack = 1 + Math.min(0.5, (2 * view.paddingPx) / (view.viewportHeight || 1));\n const distance =\n Math.max(\n (halfUp * slack) / Math.tan(vFov / 2),\n (halfRight * slack) / Math.tan(hFov / 2),\n MIN_FIT_DISTANCE,\n ) + Math.max(0, nearDepth);\n return { center, dir, distance };\n}\n","/**\n * Force wiring for the 3D simulation.\n *\n * 3d-force-graph owns a d3-force-3d simulation on the main thread; we do not\n * drive it and we do not port the 2D web worker here. This module maps the\n * engine's `SimulationConfig` onto that simulation's knobs and supplies the one\n * force d3-force-3d does not ship: the community cluster force, which is a\n * 3-axis port of `forceCluster` in `../graph/simulation.ts`. Both copies must\n * stay in sync.\n */\n\nimport type { SimulationConfig } from '@opendata-ai/openchart-engine';\nimport { forceCenter, forceCollide } from 'd3-force-3d';\nimport type { Graph3D, Node3D } from './types';\n\n/** d3's default `alphaMin`, the alpha at which a simulation stops. */\nconst DEFAULT_ALPHA_MIN = 0.001;\n/** Ceiling on the derived cooldown tick count (guards a tiny alphaDecay). */\nconst MAX_COOLDOWN_TICKS = 800;\n/** Collision solver iterations; 1 is d3's cheap default and enough at our scale. */\nconst COLLIDE_ITERATIONS = 1;\n\n/**\n * Measured cost of one simulation tick, per node, in ms (charge + link +\n * collide + cluster on a 3,000-node graph). Used to convert the engine's\n * wall-clock warmup budget into a tick count.\n */\nconst WARMUP_MS_PER_NODE_TICK = 0.0035;\n\n/** Never warm up for fewer ticks than this; below it the first frame explodes. */\nconst MIN_WARMUP_TICKS = 5;\n\n/** Fallback budget when the compilation does not carry one. */\nconst DEFAULT_WARMUP_BUDGET_MS = 250;\n\n/**\n * Warmup ticks that fit the wall-clock budget.\n *\n * The library runs `warmupTicks` synchronously inside its first digest, so the\n * engine's default of 100 blocks the main thread for ~3s on a 3,000-node graph.\n * 2D solves this with `warmupBudgetMs` inside its own tick loop; here the loop\n * belongs to the library, so the budget has to be converted to a tick count up\n * front from the measured per-node cost.\n */\nexport function budgetedWarmupTicks(\n requested: number,\n nodeCount: number,\n budgetMs = DEFAULT_WARMUP_BUDGET_MS,\n): number {\n if (requested <= 0 || nodeCount <= 0) return Math.max(0, requested);\n const perTickMs = Math.max(WARMUP_MS_PER_NODE_TICK * nodeCount, 0.05);\n const affordable = Math.floor(budgetMs / perTickMs);\n return Math.max(MIN_WARMUP_TICKS, Math.min(requested, affordable));\n}\n\n/**\n * Ticks needed to cool from alpha 1 to `alphaMin` at a given `alphaDecay`.\n * Deliberately duplicated from `graph/simulation.ts` `ticksToAlphaMin` rather\n * than imported: that module pulls in the whole 2D d3-force graph, which would\n * land in the `graph-3d` bundle for four lines of arithmetic.\n */\nexport function ticksToSettle(alphaDecay: number, alphaMin = DEFAULT_ALPHA_MIN): number {\n if (!(alphaDecay > 0) || alphaDecay >= 1) return MAX_COOLDOWN_TICKS;\n const n = Math.ceil(Math.log(alphaMin) / Math.log(1 - alphaDecay));\n return Math.min(MAX_COOLDOWN_TICKS, Math.max(1, n));\n}\n\n/**\n * A community cluster force in three axes: each node is pulled toward the\n * centroid of its own community, scaled by `strength * alpha`. Port of\n * `forceCluster` in `../graph/simulation.ts` with the z axis added.\n *\n * Uses d3's `initialize` hook rather than closing over a node array, so the\n * force keeps working after `graphData()` swaps the node objects.\n */\nexport function forceCluster3D(strength: number): (alpha: number) => void {\n let nodes: Node3D[] = [];\n\n const force = (alpha: number): void => {\n const cx = new Map<string, number>();\n const cy = new Map<string, number>();\n const cz = new Map<string, number>();\n const count = new Map<string, number>();\n\n for (const node of nodes) {\n const c = node.node?.community;\n if (!c) continue;\n cx.set(c, (cx.get(c) ?? 0) + (node.x ?? 0));\n cy.set(c, (cy.get(c) ?? 0) + (node.y ?? 0));\n cz.set(c, (cz.get(c) ?? 0) + (node.z ?? 0));\n count.set(c, (count.get(c) ?? 0) + 1);\n }\n\n for (const [c, n] of count) {\n cx.set(c, cx.get(c)! / n);\n cy.set(c, cy.get(c)! / n);\n cz.set(c, cz.get(c)! / n);\n }\n\n const k = strength * alpha;\n for (const node of nodes) {\n const c = node.node?.community;\n if (!c) continue;\n node.vx = (node.vx ?? 0) + (cx.get(c)! - (node.x ?? 0)) * k;\n node.vy = (node.vy ?? 0) + (cy.get(c)! - (node.y ?? 0)) * k;\n node.vz = (node.vz ?? 0) + (cz.get(c)! - (node.z ?? 0)) * k;\n }\n };\n\n force.initialize = (ns: Node3D[]): void => {\n nodes = ns;\n };\n\n return force;\n}\n\n/**\n * Apply the compiled `SimulationConfig` to a 3d-force-graph instance.\n *\n * `cooldownTicks` is derived from `alphaDecay` so live ticking is bounded: the\n * simulation runs on the main thread here, and an unbounded cooldown keeps a\n * 3000-node graph re-rendering forever. `warmupTicks` gets the same treatment\n * through {@link budgetedWarmupTicks}, since the library runs them all in one\n * synchronous block.\n */\nexport function applySimulationConfig(\n graph: Graph3D,\n config: SimulationConfig,\n nodeCount: number,\n): void {\n const charge = graph.d3Force('charge');\n if (charge && typeof (charge as { strength?: unknown }).strength === 'function') {\n (charge as unknown as { strength(v: number): unknown }).strength(config.chargeStrength);\n }\n\n const link = graph.d3Force('link');\n if (link) {\n const l = link as unknown as {\n distance?(v: number): unknown;\n strength?(v: number): unknown;\n };\n l.distance?.(config.linkDistance);\n if (config.linkStrength !== undefined) l.strength?.(config.linkStrength);\n }\n\n const padding = config.collisionPadding ?? 2;\n graph.d3Force(\n 'collide',\n forceCollide<Node3D>((n) => (n.node?.radius ?? 1) + padding).iterations(COLLIDE_ITERATIONS),\n );\n\n graph.d3Force('cluster', config.clustering ? forceCluster3D(config.clustering.strength) : null);\n\n // 3d-force-graph installs a `center` force of its own, and only in its\n // constructor, so honouring `centerForce: false` means removing it -- and a\n // later update that turns centering back on has to install a replacement,\n // because the library will never do it again.\n graph.d3Force(\n 'center',\n config.centerForce === false ? null : (graph.d3Force('center') ?? forceCenter()),\n );\n\n graph.d3VelocityDecay(config.velocityDecay);\n graph.d3AlphaDecay(config.alphaDecay);\n graph.warmupTicks(budgetedWarmupTicks(config.warmupTicks ?? 0, nodeCount, config.warmupBudgetMs));\n graph.cooldownTicks(ticksToSettle(config.alphaDecay));\n}\n","/**\n * Label visibility policy for the 3D renderer.\n *\n * Same two-stage shape as 2D: rank, then declutter. This module is the ranking\n * half — forced labels (seed / `alwaysShowLabel` overrides, hovered, selected,\n * search matches) first, then the highest-priority nodes, nearest camera first,\n * up to the budget. The mount does the screen-space overlap pass on the ordered\n * result, which is why the order matters and not just the membership.\n *\n * Pure: no three.js, no DOM. The mount feeds it plain positions.\n */\n\n/** A label candidate: a node with a resolved label and a compiled priority. */\nexport interface LabelCandidate {\n id: string;\n /** Compiled `labelPriority` (0..1, degree-derived). Higher wins. */\n priority: number;\n x: number;\n y: number;\n z: number;\n}\n\n/** Default 3D label budget beyond the forced set. */\nexport const LABEL_BUDGET_3D = 40;\n\n/**\n * Node ids whose label sprite may be shown, best first.\n *\n * `forced` ids lead the list and do NOT consume budget. The rest are ranked by\n * `(priority desc, camera distance asc)` and the top `budget` follow.\n */\nexport function resolveVisibleLabels(\n nodes: LabelCandidate[],\n forced: Set<string>,\n cameraPos: { x: number; y: number; z: number },\n budget: number,\n): string[] {\n const visible: string[] = [];\n for (const n of nodes) if (forced.has(n.id)) visible.push(n.id);\n if (budget <= 0) return visible;\n\n const ranked: Array<{ id: string; priority: number; dist: number }> = [];\n for (const n of nodes) {\n if (forced.has(n.id)) continue;\n const dx = n.x - cameraPos.x;\n const dy = n.y - cameraPos.y;\n const dz = n.z - cameraPos.z;\n ranked.push({ id: n.id, priority: n.priority, dist: dx * dx + dy * dy + dz * dz });\n }\n\n // Priority first (the compiled importance ordering), distance as the\n // tiebreaker, id last so the result is stable for equal-priority nodes at\n // equal distance (the seeded-layout stories rely on a deterministic set).\n ranked.sort((a, b) => b.priority - a.priority || a.dist - b.dist || (a.id < b.id ? -1 : 1));\n\n for (let i = 0; i < ranked.length && i < budget; i++) visible.push(ranked[i].id);\n return visible;\n}\n\n/** A label's screen-space footprint, in pixels. */\nexport interface LabelBox {\n x0: number;\n x1: number;\n y0: number;\n y1: number;\n}\n\n/** Breathing room around a label box in the declutter pass, in pixels. */\nconst LABEL_BOX_PAD = 2;\n\n/**\n * The screen box a label would occupy, or null when the point did not project.\n *\n * Width is estimated from the glyph count rather than measured: a sprite has no\n * `measureText`, and 2D's own fallback in `graph/canvas-renderer.ts` uses the\n * same 0.55 average-glyph ratio.\n */\nexport function labelBox(\n projected: { x: number; y: number } | null | undefined,\n text: string,\n heightPx: number,\n): LabelBox | null {\n if (!projected || !Number.isFinite(projected.x) || !Number.isFinite(projected.y)) return null;\n const width = Math.max(1, text.length) * heightPx * 0.55;\n // The sprite sits above the node; the exact offset only has to be consistent,\n // since every box uses it.\n const cy = projected.y - heightPx;\n return {\n x0: projected.x - width / 2 - LABEL_BOX_PAD,\n x1: projected.x + width / 2 + LABEL_BOX_PAD,\n y0: cy - heightPx / 2 - LABEL_BOX_PAD,\n y1: cy + heightPx / 2 + LABEL_BOX_PAD,\n };\n}\n\nexport function overlaps(a: LabelBox, b: LabelBox): boolean {\n return a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0;\n}\n","/**\n * Per-link three.js objects.\n *\n * Same reason as `nodes.ts`: `linkOpacity` is a single global number in\n * 3d-force-graph, so per-edge hover dimming needs each link to own its\n * material. (An rgba `linkColor` IS honoured for per-link opacity — see\n * `../graph-3d/README` notes in the RFC report — but changing it only takes\n * effect on a `refresh()`, which rebuilds every link object. That is exactly\n * what hover must not do.)\n *\n * Two shapes: a `Line` (constant screen width, cheap) and a cylinder `Mesh`\n * (true world-space width, needed when `edgeWidth` is encoded). Above\n * `LINK_WIDTH_MAX_EDGES` the mount stays on lines and maps `edgeWidth` to a\n * resting alpha instead.\n *\n * Positioning: three-forcegraph's own tick loop positions custom `Line` and\n * `Mesh` link objects generically (it branches on `obj.type` and, for meshes,\n * on the geometry already being a `CylinderGeometry`). The mount still installs\n * a `linkPositionUpdate` because dashed lines need `computeLineDistances()`\n * after every move, which the library's path does not call.\n */\n\nimport type { CompiledGraphEdge } from '@opendata-ai/openchart-engine';\nimport {\n BufferAttribute,\n BufferGeometry,\n CylinderGeometry,\n Line,\n LineBasicMaterial,\n LineDashedMaterial,\n Matrix4,\n Mesh,\n MeshLambertMaterial,\n} from 'three';\n\n/** Above this edge count `edgeWidth` maps to opacity steps, not cylinder radius. */\nexport const LINK_WIDTH_MAX_EDGES = 2000;\n/** Cylinder tessellation for width-encoded edges. */\nconst CYLINDER_SEGMENTS = 6;\n/** Dash geometry in world units for `edgeStyle: 'dashed' | 'dotted'`. */\nconst DASH = { dashed: { dash: 6, gap: 4 }, dotted: { dash: 1.5, gap: 3 } };\n\n/** A link's scene object plus the resources the mount must dispose. */\nexport interface LinkObject3D {\n object: Line | Mesh;\n geometry: BufferGeometry | CylinderGeometry;\n material: LineBasicMaterial | LineDashedMaterial | MeshLambertMaterial;\n /** True when the material needs `computeLineDistances()` after every move. */\n dashed: boolean;\n}\n\n/** True when `edge` asks for a dashed or dotted line material. */\nfunction isDashed(edge: CompiledGraphEdge): boolean {\n return edge.style === 'dashed' || edge.style === 'dotted';\n}\n\n/** Cylinder radius for a width-encoded edge. */\nfunction cylinderRadius(edge: CompiledGraphEdge): number {\n return Math.max(edge.strokeWidth, 0.1) / 2;\n}\n\nfunction buildCylinder(edge: CompiledGraphEdge): CylinderGeometry {\n const r = cylinderRadius(edge);\n const geometry = new CylinderGeometry(r, r, 1, CYLINDER_SEGMENTS, 1, false);\n // Match the library's cylinder convention: origin at the start point,\n // length along +z, so its tick loop can position with scale.z + lookAt.\n geometry.applyMatrix4(new Matrix4().makeTranslation(0, 0.5, 0));\n geometry.applyMatrix4(new Matrix4().makeRotationX(Math.PI / 2));\n return geometry;\n}\n\nfunction buildLineMaterial(\n edge: CompiledGraphEdge,\n opacity: number,\n): LineBasicMaterial | LineDashedMaterial {\n if (!isDashed(edge)) {\n return new LineBasicMaterial({\n color: edge.stroke,\n transparent: true,\n opacity,\n depthWrite: false,\n });\n }\n const dash = DASH[edge.style as 'dashed' | 'dotted'];\n return new LineDashedMaterial({\n color: edge.stroke,\n transparent: true,\n opacity,\n depthWrite: false,\n dashSize: dash.dash,\n gapSize: dash.gap,\n });\n}\n\n/**\n * Build the scene object for one compiled edge.\n *\n * @param useWidth - Render a cylinder of `edge.strokeWidth` instead of a line.\n * @param restingAlpha - Starting material opacity (the emphasis pass retargets it).\n */\nexport function createLinkObject(\n edge: CompiledGraphEdge,\n useWidth: boolean,\n restingAlpha: number,\n): LinkObject3D {\n if (useWidth) {\n const geometry = buildCylinder(edge);\n const material = new MeshLambertMaterial({\n color: edge.stroke,\n transparent: true,\n opacity: restingAlpha,\n depthWrite: false,\n });\n return { object: new Mesh(geometry, material), geometry, material, dashed: false };\n }\n\n const geometry = new BufferGeometry();\n geometry.setAttribute('position', new BufferAttribute(new Float32Array(2 * 3), 3));\n const material = buildLineMaterial(edge, restingAlpha);\n return { object: new Line(geometry, material), geometry, material, dashed: isDashed(edge) };\n}\n\n/**\n * Re-apply compiled visuals to an existing link object, in place.\n *\n * The visual-only update path must not touch `graphData()`, and it cannot swap\n * the scene object either: the library binds objects to datums by identity, so\n * a replacement object would never reach the scene. Geometry and material CAN\n * be swapped under the same object, which is what a changed cylinder radius or\n * a changed dash pattern needs; the old resource is disposed here.\n *\n * The shape class (line vs cylinder) is the one thing that cannot change in\n * place — see {@link linkShapeMatches}.\n */\nexport function applyLinkVisuals(\n obj: LinkObject3D,\n edge: CompiledGraphEdge,\n useWidth: boolean,\n): void {\n obj.material.color.set(edge.stroke);\n\n if (useWidth) {\n const r = cylinderRadius(edge);\n if ((obj.geometry as CylinderGeometry).parameters.radiusTop === r) return;\n const next = buildCylinder(edge);\n (obj.object as Mesh).geometry = next;\n obj.geometry.dispose();\n obj.geometry = next;\n return;\n }\n\n const dashed = isDashed(edge);\n const current = obj.material as LineDashedMaterial;\n const dash = dashed ? DASH[edge.style as 'dashed' | 'dotted'] : null;\n const sameDash =\n dashed === obj.dashed &&\n (dash === null || (current.dashSize === dash.dash && current.gapSize === dash.gap));\n if (sameDash) return;\n\n // Carry the live opacity across so a style change mid-hover doesn't flash;\n // the emphasis pass retargets it on the next paint anyway.\n const next = buildLineMaterial(edge, obj.material.opacity);\n (obj.object as Line).material = next;\n obj.material.dispose();\n obj.material = next;\n obj.dashed = dashed;\n if (dashed) (obj.object as Line).computeLineDistances();\n}\n\n/**\n * Whether an existing link object still has the right shape class for the\n * current `useLinkWidth` setting. A mismatch means the object has to be rebuilt\n * through `graphData()`, because the scene binding is by object identity.\n */\nexport function linkShapeMatches(obj: LinkObject3D, useWidth: boolean): boolean {\n return useWidth ? obj.object instanceof Mesh : obj.object instanceof Line;\n}\n\n/**\n * Write a straight segment into a line link's position buffer.\n *\n * Returns true when it handled the object (the mount reports that back to\n * `linkPositionUpdate` so the library skips its own pass); false for cylinder\n * meshes, which the library positions correctly on its own.\n */\nexport function updateLinkPosition(\n obj: LinkObject3D,\n start: { x: number; y: number; z: number },\n end: { x: number; y: number; z: number },\n): boolean {\n if (!(obj.object instanceof Line)) return false;\n const attr = obj.geometry.getAttribute('position') as BufferAttribute;\n const a = attr.array as Float32Array;\n a[0] = start.x;\n a[1] = start.y || 0;\n a[2] = start.z || 0;\n a[3] = end.x;\n a[4] = end.y || 0;\n a[5] = end.z || 0;\n attr.needsUpdate = true;\n obj.geometry.computeBoundingSphere();\n // A dashed material renders nothing until line distances exist, and they go\n // stale on every tick because the endpoints move.\n if (obj.dashed) (obj.object as Line).computeLineDistances();\n return true;\n}\n\n/** Dispose every GPU resource this link object owns. */\nexport function disposeLinkObject(obj: LinkObject3D): void {\n obj.geometry.dispose();\n obj.material.dispose();\n}\n\n/**\n * Resting alpha per edge when `edgeWidth` is encoded but the graph is too big\n * for cylinders: map the compiled stroke width onto four opacity steps between\n * `min` and 1. Quantized so a hairball reads as tiers rather than noise.\n */\nexport function widthAsAlpha(edges: CompiledGraphEdge[], min = 0.15): Map<number, number> {\n const out = new Map<number, number>();\n if (edges.length === 0) return out;\n let lo = Number.POSITIVE_INFINITY;\n let hi = Number.NEGATIVE_INFINITY;\n for (const e of edges) {\n if (e.strokeWidth < lo) lo = e.strokeWidth;\n if (e.strokeWidth > hi) hi = e.strokeWidth;\n }\n const span = hi - lo;\n for (let i = 0; i < edges.length; i++) {\n const t = span > 0 ? (edges[i].strokeWidth - lo) / span : 1;\n const step = Math.round(t * 3) / 3;\n out.set(i, min + (1 - min) * step);\n }\n return out;\n}\n","/**\n * Per-node three.js objects.\n *\n * 3d-force-graph's built-in node rendering is a shared sphere material cached\n * by color string, and `nodeOpacity` is a single global number. Highlight, dim,\n * hover and entrance all need PER-NODE opacity, so every node gets its own\n * `MeshLambertMaterial` here. That is also the only way to get always-on labels:\n * `nodeLabel` is hover-only in the library.\n *\n * Everything allocated in this module is owned by the mount and disposed in\n * `destroy()` — three.js geometries, materials and canvas-backed sprite\n * textures are all outside the GC's reach.\n */\n\nimport type { CompiledGraphNode } from '@opendata-ai/openchart-engine';\nimport { Group, Mesh, MeshLambertMaterial, SphereGeometry } from 'three';\nimport SpriteText from 'three-spritetext';\n\n/** Sphere tessellation. 12 is the library's default and reads smooth at our radii. */\nconst SPHERE_SEGMENTS = 12;\n/** Sprite cap height in world units, before the screen-size correction. */\nexport const LABEL_TEXT_HEIGHT = 4;\n/** Gap between the sphere surface and the label baseline, in world units. */\nconst LABEL_GAP = 2;\n\n/** A node's scene object plus the resources the mount must dispose. */\nexport interface NodeObject3D {\n group: Group;\n mesh: Mesh;\n geometry: SphereGeometry;\n material: MeshLambertMaterial;\n /** Null until {@link ensureLabelSprite} builds it, and for unlabelled nodes. */\n sprite: SpriteText | null;\n /** Compiled label text, or null when the node has none. */\n labelText: string | null;\n /** Resolved label colour, applied when the sprite is built. */\n labelColor: string;\n /** Sprite offset above the node centre, in world units. */\n labelOffset: number;\n /**\n * The sprite's scale at {@link LABEL_TEXT_HEIGHT}, captured because\n * `three-spritetext` only writes `scale` when it regenerates its texture, so\n * the perspective correction in the mount can multiply it every re-rank\n * without paying for a canvas redraw.\n */\n labelBaseScale: { x: number; y: number } | null;\n}\n\n/**\n * Build the scene object for one compiled node: a `Group` holding a sphere\n * `Mesh`. The label sprite is NOT built here — see {@link ensureLabelSprite}.\n */\nexport function createNodeObject(node: CompiledGraphNode, labelColor: string): NodeObject3D {\n const geometry = new SphereGeometry(node.radius, SPHERE_SEGMENTS, SPHERE_SEGMENTS);\n const material = new MeshLambertMaterial({\n color: node.fill,\n transparent: true,\n opacity: node.opacity,\n });\n const mesh = new Mesh(geometry, material);\n\n const group = new Group();\n group.add(mesh);\n\n return {\n group,\n mesh,\n geometry,\n material,\n sprite: null,\n labelBaseScale: null,\n labelText: node.label ?? null,\n labelColor,\n labelOffset: node.radius + LABEL_GAP,\n };\n}\n\n/**\n * Build this node's label sprite on first use, or return the existing one.\n *\n * Sprites are created lazily because they are not free to keep around: a\n * 3,000-node graph that eagerly built one sprite per node ran at 22fps against\n * 32fps with no sprites at all, even though at most 40 are ever visible — three\n * still walks every one of them per frame. Only the labels that win a slot in\n * the declutter pass ever exist.\n *\n * The sprite's `raycast` is stubbed to a no-op so a label never intercepts a\n * hover or click meant for the node behind it: the library raycasts the whole\n * subtree of the node object.\n */\nexport function ensureLabelSprite(obj: NodeObject3D): SpriteText | null {\n if (obj.sprite) return obj.sprite;\n if (!obj.labelText) return null;\n const sprite = new SpriteText(obj.labelText, LABEL_TEXT_HEIGHT, obj.labelColor);\n sprite.position.y = obj.labelOffset;\n sprite.raycast = () => {};\n sprite.visible = false;\n obj.group.add(sprite);\n obj.sprite = sprite;\n obj.labelBaseScale = { x: sprite.scale.x, y: sprite.scale.y };\n return sprite;\n}\n\n/**\n * Re-apply compiled visuals to an existing node object. Used by the\n * visual-only update path, which must not touch `graphData()` (that would\n * restart the layout).\n */\nexport function applyNodeVisuals(\n obj: NodeObject3D,\n node: CompiledGraphNode,\n labelColor: string,\n): void {\n obj.material.color.set(node.fill);\n if (obj.geometry.parameters.radius !== node.radius) {\n const next = new SphereGeometry(node.radius, SPHERE_SEGMENTS, SPHERE_SEGMENTS);\n obj.mesh.geometry = next;\n obj.geometry.dispose();\n obj.geometry = next;\n }\n obj.labelText = node.label ?? null;\n obj.labelColor = labelColor;\n obj.labelOffset = node.radius + LABEL_GAP;\n if (obj.sprite) {\n const regenerates =\n (node.label && obj.sprite.text !== node.label) || obj.sprite.color !== labelColor;\n if (node.label && obj.sprite.text !== node.label) obj.sprite.text = node.label;\n obj.sprite.color = labelColor;\n obj.sprite.position.y = obj.labelOffset;\n // A regenerated texture resets `scale`, so the captured base has to follow.\n if (regenerates) obj.labelBaseScale = { x: obj.sprite.scale.x, y: obj.sprite.scale.y };\n }\n}\n\n/** Dispose every GPU resource this node object owns. */\nexport function disposeNodeObject(obj: NodeObject3D): void {\n obj.geometry.dispose();\n obj.material.dispose();\n if (obj.sprite) {\n // SpriteText paints into a canvas-backed texture; disposing the material\n // alone leaks that texture.\n const mat = obj.sprite.material as { map?: { dispose(): void }; dispose(): void };\n mat.map?.dispose();\n mat.dispose();\n }\n}\n","/**\n * `@opendata-ai/openchart-vanilla/graph-3d` — the WebGL graph renderer.\n *\n * Importing this module registers the 3D renderer for `dimensions: 3`; after\n * that, `createGraph()` picks it up on its own. That side effect is the whole\n * point of the separate subpath: three.js (~30MB unpacked) never enters the\n * default bundle, and `createGraph()` never dynamically imports a renderer,\n * because mount is synchronous and every framework wrapper assumes it is.\n *\n * ```ts\n * import { createGraph } from '@opendata-ai/openchart-vanilla';\n * import '@opendata-ai/openchart-vanilla/graph-3d';\n *\n * createGraph(el, { type: 'graph', dimensions: 3, nodes, edges });\n * ```\n *\n * `three`, `3d-force-graph` and `three-spritetext` are optional peer\n * dependencies; install them alongside openchart to use this entry point, and\n * check `npm ls three` shows exactly one copy (two copies fail at runtime with\n * `Cannot read properties of undefined (reading 'VERTEX')`).\n *\n * SSR: the libraries touch `window` at import, so import this module from a\n * client-only path (a `useEffect` dynamic import in React Router / Next).\n */\n\nimport type { GraphSpec } from '@opendata-ai/openchart-core';\nimport { registerGraphRenderer } from '../graph/renderer-registry';\nimport { createGraph, type GraphInstance, type GraphMountOptions } from '../graph-mount';\nimport { createGraph3DRenderer } from './mount';\n\nregisterGraphRenderer(3, createGraph3DRenderer);\n\nexport type { EmphasisInput, EmphasisTargets } from './emphasis';\nexport { resolveEmphasis } from './emphasis';\nexport { forceCluster3D } from './forces';\nexport type { LabelCandidate } from './labels';\nexport { LABEL_BUDGET_3D, resolveVisibleLabels } from './labels';\nexport { LINK_WIDTH_MAX_EDGES } from './links';\nexport type { Camera3D, FlyTarget3D } from './mount';\nexport { createGraph3DRenderer, FIT_DISTANCE, NODE_FOCUS_DISTANCE } from './mount';\n\n/**\n * Mount a 3D graph without the caller having to set `dimensions: 3` themselves.\n *\n * A thin convenience over `createGraph()`: the spec is forced to three\n * dimensions and everything else — shell, compilation, node-count gate, handle\n * — is the shared path. A graph above `MAX_3D_NODES` still falls back to the 2D\n * renderer with a warning, exactly as `createGraph()` would.\n */\nexport function createGraph3D(\n container: HTMLElement,\n spec: GraphSpec,\n options?: GraphMountOptions,\n): GraphInstance {\n return createGraph(container, { ...spec, dimensions: 3 }, options);\n}\n"],"mappings":"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","names":["geometry","material","next","Mesh","MeshLambertMaterial","set","resolveHighlightTarget","categoryHighlightSet","defaults","at","width","height"]}
|
|
1
|
+
{"version":3,"sources":["../../src/graph-3d/mount.ts","../../src/graph-3d/emphasis.ts","../../src/graph-3d/entrance.ts","../../src/graph-3d/fit.ts","../../src/graph-3d/forces.ts","../../src/graph-3d/labels.ts","../../src/graph-3d/links.ts","../../src/graph-3d/nodes.ts","../../src/graph-3d/index.ts"],"sourcesContent":["/**\n * The 3D graph renderer: a `GraphInstance` backed by `3d-force-graph`.\n *\n * Registered against `dimensions: 3` by `./index`. `createGraph()` builds the\n * shared shell (wrapper, chrome, legend slot, tooltip manager, resize wiring)\n * and hands it here; this module owns the WebGL scene, the d3-force-3d\n * simulation the library drives, the per-node and per-link three.js objects,\n * emphasis state, the label budget, tooltip anchoring, and disposal.\n *\n * Three library facts shape almost everything below and are easy to get wrong:\n *\n * 1. `nodeOpacity` and `linkOpacity` are GLOBAL numbers, not accessors. Every\n * per-object opacity (dim, highlight, entrance, search) therefore needs a\n * custom object with its own material. See `nodes.ts` / `links.ts`.\n * 2. `refresh()` rebuilds every node and link object. It must never run on\n * hover; the hover path only writes `material.opacity`.\n * 3. `rendererConfig` and `controlType` are CONSTRUCTOR options, not chained\n * setters.\n *\n * Differences from 2D that are deliberate and documented in RFC 27: no\n * keyboard navigation, no node drag, no cursor repulsion or springy drag, and\n * no wall-clock warmup budget. A structural `update()` DOES animate like 2D\n * (enter fade, exit ghosts, churn-scaled local reheat), but it reaches the\n * impulse indirectly: `graphData()` forces alpha to 1 and the library exposes no\n * alpha setter, so `beginUpdateReheat` damps it from outside instead.\n */\n\nimport type { GraphSpec, ThemeConfig, TooltipContent } from '@opendata-ai/openchart-core';\nimport type { CompiledGraphEdge, CompiledGraphNode } from '@opendata-ai/openchart-engine';\nimport { buildEdgeTooltip } from '@opendata-ai/openchart-engine';\nimport ForceGraph3D from '3d-force-graph';\nimport type { Object3D } from 'three';\nimport { Raycaster, Vector2 } from 'three';\nimport type { CameraFlightOptions } from '../graph/camera';\nimport { ENTRANCE_STAGGER_MAX_NODES, popAlpha, popScale } from '../graph/entrance';\nimport {\n composeStandingFocus,\n type FocusSnapshot,\n layerHoverFocus,\n} from '../graph/focus-transition';\nimport {\n categoryHighlightSet as categoryHighlightIds,\n resolveHighlightTarget as resolveHighlightTargetIds,\n} from '../graph/highlight';\nimport { createGraphLegend, type GraphLegendController } from '../graph/legend';\nimport { createTween, linear, prefersReducedMotion, resolveEase } from '../graph/motion';\nimport type { GraphRendererContext } from '../graph/renderer-registry';\nimport { AnimationScheduler, type GraphAnimation } from '../graph/scheduler';\nimport { GraphSearchManager } from '../graph/search';\nimport { seedNodePositions } from '../graph/seed';\nimport type { GraphCamera, GraphFlyTarget, PositionedEdge, PositionedNode } from '../graph/types';\nimport { diffGraphUpdate, reheatAlpha } from '../graph/update-diff';\nimport type { SimNode } from '../graph/worker-protocol';\nimport type {\n GraphHighlightTarget,\n GraphInstance,\n GraphLegendData,\n GraphTooltipFormatter,\n} from '../graph-mount';\nimport { resolvedSurface } from '../theme-tokens';\nimport { resolveEmphasis } from './emphasis';\nimport { ENTRANCE_CAMERA_PULLBACK, elementProgress, planEntrance } from './entrance';\nimport { computeFit, normalize } from './fit';\nimport { applySimulationConfig, beginUpdateReheat, endUpdateReheat } from './forces';\nimport { LABEL_BUDGET_3D, type LabelBox, labelBox, overlaps, resolveVisibleLabels } from './labels';\nimport {\n applyLinkVisuals,\n createLinkObject,\n disposeLinkObject,\n LINK_WIDTH_MAX_EDGES,\n type LinkObject3D,\n linkShapeMatches,\n placeGhostLink,\n updateLinkPosition,\n widthAsAlpha,\n} from './links';\nimport {\n applyNodeVisuals,\n createNodeObject,\n disposeNodeObject,\n ensureLabelSprite,\n LABEL_TEXT_HEIGHT,\n type NodeObject3D,\n} from './nodes';\nimport type { Graph3D, Link3D, Node3D } from './types';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * The camera distance that `getCamera().k === 1` refers to. 3D has no zoom\n * scalar, so `k` is expressed as `FIT_DISTANCE / cameraDistance`: a `k` of 2 is\n * twice as close as the reference framing, matching the 2D sign convention.\n */\nexport const FIT_DISTANCE = 1000;\n\n/** Default breathing room around a fit, in pixels of viewport height. */\nconst FIT_PADDING_PX = 40;\n\n/** Target on-screen cap height for a node label, in CSS pixels. */\nconst LABEL_SCREEN_PX = 11;\n\n/** Bounds on the perspective correction, so a label never vanishes or fills the view. */\nconst LABEL_SCALE_MIN = 0.4;\nconst LABEL_SCALE_MAX = 8;\n\n/** The library's own clamp on devicePixelRatio; kept in step on resize. */\nconst MAX_PIXEL_RATIO = 2;\n\n/** Fallback when the camera has not reported its own field of view yet. */\nconst DEFAULT_FOV_DEG = 50;\n\n/** Throttle for the settle-phase auto re-fit (ms). */\nconst AUTO_FIT_INTERVAL_MS = 250;\n/** Camera standoff from a node for `zoomToNode`, in scene units. */\nexport const NODE_FOCUS_DISTANCE = 120;\n/** Fallback flight duration when `animation.camera.duration` is `'auto'`. */\nconst AUTO_FLIGHT_MS = 800;\n/**\n * The scale a node holds before its pop starts. Not 0: a zero scale collapses\n * the sphere's bounding box, and three skips frustum culling maths on a\n * degenerate box in ways that vary by build.\n */\nconst ENTRANCE_MIN_SCALE = 0.01;\n/** Entrance alpha below which a node's label stays hidden. */\nconst LABEL_ENTRANCE_MIN = 0.4;\n/** Label re-rank throttle while orbiting. */\nconst LABEL_RERANK_MS = 100;\n/** `k` clamp, mirroring 2D's `clampK`. */\nconst K_MIN = 0.05;\nconst K_MAX = 40;\n/** Second seed stream for the z axis (golden-ratio salt, as in `seed.ts`). */\nconst Z_SEED_SALT = 0x9e3779b9;\n\n/**\n * A camera pose: the shared `GraphCamera` with the pose fields narrowed to\n * required, since the 3D renderer always reports them.\n */\nexport interface Camera3D extends GraphCamera {\n position: { x: number; y: number; z: number };\n target: { x: number; y: number; z: number };\n}\n\n/** `flyTo` accepts either the 2D shape or a full 3D pose from `getCamera()`. */\nexport type FlyTarget3D = GraphFlyTarget;\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nexport function createGraph3DRenderer(ctx: GraphRendererContext): GraphInstance {\n const { shell, options } = ctx;\n let currentSpec = ctx.spec;\n let compilation = ctx.compilation;\n let destroyed = false;\n\n // -- Scene ---------------------------------------------------------------\n\n const surfaceEl = document.createElement('div');\n surfaceEl.className = 'oc-graph-3d';\n surfaceEl.setAttribute('role', 'img');\n if (compilation.a11y?.altText) surfaceEl.setAttribute('aria-label', compilation.a11y.altText);\n // Absolute inside the (relative) wrapper, not just width/height 100%.\n // three's `setSize` writes the drawing-buffer size onto the canvas as an\n // inline pixel width, which would otherwise become the min-content width of\n // every flex ancestor: the container could then never shrink below whatever\n // it happened to measure on the first frame. Taking the surface out of flow\n // breaks that feedback loop, and it still fills the wrapper.\n surfaceEl.style.position = 'absolute';\n surfaceEl.style.inset = '0';\n shell.mountSurface(surfaceEl);\n\n // The library's declaration is not generic at the constructor, so the datum\n // types are asserted here rather than parameterized.\n const graph = new ForceGraph3D(surfaceEl, {\n controlType: 'trackball',\n // `alpha` is what lets a transparent theme show the host page through the\n // canvas; both are constructor-only in 3d-force-graph.\n rendererConfig: { alpha: true, antialias: true },\n }) as unknown as Graph3D;\n\n // -- Scene object registries ---------------------------------------------\n\n const nodeObjects = new Map<string, NodeObject3D>();\n /** Keyed by {@link edgeKeys} so a link between two survivors keeps its object. */\n const linkObjects = new Map<string, LinkObject3D>();\n let nodeData: Node3D[] = [];\n let nodeById = new Map<string, Node3D>();\n let linkData: Link3D[] = [];\n\n // -- Derived data --------------------------------------------------------\n\n let adjacency = new Map<string, Set<string>>();\n let nodeDataMap = new Map<string, Record<string, unknown>>();\n let nodeCategory = new Map<string, string>();\n let seedIds = new Set<string>();\n let edgeBaseAlpha: Map<number, number> | undefined;\n let useLinkWidth = false;\n\n // -- Interaction state ---------------------------------------------------\n\n const searchManager = new GraphSearchManager();\n let legendController: GraphLegendController | null = null;\n let hoveredNodeId: string | null = null;\n let hoveredLinkIndex: number | null = null;\n let selectedNodeIds = new Set<string>();\n let activeCategories = new Set<string>();\n let transientHighlight: Set<string> | null = null;\n let transientTarget: GraphHighlightTarget | null = null;\n let highlightSet: Set<string> | null = null;\n let highlightDimOpacity: number | null = null;\n\n // -- Opacity state -------------------------------------------------------\n // `display*` is what the emphasis model says each element should show;\n // `entrance*` is a per-element multiplier on top of it, so the entrance and\n // the emphasis crossfade compose instead of fighting over `material.opacity`.\n // While `entranceActive` is false the multipliers are skipped entirely, which\n // keeps the steady-state paint a plain map read.\n\n const displayNodeAlpha = new Map<string, number>();\n const displayEdgeAlpha = new Map<number, number>();\n let entranceActive = false;\n const entranceNodeAlpha = new Map<string, number>();\n const entranceNodeScale = new Map<string, number>();\n const entranceEdgeAlpha = new Map<number, number>();\n /** Standoff multiplier the fit is scaled by while the camera flies in. */\n let entranceCameraPull = 1;\n let emphasisTween: GraphAnimation | null = null;\n\n // -- Update transition state ---------------------------------------------\n /** Marks that left, kept on screen at their last positions while they fade. */\n let ghosts: Ghost[] = [];\n /** True while a structural update's damped reheat is in flight. */\n let reheatActive = false;\n\n // -- Frame loop ----------------------------------------------------------\n // The library runs its own render loop; this scheduler only drives our tweens.\n\n let pumpId: number | null = null;\n const scheduler = new AnimationScheduler(() => pump());\n\n function pump(): void {\n if (pumpId !== null || destroyed) return;\n pumpId = requestAnimationFrame((now) => {\n pumpId = null;\n if (destroyed) return;\n scheduler.tick(now);\n if (scheduler.active) pump();\n });\n }\n\n // -- Camera / label bookkeeping ------------------------------------------\n\n // The layout starts collapsed near the origin, so a single fit on the first\n // tick frames a blob and leaves the camera inside the cloud once it expands.\n // Re-fit on a throttle while the simulation settles, and stop the moment the\n // viewer takes the camera themselves.\n let autoFit = true;\n let lastAutoFit = 0;\n let cameraChangeQueued = false;\n /** Ids whose label won a slot in the last rank. `paint()` reads it so the\n * entrance can hide a label whose node has not popped in yet. */\n const labelShown = new Set<string>();\n let lastLabelRank = 0;\n let labelRankQueued = false;\n let controlsListener: (() => void) | null = null;\n\n // -- Tooltip state -------------------------------------------------------\n\n let openTooltip: { kind: 'node'; id: string } | { kind: 'edge'; index: number } | null = null;\n /**\n * The target whose content is currently rendered into the tooltip element.\n * `anchorTooltip()` runs on every engine tick and every camera change, so\n * re-running the host formatter and rewriting the DOM each time would be a\n * per-frame cost for content that only changes when the target does.\n */\n let renderedTooltip: string | null = null;\n\n // =========================================================================\n // Derived-data builders\n // =========================================================================\n\n function rebuildDerived(): void {\n adjacency = new Map();\n for (const e of compilation.edges) {\n if (!adjacency.has(e.source)) adjacency.set(e.source, new Set());\n if (!adjacency.has(e.target)) adjacency.set(e.target, new Set());\n adjacency.get(e.source)!.add(e.target);\n adjacency.get(e.target)!.add(e.source);\n }\n nodeDataMap = new Map(compilation.nodes.map((n) => [n.id, n.data ?? {}]));\n nodeCategory = new Map();\n const field = compilation.legendField;\n if (field) {\n for (const n of compilation.nodes) {\n const v = n.data?.[field];\n if (v != null) nodeCategory.set(n.id, String(v));\n }\n }\n seedIds = new Set(compilation.seedNodeIds);\n\n // Cylinders are only affordable below the edge threshold; above it the\n // width encoding becomes a resting-opacity ramp on plain lines.\n const widthEncoded = currentSpec.encoding?.edgeWidth != null;\n useLinkWidth = widthEncoded && compilation.edges.length <= LINK_WIDTH_MAX_EDGES;\n edgeBaseAlpha = widthEncoded && !useLinkWidth ? widthAsAlpha(compilation.edges) : undefined;\n }\n\n /** Label sprite color: the theme's text ink, as in 2D's canvas label layer. */\n function labelColor(): string {\n return compilation.theme.colors.text;\n }\n\n // =========================================================================\n // Seeding\n // =========================================================================\n\n /**\n * Deterministic 3D start positions. x/y come from the shared 2D seeder so a\n * spec settles to a recognisably similar shape in both dimensions; z reuses\n * the same disc placement under a salted seed and takes its x component.\n */\n function seedPositions(nodes: CompiledGraphNode[]): Map<string, [number, number, number]> {\n const xy: SimNode[] = nodes.map((n) => ({\n id: n.id,\n radius: n.radius,\n community: n.community,\n }));\n const seed = compilation.simulationConfig.seed ?? 0;\n seedNodePositions(xy, seed);\n const z: SimNode[] = nodes.map((n) => ({\n id: n.id,\n radius: n.radius,\n community: n.community,\n }));\n seedNodePositions(z, seed ^ Z_SEED_SALT);\n\n const out = new Map<string, [number, number, number]>();\n for (let i = 0; i < nodes.length; i++) {\n out.set(nodes[i].id, [xy[i].x ?? 0, xy[i].y ?? 0, z[i].x ?? 0]);\n }\n return out;\n }\n\n /** The z a single entering node should spawn at (same stream as `seedPositions`). */\n function seedZ(id: string, radius: number, community: string | undefined): number {\n const one: SimNode[] = [{ id, radius, community }];\n seedNodePositions(one, (compilation.simulationConfig.seed ?? 0) ^ Z_SEED_SALT);\n return one[0].x ?? 0;\n }\n\n // =========================================================================\n // Scene data\n // =========================================================================\n\n function buildGraphData(positions: Map<string, [number, number, number]>): void {\n nodeData = compilation.nodes.map((node) => {\n const p = positions.get(node.id) ?? [0, 0, 0];\n return { id: node.id, node, x: p[0], y: p[1], z: p[2] };\n });\n const keys = edgeKeys(compilation.edges);\n linkData = compilation.edges.map((edge, edgeIndex) => ({\n source: edge.source,\n target: edge.target,\n edge,\n edgeIndex,\n key: keys[edgeIndex],\n }));\n nodeById = new Map(nodeData.map((d) => [d.id, d]));\n graph.graphData({ nodes: nodeData, links: linkData });\n }\n\n function nodeObjectFor(datum: Node3D): NodeObject3D {\n let obj = nodeObjects.get(datum.id);\n if (!obj) {\n obj = createNodeObject(datum.node, labelColor());\n obj.group.userData.nodeId = datum.id;\n nodeObjects.set(datum.id, obj);\n const base = displayNodeAlpha.get(datum.id) ?? datum.node.opacity;\n if (entranceActive) {\n obj.material.opacity = base * (entranceNodeAlpha.get(datum.id) ?? 0);\n obj.mesh.scale.setScalar(entranceNodeScale.get(datum.id) ?? ENTRANCE_MIN_SCALE);\n } else {\n obj.material.opacity = base;\n }\n }\n return obj;\n }\n\n /**\n * The `linkThreeObject` accessor, hoisted out of the boot chain so the\n * shape-class flip in `update()` can re-set the same function: that prop\n * changing is what makes three-forcegraph rebuild link objects on their own.\n */\n const linkThreeObject = (datum: Link3D): LinkObject3D['object'] => linkObjectFor(datum).object;\n\n /**\n * Same identity contract as {@link nodeObjectFor}, which is why the map is\n * keyed by the datum's stable {@link edgeKeys} key rather than by edge index:\n * a link between two survivors keeps BOTH its datum and its object across a\n * structural update, so the digest never hands a cached object to a new datum\n * while the old datum's remove hook is still pending on it. Only links that\n * actually left (or a shape-class flip) drop out of the map.\n */\n function linkObjectFor(datum: Link3D): LinkObject3D {\n let obj = linkObjects.get(datum.key);\n if (!obj) {\n const resting = edgeBaseAlpha?.get(datum.edgeIndex) ?? 0.3;\n obj = createLinkObject(datum.edge, useLinkWidth, resting);\n linkObjects.set(datum.key, obj);\n const base = displayEdgeAlpha.get(datum.edgeIndex) ?? resting;\n obj.material.opacity = entranceActive\n ? base * (entranceEdgeAlpha.get(datum.edgeIndex) ?? 0)\n : base;\n }\n return obj;\n }\n\n // =========================================================================\n // Emphasis\n // =========================================================================\n\n function hoverConnectedSet(nodeId: string | null): Set<string> | null {\n if (nodeId === null) return null;\n if (compilation.interaction.hoverMode === 'none') return null;\n if (compilation.interaction.hoverMode === 'category') {\n const cat = nodeCategory.get(nodeId);\n const set = new Set<string>([nodeId]);\n if (cat !== undefined) for (const [id, c] of nodeCategory) if (c === cat) set.add(id);\n return set;\n }\n if (compilation.interaction.hoverMode === 'node') return new Set([nodeId]);\n const set = new Set<string>([nodeId]);\n const neighbors = adjacency.get(nodeId);\n if (neighbors) for (const nid of neighbors) set.add(nid);\n return set;\n }\n\n function currentFocus(): FocusSnapshot {\n const standing = composeStandingFocus(\n highlightSet,\n searchManager.getMatches(),\n selectedNodeIds,\n adjacency,\n );\n return layerHoverFocus(standing, hoveredNodeId, hoverConnectedSet(hoveredNodeId));\n }\n\n function effectiveDimOpacity(): number {\n const custom = transientHighlight !== null ? highlightDimOpacity : null;\n return custom ?? compilation.interaction.dimOpacity;\n }\n\n /**\n * Push the composed opacity (and, mid-entrance, the pop scale) onto every\n * material. Called from both the emphasis tween and the entrance tween, so it\n * allocates nothing: every map it reads is preallocated and reused.\n */\n function paint(): void {\n for (const [id, obj] of nodeObjects) {\n const a = displayNodeAlpha.get(id);\n if (a === undefined) continue;\n if (entranceActive) {\n const factor = entranceNodeAlpha.get(id) ?? 0;\n obj.material.opacity = a * factor;\n obj.mesh.scale.setScalar(entranceNodeScale.get(id) ?? ENTRANCE_MIN_SCALE);\n // SpriteText has no opacity channel of its own, so a label whose node\n // has not popped yet would otherwise float over empty space.\n if (obj.sprite) obj.sprite.visible = labelShown.has(id) && factor > LABEL_ENTRANCE_MIN;\n } else {\n obj.material.opacity = a;\n }\n }\n // Iterated over the datums rather than `linkObjects`, because the alphas are\n // keyed by edge index while the objects are keyed by edge identity.\n for (const datum of linkData) {\n const obj = linkObjects.get(datum.key);\n const a = displayEdgeAlpha.get(datum.edgeIndex);\n if (!obj || a === undefined) continue;\n obj.material.opacity = entranceActive ? a * (entranceEdgeAlpha.get(datum.edgeIndex) ?? 0) : a;\n }\n }\n\n /**\n * Retarget every material toward the composed emphasis state.\n *\n * Never calls `refresh()`: that rebuilds every node and link object, which at\n * hover rate would be a full scene teardown per pointer move.\n */\n function armEmphasis(): void {\n const targets = resolveEmphasis({\n nodes: compilation.nodes,\n edges: compilation.edges,\n focus: currentFocus(),\n exemptIds: seedIds,\n dimOpacity: effectiveDimOpacity(),\n edgeBaseAlpha,\n });\n\n if (emphasisTween) {\n scheduler.remove(emphasisTween);\n emphasisTween = null;\n }\n\n const hoverCfg = compilation.animation?.hover ?? null;\n const duration = hoverCfg && !prefersReducedMotion() ? hoverCfg.duration : 0;\n\n if (duration <= 0) {\n for (const [id, a] of targets.nodes) displayNodeAlpha.set(id, a);\n for (const [i, a] of targets.edges) displayEdgeAlpha.set(i, a);\n paint();\n refreshLabels();\n return;\n }\n\n const fromNodes = new Map(displayNodeAlpha);\n const fromEdges = new Map(displayEdgeAlpha);\n const tween = createTween({\n duration,\n ease: resolveEase(hoverCfg?.ease ?? 'smooth'),\n apply: (t) => {\n for (const [id, to] of targets.nodes) {\n const from = fromNodes.get(id) ?? to;\n displayNodeAlpha.set(id, from + (to - from) * t);\n }\n for (const [i, to] of targets.edges) {\n const from = fromEdges.get(i) ?? to;\n displayEdgeAlpha.set(i, from + (to - from) * t);\n }\n paint();\n },\n onDone: () => {\n emphasisTween = null;\n },\n });\n emphasisTween = tween;\n scheduler.add(tween);\n refreshLabels();\n }\n\n // =========================================================================\n // Highlight composition (mirrors graph-mount's two-layer model)\n // =========================================================================\n\n function resolveHighlightTarget(target: GraphHighlightTarget): Set<string> {\n return resolveHighlightTargetIds(target, compilation.nodes, adjacency);\n }\n\n function categoryHighlightSet(): Set<string> | null {\n return categoryHighlightIds(activeCategories, nodeCategory);\n }\n\n function recomputeHighlight(): void {\n const filter = categoryHighlightSet();\n const transient =\n transientHighlight !== null && transientHighlight.size > 0 ? transientHighlight : null;\n if (filter !== null && transient !== null) {\n const inter = new Set<string>();\n for (const id of transient) if (filter.has(id)) inter.add(id);\n highlightSet = inter.size > 0 ? inter : transient;\n return;\n }\n highlightSet = filter ?? transient;\n }\n\n function refreshTransientHighlight(): void {\n if (transientTarget === null) {\n transientHighlight = null;\n return;\n }\n const nextIds = new Set(compilation.nodes.map((n) => n.id));\n const resolved = new Set(\n [...resolveHighlightTarget(transientTarget)].filter((id) => nextIds.has(id)),\n );\n transientHighlight = resolved.size > 0 ? resolved : null;\n }\n\n function emitHighlightChange(): void {\n options?.onHighlightChange?.(highlightSet ? [...highlightSet] : null);\n }\n\n // =========================================================================\n // Labels\n // =========================================================================\n\n /** Labels that are always shown, regardless of the distance budget. */\n function forcedLabelIds(): Set<string> {\n const forced = new Set<string>();\n for (const id of seedIds) forced.add(id);\n for (const [id, override] of Object.entries(currentSpec.nodeOverrides ?? {})) {\n if (override?.alwaysShowLabel) forced.add(id);\n }\n if (hoveredNodeId) forced.add(hoveredNodeId);\n for (const id of selectedNodeIds) forced.add(id);\n const matches = searchManager.getMatches();\n if (matches) for (const id of matches) forced.add(id);\n return forced;\n }\n\n function refreshLabels(): void {\n if (destroyed || nodeObjects.size === 0) return;\n const camera = graph.camera();\n const cameraPos = camera?.position ?? { x: 0, y: 0, z: FIT_DISTANCE };\n const forced = forcedLabelIds();\n const ranked = resolveVisibleLabels(\n // Only nodes that actually compiled a label are candidates. Filtering on\n // the datum rather than `nodeObjects` matters: the library's digest is\n // deferred, so on the first rank the objects may not exist yet and an\n // object-side filter would let unlabelled nodes eat budget slots.\n nodeData\n .filter((d) => d.node.label)\n .map((d) => ({\n id: d.id,\n priority: d.node.labelPriority,\n x: d.x ?? 0,\n y: d.y ?? 0,\n z: d.z ?? 0,\n })),\n forced,\n cameraPos,\n LABEL_BUDGET_3D,\n );\n\n // Sprites are attenuated by perspective, so a label 2,000 units out is\n // unreadable while one at 200 shouts. Counter it: scale each label so it\n // lands at roughly LABEL_SCREEN_PX on screen whatever the distance. That is\n // also what makes a screen-space declutter pass meaningful — the boxes only\n // mean something once every label is the same size.\n const fov =\n ((camera as { fov?: number } | undefined)?.fov ?? DEFAULT_FOV_DEG) * (Math.PI / 180);\n const viewportHeight = shell.getSize().height || 1;\n const worldPerPixel = (2 * Math.tan(fov / 2)) / viewportHeight;\n\n labelShown.clear();\n const placed: LabelBox[] = [];\n for (const id of ranked) {\n const obj = nodeObjects.get(id);\n const datum = nodeById.get(id);\n if (!obj || !datum || !obj.labelText) continue;\n\n const dist = Math.hypot(\n (datum.x ?? 0) - cameraPos.x,\n (datum.y ?? 0) - cameraPos.y,\n (datum.z ?? 0) - cameraPos.z,\n );\n const desiredHeight = LABEL_SCREEN_PX * worldPerPixel * dist;\n const factor = Math.min(\n LABEL_SCALE_MAX,\n Math.max(LABEL_SCALE_MIN, desiredHeight / LABEL_TEXT_HEIGHT),\n );\n\n // Same declutter contract as 2D's `drawLabels`: forced labels reserve\n // their box unconditionally, everything else takes the first free slot.\n const projected = graph.graph2ScreenCoords(datum.x ?? 0, datum.y ?? 0, datum.z ?? 0);\n const box = labelBox(projected, obj.labelText, LABEL_SCREEN_PX * factor);\n if (box && !forced.has(id) && placed.some((other) => overlaps(box, other))) continue;\n if (box) placed.push(box);\n\n // Only a label that actually won a slot is worth a sprite.\n const sprite = ensureLabelSprite(obj);\n if (!sprite) continue;\n labelShown.add(id);\n if (obj.labelBaseScale) {\n sprite.scale.set(obj.labelBaseScale.x * factor, obj.labelBaseScale.y * factor, 0);\n }\n }\n\n for (const [id, obj] of nodeObjects) {\n if (obj.sprite) {\n obj.sprite.visible =\n labelShown.has(id) &&\n (!entranceActive || (entranceNodeAlpha.get(id) ?? 0) > LABEL_ENTRANCE_MIN);\n }\n }\n lastLabelRank = performance.now();\n }\n\n /** Throttled re-rank, driven by the controls `change` event while orbiting. */\n function scheduleLabelRank(): void {\n if (labelRankQueued || destroyed) return;\n const wait = Math.max(0, LABEL_RERANK_MS - (performance.now() - lastLabelRank));\n labelRankQueued = true;\n setTimeout(() => {\n labelRankQueued = false;\n if (!destroyed) refreshLabels();\n }, wait);\n }\n\n // =========================================================================\n // Camera\n // =========================================================================\n\n function controlsTarget(): { x: number; y: number; z: number } {\n const t = (graph.controls() as { target?: { x: number; y: number; z: number } }).target;\n return t ? { x: t.x, y: t.y, z: t.z } : { x: 0, y: 0, z: 0 };\n }\n\n function getCamera(): Camera3D {\n const pos = graph.cameraPosition();\n const target = controlsTarget();\n const dist = Math.hypot(pos.x - target.x, pos.y - target.y, pos.z - target.z) || FIT_DISTANCE;\n return {\n x: target.x,\n y: target.y,\n k: FIT_DISTANCE / dist,\n position: { x: pos.x, y: pos.y, z: pos.z },\n target,\n };\n }\n\n /** Flight length in ms; 0 snaps (reduced motion, `animation: false`, `duration: 0`). */\n function flightMs(opts?: CameraFlightOptions): number {\n const cfg = compilation.animation?.camera ?? null;\n if (cfg === null || prefersReducedMotion() || opts?.duration === 0) return 0;\n const requested = opts?.duration ?? cfg.duration;\n return requested === 'auto' ? AUTO_FLIGHT_MS : requested;\n }\n\n function clampK(k: number): number {\n return Math.min(K_MAX, Math.max(K_MIN, k));\n }\n\n function flyTo(target: FlyTarget3D, opts?: CameraFlightOptions): void {\n if (destroyed) return;\n autoFit = false;\n const ms = flightMs(opts);\n if (target.position) {\n graph.cameraPosition(\n target.position,\n target.target ?? { x: target.x, y: target.y, z: 0 },\n ms,\n );\n queueCameraChange();\n return;\n }\n const look = { x: target.x, y: target.y, z: 0 };\n const cam = getCamera();\n const dist = FIT_DISTANCE / clampK(target.k ?? cam.k);\n // Keep the current viewing direction so a `{ x, y }` recentre orbits nothing.\n const dir = normalize({\n x: cam.position.x - cam.target.x,\n y: cam.position.y - cam.target.y,\n z: cam.position.z - cam.target.z,\n });\n graph.cameraPosition(\n { x: look.x + dir.x * dist, y: look.y + dir.y * dist, z: look.z + dir.z * dist },\n look,\n ms,\n );\n queueCameraChange();\n }\n\n /** Frame every node from the current viewing angle. See `computeFit`. */\n function zoomToFit(opts?: CameraFlightOptions & { padding?: number }): void {\n // An explicit fit is the host taking the camera, so the settle-phase\n // auto-fit stops second-guessing it.\n autoFit = false;\n fitNow(opts);\n }\n\n function fitNow(opts?: CameraFlightOptions & { padding?: number }): void {\n if (destroyed || nodeData.length === 0) return;\n const camera = graph.camera() as { fov?: number; aspect?: number } | undefined;\n const fit = computeFit(\n nodeData.map((d) => ({\n x: d.x ?? 0,\n y: d.y ?? 0,\n z: d.z ?? 0,\n radius: d.node.radius ?? 0,\n })),\n {\n cameraPos: getCamera().position,\n fovDeg: camera?.fov ?? DEFAULT_FOV_DEG,\n aspect: camera?.aspect ?? 1,\n viewportHeight: shell.getSize().height,\n paddingPx: opts?.padding ?? FIT_PADDING_PX,\n },\n );\n if (!fit) return;\n // Mid-entrance the camera stands `entranceCameraPull ×` further back than\n // the true fit, easing to 1. See `startEntrance`.\n const distance = fit.distance * entranceCameraPull;\n graph.cameraPosition(\n {\n x: fit.center.x + fit.dir.x * distance,\n y: fit.center.y + fit.dir.y * distance,\n z: fit.center.z + fit.dir.z * distance,\n },\n fit.center,\n flightMs(opts),\n );\n queueCameraChange();\n }\n\n function zoomToNode(nodeId: string, opts?: CameraFlightOptions & { scale?: number }): void {\n if (destroyed) return;\n autoFit = false;\n const datum = nodeById.get(nodeId);\n if (!datum) return;\n const at = { x: datum.x ?? 0, y: datum.y ?? 0, z: datum.z ?? 0 };\n // Approach along the node's own radial direction so the camera ends up\n // outside the cloud rather than inside it.\n const dir = normalize(at);\n const dist = opts?.scale ? NODE_FOCUS_DISTANCE / clampK(opts.scale) : NODE_FOCUS_DISTANCE;\n graph.cameraPosition(\n { x: at.x + dir.x * dist, y: at.y + dir.y * dist, z: at.z + dir.z * dist },\n at,\n flightMs(opts),\n );\n queueCameraChange();\n }\n\n function queueCameraChange(): void {\n if (cameraChangeQueued || destroyed || !options?.onCameraChange) return;\n cameraChangeQueued = true;\n requestAnimationFrame(() => {\n cameraChangeQueued = false;\n if (destroyed) return;\n options.onCameraChange?.(getCamera());\n });\n }\n\n // =========================================================================\n // Tooltips\n // =========================================================================\n\n function tooltipFormatter(): GraphTooltipFormatter | null {\n const t = options?.tooltip;\n return t && typeof t === 'object' ? (t.formatter ?? null) : null;\n }\n\n /**\n * Project a scene point into wrapper-relative pixels. `graph2ScreenCoords` is\n * canvas-relative, and the tooltip manager positions against the wrapper, so\n * the surface's offset inside the wrapper is added here.\n */\n function toWrapperXY(p: { x: number; y: number; z: number }): { x: number; y: number } | null {\n const screen = graph.graph2ScreenCoords(p.x, p.y, p.z);\n if (!screen || !Number.isFinite(screen.x) || !Number.isFinite(screen.y)) return null;\n const s = surfaceEl.getBoundingClientRect();\n const w = shell.wrapper.getBoundingClientRect();\n return { x: screen.x + (s.left - w.left), y: screen.y + (s.top - w.top) };\n }\n\n function applyFormatterResult(\n result: TooltipContent | string | HTMLElement | null,\n x: number,\n y: number,\n ): void {\n const tm = shell.tooltipManager;\n if (!tm) return;\n // A suppressed item has to clear `openTooltip`/`renderedTooltip` too:\n // leaving them set would send the next `anchorTooltip()` down the\n // reposition fast path, which re-shows the previous item's content.\n if (result === null) hideTooltip();\n else if (typeof result === 'string') tm.show({ text: result }, x, y);\n else if (result instanceof HTMLElement) tm.show({ element: result }, x, y);\n else tm.show(result, x, y);\n }\n\n /**\n * Re-anchor whatever tooltip is open, rendering its content only when the\n * open target has changed since the last render. Everything else is a\n * reposition, which is what the tick and orbit paths actually need.\n */\n function anchorTooltip(): void {\n const tm = shell.tooltipManager;\n if (!tm || openTooltip === null) return;\n const formatter = tooltipFormatter();\n const key =\n openTooltip.kind === 'node' ? `node:${openTooltip.id}` : `edge:${openTooltip.index}`;\n\n if (openTooltip.kind === 'node') {\n const id = openTooltip.id;\n const datum = nodeById.get(id);\n const defaults = compilation.tooltipDescriptors.get(id);\n if (!datum || !defaults) return;\n const at = toWrapperXY({ x: datum.x ?? 0, y: datum.y ?? 0, z: datum.z ?? 0 });\n if (!at) return;\n if (key === renderedTooltip) {\n tm.move(at.x, at.y);\n return;\n }\n renderedTooltip = key;\n if (!formatter) {\n tm.show(defaults, at.x, at.y);\n return;\n }\n applyFormatterResult(\n formatter({ kind: 'node', data: nodeDataMap.get(id) ?? {} }, defaults),\n at.x,\n at.y,\n );\n return;\n }\n\n const link = linkData[openTooltip.index];\n if (!link) return;\n const a = endpointPos(link.source);\n const b = endpointPos(link.target);\n const at = toWrapperXY({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 });\n if (!at) return;\n if (key === renderedTooltip) {\n tm.move(at.x, at.y);\n return;\n }\n renderedTooltip = key;\n const defaults = buildEdgeTooltip(link.edge);\n if (!formatter) {\n tm.show(defaults, at.x, at.y);\n return;\n }\n applyFormatterResult(\n formatter({ kind: 'edge', data: link.edge.data ?? {} }, defaults),\n at.x,\n at.y,\n );\n }\n\n function endpointPos(endpoint: string | Node3D): { x: number; y: number; z: number } {\n if (typeof endpoint !== 'string') {\n return { x: endpoint.x ?? 0, y: endpoint.y ?? 0, z: endpoint.z ?? 0 };\n }\n const datum = nodeById.get(endpoint);\n return { x: datum?.x ?? 0, y: datum?.y ?? 0, z: datum?.z ?? 0 };\n }\n\n function hideTooltip(): void {\n openTooltip = null;\n renderedTooltip = null;\n shell.tooltipManager?.hide();\n }\n\n // =========================================================================\n // Legend\n // =========================================================================\n\n function legendSetting(): boolean | { interactive?: boolean; counts?: boolean } | undefined {\n return options?.legend ?? currentSpec.legend;\n }\n\n function legendConfig(): { interactive: boolean; counts: boolean } {\n const l = legendSetting();\n if (l && typeof l === 'object') {\n return { interactive: l.interactive ?? true, counts: l.counts ?? true };\n }\n return { interactive: true, counts: true };\n }\n\n function getLegend(): GraphLegendData {\n const nodeEntries = 'entries' in compilation.legend ? compilation.legend.entries : [];\n return {\n field: compilation.legendField,\n nodes: nodeEntries\n .filter((e) => !e.overflow)\n .map((e) => ({\n label: e.label,\n color: e.color,\n count: e.count,\n active: activeCategories.size === 0 || activeCategories.has(e.label),\n })),\n edges: (compilation.edgeLegend ?? []).map((e) => ({\n label: e.label,\n color: e.color,\n count: e.count,\n })),\n };\n }\n\n function renderLegend(): void {\n if (!shell.legendEl) return;\n const view = { nodes: getLegend().nodes, edges: getLegend().edges };\n if (!legendController) {\n const cfg = legendConfig();\n legendController = createGraphLegend(shell.legendEl, view, {\n interactive: cfg.interactive,\n counts: cfg.counts,\n onToggle: (value) => toggleLegendCategory(value),\n onHover: (value) => {\n const field = compilation.legendField;\n options?.onLegendHover?.(value !== null && field ? { field, value } : null);\n },\n });\n } else {\n legendController.update(view);\n }\n shell.syncChromeInset();\n }\n\n function toggleLegendCategory(value: string): void {\n if (activeCategories.has(value)) activeCategories.delete(value);\n else activeCategories.add(value);\n recomputeHighlight();\n renderLegend();\n armEmphasis();\n options?.onLegendToggle?.([...activeCategories]);\n emitHighlightChange();\n }\n\n // =========================================================================\n // Interaction wiring\n // =========================================================================\n\n function handleNodeClick(nodeId: string, shiftKey: boolean): void {\n if (shiftKey) {\n if (selectedNodeIds.has(nodeId)) selectedNodeIds.delete(nodeId);\n else selectedNodeIds.add(nodeId);\n } else {\n selectedNodeIds = new Set([nodeId]);\n }\n armEmphasis();\n options?.onSelectionChange?.([...selectedNodeIds]);\n options?.onNodeClick?.(nodeDataMap.get(nodeId) ?? {});\n }\n\n const raycaster = new Raycaster();\n const pointer = new Vector2();\n\n /** Node id under a mouse event, via a raycast against the node groups. */\n function pickNode(event: MouseEvent): string | null {\n const canvas = graph.renderer()?.domElement;\n const camera = graph.camera();\n if (!canvas || !camera) return null;\n const rect = canvas.getBoundingClientRect();\n pointer.x = ((event.clientX - rect.left) / (rect.width || 1)) * 2 - 1;\n pointer.y = -((event.clientY - rect.top) / (rect.height || 1)) * 2 + 1;\n raycaster.setFromCamera(pointer, camera);\n const groups = [...nodeObjects.values()].map((o) => o.group);\n const hits = raycaster.intersectObjects(groups, true);\n for (const hit of hits) {\n // The hit is the sphere mesh; the node id lives on its parent group.\n let obj: Object3D | null = hit.object;\n while (obj) {\n const id = obj.userData?.nodeId;\n if (typeof id === 'string') return id;\n obj = obj.parent;\n }\n }\n return null;\n }\n\n const onCanvasCameraInput = (): void => {\n autoFit = false;\n };\n\n const onCanvasDblClick = (event: MouseEvent): void => {\n const id = pickNode(event);\n // Matches 2D exactly: the two clicks already fired, and the double-click\n // fires IN ADDITION rather than suppressing them.\n if (id) options?.onNodeDoubleClick?.(nodeDataMap.get(id) ?? {});\n };\n\n // three's own `onContextRestore` rebuilds the GL resources for every object\n // already in the scene, so there is nothing left for us to rebuild. Calling\n // `refresh()` here would tear the scene down and re-add it, and resume the\n // engine for a spurious tick.\n const onContextLost = (event: Event): void => {\n event.preventDefault();\n };\n\n // =========================================================================\n // Entrance\n // =========================================================================\n\n /** Drop the entrance multipliers and restore every node to full scale. */\n function endEntrance(): void {\n entranceActive = false;\n entranceCameraPull = 1;\n for (const obj of nodeObjects.values()) obj.mesh.scale.setScalar(1);\n paint();\n }\n\n /**\n * Nodes pop in (scale 0.01 → 1 with an overshoot, opacity 0 → their emphasis\n * alpha), staggered by `entranceOrder`; links fade in a beat after the later\n * of their endpoints; the camera starts pulled back to\n * `ENTRANCE_CAMERA_PULLBACK ×` the fit and flies in over the same window.\n *\n * The pull-in is expressed as a MULTIPLIER on the fit distance rather than as\n * its own camera flight. The settle-phase auto-fit re-frames the cloud every\n * `AUTO_FIT_INTERVAL_MS` while the layout expands from its seeded blob, and a\n * flight to a distance computed at t=0 would be re-targeting a framing that is\n * already stale. As a multiplier the two compose: the auto-fit keeps deciding\n * WHAT to frame, the entrance only decides how far back to stand.\n *\n * Runs once per mount. `update()` never calls it.\n */\n function startEntrance(): void {\n const enter = compilation.animation?.enter ?? null;\n if (options?.suppressEntrance || !enter || prefersReducedMotion()) {\n endEntrance();\n return;\n }\n\n // Same gate as 2D: thousands of distinct start times read as noise, so\n // above the cap the whole graph reveals as one.\n const stagger = enter.stagger && compilation.nodes.length <= ENTRANCE_STAGGER_MAX_NODES;\n const plan = planEntrance(\n nodeData.map((d) => ({ id: d.id, x: d.x ?? 0, y: d.y ?? 0 })),\n compilation.edges,\n enter.duration,\n stagger,\n );\n const ease = resolveEase(enter.ease);\n\n entranceActive = true;\n entranceCameraPull = enter.cameraFit ? ENTRANCE_CAMERA_PULLBACK : 1;\n for (const id of plan.nodeOffset.keys()) {\n entranceNodeAlpha.set(id, 0);\n entranceNodeScale.set(id, ENTRANCE_MIN_SCALE);\n }\n for (const index of plan.linkOffset.keys()) entranceEdgeAlpha.set(index, 0);\n paint();\n\n // The driver runs linear over the whole span so each element can apply the\n // spec's ease across its OWN window; easing the global clock instead would\n // squeeze the stagger rather than shape each pop.\n const tween = createTween({\n duration: plan.span,\n ease: linear,\n apply: (t) => {\n const elapsed = t * plan.span;\n for (const [id, offset] of plan.nodeOffset) {\n const p = elementProgress(elapsed, offset, enter.duration, ease);\n entranceNodeAlpha.set(id, popAlpha(p));\n entranceNodeScale.set(id, Math.max(ENTRANCE_MIN_SCALE, popScale(p)));\n }\n for (const [index, offset] of plan.linkOffset) {\n entranceEdgeAlpha.set(index, elementProgress(elapsed, offset, enter.duration, ease));\n }\n if (enter.cameraFit) {\n entranceCameraPull = ENTRANCE_CAMERA_PULLBACK + (1 - ENTRANCE_CAMERA_PULLBACK) * ease(t);\n // The auto-fit throttle is too coarse to carry a flight, so the\n // entrance re-fits every frame while it owns the standoff.\n if (autoFit && options?.fitOnLoad !== false) fitNow({ duration: 0 });\n }\n paint();\n },\n onDone: endEntrance,\n });\n scheduler.add(tween);\n }\n\n // =========================================================================\n // Update transitions (enter fade + exit ghosts)\n // =========================================================================\n\n /** Where a ghost is parked: the node groups' own parent, else the scene root. */\n function ghostParent(): GhostParent | null {\n for (const obj of nodeObjects.values()) {\n if (obj.group.parent) return obj.group.parent as unknown as GhostParent;\n }\n return (graph.scene() as unknown as GhostParent | undefined) ?? null;\n }\n\n /** A clone of a departed node, parked at its last position. */\n function nodeGhost(\n node: CompiledGraphNode,\n pos: { x: number; y: number; z: number },\n alpha: number,\n withLabel: boolean,\n ): Ghost {\n const obj = createNodeObject(node, labelColor());\n obj.group.position.set(pos.x, pos.y, pos.z);\n obj.material.opacity = alpha;\n const fades: Ghost['fades'] = [{ material: obj.material, base: alpha }];\n if (withLabel) {\n const sprite = ensureLabelSprite(obj);\n if (sprite) {\n sprite.visible = true;\n fades.push({\n material: sprite.material as unknown as Ghost['fades'][number]['material'],\n base: 1,\n });\n }\n }\n return { object: obj.group, fades, dispose: () => disposeNodeObject(obj) };\n }\n\n /** A clone of a departed link, frozen on its last segment. */\n function linkGhost(\n edge: CompiledGraphEdge,\n start: { x: number; y: number; z: number },\n end: { x: number; y: number; z: number },\n alpha: number,\n useWidth: boolean,\n ): Ghost {\n const obj = createLinkObject(edge, useWidth, alpha);\n placeGhostLink(obj, start, end);\n return {\n object: obj.object,\n fades: [{ material: obj.material, base: alpha }],\n dispose: () => disposeLinkObject(obj),\n };\n }\n\n /**\n * Park `pending` in the scene and fade it to nothing over `exit.duration`.\n * With the phase off, under reduced motion, or with nowhere to park them, the\n * departed marks simply go — same snap 2D takes.\n */\n function startExitFade(pending: Ghost[]): void {\n if (pending.length === 0) return;\n const cfg = compilation.animation?.exit ?? null;\n const parent = ghostParent();\n if (!cfg || prefersReducedMotion() || !parent) {\n for (const g of pending) g.dispose();\n return;\n }\n for (const g of pending) {\n for (const f of g.fades) f.material.transparent = true;\n parent.add(g.object);\n }\n ghosts = ghosts.concat(pending);\n scheduler.add(\n createTween({\n duration: cfg.duration,\n ease: resolveEase(cfg.ease),\n apply: (t) => {\n for (const g of pending) {\n for (const f of g.fades) f.material.opacity = f.base * (1 - t);\n }\n },\n onDone: () => removeGhosts(pending),\n }),\n );\n }\n\n /** Detach and free `list`, dropping it from the live ghost set. */\n function removeGhosts(list: Ghost[]): void {\n for (const g of list) {\n g.object.parent?.remove(g.object);\n g.dispose();\n }\n const gone = new Set(list);\n ghosts = ghosts.filter((g) => !gone.has(g));\n }\n\n /**\n * Fade and pop in what a structural update added, over `update.duration`.\n *\n * Reuses the entrance multipliers rather than adding a parallel set: `paint()`\n * already composes them with the emphasis alphas, and everything that survived\n * is pinned at full strength so only the arriving marks are shaped.\n */\n function startEnterFade(enteringIds: string[], enteringEdgeIndices: number[]): void {\n const cfg = compilation.animation?.update ?? null;\n if (enteringIds.length === 0 && enteringEdgeIndices.length === 0) return;\n if (!cfg || prefersReducedMotion()) return;\n\n entranceActive = true;\n for (const node of compilation.nodes) {\n entranceNodeAlpha.set(node.id, 1);\n entranceNodeScale.set(node.id, 1);\n }\n for (let i = 0; i < compilation.edges.length; i++) entranceEdgeAlpha.set(i, 1);\n for (const id of enteringIds) {\n entranceNodeAlpha.set(id, 0);\n entranceNodeScale.set(id, ENTRANCE_MIN_SCALE);\n }\n for (const index of enteringEdgeIndices) entranceEdgeAlpha.set(index, 0);\n paint();\n\n scheduler.add(\n createTween({\n duration: cfg.duration,\n ease: resolveEase(cfg.ease),\n apply: (t) => {\n for (const id of enteringIds) {\n entranceNodeAlpha.set(id, popAlpha(t));\n entranceNodeScale.set(id, Math.max(ENTRANCE_MIN_SCALE, popScale(t)));\n }\n for (const index of enteringEdgeIndices) entranceEdgeAlpha.set(index, t);\n paint();\n },\n onDone: endEntrance,\n }),\n );\n }\n\n // =========================================================================\n // Public handle\n // =========================================================================\n\n function search(query: string): void {\n if (destroyed) return;\n searchManager.search(query, compilation.nodes);\n armEmphasis();\n }\n\n function clearSearch(): void {\n if (destroyed) return;\n searchManager.clearSearch();\n armEmphasis();\n }\n\n function selectNode(nodeId: string, opts?: { fly?: boolean } & CameraFlightOptions): void {\n if (destroyed) return;\n selectedNodeIds = new Set([nodeId]);\n armEmphasis();\n options?.onSelectionChange?.([nodeId]);\n const shouldFly = opts?.fly ?? compilation.interaction.selectFlyTo;\n if (shouldFly) zoomToNode(nodeId, opts);\n }\n\n function highlight(target: GraphHighlightTarget, opts?: { dimOpacity?: number }): void {\n if (destroyed) return;\n transientTarget = target;\n refreshTransientHighlight();\n highlightDimOpacity = opts?.dimOpacity ?? null;\n recomputeHighlight();\n armEmphasis();\n emitHighlightChange();\n }\n\n function clearHighlight(): void {\n if (destroyed) return;\n transientTarget = null;\n transientHighlight = null;\n highlightDimOpacity = null;\n recomputeHighlight();\n armEmphasis();\n emitHighlightChange();\n }\n\n function setActiveCategories(values: string[]): void {\n if (destroyed) return;\n activeCategories = new Set(values);\n recomputeHighlight();\n renderLegend();\n armEmphasis();\n emitHighlightChange();\n }\n\n function doResize(): void {\n if (destroyed) return;\n // three-render-objects reads devicePixelRatio once, at construction, so a\n // window dragged onto a retina display (or a browser zoom change) would\n // stay soft until remount. Re-apply it before the size, which is what\n // actually reallocates the drawing buffer.\n const renderer = graph.renderer() as\n | { getPixelRatio?(): number; setPixelRatio?(v: number): void }\n | undefined;\n const dpr = Math.min(MAX_PIXEL_RATIO, globalThis.devicePixelRatio || 1);\n if (renderer?.getPixelRatio?.() !== dpr) renderer?.setPixelRatio?.(dpr);\n const { width, height } = shell.getSize();\n graph.width(width).height(height);\n shell.syncChromeInset();\n }\n\n /**\n * Unified data update.\n *\n * `diffGraphUpdate` classifies the change exactly as in 2D. A visual-only\n * change mutates materials and geometry in place and never touches\n * `graphData()`. A structural change reuses the surviving node datums (so\n * their positions, velocities AND their scene-object bindings all carry\n * over), spawns z for entering nodes from the seeded stream, and then calls\n * `graphData()`.\n *\n * 3D difference: `graphData()` reheats the layout globally (alpha 1). 2D\n * applies a local impulse instead (`initialAlpha` + `suppressCenter`), which\n * 3d-force-graph gives us no seam for. Carried positions stop survivors from\n * respawning, but they do drift; `d3AlphaDecay` from the compiled `settle`\n * bounds how far.\n */\n function update(newSpec: GraphSpec): void {\n if (destroyed) return;\n\n const next = ctx.compile(newSpec);\n if (next.numDimensions !== 3) {\n shell.warn('createGraph: update() cannot change dimensions; remount the graph');\n return;\n }\n\n scheduler.finishAll();\n\n const prevNodes: PositionedNode[] = nodeData.map((d, index) => ({\n ...d.node,\n x: d.x ?? 0,\n y: d.y ?? 0,\n index,\n }));\n const prevEdges: PositionedEdge[] = compilation.edges.map((edge) => {\n const a = endpointPos(edge.source);\n const b = endpointPos(edge.target);\n return { ...edge, sourceX: a.x, sourceY: a.y, targetX: b.x, targetY: b.y };\n });\n const prevConfig = compilation.simulationConfig;\n const prevUseLinkWidth = useLinkWidth;\n\n currentSpec = newSpec;\n compilation = next;\n // The compiled descriptors behind an open tooltip may have changed, so the\n // next anchor must re-render rather than take the reposition fast path.\n renderedTooltip = null;\n\n const diff = diffGraphUpdate(\n prevNodes,\n prevEdges,\n compilation,\n prevConfig,\n prevConfig.seed ?? 0,\n );\n\n rebuildDerived();\n shell.renderChrome(compilation);\n renderLegend();\n refreshTransientHighlight();\n recomputeHighlight();\n reRunSearch();\n\n // An `edgeWidth` encoding appearing or disappearing swaps every link\n // between a cylinder mesh and a line, which cannot be done in place: the\n // scene binds objects to datums by identity, so a replacement object never\n // reaches the scene on its own.\n const shapeClassChanged =\n useLinkWidth !== prevUseLinkWidth ||\n [...linkObjects.values()].some((obj) => !linkShapeMatches(obj, useLinkWidth));\n\n // Lines are thin hit targets compared with a cylinder, so the precision\n // has to follow the shape class rather than stay at its boot value.\n if (useLinkWidth !== prevUseLinkWidth) graph.linkHoverPrecision(useLinkWidth ? 4 : 8);\n\n if (diff.visualOnly) {\n // Same ids in the same order, so the datum's compiled node/edge can be\n // swapped under the existing scene objects with no layout restart.\n for (let i = 0; i < compilation.nodes.length; i++) {\n const node = compilation.nodes[i];\n nodeData[i].node = node;\n const obj = nodeObjects.get(node.id);\n if (obj) applyNodeVisuals(obj, node, labelColor());\n }\n for (let i = 0; i < compilation.edges.length; i++) {\n const edge = compilation.edges[i];\n linkData[i].edge = edge;\n if (shapeClassChanged) continue;\n const obj = linkObjects.get(linkData[i].key);\n // Width, dash pattern and color all move here: color alone would leave\n // a cylinder at its old radius and a line at its old dash geometry.\n if (obj) applyLinkVisuals(obj, edge, useLinkWidth);\n }\n if (shapeClassChanged) {\n // Re-setting `linkThreeObject` makes three-forcegraph clear its link\n // mapper and re-digest the links alone, so every link object is\n // rebuilt from the cleared cache without `graphData()` reheating the\n // layout at alpha 1.\n for (const obj of linkObjects.values()) disposeLinkObject(obj);\n linkObjects.clear();\n graph.linkThreeObject(linkThreeObject);\n }\n pruneInteractionState();\n armEmphasis();\n return;\n }\n\n // Structural: ghost what left, carry survivors across, fade in what entered.\n const nextNodeIds = new Set(compilation.nodes.map((n) => n.id));\n const keys = edgeKeys(compilation.edges);\n const nextKeys = new Set(keys);\n const prevLinkByKey = new Map(linkData.map((d) => [d.key, d]));\n // Cloned BEFORE the data arrays are rebuilt: the last positions and the live\n // emphasis alphas only exist until then. The originals still go — the\n // library deallocates any object whose datum leaves `graphData()`, so a\n // ghost has to be a copy we own outright.\n const pendingGhosts: Ghost[] = [];\n\n for (const [id, obj] of nodeObjects) {\n if (nextNodeIds.has(id)) continue;\n const datum = nodeById.get(id);\n if (datum) {\n pendingGhosts.push(\n nodeGhost(\n datum.node,\n { x: datum.x ?? 0, y: datum.y ?? 0, z: datum.z ?? 0 },\n displayNodeAlpha.get(id) ?? datum.node.opacity,\n Boolean(obj.sprite?.visible),\n ),\n );\n }\n disposeNodeObject(obj);\n nodeObjects.delete(id);\n displayNodeAlpha.delete(id);\n entranceNodeAlpha.delete(id);\n entranceNodeScale.delete(id);\n }\n\n for (const [key, obj] of linkObjects) {\n if (nextKeys.has(key) && !shapeClassChanged) continue;\n const datum = prevLinkByKey.get(key);\n if (datum && !nextKeys.has(key)) {\n pendingGhosts.push(\n linkGhost(\n datum.edge,\n endpointPos(datum.source),\n endpointPos(datum.target),\n displayEdgeAlpha.get(datum.edgeIndex) ?? 0.3,\n prevUseLinkWidth,\n ),\n );\n }\n disposeLinkObject(obj);\n linkObjects.delete(key);\n }\n // The alphas are keyed by edge index, which the new compilation renumbers.\n displayEdgeAlpha.clear();\n entranceEdgeAlpha.clear();\n\n // Survivors keep their EXISTING datum OBJECT, mutated in place.\n //\n // This is not a micro-optimisation, it is the only correct thing to do.\n // `graphData()` runs a `DataBindMapper` digest whose id accessor is\n // identity (`d => d`); three-forcegraph never sets one, and `graphData()`\n // does not clear the node mapper. Handing the digest a fresh literal for a\n // node whose cached group `nodeThreeObject` still returns therefore binds\n // that group to the NEW datum, and then the OLD datum -- absent from the\n // new set -- runs the remove hook on the SAME group: `scene.remove`,\n // `_deallocate`, and the binding deleted. Every survivor silently leaves\n // the scene and the tick loop. Reusing the datum keeps the binding, and it\n // already carries the x/y/z and vx/vy/vz that used to be copied out into\n // carry-over maps.\n const survivors = new Map(nodeById);\n nodeData = compilation.nodes.map((node) => {\n const existing = survivors.get(node.id);\n if (existing) {\n existing.node = node;\n // The cached group was built from the OLD compiled node, so a\n // structural update that also recolored or resized a survivor would\n // otherwise keep painting the stale fill, radius and label.\n const obj = nodeObjects.get(node.id);\n if (obj) applyNodeVisuals(obj, node, labelColor());\n return existing;\n }\n const spawn = diff.spawnPositions.get(node.id) ?? { x: 0, y: 0 };\n return {\n id: node.id,\n node,\n x: spawn.x,\n y: spawn.y,\n z: seedZ(node.id, node.radius, node.community),\n };\n });\n // Link datums are reused by key for the same reason node datums are: the\n // digest binds by identity, so a fresh literal for a surviving edge would\n // hand its object to the new datum and then remove it for the old one. A\n // shape-class flip is the one case that must rebuild, so it opts out.\n const enteringEdgeIndices: number[] = [];\n linkData = compilation.edges.map((edge, edgeIndex) => {\n const key = keys[edgeIndex];\n const existing = shapeClassChanged ? undefined : prevLinkByKey.get(key);\n if (!prevLinkByKey.has(key)) enteringEdgeIndices.push(edgeIndex);\n if (existing) {\n existing.edge = edge;\n existing.edgeIndex = edgeIndex;\n const obj = linkObjects.get(key);\n if (obj) applyLinkVisuals(obj, edge, useLinkWidth);\n return existing;\n }\n return { source: edge.source, target: edge.target, edge, edgeIndex, key };\n });\n\n nodeById = new Map(nodeData.map((d) => [d.id, d]));\n\n // No synchronous warmup here. The library runs `warmupTicks` in one blocking\n // loop inside the digest, which is exactly the single-frame snap this path\n // must not do; the mount-time path still warms up. `beginUpdateReheat` then\n // scales the impulse down to `reheatAlpha`, and `onEngineStop` restores both.\n applySimulationConfig(graph, compilation.simulationConfig, nodeData.length, {\n warmupTicks: 0,\n });\n beginUpdateReheat(\n graph,\n compilation.simulationConfig,\n reheatAlpha(diff, compilation.edges.length),\n );\n reheatActive = true;\n\n graph.graphData({ nodes: nodeData, links: linkData });\n startEnterFade(diff.enteringIds, enteringEdgeIndices);\n startExitFade(pendingGhosts);\n pruneInteractionState();\n armEmphasis();\n }\n\n function pruneInteractionState(): void {\n const ids = new Set(compilation.nodes.map((n) => n.id));\n if (hoveredNodeId && !ids.has(hoveredNodeId)) hoveredNodeId = null;\n // Edge indices are renumbered by every update, so the hover always drops.\n // Tell the host: a listener that opened something on hover would otherwise\n // never get the close.\n const hadEdgeHover = hoveredLinkIndex !== null;\n hoveredLinkIndex = null;\n if (hadEdgeHover) options?.onEdgeHover?.(null);\n selectedNodeIds = new Set([...selectedNodeIds].filter((id) => ids.has(id)));\n if (openTooltip?.kind === 'node' && !ids.has(openTooltip.id)) hideTooltip();\n if (openTooltip?.kind === 'edge') hideTooltip();\n }\n\n function reRunSearch(): void {\n const q = searchManager.getQuery();\n if (q !== null) searchManager.search(q, compilation.nodes);\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n scheduler.cancelAll();\n // `cancelAll` does not run `onDone`, so the ghosts still have to be freed.\n removeGhosts(ghosts);\n if (pumpId !== null) {\n cancelAnimationFrame(pumpId);\n pumpId = null;\n }\n\n const canvas = graph.renderer()?.domElement;\n canvas?.removeEventListener('dblclick', onCanvasDblClick);\n canvas?.removeEventListener('pointerdown', onCanvasCameraInput);\n canvas?.removeEventListener('wheel', onCanvasCameraInput);\n canvas?.removeEventListener('webglcontextlost', onContextLost);\n if (controlsListener) {\n (\n graph.controls() as { removeEventListener?(t: string, f: () => void): void }\n ).removeEventListener?.('change', controlsListener);\n controlsListener = null;\n }\n disconnectResize();\n\n // Grab the renderer BEFORE _destructor(): it tears down the render loop and\n // the DOM, after which the accessor is no longer reliable.\n const renderer = graph.renderer();\n graph._destructor();\n\n for (const obj of nodeObjects.values()) disposeNodeObject(obj);\n nodeObjects.clear();\n for (const obj of linkObjects.values()) disposeLinkObject(obj);\n linkObjects.clear();\n\n // Browsers cap live WebGL contexts (~16). `dispose()` alone does not free\n // one; the context has to be explicitly lost.\n renderer?.dispose();\n (renderer as { forceContextLoss?(): void })?.forceContextLoss?.();\n\n legendController?.destroy();\n legendController = null;\n\n shell.destroy();\n }\n\n // =========================================================================\n // Boot\n // =========================================================================\n\n rebuildDerived();\n shell.renderChrome(compilation);\n renderLegend();\n\n const initial = compilation.initialHighlight;\n if (initial) {\n activeCategories = new Set(initial.values);\n recomputeHighlight();\n }\n\n // Read the SPEC's background, not the resolved theme's: the default resolved\n // theme also reports 'transparent', so keying off the compilation would make\n // every 3D graph see-through. 2D resolves transparent to an opaque surface\n // because it paints on a canvas; 3D honours it only when a host asked for it.\n const requestedBackground = themeBackground(options?.theme) ?? themeBackground(currentSpec.theme);\n if (requestedBackground === 'transparent') {\n graph.backgroundColor('rgba(0,0,0,0)');\n // The shared wrapper paints `--oc-bg` behind the canvas, which would defeat\n // the transparent renderer clear color.\n shell.wrapper.style.background = 'transparent';\n } else {\n graph.backgroundColor(resolvedSurface(compilation.theme));\n }\n\n graph\n .showNavInfo(false)\n .enableNodeDrag(false)\n .nodeId('id')\n // Our own DOM tooltip owns hover text; the library's built-in label would\n // render a second, unstyled one on top of it.\n .nodeLabel('')\n .linkLabel('')\n .nodeVal((d: Node3D) => d.node.radius)\n .nodeColor((d: Node3D) => d.node.fill)\n .nodeThreeObject((d: Node3D) => nodeObjectFor(d).group)\n .linkColor((d: Link3D) => d.edge.stroke)\n .linkWidth((d: Link3D) => (useLinkWidth ? d.edge.strokeWidth : 0))\n .linkThreeObject(linkThreeObject)\n .linkPositionUpdate((_obj, coords, d: Link3D) => {\n const link = linkObjects.get(d.key);\n // Returning false hands cylinder meshes back to the library, which\n // already positions them correctly (translate + scale.z + lookAt).\n return link ? updateLinkPosition(link, coords.start, coords.end) : false;\n })\n // Lines are thin hit targets compared with a 2D 5px hit band.\n .linkHoverPrecision(useLinkWidth ? 4 : 8);\n\n const { width, height } = shell.getSize();\n graph.width(width).height(height);\n\n applySimulationConfig(graph, compilation.simulationConfig, compilation.nodes.length);\n buildGraphData(seedPositions(compilation.nodes));\n\n // Resting opacities before the first frame, so nothing flashes at full alpha.\n for (const n of compilation.nodes) displayNodeAlpha.set(n.id, n.opacity);\n for (let i = 0; i < compilation.edges.length; i++) {\n displayEdgeAlpha.set(i, edgeBaseAlpha?.get(i) ?? 0.3);\n }\n armEmphasis();\n startEntrance();\n\n graph\n .onNodeHover((node) => {\n const id = node ? node.id : null;\n if (id === hoveredNodeId) return;\n hoveredNodeId = id;\n if (id && hoveredLinkIndex !== null) {\n hoveredLinkIndex = null;\n options?.onEdgeHover?.(null);\n }\n armEmphasis();\n options?.onNodeHover?.(id ? (nodeDataMap.get(id) ?? {}) : null);\n if (id && shell.tooltipManager) {\n openTooltip = { kind: 'node', id };\n anchorTooltip();\n } else {\n hideTooltip();\n }\n })\n .onLinkHover((link) => {\n const index = link ? link.edgeIndex : null;\n if (index === hoveredLinkIndex) return;\n // A live node hover owns the tooltip; don't let an edge steal it.\n if (hoveredNodeId !== null) return;\n hoveredLinkIndex = index;\n options?.onEdgeHover?.(link ? (link.edge.data ?? {}) : null);\n if (index !== null && shell.tooltipManager) {\n openTooltip = { kind: 'edge', index };\n anchorTooltip();\n } else {\n hideTooltip();\n }\n })\n .onNodeClick((node, event) => {\n handleNodeClick(node.id, Boolean(event?.shiftKey));\n })\n .onBackgroundClick(() => {\n if (selectedNodeIds.size === 0) return;\n selectedNodeIds = new Set();\n armEmphasis();\n options?.onSelectionChange?.([]);\n })\n .onEngineTick(() => {\n if (destroyed) return;\n // The node moves under a still pointer while the layout settles, so an\n // open tooltip has to follow it.\n anchorTooltip();\n if (autoFit && options?.fitOnLoad !== false) {\n const now = performance.now();\n if (now - lastAutoFit > AUTO_FIT_INTERVAL_MS) {\n lastAutoFit = now;\n fitNow({ duration: 0 });\n refreshLabels();\n }\n }\n })\n .onEngineStop(() => {\n if (destroyed) return;\n if (reheatActive) {\n reheatActive = false;\n endUpdateReheat(graph, compilation.simulationConfig, nodeData.length);\n }\n if (autoFit && options?.fitOnLoad !== false) {\n autoFit = false;\n fitNow({ duration: 0 });\n }\n refreshLabels();\n });\n\n // Orbiting changes both the label ranking and the tooltip anchor.\n controlsListener = (): void => {\n if (destroyed) return;\n scheduleLabelRank();\n anchorTooltip();\n queueCameraChange();\n };\n (graph.controls() as { addEventListener?(t: string, f: () => void): void }).addEventListener?.(\n 'change',\n controlsListener,\n );\n\n const canvasEl = graph.renderer()?.domElement;\n canvasEl?.addEventListener('dblclick', onCanvasDblClick);\n canvasEl?.addEventListener('pointerdown', onCanvasCameraInput);\n canvasEl?.addEventListener('wheel', onCanvasCameraInput, { passive: true });\n canvasEl?.addEventListener('webglcontextlost', onContextLost);\n\n const disconnectResize = shell.observeResize(() => doResize());\n\n return {\n update,\n // `update()` already routes an id-set-preserving change through the\n // material-mutation path, which never touches `graphData()`.\n updateVisuals: update,\n search,\n clearSearch,\n zoomToFit,\n zoomToNode,\n flyTo,\n centerAt: (x, y, opts) => flyTo({ x, y }, opts),\n getCamera,\n selectNode,\n getSelectedNodes: () => [...selectedNodeIds],\n getSearchMatches: () => [...(searchManager.getMatches() ?? [])],\n highlight,\n clearHighlight,\n getHighlight: () => (highlightSet ? [...highlightSet] : null),\n getLegend,\n setActiveCategories,\n getActiveCategories: () => [...activeCategories],\n resize: doResize,\n destroy,\n };\n}\n\n/**\n * Stable per-edge keys for the current edge list.\n *\n * Endpoints plus an occurrence counter, so two parallel A->B edges stay\n * distinct while an edge that merely moved position in the list keeps its\n * identity across a structural update.\n */\nfunction edgeKeys(edges: CompiledGraphEdge[]): string[] {\n const seen = new Map<string, number>();\n return edges.map((edge) => {\n const base = `${edge.source}\\u0000${edge.target}`;\n const n = seen.get(base) ?? 0;\n seen.set(base, n + 1);\n return `${base}\\u0000${n}`;\n });\n}\n\n/** Whatever a ghost is parented to: the node groups' parent, or the scene root. */\ntype GhostParent = { add(o: Object3D): unknown };\n\n/**\n * A mark that has left the data binding but is still on screen, fading out.\n *\n * The library deallocates any object whose datum leaves `graphData()`, so a\n * ghost cannot be the real scene object held back — it is a clone parked at the\n * departed mark's last position and owned entirely by us.\n */\ninterface Ghost {\n /** Scene object to detach when the fade ends. */\n object: Object3D;\n /** Materials the fade writes, with the alpha each starts from. */\n fades: Array<{ material: { opacity: number; transparent: boolean }; base: number }>;\n /** Release this ghost's GPU resources. */\n dispose(): void;\n}\n\n/**\n * The background a spec or mount option explicitly asked for.\n *\n * `ThemeConfig.colors` also accepts a bare palette array, which carries no\n * background, hence the shape guard.\n */\nfunction themeBackground(theme: ThemeConfig | undefined): string | undefined {\n const colors = theme?.colors;\n if (!colors || Array.isArray(colors)) return undefined;\n return typeof colors.background === 'string' ? colors.background : undefined;\n}\n","/**\n * Emphasis composition for the 3D renderer: turns the focus model into a\n * per-node and per-edge target opacity.\n *\n * The composition rules are NOT re-derived here. `composeStandingFocus` and\n * `layerHoverFocus` from `../graph/focus-transition` are the single source of\n * truth for \"highlight ∩ search, hover on top\" and are reused verbatim; this\n * module only maps the resulting {@link FocusSnapshot} onto opacity numbers,\n * mirroring the tier rules the 2D canvas renderer applies\n * (`graph/canvas-renderer.ts` `nodeTier` / `edgeTier` / `*TierAlpha`).\n *\n * The one 3D-specific input is `edgeBaseAlpha`: above `LINK_WIDTH_MAX_EDGES`\n * the renderer draws plain lines and maps `edgeWidth` to a resting alpha\n * instead of a cylinder radius, so the resting tier is per-edge rather than a\n * single constant.\n *\n * Pure: no three.js, no DOM.\n */\n\nimport type { FocusSnapshot } from '../graph/focus-transition';\n\n/** Resting edge alpha when nothing is emphasized (matches the 2D light tier). */\nexport const EDGE_ALPHA_DEFAULT = 0.3;\n/** Edge alpha when both endpoints are in the connected set. */\nexport const EDGE_ALPHA_CONNECTED = 1;\n/** Multiplier applied to nodes/edges outside an active search. */\nexport const SEARCH_NON_MATCH_ALPHA = 0.25;\n\n/** Minimal node shape the composition needs. */\nexport interface EmphasisNode {\n id: string;\n /** Compiled `nodeOpacity` encoding value; multiplies into the result. */\n opacity: number;\n}\n\n/** Minimal edge shape the composition needs. Index is the link object key. */\nexport interface EmphasisEdge {\n source: string;\n target: string;\n}\n\nexport interface EmphasisInput {\n nodes: EmphasisNode[];\n edges: EmphasisEdge[];\n /** The composed focus snapshot (standing state with the hover layer on top). */\n focus: FocusSnapshot;\n /** Ids that never dim (the spec's `seedNode`). */\n exemptIds: Set<string>;\n /** Resolved `interaction.hover.dimOpacity`. */\n dimOpacity: number;\n /** Per-edge resting alpha keyed by edge index; defaults to EDGE_ALPHA_DEFAULT. */\n edgeBaseAlpha?: Map<number, number>;\n}\n\nexport interface EmphasisTargets {\n /** Target material opacity by node id. */\n nodes: Map<string, number>;\n /** Target material opacity by edge index. */\n edges: Map<number, number>;\n}\n\n/**\n * Compose the focus snapshot into target opacities.\n *\n * Node rule (mirrors `nodeTier` + `nodeTierAlpha`): nothing active → 1; an\n * exempt id → 1; in the connected set → 1; otherwise `dimOpacity`. An active\n * search multiplies non-matching nodes by {@link SEARCH_NON_MATCH_ALPHA}, and\n * exemption deliberately does NOT apply to search (a seed that doesn't match\n * the query should not pretend to). The compiled `nodeOpacity` multiplies last.\n *\n * Edge rule (mirrors `edgeTier` + `edgeTierAlpha`): nothing active → the edge's\n * resting alpha; both endpoints connected → 1; otherwise `dimOpacity / 3`,\n * which preserves the node-to-edge dim ratio that keeps dense hairballs quiet.\n * Edges are NOT exempted by `exemptIds`: the seed stays lit while its edges dim\n * with everything else.\n */\nexport function resolveEmphasis(input: EmphasisInput): EmphasisTargets {\n const { focus, exemptIds, dimOpacity, edgeBaseAlpha } = input;\n const search = focus.searchMatches;\n\n const nodes = new Map<string, number>();\n for (const n of input.nodes) {\n let alpha = 1;\n if (focus.hasActive && !exemptIds.has(n.id) && !focus.connected.has(n.id)) {\n alpha = dimOpacity;\n }\n if (search !== null && !search.has(n.id)) alpha *= SEARCH_NON_MATCH_ALPHA;\n nodes.set(n.id, alpha * n.opacity);\n }\n\n const edges = new Map<number, number>();\n for (let i = 0; i < input.edges.length; i++) {\n const e = input.edges[i];\n const resting = edgeBaseAlpha?.get(i) ?? EDGE_ALPHA_DEFAULT;\n let alpha: number;\n if (!focus.hasActive) {\n alpha = resting;\n } else if (focus.connected.has(e.source) && focus.connected.has(e.target)) {\n alpha = EDGE_ALPHA_CONNECTED;\n } else {\n alpha = dimOpacity / 3;\n }\n if (search !== null && !search.has(e.source) && !search.has(e.target)) {\n alpha *= SEARCH_NON_MATCH_ALPHA;\n }\n edges.set(i, alpha);\n }\n\n return { nodes, edges };\n}\n","/**\n * Entrance choreography timing for the 3D renderer.\n *\n * Pure scheduling math: which node starts revealing when, and how far along its\n * own pop a node is at a given wall-clock offset. The mount owns the tween, the\n * materials and the camera; this module owns nothing but numbers, so the\n * stagger can be tested without a WebGL scene.\n *\n * The stagger rank comes from 2D's `entranceOrder` (a deterministic hash\n * shuffle of the node ids), so a spec reveals in the same order in both\n * dimensions. 2D's companion `entranceOffsets` deliberately does NOT port:\n * three-forcegraph writes each node group's position from its datum on every\n * engine tick, so a convergence drift we wrote into `group.position` would be\n * clobbered before it was ever painted. 3D pops in scale and opacity only.\n */\n\nimport { entranceOrder } from '../graph/entrance';\n\n/**\n * How much of `enter.duration` the stagger spreads start times across. The last\n * node therefore begins at `0.5 · duration` and finishes at `1.5 · duration`.\n * Wider reads as a slow drip on a hundred-node graph; narrower stops reading as\n * a stagger at all.\n */\nexport const ENTRANCE_STAGGER_FRACTION = 0.5;\n\n/**\n * How long after the later of its two endpoints a link starts fading in, as a\n * fraction of `enter.duration`. An edge that arrives with its endpoints reads\n * as a mesh snapping into place; a beat later reads as the structure being\n * drawn on.\n */\nexport const LINK_ENTRANCE_DELAY = 0.25;\n\n/** The camera's starting standoff, as a multiple of the fit distance. */\nexport const ENTRANCE_CAMERA_PULLBACK = 1.6;\n\n/** Start times for every node and link, plus the total wall time they span. */\nexport interface EntrancePlan {\n /** ms from entrance start at which each node begins its pop. */\n nodeOffset: Map<string, number>;\n /** ms from entrance start at which each link begins its fade, by edge index. */\n linkOffset: Map<number, number>;\n /** Total choreography length: `duration + max(offset)`. */\n span: number;\n}\n\n/**\n * Plan the reveal.\n *\n * With `stagger` off every offset is 0 and the whole graph fades as one, which\n * is also what happens above `ENTRANCE_STAGGER_MAX_NODES` (the caller applies\n * that gate, mirroring 2D).\n */\nexport function planEntrance(\n nodes: Array<{ id: string; x: number; y: number }>,\n edges: Array<{ source: string; target: string }>,\n duration: number,\n stagger: boolean,\n): EntrancePlan {\n const nodeOffset = new Map<string, number>();\n const linkOffset = new Map<number, number>();\n\n if (!stagger || nodes.length === 0) {\n for (const n of nodes) nodeOffset.set(n.id, 0);\n for (let i = 0; i < edges.length; i++) linkOffset.set(i, duration * LINK_ENTRANCE_DELAY);\n return { nodeOffset, linkOffset, span: duration * (1 + LINK_ENTRANCE_DELAY) };\n }\n\n const rank = entranceOrder(nodes);\n const last = Math.max(1, nodes.length - 1);\n const spread = duration * ENTRANCE_STAGGER_FRACTION;\n let maxOffset = 0;\n for (const n of nodes) {\n const offset = ((rank.get(n.id) ?? 0) / last) * spread;\n nodeOffset.set(n.id, offset);\n if (offset > maxOffset) maxOffset = offset;\n }\n\n // A link waits for the later of its endpoints, then a beat.\n const delay = duration * LINK_ENTRANCE_DELAY;\n for (let i = 0; i < edges.length; i++) {\n const a = nodeOffset.get(edges[i].source) ?? 0;\n const b = nodeOffset.get(edges[i].target) ?? 0;\n const offset = Math.max(a, b) + delay;\n linkOffset.set(i, offset);\n if (offset > maxOffset) maxOffset = offset;\n }\n\n return { nodeOffset, linkOffset, span: duration + maxOffset };\n}\n\n/**\n * A single element's eased progress through its own window at `elapsed` ms.\n *\n * Returns 0 before the element's start and 1 once its `duration` has run, so a\n * caller can read every element from one global clock.\n */\nexport function elementProgress(\n elapsed: number,\n offset: number,\n duration: number,\n ease: (t: number) => number,\n): number {\n if (duration <= 0) return elapsed >= offset ? 1 : 0;\n const local = (elapsed - offset) / duration;\n if (local <= 0) return 0;\n if (local >= 1) return 1;\n return ease(local);\n}\n","/**\n * Camera framing math for the 3D renderer.\n *\n * Split out of `mount.ts` because it is pure vector arithmetic over positions\n * the mount already has: no three.js, no library instance, no DOM. The mount\n * reads the camera and hands the numbers in.\n */\n\n/** A point or direction in scene space. */\nexport interface Vec3 {\n x: number;\n y: number;\n z: number;\n}\n\n/** A node as the fit sees it: a position and the radius to keep in frame. */\nexport interface FitPoint {\n x: number;\n y: number;\n z: number;\n radius: number;\n}\n\n/** Where the camera should sit, and what it should look at. */\nexport interface FitResult {\n center: Vec3;\n /** Unit vector from `center` towards the camera. */\n dir: Vec3;\n distance: number;\n}\n\n/** Inputs the mount reads off the live camera and shell. */\nexport interface FitView {\n cameraPos: Vec3;\n /** Vertical field of view, in degrees. */\n fovDeg: number;\n /** Viewport width / height. */\n aspect: number;\n /** Viewport height in pixels, used to turn pixel padding into a ratio. */\n viewportHeight: number;\n /** Breathing room around the cloud, in pixels of viewport height. */\n paddingPx: number;\n}\n\n/** Floor on the fit distance so a single-node graph does not sit on the camera. */\nconst MIN_FIT_DISTANCE = 60;\n\nexport function normalize(v: Vec3): Vec3 {\n const len = Math.hypot(v.x, v.y, v.z);\n // A degenerate direction (camera sitting on its target) falls back to +z,\n // the library's own default framing axis.\n if (!(len > 1e-6)) return { x: 0, y: 0, z: 1 };\n return { x: v.x / len, y: v.y / len, z: v.z / len };\n}\n\nexport function cross(a: Vec3, b: Vec3): Vec3 {\n return {\n x: a.y * b.z - a.z * b.y,\n y: a.z * b.x - a.x * b.z,\n z: a.x * b.y - a.y * b.x,\n };\n}\n\n/**\n * Camera placement that frames every point from the current viewing angle.\n *\n * Deliberately not `graph.zoomToFit()`: three-render-objects computes its\n * distance as `maxBoxSide / Math.atan(fov)` where the geometry calls for\n * `maxBoxSide / (2 * Math.tan(fov / 2))`, so it lands the camera about twice as\n * far out as it needs to be, and it frames the bbox as a cube centred on the\n * world origin, so depth inflates the height and an off-centre layout drifts\n * out of frame. This measures the cloud in the camera's own basis instead: how\n * wide and tall it actually appears, plus how much of it sits in front.\n *\n * Returns null for an empty or non-finite cloud.\n */\nexport function computeFit(points: FitPoint[], view: FitView): FitResult | null {\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let minZ = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n let maxZ = Number.NEGATIVE_INFINITY;\n for (const p of points) {\n if (p.x < minX) minX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.z < minZ) minZ = p.z;\n if (p.x > maxX) maxX = p.x;\n if (p.y > maxY) maxY = p.y;\n if (p.z > maxZ) maxZ = p.z;\n }\n // All six extents, not just minX: an empty cloud leaves every one at its\n // sentinel, but a single NaN or Infinity coordinate poisons only the extents\n // it touches, and any one of them would carry through into center/distance.\n if (\n !Number.isFinite(minX) ||\n !Number.isFinite(minY) ||\n !Number.isFinite(minZ) ||\n !Number.isFinite(maxX) ||\n !Number.isFinite(maxY) ||\n !Number.isFinite(maxZ)\n ) {\n return null;\n }\n\n const center = { x: (minX + maxX) / 2, y: (minY + maxY) / 2, z: (minZ + maxZ) / 2 };\n // Keep the viewing angle the viewer already chose; only the distance moves.\n const dir = normalize({\n x: view.cameraPos.x - center.x,\n y: view.cameraPos.y - center.y,\n z: view.cameraPos.z - center.z,\n });\n // Screen right/up for that direction. World +y is the camera's up axis unless\n // the view is looking straight down it, where +z takes over.\n const worldUp = Math.abs(dir.y) > 0.999 ? { x: 0, y: 0, z: 1 } : { x: 0, y: 1, z: 0 };\n const right = normalize(cross(worldUp, dir));\n const up = cross(dir, right);\n\n let halfRight = 0;\n let halfUp = 0;\n let nearDepth = 0;\n for (const p of points) {\n const dx = p.x - center.x;\n const dy = p.y - center.y;\n const dz = p.z - center.z;\n halfRight = Math.max(\n halfRight,\n Math.abs(dx * right.x + dy * right.y + dz * right.z) + p.radius,\n );\n halfUp = Math.max(halfUp, Math.abs(dx * up.x + dy * up.y + dz * up.z) + p.radius);\n // Only depth towards the camera pushes it back; the far side is free.\n nearDepth = Math.max(nearDepth, dx * dir.x + dy * dir.y + dz * dir.z + p.radius);\n }\n\n const vFov = (view.fovDeg * Math.PI) / 180;\n const hFov = 2 * Math.atan(Math.tan(vFov / 2) * view.aspect);\n // Padding is quoted in pixels; express it as a share of the viewport so the\n // margin holds at any canvas size.\n const slack = 1 + Math.min(0.5, (2 * view.paddingPx) / (view.viewportHeight || 1));\n const distance =\n Math.max(\n (halfUp * slack) / Math.tan(vFov / 2),\n (halfRight * slack) / Math.tan(hFov / 2),\n MIN_FIT_DISTANCE,\n ) + Math.max(0, nearDepth);\n return { center, dir, distance };\n}\n","/**\n * Force wiring for the 3D simulation.\n *\n * 3d-force-graph owns a d3-force-3d simulation on the main thread; we do not\n * drive it and we do not port the 2D web worker here. This module maps the\n * engine's `SimulationConfig` onto that simulation's knobs and supplies the one\n * force d3-force-3d does not ship: the community cluster force, which is a\n * 3-axis port of `forceCluster` in `../graph/simulation.ts`. Both copies must\n * stay in sync.\n */\n\nimport type { SimulationConfig } from '@opendata-ai/openchart-engine';\nimport { forceCenter, forceCollide } from 'd3-force-3d';\nimport type { Graph3D, Node3D } from './types';\n\n/** d3's default `alphaMin`, the alpha at which a simulation stops. */\nconst DEFAULT_ALPHA_MIN = 0.001;\n/** Ceiling on the derived cooldown tick count (guards a tiny alphaDecay). */\nconst MAX_COOLDOWN_TICKS = 800;\n/** Collision solver iterations; 1 is d3's cheap default and enough at our scale. */\nconst COLLIDE_ITERATIONS = 1;\n\n/**\n * Measured cost of one simulation tick, per node, in ms (charge + link +\n * collide + cluster on a 3,000-node graph). Used to convert the engine's\n * wall-clock warmup budget into a tick count.\n */\nconst WARMUP_MS_PER_NODE_TICK = 0.0035;\n\n/** Never warm up for fewer ticks than this; below it the first frame explodes. */\nconst MIN_WARMUP_TICKS = 5;\n\n/** Fallback budget when the compilation does not carry one. */\nconst DEFAULT_WARMUP_BUDGET_MS = 250;\n\n/**\n * Warmup ticks that fit the wall-clock budget.\n *\n * The library runs `warmupTicks` synchronously inside its first digest, so the\n * engine's default of 100 blocks the main thread for ~3s on a 3,000-node graph.\n * 2D solves this with `warmupBudgetMs` inside its own tick loop; here the loop\n * belongs to the library, so the budget has to be converted to a tick count up\n * front from the measured per-node cost.\n */\nexport function budgetedWarmupTicks(\n requested: number,\n nodeCount: number,\n budgetMs = DEFAULT_WARMUP_BUDGET_MS,\n): number {\n if (requested <= 0 || nodeCount <= 0) return Math.max(0, requested);\n const perTickMs = Math.max(WARMUP_MS_PER_NODE_TICK * nodeCount, 0.05);\n const affordable = Math.floor(budgetMs / perTickMs);\n return Math.max(MIN_WARMUP_TICKS, Math.min(requested, affordable));\n}\n\n/**\n * Ticks needed to cool from alpha 1 to `alphaMin` at a given `alphaDecay`.\n * Deliberately duplicated from `graph/simulation.ts` `ticksToAlphaMin` rather\n * than imported: that module pulls in the whole 2D d3-force graph, which would\n * land in the `graph-3d` bundle for four lines of arithmetic.\n */\nexport function ticksToSettle(alphaDecay: number, alphaMin = DEFAULT_ALPHA_MIN): number {\n if (!(alphaDecay > 0) || alphaDecay >= 1) return MAX_COOLDOWN_TICKS;\n const n = Math.ceil(Math.log(alphaMin) / Math.log(1 - alphaDecay));\n return Math.min(MAX_COOLDOWN_TICKS, Math.max(1, n));\n}\n\n/**\n * A community cluster force in three axes: each node is pulled toward the\n * centroid of its own community, scaled by `strength * alpha`. Port of\n * `forceCluster` in `../graph/simulation.ts` with the z axis added.\n *\n * Uses d3's `initialize` hook rather than closing over a node array, so the\n * force keeps working after `graphData()` swaps the node objects.\n */\nexport function forceCluster3D(strength: number): (alpha: number) => void {\n let nodes: Node3D[] = [];\n\n const force = (alpha: number): void => {\n const cx = new Map<string, number>();\n const cy = new Map<string, number>();\n const cz = new Map<string, number>();\n const count = new Map<string, number>();\n\n for (const node of nodes) {\n const c = node.node?.community;\n if (!c) continue;\n cx.set(c, (cx.get(c) ?? 0) + (node.x ?? 0));\n cy.set(c, (cy.get(c) ?? 0) + (node.y ?? 0));\n cz.set(c, (cz.get(c) ?? 0) + (node.z ?? 0));\n count.set(c, (count.get(c) ?? 0) + 1);\n }\n\n for (const [c, n] of count) {\n cx.set(c, cx.get(c)! / n);\n cy.set(c, cy.get(c)! / n);\n cz.set(c, cz.get(c)! / n);\n }\n\n const k = strength * alpha;\n for (const node of nodes) {\n const c = node.node?.community;\n if (!c) continue;\n node.vx = (node.vx ?? 0) + (cx.get(c)! - (node.x ?? 0)) * k;\n node.vy = (node.vy ?? 0) + (cy.get(c)! - (node.y ?? 0)) * k;\n node.vz = (node.vz ?? 0) + (cz.get(c)! - (node.z ?? 0)) * k;\n }\n };\n\n force.initialize = (ns: Node3D[]): void => {\n nodes = ns;\n };\n\n return force;\n}\n\n/**\n * Apply the compiled `SimulationConfig` to a 3d-force-graph instance.\n *\n * `cooldownTicks` is derived from `alphaDecay` so live ticking is bounded: the\n * simulation runs on the main thread here, and an unbounded cooldown keeps a\n * 3000-node graph re-rendering forever. `warmupTicks` gets the same treatment\n * through {@link budgetedWarmupTicks}, since the library runs them all in one\n * synchronous block.\n */\nexport interface ApplySimulationOptions {\n /**\n * Warmup tick override. The structural `update()` passes 0: the library runs\n * `warmupTicks` in ONE synchronous block inside its digest, which lands the\n * whole re-layout in a single frame instead of animating it.\n */\n warmupTicks?: number;\n}\n\nexport function applySimulationConfig(\n graph: Graph3D,\n config: SimulationConfig,\n nodeCount: number,\n opts: ApplySimulationOptions = {},\n): void {\n const charge = graph.d3Force('charge');\n if (charge && typeof (charge as { strength?: unknown }).strength === 'function') {\n (charge as unknown as { strength(v: number): unknown }).strength(config.chargeStrength);\n }\n\n const link = graph.d3Force('link');\n if (link) {\n const l = link as unknown as {\n distance?(v: number): unknown;\n strength?(v: number): unknown;\n };\n l.distance?.(config.linkDistance);\n if (config.linkStrength !== undefined) l.strength?.(config.linkStrength);\n }\n\n const padding = config.collisionPadding ?? 2;\n graph.d3Force(\n 'collide',\n forceCollide<Node3D>((n) => (n.node?.radius ?? 1) + padding).iterations(COLLIDE_ITERATIONS),\n );\n\n graph.d3Force('cluster', config.clustering ? forceCluster3D(config.clustering.strength) : null);\n\n // 3d-force-graph installs a `center` force of its own, and only in its\n // constructor, so honouring `centerForce: false` means removing it -- and a\n // later update that turns centering back on has to install a replacement,\n // because the library will never do it again.\n graph.d3Force(\n 'center',\n config.centerForce === false ? null : (graph.d3Force('center') ?? forceCenter()),\n );\n\n graph.d3VelocityDecay(config.velocityDecay);\n graph.d3AlphaDecay(config.alphaDecay);\n graph.warmupTicks(opts.warmupTicks ?? configuredWarmupTicks(config, nodeCount));\n graph.cooldownTicks(ticksToSettle(config.alphaDecay));\n}\n\n/** The warmup tick count this config resolves to at this node count. */\nfunction configuredWarmupTicks(config: SimulationConfig, nodeCount: number): number {\n return budgetedWarmupTicks(config.warmupTicks ?? 0, nodeCount, config.warmupBudgetMs);\n}\n\n/**\n * Damp a structural update's reheat so it reads as a local settle rather than a\n * global explosion.\n *\n * 2D restarts its own simulation at `reheatAlpha(diff)`. Here the simulation\n * belongs to the library: `graphData()` unconditionally does `.stop().alpha(1)`\n * and three-forcegraph exposes no alpha setter (`d3Force` hands back forces, not\n * the simulation), so the impulse has to be scaled from the outside. Two knobs\n * do it, and they were picked over pinning survivors with `fx/fy/fz` because a\n * pin release mid-cooldown is itself a visible jump:\n *\n * - The `center` force is dropped for the duration, mirroring 2D's\n * `suppressCenter`. Otherwise the whole cloud translates toward the origin\n * while the delta settles, which reads as the graph jumping.\n * - `velocityDecay` is raised so the fraction of velocity a node keeps per tick\n * is scaled by `initialAlpha`. Every force left in the set writes velocities\n * and is linear in alpha, so scaling the retained velocity is a first-order\n * stand-in for scaling alpha itself.\n *\n * {@link endUpdateReheat} puts both back on `onEngineStop`.\n */\nexport function beginUpdateReheat(\n graph: Graph3D,\n config: SimulationConfig,\n initialAlpha: number,\n): void {\n graph.d3Force('center', null);\n const scale = Math.min(1, Math.max(0, initialAlpha));\n graph.d3VelocityDecay(1 - (1 - config.velocityDecay) * scale);\n}\n\n/**\n * Undo {@link beginUpdateReheat} and restore the config's warmup budget, which\n * the update call zeroed. Runs on `onEngineStop`, not straight after\n * `graphData()`: Kapsule's digest is debounced, so the library reads\n * `warmupTicks` a tick LATER than the call that set it.\n */\nexport function endUpdateReheat(graph: Graph3D, config: SimulationConfig, nodeCount: number): void {\n graph.d3VelocityDecay(config.velocityDecay);\n graph.warmupTicks(configuredWarmupTicks(config, nodeCount));\n if (config.centerForce !== false) {\n graph.d3Force('center', graph.d3Force('center') ?? forceCenter());\n }\n}\n","/**\n * Label visibility policy for the 3D renderer.\n *\n * Same two-stage shape as 2D: rank, then declutter. This module is the ranking\n * half — forced labels (seed / `alwaysShowLabel` overrides, hovered, selected,\n * search matches) first, then the highest-priority nodes, nearest camera first,\n * up to the budget. The mount does the screen-space overlap pass on the ordered\n * result, which is why the order matters and not just the membership.\n *\n * Pure: no three.js, no DOM. The mount feeds it plain positions.\n */\n\n/** A label candidate: a node with a resolved label and a compiled priority. */\nexport interface LabelCandidate {\n id: string;\n /** Compiled `labelPriority` (0..1, degree-derived). Higher wins. */\n priority: number;\n x: number;\n y: number;\n z: number;\n}\n\n/** Default 3D label budget beyond the forced set. */\nexport const LABEL_BUDGET_3D = 40;\n\n/**\n * Node ids whose label sprite may be shown, best first.\n *\n * `forced` ids lead the list and do NOT consume budget. The rest are ranked by\n * `(priority desc, camera distance asc)` and the top `budget` follow.\n */\nexport function resolveVisibleLabels(\n nodes: LabelCandidate[],\n forced: Set<string>,\n cameraPos: { x: number; y: number; z: number },\n budget: number,\n): string[] {\n const visible: string[] = [];\n for (const n of nodes) if (forced.has(n.id)) visible.push(n.id);\n if (budget <= 0) return visible;\n\n const ranked: Array<{ id: string; priority: number; dist: number }> = [];\n for (const n of nodes) {\n if (forced.has(n.id)) continue;\n const dx = n.x - cameraPos.x;\n const dy = n.y - cameraPos.y;\n const dz = n.z - cameraPos.z;\n ranked.push({ id: n.id, priority: n.priority, dist: dx * dx + dy * dy + dz * dz });\n }\n\n // Priority first (the compiled importance ordering), distance as the\n // tiebreaker, id last so the result is stable for equal-priority nodes at\n // equal distance (the seeded-layout stories rely on a deterministic set).\n ranked.sort((a, b) => b.priority - a.priority || a.dist - b.dist || (a.id < b.id ? -1 : 1));\n\n for (let i = 0; i < ranked.length && i < budget; i++) visible.push(ranked[i].id);\n return visible;\n}\n\n/** A label's screen-space footprint, in pixels. */\nexport interface LabelBox {\n x0: number;\n x1: number;\n y0: number;\n y1: number;\n}\n\n/** Breathing room around a label box in the declutter pass, in pixels. */\nconst LABEL_BOX_PAD = 2;\n\n/**\n * The screen box a label would occupy, or null when the point did not project.\n *\n * Width is estimated from the glyph count rather than measured: a sprite has no\n * `measureText`, and 2D's own fallback in `graph/canvas-renderer.ts` uses the\n * same 0.55 average-glyph ratio.\n */\nexport function labelBox(\n projected: { x: number; y: number } | null | undefined,\n text: string,\n heightPx: number,\n): LabelBox | null {\n if (!projected || !Number.isFinite(projected.x) || !Number.isFinite(projected.y)) return null;\n const width = Math.max(1, text.length) * heightPx * 0.55;\n // The sprite sits above the node; the exact offset only has to be consistent,\n // since every box uses it.\n const cy = projected.y - heightPx;\n return {\n x0: projected.x - width / 2 - LABEL_BOX_PAD,\n x1: projected.x + width / 2 + LABEL_BOX_PAD,\n y0: cy - heightPx / 2 - LABEL_BOX_PAD,\n y1: cy + heightPx / 2 + LABEL_BOX_PAD,\n };\n}\n\nexport function overlaps(a: LabelBox, b: LabelBox): boolean {\n return a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0;\n}\n","/**\n * Per-link three.js objects.\n *\n * Same reason as `nodes.ts`: `linkOpacity` is a single global number in\n * 3d-force-graph, so per-edge hover dimming needs each link to own its\n * material. (An rgba `linkColor` IS honoured for per-link opacity — see\n * `../graph-3d/README` notes in the RFC report — but changing it only takes\n * effect on a `refresh()`, which rebuilds every link object. That is exactly\n * what hover must not do.)\n *\n * Two shapes: a `Line` (constant screen width, cheap) and a cylinder `Mesh`\n * (true world-space width, needed when `edgeWidth` is encoded). Above\n * `LINK_WIDTH_MAX_EDGES` the mount stays on lines and maps `edgeWidth` to a\n * resting alpha instead.\n *\n * Positioning: three-forcegraph's own tick loop positions custom `Line` and\n * `Mesh` link objects generically (it branches on `obj.type` and, for meshes,\n * on the geometry already being a `CylinderGeometry`). The mount still installs\n * a `linkPositionUpdate` because dashed lines need `computeLineDistances()`\n * after every move, which the library's path does not call.\n */\n\nimport type { CompiledGraphEdge } from '@opendata-ai/openchart-engine';\nimport {\n BufferAttribute,\n BufferGeometry,\n CylinderGeometry,\n Line,\n LineBasicMaterial,\n LineDashedMaterial,\n Matrix4,\n Mesh,\n MeshLambertMaterial,\n} from 'three';\n\n/** Above this edge count `edgeWidth` maps to opacity steps, not cylinder radius. */\nexport const LINK_WIDTH_MAX_EDGES = 2000;\n/** Cylinder tessellation for width-encoded edges. */\nconst CYLINDER_SEGMENTS = 6;\n/** Dash geometry in world units for `edgeStyle: 'dashed' | 'dotted'`. */\nconst DASH = { dashed: { dash: 6, gap: 4 }, dotted: { dash: 1.5, gap: 3 } };\n\n/** A link's scene object plus the resources the mount must dispose. */\nexport interface LinkObject3D {\n object: Line | Mesh;\n geometry: BufferGeometry | CylinderGeometry;\n material: LineBasicMaterial | LineDashedMaterial | MeshLambertMaterial;\n /** True when the material needs `computeLineDistances()` after every move. */\n dashed: boolean;\n}\n\n/** True when `edge` asks for a dashed or dotted line material. */\nfunction isDashed(edge: CompiledGraphEdge): boolean {\n return edge.style === 'dashed' || edge.style === 'dotted';\n}\n\n/** Cylinder radius for a width-encoded edge. */\nfunction cylinderRadius(edge: CompiledGraphEdge): number {\n return Math.max(edge.strokeWidth, 0.1) / 2;\n}\n\nfunction buildCylinder(edge: CompiledGraphEdge): CylinderGeometry {\n const r = cylinderRadius(edge);\n const geometry = new CylinderGeometry(r, r, 1, CYLINDER_SEGMENTS, 1, false);\n // Match the library's cylinder convention: origin at the start point,\n // length along +z, so its tick loop can position with scale.z + lookAt.\n geometry.applyMatrix4(new Matrix4().makeTranslation(0, 0.5, 0));\n geometry.applyMatrix4(new Matrix4().makeRotationX(Math.PI / 2));\n return geometry;\n}\n\nfunction buildLineMaterial(\n edge: CompiledGraphEdge,\n opacity: number,\n): LineBasicMaterial | LineDashedMaterial {\n if (!isDashed(edge)) {\n return new LineBasicMaterial({\n color: edge.stroke,\n transparent: true,\n opacity,\n depthWrite: false,\n });\n }\n const dash = DASH[edge.style as 'dashed' | 'dotted'];\n return new LineDashedMaterial({\n color: edge.stroke,\n transparent: true,\n opacity,\n depthWrite: false,\n dashSize: dash.dash,\n gapSize: dash.gap,\n });\n}\n\n/**\n * Build the scene object for one compiled edge.\n *\n * @param useWidth - Render a cylinder of `edge.strokeWidth` instead of a line.\n * @param restingAlpha - Starting material opacity (the emphasis pass retargets it).\n */\nexport function createLinkObject(\n edge: CompiledGraphEdge,\n useWidth: boolean,\n restingAlpha: number,\n): LinkObject3D {\n if (useWidth) {\n const geometry = buildCylinder(edge);\n const material = new MeshLambertMaterial({\n color: edge.stroke,\n transparent: true,\n opacity: restingAlpha,\n depthWrite: false,\n });\n return { object: new Mesh(geometry, material), geometry, material, dashed: false };\n }\n\n const geometry = new BufferGeometry();\n geometry.setAttribute('position', new BufferAttribute(new Float32Array(2 * 3), 3));\n const material = buildLineMaterial(edge, restingAlpha);\n return { object: new Line(geometry, material), geometry, material, dashed: isDashed(edge) };\n}\n\n/**\n * Re-apply compiled visuals to an existing link object, in place.\n *\n * The visual-only update path must not touch `graphData()`, and it cannot swap\n * the scene object either: the library binds objects to datums by identity, so\n * a replacement object would never reach the scene. Geometry and material CAN\n * be swapped under the same object, which is what a changed cylinder radius or\n * a changed dash pattern needs; the old resource is disposed here.\n *\n * The shape class (line vs cylinder) is the one thing that cannot change in\n * place — see {@link linkShapeMatches}.\n */\nexport function applyLinkVisuals(\n obj: LinkObject3D,\n edge: CompiledGraphEdge,\n useWidth: boolean,\n): void {\n obj.material.color.set(edge.stroke);\n\n if (useWidth) {\n const r = cylinderRadius(edge);\n if ((obj.geometry as CylinderGeometry).parameters.radiusTop === r) return;\n const next = buildCylinder(edge);\n (obj.object as Mesh).geometry = next;\n obj.geometry.dispose();\n obj.geometry = next;\n return;\n }\n\n const dashed = isDashed(edge);\n const current = obj.material as LineDashedMaterial;\n const dash = dashed ? DASH[edge.style as 'dashed' | 'dotted'] : null;\n const sameDash =\n dashed === obj.dashed &&\n (dash === null || (current.dashSize === dash.dash && current.gapSize === dash.gap));\n if (sameDash) return;\n\n // Carry the live opacity across so a style change mid-hover doesn't flash;\n // the emphasis pass retargets it on the next paint anyway.\n const next = buildLineMaterial(edge, obj.material.opacity);\n (obj.object as Line).material = next;\n obj.material.dispose();\n obj.material = next;\n obj.dashed = dashed;\n if (dashed) (obj.object as Line).computeLineDistances();\n}\n\n/**\n * Whether an existing link object still has the right shape class for the\n * current `useLinkWidth` setting. A mismatch means the object has to be rebuilt\n * through `graphData()`, because the scene binding is by object identity.\n */\nexport function linkShapeMatches(obj: LinkObject3D, useWidth: boolean): boolean {\n return useWidth ? obj.object instanceof Mesh : obj.object instanceof Line;\n}\n\n/**\n * Write a straight segment into a line link's position buffer.\n *\n * Returns true when it handled the object (the mount reports that back to\n * `linkPositionUpdate` so the library skips its own pass); false for cylinder\n * meshes, which the library positions correctly on its own.\n */\nexport function updateLinkPosition(\n obj: LinkObject3D,\n start: { x: number; y: number; z: number },\n end: { x: number; y: number; z: number },\n): boolean {\n if (!(obj.object instanceof Line)) return false;\n const attr = obj.geometry.getAttribute('position') as BufferAttribute;\n const a = attr.array as Float32Array;\n a[0] = start.x;\n a[1] = start.y || 0;\n a[2] = start.z || 0;\n a[3] = end.x;\n a[4] = end.y || 0;\n a[5] = end.z || 0;\n attr.needsUpdate = true;\n obj.geometry.computeBoundingSphere();\n // A dashed material renders nothing until line distances exist, and they go\n // stale on every tick because the endpoints move.\n if (obj.dashed) (obj.object as Line).computeLineDistances();\n return true;\n}\n\n/**\n * Park a link object on a fixed segment, outside the library's tick loop.\n *\n * `updateLinkPosition` deliberately reports cylinders back to the library, which\n * owns their placement. An exit ghost has left the data binding, so nothing\n * positions it any more and both shape classes have to be handled here.\n */\nexport function placeGhostLink(\n obj: LinkObject3D,\n start: { x: number; y: number; z: number },\n end: { x: number; y: number; z: number },\n): void {\n if (updateLinkPosition(obj, start, end)) return;\n const mesh = obj.object as Mesh;\n mesh.position.set(start.x, start.y, start.z);\n mesh.lookAt(end.x, end.y, end.z);\n mesh.scale.z = Math.hypot(end.x - start.x, end.y - start.y, end.z - start.z);\n}\n\n/** Dispose every GPU resource this link object owns. */\nexport function disposeLinkObject(obj: LinkObject3D): void {\n obj.geometry.dispose();\n obj.material.dispose();\n}\n\n/**\n * Resting alpha per edge when `edgeWidth` is encoded but the graph is too big\n * for cylinders: map the compiled stroke width onto four opacity steps between\n * `min` and 1. Quantized so a hairball reads as tiers rather than noise.\n */\nexport function widthAsAlpha(edges: CompiledGraphEdge[], min = 0.15): Map<number, number> {\n const out = new Map<number, number>();\n if (edges.length === 0) return out;\n let lo = Number.POSITIVE_INFINITY;\n let hi = Number.NEGATIVE_INFINITY;\n for (const e of edges) {\n if (e.strokeWidth < lo) lo = e.strokeWidth;\n if (e.strokeWidth > hi) hi = e.strokeWidth;\n }\n const span = hi - lo;\n for (let i = 0; i < edges.length; i++) {\n const t = span > 0 ? (edges[i].strokeWidth - lo) / span : 1;\n const step = Math.round(t * 3) / 3;\n out.set(i, min + (1 - min) * step);\n }\n return out;\n}\n","/**\n * Per-node three.js objects.\n *\n * 3d-force-graph's built-in node rendering is a shared sphere material cached\n * by color string, and `nodeOpacity` is a single global number. Highlight, dim,\n * hover and entrance all need PER-NODE opacity, so every node gets its own\n * `MeshLambertMaterial` here. That is also the only way to get always-on labels:\n * `nodeLabel` is hover-only in the library.\n *\n * Everything allocated in this module is owned by the mount and disposed in\n * `destroy()` — three.js geometries, materials and canvas-backed sprite\n * textures are all outside the GC's reach.\n */\n\nimport type { CompiledGraphNode } from '@opendata-ai/openchart-engine';\nimport { Group, Mesh, MeshLambertMaterial, SphereGeometry } from 'three';\nimport SpriteText from 'three-spritetext';\n\n/** Sphere tessellation. 12 is the library's default and reads smooth at our radii. */\nconst SPHERE_SEGMENTS = 12;\n/** Sprite cap height in world units, before the screen-size correction. */\nexport const LABEL_TEXT_HEIGHT = 4;\n/** Gap between the sphere surface and the label baseline, in world units. */\nconst LABEL_GAP = 2;\n\n/** A node's scene object plus the resources the mount must dispose. */\nexport interface NodeObject3D {\n group: Group;\n mesh: Mesh;\n geometry: SphereGeometry;\n material: MeshLambertMaterial;\n /** Null until {@link ensureLabelSprite} builds it, and for unlabelled nodes. */\n sprite: SpriteText | null;\n /** Compiled label text, or null when the node has none. */\n labelText: string | null;\n /** Resolved label colour, applied when the sprite is built. */\n labelColor: string;\n /** Sprite offset above the node centre, in world units. */\n labelOffset: number;\n /**\n * The sprite's scale at {@link LABEL_TEXT_HEIGHT}, captured because\n * `three-spritetext` only writes `scale` when it regenerates its texture, so\n * the perspective correction in the mount can multiply it every re-rank\n * without paying for a canvas redraw.\n */\n labelBaseScale: { x: number; y: number } | null;\n}\n\n/**\n * Build the scene object for one compiled node: a `Group` holding a sphere\n * `Mesh`. The label sprite is NOT built here — see {@link ensureLabelSprite}.\n */\nexport function createNodeObject(node: CompiledGraphNode, labelColor: string): NodeObject3D {\n const geometry = new SphereGeometry(node.radius, SPHERE_SEGMENTS, SPHERE_SEGMENTS);\n const material = new MeshLambertMaterial({\n color: node.fill,\n transparent: true,\n opacity: node.opacity,\n });\n const mesh = new Mesh(geometry, material);\n\n const group = new Group();\n group.add(mesh);\n\n return {\n group,\n mesh,\n geometry,\n material,\n sprite: null,\n labelBaseScale: null,\n labelText: node.label ?? null,\n labelColor,\n labelOffset: node.radius + LABEL_GAP,\n };\n}\n\n/**\n * Build this node's label sprite on first use, or return the existing one.\n *\n * Sprites are created lazily because they are not free to keep around: a\n * 3,000-node graph that eagerly built one sprite per node ran at 22fps against\n * 32fps with no sprites at all, even though at most 40 are ever visible — three\n * still walks every one of them per frame. Only the labels that win a slot in\n * the declutter pass ever exist.\n *\n * The sprite's `raycast` is stubbed to a no-op so a label never intercepts a\n * hover or click meant for the node behind it: the library raycasts the whole\n * subtree of the node object.\n */\nexport function ensureLabelSprite(obj: NodeObject3D): SpriteText | null {\n if (obj.sprite) return obj.sprite;\n if (!obj.labelText) return null;\n const sprite = new SpriteText(obj.labelText, LABEL_TEXT_HEIGHT, obj.labelColor);\n sprite.position.y = obj.labelOffset;\n sprite.raycast = () => {};\n sprite.visible = false;\n obj.group.add(sprite);\n obj.sprite = sprite;\n obj.labelBaseScale = { x: sprite.scale.x, y: sprite.scale.y };\n return sprite;\n}\n\n/**\n * Re-apply compiled visuals to an existing node object. Used by the\n * visual-only update path, which must not touch `graphData()` (that would\n * restart the layout).\n */\nexport function applyNodeVisuals(\n obj: NodeObject3D,\n node: CompiledGraphNode,\n labelColor: string,\n): void {\n obj.material.color.set(node.fill);\n if (obj.geometry.parameters.radius !== node.radius) {\n const next = new SphereGeometry(node.radius, SPHERE_SEGMENTS, SPHERE_SEGMENTS);\n obj.mesh.geometry = next;\n obj.geometry.dispose();\n obj.geometry = next;\n }\n obj.labelText = node.label ?? null;\n obj.labelColor = labelColor;\n obj.labelOffset = node.radius + LABEL_GAP;\n if (obj.sprite) {\n const regenerates =\n (node.label && obj.sprite.text !== node.label) || obj.sprite.color !== labelColor;\n if (node.label && obj.sprite.text !== node.label) obj.sprite.text = node.label;\n obj.sprite.color = labelColor;\n obj.sprite.position.y = obj.labelOffset;\n // A regenerated texture resets `scale`, so the captured base has to follow.\n if (regenerates) obj.labelBaseScale = { x: obj.sprite.scale.x, y: obj.sprite.scale.y };\n }\n}\n\n/** Dispose every GPU resource this node object owns. */\nexport function disposeNodeObject(obj: NodeObject3D): void {\n obj.geometry.dispose();\n obj.material.dispose();\n if (obj.sprite) {\n // SpriteText paints into a canvas-backed texture; disposing the material\n // alone leaks that texture.\n const mat = obj.sprite.material as { map?: { dispose(): void }; dispose(): void };\n mat.map?.dispose();\n mat.dispose();\n }\n}\n","/**\n * `@opendata-ai/openchart-vanilla/graph-3d` — the WebGL graph renderer.\n *\n * Importing this module registers the 3D renderer for `dimensions: 3`; after\n * that, `createGraph()` picks it up on its own. That side effect is the whole\n * point of the separate subpath: three.js (~30MB unpacked) never enters the\n * default bundle, and `createGraph()` never dynamically imports a renderer,\n * because mount is synchronous and every framework wrapper assumes it is.\n *\n * ```ts\n * import { createGraph } from '@opendata-ai/openchart-vanilla';\n * import '@opendata-ai/openchart-vanilla/graph-3d';\n *\n * createGraph(el, { type: 'graph', dimensions: 3, nodes, edges });\n * ```\n *\n * `three`, `3d-force-graph` and `three-spritetext` are optional peer\n * dependencies; install them alongside openchart to use this entry point, and\n * check `npm ls three` shows exactly one copy (two copies fail at runtime with\n * `Cannot read properties of undefined (reading 'VERTEX')`).\n *\n * SSR: the libraries touch `window` at import, so import this module from a\n * client-only path (a `useEffect` dynamic import in React Router / Next).\n */\n\nimport type { GraphSpec } from '@opendata-ai/openchart-core';\nimport { registerGraphRenderer } from '../graph/renderer-registry';\nimport { createGraph, type GraphInstance, type GraphMountOptions } from '../graph-mount';\nimport { createGraph3DRenderer } from './mount';\n\nregisterGraphRenderer(3, createGraph3DRenderer);\n\nexport type { EmphasisInput, EmphasisTargets } from './emphasis';\nexport { resolveEmphasis } from './emphasis';\nexport { forceCluster3D } from './forces';\nexport type { LabelCandidate } from './labels';\nexport { LABEL_BUDGET_3D, resolveVisibleLabels } from './labels';\nexport { LINK_WIDTH_MAX_EDGES } from './links';\nexport type { Camera3D, FlyTarget3D } from './mount';\nexport { createGraph3DRenderer, FIT_DISTANCE, NODE_FOCUS_DISTANCE } from './mount';\n\n/**\n * Mount a 3D graph without the caller having to set `dimensions: 3` themselves.\n *\n * A thin convenience over `createGraph()`: the spec is forced to three\n * dimensions and everything else — shell, compilation, node-count gate, handle\n * — is the shared path. A graph above `MAX_3D_NODES` still falls back to the 2D\n * renderer with a warning, exactly as `createGraph()` would.\n */\nexport function createGraph3D(\n container: HTMLElement,\n spec: GraphSpec,\n options?: GraphMountOptions,\n): GraphInstance {\n return createGraph(container, { ...spec, dimensions: 3 }, options);\n}\n"],"mappings":"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","names":["geometry","material","next","Mesh","MeshLambertMaterial","set","resolveHighlightTarget","categoryHighlightSet","defaults","at","width","height"]}
|