diff --git a/modules/graph-layers/src/core/constants.ts b/modules/graph-layers/src/core/constants.ts index 9a21cb16..33590c1f 100644 --- a/modules/graph-layers/src/core/constants.ts +++ b/modules/graph-layers/src/core/constants.ts @@ -23,6 +23,12 @@ export type NodeType = // edge shape export type EdgeType = 'spline' | 'line' | 'path'; +export const EDGE_TYPE = { + SPLINE: 'spline', + LINE: 'line', + PATH: 'path' +} as const satisfies Record; + // decorators on edges export type EdgeDecoratorType = 'label' | 'flow' | 'arrow'; diff --git a/modules/graph-layers/src/layouts/d3-dag/d3-dag-layout.ts b/modules/graph-layers/src/layouts/d3-dag/d3-dag-layout.ts index ac93ffea..fb91fb60 100644 --- a/modules/graph-layers/src/layouts/d3-dag/d3-dag-layout.ts +++ b/modules/graph-layers/src/layouts/d3-dag/d3-dag-layout.ts @@ -6,8 +6,6 @@ import {GraphLayout, GraphLayoutOptions} from '../../core/graph-layout'; import type {Graph} from '../../graph/graph'; import {Node} from '../../graph/node'; import {Edge} from '../../graph/edge'; -import {EDGE_TYPE} from '../../core/constants'; - import { coordCenter, coordGreedy, @@ -31,10 +29,10 @@ import { type DefaultGrid, type DefaultSugiyama, type DefaultZherebko, - type Graph as DagGraph, type LayoutResult, type MutGraph, type MutGraphNode, + type MutGraphLink, type NodeSize } from 'd3-dag'; @@ -77,9 +75,20 @@ export type D3DagLayoutOptions = GraphLayoutOptions & { type DagBuilder = (graph: Graph) => MutGraph; -type LayeringOperator = ReturnType; -type DecrossOperator = ReturnType; -type CoordOperator = ReturnType; +type LayeringOperator = + | ReturnType + | ReturnType + | ReturnType; +type DecrossOperator = + | ReturnType + | ReturnType + | ReturnType; +type CoordOperator = + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType; type LayoutCallable = (dag: MutGraph) => LayoutResult; @@ -291,7 +300,7 @@ export class D3DagLayout extends GraphLayout { try { this._dag = this._buildDag(); const layout = this._getLayoutOperator(); - layout(this._dag as DagGraph); + layout(this._dag); this._cacheGeometry(); this._onLayoutChange(); this._onLayoutDone(); @@ -306,8 +315,7 @@ export class D3DagLayout extends GraphLayout { if (typeof builder === 'function') { const dag = builder(this._graph); - this._ensureEdgeData(dag); - return dag; + return this._ensureEdgeData(dag); } switch (builder) { @@ -346,13 +354,15 @@ export class D3DagLayout extends GraphLayout { } private _buildDagWithConnect(): MutGraph { + type ConnectDatum = {source: string; target: string; edge: Edge}; + const connect = graphConnect() - .sourceId((link) => link.source) - .targetId((link) => link.target) - .nodeDatum((id) => this._nodeLookup.get(this._fromDagId(id)) ?? new Node({id})) + .sourceId(({source}: ConnectDatum): string => source) + .targetId(({target}: ConnectDatum): string => target) + .nodeDatum((id: string): Node => this._nodeLookup.get(this._fromDagId(id)) ?? new Node({id})) .single(true); - const data = this._graph + const data: ConnectDatum[] = this._graph .getEdges() .filter((edge) => edge.isDirected()) .map((edge) => ({ @@ -366,8 +376,8 @@ export class D3DagLayout extends GraphLayout { const seenIds = new Set(); for (const dagNode of dag.nodes()) { const datum = dagNode.data; - if (datum) { - seenIds.add((datum).getId()); + if (datum instanceof Node) { + seenIds.add(datum.getId()); } } @@ -377,57 +387,41 @@ export class D3DagLayout extends GraphLayout { } } - for (const link of dag.links()) { - const datum = link.data as {edge?: Edge}; - if (datum?.edge instanceof Edge) { - link.data = datum.edge; - } - } - - this._ensureEdgeData(dag); - return dag; + return this._ensureEdgeData(dag); } private _buildDagWithStratify(): MutGraph { - const stratify = graphStratify(); - - const nodeData = this._graph.getNodes().map((node) => { - const id = this._toDagId(node.getId()); - const parentIds = (this._incomingParentMap.get(node.getId()) ?? []) - .filter((parentId) => this._nodeLookup.has(parentId)) - .map((parentId) => this._toDagId(parentId)); - - return { - id, - node, - parentIds - }; - }); - - const dag = stratify(nodeData); - - for (const dagNode of dag.nodes()) { - const datum = dagNode.data as {node: Node}; - dagNode.data = datum.node; - } - - this._ensureEdgeData(dag); - return dag as MutGraph; + const stratify = graphStratify() + .id((node: Node): string => this._toDagId(node.getId())) + .parentIds((node: Node): Iterable => { + const parentIds = this._incomingParentMap.get(node.getId()) ?? []; + return parentIds + .filter((parentId) => this._nodeLookup.has(parentId)) + .map((parentId) => this._toDagId(parentId)); + }); + + const dag = stratify(this._graph.getNodes()); + return this._ensureEdgeData(dag); } - private _ensureEdgeData(dag: MutGraph): void { + private _ensureEdgeData(dag: MutGraph): MutGraph { for (const link of dag.links()) { if (link.data instanceof Edge) { continue; } const sourceNode = link.source.data; const targetNode = link.target.data; + if (!(sourceNode instanceof Node) || !(targetNode instanceof Node)) { + continue; + } const key = this._edgeKey(sourceNode.getId(), targetNode.getId()); const edge = this._edgeLookup.get(key); if (edge) { - link.data = edge; + (link as unknown as MutGraphLink).data = edge; } } + + return dag as unknown as MutGraph; } private _getLayoutOperator(): LayoutWithConfiguration { diff --git a/modules/graph-layers/test/layouts/d3-dag-layout.spec.ts b/modules/graph-layers/test/layouts/d3-dag-layout.spec.ts index c3759550..753c95f5 100644 --- a/modules/graph-layers/test/layouts/d3-dag-layout.spec.ts +++ b/modules/graph-layers/test/layouts/d3-dag-layout.spec.ts @@ -7,7 +7,6 @@ import {describe, it, expect} from 'vitest'; import {Graph} from '../../src/graph/graph'; import {Node} from '../../src/graph/node'; import {Edge} from '../../src/graph/edge'; -import {EDGE_TYPE} from '../../src/core/constants'; import {D3DagLayout} from '../../src/layouts/d3-dag/d3-dag-layout'; type SampleGraph = { @@ -54,7 +53,7 @@ describe('D3DagLayout', () => { expect(layout.getNodePosition(nodes.d)).toEqual([0, -1.5]); const edgeAB = layout.getEdgePosition(edges.ab); - expect(edgeAB?.type).toBe(EDGE_TYPE.LINE); + expect(edgeAB?.type).toBe('line'); expect(edgeAB?.sourcePosition).toEqual(layout.getNodePosition(nodes.a)); expect(edgeAB?.targetPosition).toEqual(layout.getNodePosition(nodes.b)); expect(edgeAB?.controlPoints).toEqual([]);