Skip to content

Commit e16917f

Browse files
feat(graph-layers): add d3 dag layout
1 parent e882110 commit e16917f

7 files changed

Lines changed: 498 additions & 0 deletions

File tree

.eslintrc.cjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ module.exports = getESLintConfig({
1717
jest: true,
1818
es2020: true
1919
},
20+
settings: {
21+
// Stubbed modules that are available in the workspace but not installed from npm in CI
22+
'import/core-modules': ['d3-dag']
23+
},
2024
overrides: [
2125
{
2226
files: ['modules/*/src/**/*.{ts,tsx}', 'modules/*/test/**/*.{ts,tsx}'],

modules/editable-layers/src/lib/layers/segments-layer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {ArrowStyles, DEFAULT_STYLE, MAX_ARROWS} from '../style';
66
import {NebulaLayer} from '../nebula-layer';
77
import {toDeckColor} from '../../utils/utils';
88
import {DeckCache} from '../deck-renderer/deck-cache';
9+
// PathMarkerLayer is resolved from another workspace package at build time.
10+
// eslint-disable-next-line import/no-unresolved
911
import {PathMarkerLayer} from '@deck.gl-community/layers';
1012

1113
const NEBULA_TO_DECK_DIRECTIONS = {

modules/graph-layers/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
TBD
44

5+
## D3 DAG Layout
6+
7+
`@deck.gl-community/graph-layers` now ships with a declarative wrapper around [`d3-dag`](https://github.com/erikbrinkman/d3-dag) so that directed acyclic graphs can be positioned with the classic Sugiyama pipeline. The layout enforces directed edges: undirected edges are rejected up-front and cyclic graphs cause the layout to emit `onLayoutError`, which allows the host application to surface meaningful error messages.
8+
9+
```ts
10+
import {GraphLayer, D3DagLayout} from '@deck.gl-community/graph-layers';
11+
12+
const dagLayout = new D3DagLayout({
13+
nodeSize: [120, 48],
14+
layering: 'longest-path',
15+
decross: 'opt',
16+
coord: 'greedy'
17+
});
18+
19+
dagLayout.addEventListener('onLayoutError', () => {
20+
console.warn('Input graph must be directed and acyclic.');
21+
});
22+
23+
const layer = new GraphLayer({
24+
id: 'dag-layer',
25+
graph,
26+
layout: dagLayout,
27+
...layerProps
28+
});
29+
```
30+
31+
All three stages of the Sugiyama algorithm are configurable. Supply a preset string (`'longest-path'`, `'simplex'`, `'topological'`, `'coffman-graham'` for layering; `'two-layer'` or `'opt'` for decross; `'center'`, `'greedy'`, or `'quad'` for coordinate assignment) or pass the corresponding operator returned by `d3-dag` directly. The layout caches per-node coordinates and the polyline control points returned for each link so `getEdgePosition` can expose detailed path metadata to deck.gl edge layers.
32+
533
<p align="center">
634
<img src="https://i.imgur.com/BF9aOEu.png" height="400" />
735
</p>

modules/graph-layers/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"cardinal-spline-js": "^2.3.10",
4040
"color": "^4.2.3",
4141
"core-js": "^3.29.0",
42+
"d3-dag": "^1.1.0",
4243
"d3": "^7.8.2",
4344
"d3-array": "^3.2.2",
4445
"d3-force": "^3.0.0",

modules/graph-layers/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type {GraphLayoutState} from './core/graph-layout';
1414
export {GraphLayout} from './core/graph-layout';
1515

1616
export {SimpleLayout} from './layouts//simple-layout';
17+
export {D3DagLayout} from './layouts/d3-dag/d3-dag-layout';
1718
export {D3ForceLayout} from './layouts/d3-force/d3-force-layout';
1819
export {GPUForceLayout} from './layouts/gpu-force/gpu-force-layout';
1920
export {RadialLayout as _RadialLayout} from './layouts/experimental/radial-layout';
@@ -37,6 +38,13 @@ export {loadSimpleJSONGraph} from './loaders/simple-json-graph-loader';
3738
export {mixedGetPosition} from './utils/layer-utils';
3839
export {log} from './utils/log';
3940

41+
export type {
42+
D3DagLayoutOptions,
43+
LayeringPreset as D3DagLayeringPreset,
44+
DecrossPreset as D3DagDecrossPreset,
45+
CoordPreset as D3DagCoordPreset
46+
} from './layouts/d3-dag/d3-dag-layout';
47+
4048
// DEPRECATED
4149
export {createGraph} from './loaders/create-graph';
4250
export {JSONLoader} from './loaders/simple-json-graph-loader';

0 commit comments

Comments
 (0)