Skip to content

Release v3.5

Latest

Choose a tag to compare

@Stukova Stukova released this 05 Oct 12:18
· 104 commits to main since this release

A small oops. We published @cosmos.gl/deck-layers 3.5.0 a little too eagerly, and its layer API changed right after. 3.5.0 is deprecated: please use 3.5.1. If you were quick enough to try 3.5.0, thank you, and sorry. The migration notes have the mapping.

🎉 What's New

deck.gl Integration: @cosmos.gl/deck-layers

cosmos.gl is now a deck.gl layer. The graph runs on deck's GPU device, and cosmos.gl's own renderer draws it into deck's render pass under deck's camera. Positions never leave the GPU, and everything cosmos.gl draws (point shapes, curved links, arrows, dashes) works in the layer.

A graph on a map: cities pinned at their real places on a dark world map, travellers strung along the routes between them
import { CosmosGraphLayer } from '@cosmos.gl/deck-layers'

new CosmosGraphLayer({ id: 'graph', points: nodes, links: edges, getPointId: (p) => p.id })

Points and links are your objects, or cosmos.gl's flat arrays:

// objects, the deck.gl way
points: [{ id: 'a', group: 0 }, …]
links: [{ source: 'a', target: 'b' }, …]

// flat arrays, no copies: [x0, y0, x1, y1, …] and [src0, tgt0, src1, tgt1, …]
points: { length, initialPositions }
links: pairs

Looks are accessors, as in any deck.gl layer, with colors in 0..255:

getPointColor: (p) => COLORS[p.group],
getPointSize: (p) => p.weight,
getLinkColor: [150, 170, 220, 60],
getLinkWidth: 1,

An accessor you leave out sends nothing, and the matching config key draws instead: pointDefaultColor, pointDefaultSize, linkDefaultColor, linkDefaultWidth.

Everything else is cosmos.gl's configuration, through config:

config: { simulationGravity: 0.25, curvedLinks: true, linkDefaultArrows: true, pointDefaultShape: PointShape.Hexagon }

The view must be flat and y-up, like cosmos.gl's space: OrthographicView({ flipY: false }), or a map at pitch 0 and bearing 0.

The layer runs the simulation and draws it. Picking and drag are opt-in, as in any deck.gl layer: pickable: true, enablePointDrag: true. autoHighlight: true lights the hovered point or link with cosmos.gl's hover ring. Get started → · Live examples → under Examples → Integrations.

GraphSimulation: The Layout on Its Own

The simulation is now its own class. Data in, positions out, no canvas and no rendering:

import { GraphSimulation } from '@cosmos.gl/graph'

const simulation = new GraphSimulation(config, devicePromise)   // or (config) alone: a hidden device of its own
simulation.setPointPositions(positions)
simulation.setLinks(links)
simulation.applyData()
simulation.start()

// each frame
simulation.step()
simulation.getPointPositionTexture()

// follow a few points, for labels
const tracker = simulation.trackPoints([0, 5, 9])

// or draw it with deck.gl layers of your own, which read its position texture:
// the Custom deck layers story shows how. The device must be deck's own, from its onDeviceInitialized

Graph is built on it. CosmosGraphLayer creates a headless Graph, or steps and draws the one you give it through graph.

Run cosmos.gl Inside Your Own Renderer

For applications that own the GPU device and the frame loop. A headless Graph takes no container, schedules no frames, and draws when you tell it to:

const graph = new Graph(null, config, devicePromise)   // headless, on your device
graph.setPointPositions(positions)
graph.render()                                          // uploads the data; drawToRenderPass draws nothing until this

// in your frame loop
graph.step()                                            // advance the simulation
graph.setViewTransform({ k, x, y }, [width, height])    // your camera
graph.drawToRenderPass(renderPass)                      // your render pass

For your renderer's own picking:

graph.drawToRenderPass(pickPass, { picking: { alpha } })   // index colors, in deck.gl's encoding
graph.setHostHoveredPoint(index)                          // cosmos.gl's hover ring on a point you picked

Read positions the way that suits your renderer:

graph.getPointPositionTexture()           // the live texture, sample it by index in your shaders
graph.getPointPositionsArray(out)         // a snapshot into your Float32Array
await graph.getPointPositionsAsync(out)   // the same, without blocking the main thread

Move or pin a few points at a time, the way a drag does:

graph.setPointPosition(index, x, y)
graph.setPinnedPoint(index, true)
graph.isPointPinned(index)

To keep cosmos.gl's canvas but drive the frames yourself, set enableRenderLoop: false and call renderOneFrame() when you want a frame. It draws, and steps the simulation while it is running.

Position Reads That Don't Wait for the GPU

graph.getTrackedPointPositionsMap({ nonBlocking: true })
graph.getClusterPositions({ nonBlocking: true })

Returns the latest positions the GPU has already handed back instead of waiting for the current tick to finish. Labels that read positions every tick no longer stall the page, most noticeably in Safari. The first read, and the first after the tracked points or the clusters change, still waits: there is nothing earlier to return.

Sizes and Widths As Drawn

For overlays that follow a point or a link while its size animates:

graph.getPointRadiusByIndex(index)   // moves with a size transition
graph.getLinkWidthByIndex(index)     // new, the same for links

Faster Updates on Large Graphs

Changing colors, sizes or positions no longer rebuilds the per-point link index on every render. That work now happens only when the links or the point count change. On graph.graph, linksBySource and linksByTarget hold the grouping; the sourceIndexToTargetIndices and targetIndexToSourceIndices getters still work but are deprecated.

⚠️ Breaking Change: luma.gl Is Now a Peer Dependency

@luma.gl/* moved from dependencies to peerDependencies at ~9.3.0, so that cosmos.gl and a host such as deck.gl share one luma.gl.

  • npm 7+ — peers install themselves. With deck.gl in the app, run the check below: deck's own range admits luma.gl 9.4.
  • Yarn, or pnpm without auto-install-peers — install luma.gl next to cosmos.gl, at the range (a bare install gets 9.4):
    yarn add @cosmos.gl/graph @luma.gl/core@~9.3.0 @luma.gl/engine@~9.3.0 @luma.gl/shadertools@~9.3.0 @luma.gl/webgl@~9.3.0
    # or
    pnpm add @cosmos.gl/graph @luma.gl/core@~9.3.0 @luma.gl/engine@~9.3.0 @luma.gl/shadertools@~9.3.0 @luma.gl/webgl@~9.3.0
  • CDN / UMD — nothing to do, the bundle still includes luma.gl.

Check that one copy resolves, inside ~9.3.0: npm ls @luma.gl/core. Migration guide →

🐛 Fixes

  • No more jitter on small graphs — the many-body force is exact on small graphs and samples adaptively on large ones.
  • Every edge is anti-aliased at every pixel ratio — a one-device-pixel ramp on every edge of links, rings and point shapes.
  • Setters during initialization no longer crash the graph — they queue and run once ready resolves.
  • Multiple Graph instances on one page no longer interfere with each other.
  • On a shared GPU device, the host keeps its GL state — cosmos.gl's passes hand the host's blending and depth back, so deck.gl layers drawn after the graph, text labels first, keep their anti-aliased edges.

Also: invalid input is neutralized instead of corrupting the layout, selection and GPU reads return the right points, config changes that were silently ignored now apply, lifecycle calls made before the graph is ready take effect in order, an async position snapshot never returns stale data, and the package builds with modern bundlers.