NetworkLODOptions
Defined in: network/network.ts:544
Level-of-detail (#103): an adaptive hierarchy cut so a large network draws only what’s visible.
Each pan/zoom re-cuts a retained coarsening tree — dense regions collapse to aggregate glyphs and
expand into their members as you zoom in, as soon as their glyphs would no longer overlap (#426) —
bounding per-frame work to the visible frontier. Opt-in
via Network.lod; off by default (every node/link drawn). The tree’s geometry updates as the
layout converges (so LOD helps during the solve, not only after), and the zoom-time path re-cuts
only the visible frontier. Best paired with style({ sizeMode: "screen" }).
On the WebGL lane the cut re-runs live every pan/zoom frame. On the Canvas/SVG (retained)
backends the same frontier draws as Scene layers — so toSVG() exports a level-of-detail map (#138) —
but the retained Scene can’t re-tessellate per frame, so there the frontier is static during a gesture
and re-cuts on release, and after a programmatic setTransform while zoom is enabled (the
redraw-on-zoom-end model; force one with Network.syncScreenGeometry).
Properties
Section titled “Properties”aggregateFill?
Section titled “aggregateFill?”
optionalaggregateFill?:string
Defined in: network/network.ts:606
Aggregate-glyph fill (any CSS color). Default = nodeFill.
aggregateOutline?
Section titled “aggregateOutline?”
optionalaggregateOutline?:false| {color?:string;gap?:number;opacity?:number;width?:number; }
Defined in: network/network.ts:638
Mark aggregate glyphs (collapsed modules/subtrees, not leaves) with a thin outline ring set
a gap px outside the glyph, so it reads as expandable — distinguishing a collapsed module from an
individual node at intermediate zoom. width/gap in px (default 1.5 / 2.5), color any CSS
colour (default a dark neutral), opacity 0-1 (default 1).
Default: with moduleBoundary set, collapsed modules get the same line as expanded ones
(its width, color and opacity, at the default gap), so a module keeps one outline whether it
is collapsed or open. Without it, off. Pass an object to style it separately, or false to turn it
off.
coarsen?
Section titled “coarsen?”
optionalcoarsen?:CoarsenOptions
Defined in: network/network.ts:716
Coarsening granularity for the LOD tree (depth / minimum aggregate size).
crossFade?
Section titled “crossFade?”
optionalcrossFade?:number
Defined in: network/network.ts:714
Cross-fade level transitions (#133): the half-width, as a fraction of expandPx, of the
zoom band around the expand threshold over which an aggregate and its children are drawn together
— the aggregate easing out (opacity 1→0) as its children ease in (0→1, smoothstep) — so a split/merge
reads smoothly instead of popping. e.g. 0.3 fades over [expandPx·0.7, expandPx·1.3]. Applies to
the frontier glyphs, their borders/halos, and the super-edges (which fade with their endpoints).
Off by default and zero added cost when off (0/omitted ⇒ the hard threshold): only the
transitioning band of the frontier is doubled, and the per-node alpha pass runs only when set.
crossLevelEdges?
Section titled “crossLevelEdges?”
optionalcrossLevelEdges?:boolean
Defined in: network/network.ts:703
Also draw super-edges between mixed-level visible nodes — a visible leaf (or finer aggregate) and a visible coarser aggregate at a different cut level (the collapsed↔expanded mismatch). By default such an edge is dropped: when you zoom into one region, its leaves lose their links to the still-collapsed regions until both sides are at the same level again. With this on, the off-frontier on-screen endpoint is projected to its nearest present ancestor and the edge is drawn there (flows deduped), so aggregates keep their context across a mixed frontier (#139).
Off by default and zero added cost when off — the projection (an O(depth) ancestor walk per
off-frontier on-screen edge + a dedup map) runs only when enabled; the same-level gather is unchanged.
Needs the directed super-edge CSR (the cut drawing a module hierarchy); ignored otherwise.
declutter?
Section titled “declutter?”
optionaldeclutter?:boolean
Defined in: network/network.ts:619
Thin overlapping frontier glyphs in screen space, keeping the most important (by strength) and
hiding a glyph whose centre is within declutterSpacing·(rᵢ + rⱼ) of a kept glyph — so dense
regions stay readable instead of a solid mass. Zoom-dependent (more resolve as you zoom in).
Default true; false hides nothing.
declutterSpacing?
Section titled “declutterSpacing?”
optionaldeclutterSpacing?:number
Defined in: network/network.ts:626
Spacing multiplier for declutter: a glyph is hidden when its centre is within
declutterSpacing·(rᵢ + rⱼ) of a kept glyph. Default 2: kept glyphs keep a gap around them, so
the links between them stay visible. The cost: in a small graph a whole non-overlapping module
aggregate can be hidden (#512). 1 hides only real overlaps; <1 lets glyphs overlap partly.
expandPx?
Section titled “expandPx?”
optionalexpandPx?:number
Defined in: network/network.ts:604
Expand threshold (px): an aggregate whose on-screen footprint (2·extent·k) reaches this
expands into its children; below it it draws as a single glyph — unless its members’ glyphs would
not overlap at the current zoom, when it expands whatever its size (#426: an aggregate is drawn only
where its members would overlap, so a small graph opens on every node). The threshold therefore sets
how coarse the map is where members would overlap. Larger → coarser (fewer, bigger aggregates).
After a layout({ nested }), a module’s extent is its disc’s radius (#329).
Omit it to get the tree-adaptive default (#191), which scales with how many children the
tree’s finest aggregates hold: 48 px for structural coarsening / a spatial quadtree (unchanged),
~190–280 px for a provided module partition — so lod({ modules }) opens on a map of modules
instead of raw nodes. Set it to pin an absolute pixel size (the meaning is unchanged).
maxAggregateRadius?
Section titled “maxAggregateRadius?”
optionalmaxAggregateRadius?:number
Defined in: network/network.ts:612
Cap on an aggregate glyph’s draw radius (in the active sizeMode’s units). The tree’s
area-additive radius grows with subtree size — fine in world units, but set this (e.g. ~24) in
screen mode so large aggregates stay readable rather than ballooning to hundreds of pixels.
moduleBoundary?
Section titled “moduleBoundary?”
optionalmoduleBoundary?:object
Defined in: network/network.ts:682
Draw a thin boundary ring around every expanded module in view (#329) — the aggregates the
cut has opened into their members (in a crossFade band: those whose members it draws), so
the hierarchy stays readable as a map of nested modules while you zoom in. After a
layout({ nested }) of the tree being cut, the ring is that module’s disc — which is then also the
module’s LOD geometry, so it follows its members through a drag or a transition; otherwise it is
centred on the module’s members with their extent as its radius. width in the active sizeMode’s units (constant px in screen mode, default
1), color any CSS colour (default a dark neutral), opacity 0-1 (default 0.5). Rings are drawn
above the links and below the nodes (#471), so a dense module’s links never hide its boundary; they
fade with the members they enclose under crossFade, and export with toSVG() / toPNG(). Omit
to disable.
Per module (#471). A ring can be styled by its module:
color: "fill"— each ring in its module’s own fill, the colour the collapsed module is drawn in (its NetworkStyle.nodeFill module colour, or aggregateFill).width: (path) => number | undefined— each ring’s width by the module’s Infomappath(e.g.[2, 1]= sub-module 1 of top module 2), in the active sizeMode’s units — e.g. the module’s enter flow through the flow border’s scale, so the boundary reads as the flow into the module.undefined(or a tree that is not a module hierarchy, whose aggregates have no path) takes the constant default;0draws no ring.
Both are resolved once per module when the style is resolved against the tree (like
FlowBorderSpec.moduleFlow) — again on a style() or lod() call or a new tree — never per
frame. A collapsed module’s default aggregateOutline still takes moduleBoundary’s line, with
the constant defaults standing in for a width accessor and "fill".
With crossLevelEdges on, a module link (data(graph, { modules, moduleLinks }), #199)
whose endpoint is an expanded module in view — one that no finer pair can carry, as in an Infomap
.ftree — is drawn to or from that module’s ring, with its flow, instead of disappearing when the
module opens. Links between two expanded modules run ring to ring. Where the two circles overlap
(routine for the centroid + extent rings, or when the other end sits inside the ring) there is no gap
to run it across, so it runs between their centres instead. An expanded module whose centre is
off-screen keeps the off-screen rule (its links are drawn toward its centre, leaving the view).
Links derived from the graph’s own edges are unchanged (their flow is drawn at the members), so
nothing is counted twice.
Per frame the cost is O(1) per module in view the cut expands (it visits them anyway; the walk is the same with rings on or off) plus, when anchoring, their own module links — never the whole tree.
Picking (#476). A ring is how an open module is pointed at: hover, click and select it on its ring
(see Network.interactive); the hit is the module’s own, with datum.open === true. A pointer
move tests the rings the frame drew, O(rings drawn), allocation-free, and resolves nothing.
color?
Section titled “color?”
optionalcolor?:string
opacity?
Section titled “opacity?”
optionalopacity?:number
width?
Section titled “width?”
optionalwidth?:number| ((path) =>number|undefined)
moduleLinks?
Section titled “moduleLinks?”
optionalmoduleLinks?:ArrayLike<ModuleLink>
Defined in: network/network.ts:590
Module-level links for modules (#199) — prefer data(graph, { modules, moduleLinks }).
Summed into the map’s super-edges alongside those derived from the graph’s edges. Requires
modules.
NetworkDataOptions.moduleLinks
modules?
Section titled “modules?”
optionalmodules?:ArrayLike<ModuleNode>
Defined in: network/network.ts:584
A module hierarchy scoped to these LOD options — prefer Network.data(graph, { modules }),
which the engine keeps across lod(false) and every LOD source (#326). Kept as a back-compat alias:
when given, it takes priority over the engine hierarchy for the cut and every other module consumer
(nested layout, GPU seed, hit paths) while these options are set, and lod(false) drops it.
Pass Infomap’s JSON nodes array directly — each record’s id is the dense node index (aligned
with buildGraph) and path its 1-based module chain. Records must cover every node.
source?
Section titled “source?”
optionalsource?:"structure"|"spatial"|"modules"
Defined in: network/network.ts:573
Which hierarchy the cut draws (#326, #343).
"modules"— the default whenever the engine holds a module hierarchy (Network.data(graph, { modules })) — cuts that module tree: modules expand → sub-modules → leaves on zoom."structure"— the default without one — coarsens the graph structurally (heavy-edge matching): an aggregate is a tightly-linked group of nodes, wherever the layout put its members."spatial"groups nodes by where they are: a quadtree over the layout (up to 8 nodes per bottom cell), so an aggregate is always a compact region, drawn where its members are. On a force layout that spreads communities apart (web graphs), the structural tree’s aggregates cover the whole view and the cut draws a large share of the graph at every zoom; the spatial one keeps the frontier bounded by the screen (a few hundred to ~2k glyphs) and its per-frame cost small. The tree follows the positions: the"worker"backend rebuilds it off the main thread on every streamed frame, and so does the"gpu"backend’s LOD worker for every frame the GPU reads back (both stop once the layout has converged); the other backends rebuild it when a layout lands, and a position transition or a drag refits it until the nodes come to rest. An aggregate’s id changes with each rebuild, so a selected or hovered aggregate is carried over to the cell in the same place; leaves keep their ids — read a hit’smembers()when the event fires, as the tree it came from is replaced by the next streamed frame. Super-edges are summed from the graph’s edges per frame (no super-edge table is built): that costs O(edges of the drawn glyphs’ members) whenever the view changes — ~10 ms at the fit view for 1.5M edges, ~190 ms for 5.8M — so the source suits graphs up to a few hundred thousand nodes. Aggregates are sized area-additively.
An edge-less graph always gets the spatial tree. With a hierarchy, "structure" and "spatial" still
leave it driving the nested layout, the GPU module seed and NetworkHit.path. An explicit
modules here overrides the source.
spatial?
Section titled “spatial?”
optionalspatial?:SpatialLODOptions
Defined in: network/network.ts:721
Options for the spatial tree (#103, #343): source: "spatial", and every edge-less graph (which cannot
be coarsened). bucket (default 8) is the most nodes a bottom cell holds.
superEdges?
Section titled “superEdges?”
optionalsuperEdges?:boolean
Defined in: network/network.ts:690
Draw super-edges: links between both-visible frontier nodes (leaf↔leaf, module↔module, or
aggregate↔aggregate — whatever the cut exposes), sized + coloured by their accumulated flow and
rendered in the active linkStyle. In a ragged module tree, where leaves sit at different depths, a
leaf is also linked to a visible deeper node (a finer module or a deeper leaf) holding the other end
of one of its edges (#325). Default true.