Skip to content

NetworkLayoutOptions

Defined in: network/network.ts:328

How node positions are produced. Applies to plain graphs (Network.layout) and, since #182, state networks (Network.stateNetwork) — there each view keeps a layout of its own (#495): the physical and both views the physical graph’s, the state view the state graph’s.

optional backend?: "positions" | "auto" | "force" | "gpu" | "worker"

Defined in: network/network.ts:356

"positions" uses caller-supplied coordinates; "force" runs the in-library force layout on the main thread; "worker" runs it off-thread with progressive streaming; "gpu" runs a WebGL2 Barnes-Hut solve, streamed without blocking the main thread: each frame spends at most min(10 ms, 0.6 × the frame interval) of GPU time on the layout, positions come back through an asynchronous (fenced) readback, and layout repaints are throttled to at most 20 per second and about half of the main thread and GPU time (the main-thread figures are measured in Chromium; Firefox and Safari are not measured yet). With Network.lod on, "gpu" keeps the LOD tree off the main thread like "worker" (#377): a layout worker builds the tree and refits it to each streamed frame, which is painted one worker round trip after it is read back. "gpu" falls back to "worker", with one console warning naming the reason, when the render backend is not WebGL or the device lacks float render targets, float blending (EXT_float_blend) or a large enough texture size for the graph. The fallback is a full worker run: it honours multilevel and streams the LOD tree like "worker". A GPU layout lives on the render backend’s WebGL device: when Network.setBackend swaps that backend out, or its WebGL context is lost, the layout frees its GPU resources and continues warm — from its last positions, with the ticks left of its budget and its current heat — on the GPU of a new WebGL backend, else on the worker (one console warning; none for "auto" after a swap). A layout that had settled continues idle, so a node drag still reflows it (#311).

"auto" (#375) asks for “the GPU where it works”: it resolves to the GPU solve wherever "gpu" would run it and to "worker" everywhere else, without a warning, because there the worker is an expected outcome. From then on the layout behaves exactly as on the backend it resolved to — fit, drag reheat, state networks, nested, LOD streaming — and Network.layoutTransport reports which one runs. A GPU run that fails rather than being unsupported still warns: a driver error while starting, or an engine created with render backend "webgl" whose WebGL setup failed (its device never arrives; with render backend "auto" a failed upgrade stays on Canvas, which is unsupported and silent). The default does not change: omitting backend behaves as before.


optional fit?: boolean

Defined in: network/network.ts:441

Frame the layout, on every backend: the camera ends on the bounding box of the final node positions (box centre → view centre, longest side → ~85% of the view, padded by the largest node radius), and gets there along with the layout (#427):

  • a streamed layout (the "worker" / "gpu" / "auto" force layouts, cold or warm, and a nested map on any of them without a transition, cold or warm) is reframed on every streamed frame — a warm map on every frame of its ease (#454) — so it opens framed and converges in place. Without it a streaming layout converges wherever the solver centres it — the GPU solve centres the centroid at the origin, so it would render at the top-left corner until it settles;
  • a layout with a transition eases the camera along with the nodes, from the current view to the one framing the final layout, on the same easing in the same frames — so a re-clustering ends framed, with no jump. The world rectangle the view shows moves in a straight line, as the nodes do: the zoom is monotonic, and a node on screen at both ends stays on screen throughout. A resize or a node-size change mid-ease re-aims the camera from where it is, so it still ends framed, with no jump;
  • any other layout ("positions", "force", and a state network’s layouts alike) is framed once, when it lands.

The last frame is the landed layout’s exact box, every node included; then the view is the user’s again. It is also released — left where it is, never reframed — as soon as the user zooms, pans or grabs a node, or the view is set with setTransform, so the camera never moves away from a view the user chose. The engine’s own camera moves are not gestures and don’t release it. The fit ends with its layout: stopLayout(), a new layout() or data() leave the camera where it is. Default false.

The box is tight whether LOD is on or off. While a force layout streams it ignores a handful of flung-out stragglers: at most min(64, 0.5% of the nodes) per side, and only when they sit 10-30% or more of the layout’s size beyond the rest, so a fling-out cannot shrink the rest to a dot mid-run. A small disconnected component that far out is dropped the same way and streams just outside the frame. A cold nested map streams top-down on the worker, each depth’s leaves collapsed onto their module centres, so while it streams it is framed on a box the worker knows its final layout lies in — the root disc, then each depth’s placed discs — which only shrinks as depths land: the camera only zooms in, down to the leaves’ exact box. A stream with no such bound frames its live leaves, like a force layout. The pad covers the largest node glyph; with LOD on, an aggregate glyph larger than that can overhang the frame’s edge margin. Cost: the box is O(nodes) per streamed frame, only while the fit is on; a transition computes it once, when it starts, and moves the camera in O(1) per frame.


optional force?: Partial<ForceParams>

Defined in: network/network.ts:373

Force parameters for the force backends. The layout settles into a disc of radius √(repulsion·N/centering) — node spacing √(π·repulsion/centering) — and is seeded at that scale.


optional iterations?: number

Defined in: network/network.ts:368

Tick budget of the force layout ("force" / "worker" / "gpu" / "auto", default 300) — a maximum, not a fixed count (#124), and the length of the anneal: a multilevel-seeded layout cools over it, so a larger budget cools more slowly rather than only adding headroom. A cold disc start keeps full heat to untangle. Every backend stops as soon as the layout has converged (nodes moving a small fraction of the equilibrium spacing per tick, and no faster than the tick before), resolving Network.whenSettled; the GPU solve ("gpu", or "auto" resolved to it) decides that on the GPU, once per tick, so its stop tick does not depend on frame timing (#376).


optional multilevel?: boolean

Defined in: network/network.ts:386

Seed the force layout ("force" / "worker" / "gpu" / "auto") via multilevel coarsening (heavy-edge matching) for faster convergence and fewer tangles on clustered graphs. Default true; set false for a plain cold-start force run from a disc. Tiny / edgeless graphs skip coarsening automatically. The GPU solve ("gpu", or "auto" resolved to it) seeds from the module hierarchy when there is one (#180), else from the graph’s coarsening, which a layout worker builds while the GPU solver is built (#353); the GPU lays every coarse level out on its one solver inside the per-frame GPU budget, and the first frame it paints is the seed (paced by animation frames: on web-NotreDame about 0.8 s after a disc start’s first frame at 120 Hz, about twice that at 60 Hz). false starts the GPU layout from the disc with or without a module hierarchy. Where "gpu" / "auto" resolve to the worker, it honours multilevel too. A warm start has no seed and ignores it.


optional nested?: boolean | NestedLayoutConfig

Defined in: network/network.ts:470

Nested module layout (#324) — the “map of modules”: with a module hierarchy set (data(graph, { modules }), #326, or lod({ modules })), lay the module tree out top-down, each module’s children inside its own disc, arranged only by their sibling links (the super-edges between them — for an .ftree exactly its *Links rows, #199). Every module stays a compact region inside its parent, so the map opens on the top modules and expands in place; each depth is final, so a streamed layout never oscillates. Works with LOD off or on any NetworkLODOptions.source. Once it lands, a LOD cut of the laid-out module tree treats each module as its disc (#329): drawn at the disc’s centre, culled by it, and expanded once the disc’s diameter on screen reaches expandPx. Runs off-thread on backend: "worker" (streamed top-down, one frame per depth), on the GPU on "gpu" (#355: every module at every depth solved at once, streamed as one animation of all depths converging together; the worker when the device cannot run it, with a warning), and synchronously on "force". "auto" runs the same GPU solve wherever "gpu" would, and the worker elsewhere without a warning (#375). Ignored without a hierarchy.

true sizes discs by node flow (leaf count when the graph has none); pass { size: "count" } to size by leaf count, and iterations / packing to tune each module’s solve. { warm: true } (or warm) re-lays the map out from the current positions — for a re-clustering (#328), or a switch from another layout (#454).

Dragging a node (interactive({ draggable })) on a landed nested map reheats it as a flat drag reheats a flat map — the same heat, loop and re-cool, on the main thread whatever the backend — with the nested layout’s own forces (and its module links) at every level above the node, and only the node pinned: its module, sibling modules and the discs above all respond; discs follow their members (rings with them). O(the re-solved modules’ children + the nodes that moved) per frame. A grab while a landed map still eases in (a transition, or a warm stream’s ease, #454) finishes the ease and drags so too.

nestedLayout


optional positions?: Float32Array<ArrayBufferLike>

Defined in: network/network.ts:358

Interleaved [x, y, …] world coordinates for backend: "positions".


optional transition?: number

Defined in: network/network.ts:490

Transition (#328): ease the nodes from their current positions to the new layout over this many milliseconds (cubic ease-in-out) on the main thread, instead of jumping or streaming. Default 0 (no transition). Applies to every layout computed in one go — "positions", "force", and a nested layout on any backend (whose worker then posts only the final layout, no depth frames); the streaming "worker" / "gpu" / "auto" force layouts ignore it (they already animate as they converge), as does a state network’s layout.

A warm nested map on a streaming backend with a transition is computed first and then eased to, as before; without one it streams, eased toward frame by frame instead (#454).

Each transition frame is a positions-only repaint — one O(nodes) interpolation, the LOD tree’s O(tree size) position pass (no style pass) and the normal re-emit — no more than a streamed layout frame. A spatial LOD tree (lod({ source: "spatial" }), #343) is refit the same way, not rebuilt, and rebuilt once when the transition ends. Network.whenSettled resolves when it ends. A new layout() or data(), Network.stopLayout and destroy() stop it where it is; grabbing a node (draggable) finishes it. The camera stays where it is unless fit is set, which eases it along to frame the final layout (#427) — a re-clustering then ends framed, with no jump — for O(1) per frame.


optional warm?: boolean

Defined in: network/network.ts:406

Continue from where the nodes are (#454) instead of laying the graph out anew — e.g. after switching between a nested map and a force layout. On the force layouts ("force", "worker", "gpu", "auto") the solve starts from the current positions: no seed disc and no multilevel seed (multilevel is ignored), at full heat cooling over iterations, until it converges — pulling along any module links (#455, NetworkDataOptions.moduleLinks) as a cold run does. A streamed one ("worker", "gpu", "auto") is followed as a warm nested map is: the nodes glide from where they are toward each frame of the solve as it lands, on one ease of about 600 ms retargeted to every newer frame (see NestedLayoutConfig.warm); frames after it are painted as they come. With fit the camera follows it, as it follows any stream — also when the layout grows or shrinks to the force model’s own scale (a nested map is about a third as wide as a force layout of the same graph). With a structure or spatial LOD tree, which a worker streams with the solve’s own geometry, its frames are painted as they land instead.

With nested it is nested: { warm: true } (NestedLayoutConfig.warm): the map is laid out from the current positions, and on a streaming backend without a transition it streams from them too (see there). On a graph never laid out (all positions equal) it is the cold layout. Ignored by "positions" and by a state network’s layout. Default false.