@deck.gl-community/graph-layers 9.1.1 → 9.2.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/dist/_deprecated/old-constants.d.ts +107 -0
  2. package/dist/_deprecated/old-constants.d.ts.map +1 -0
  3. package/dist/_deprecated/old-constants.js +111 -0
  4. package/dist/_deprecated/old-constants.js.map +1 -0
  5. package/dist/core/cache.d.ts +0 -1
  6. package/dist/core/cache.js +0 -1
  7. package/dist/core/constants.d.ts +12 -100
  8. package/dist/core/constants.d.ts.map +1 -1
  9. package/dist/core/constants.js +3 -44
  10. package/dist/core/constants.js.map +1 -1
  11. package/dist/core/graph-engine.d.ts +12 -11
  12. package/dist/core/graph-engine.d.ts.map +1 -1
  13. package/dist/core/graph-engine.js +22 -11
  14. package/dist/core/graph-engine.js.map +1 -1
  15. package/dist/core/graph-layout.d.ts +48 -21
  16. package/dist/core/graph-layout.d.ts.map +1 -1
  17. package/dist/core/graph-layout.js +91 -24
  18. package/dist/core/graph-layout.js.map +1 -1
  19. package/dist/core/interaction-manager.d.ts +6 -4
  20. package/dist/core/interaction-manager.d.ts.map +1 -1
  21. package/dist/core/interaction-manager.js +59 -17
  22. package/dist/core/interaction-manager.js.map +1 -1
  23. package/dist/graph/edge.d.ts +7 -7
  24. package/dist/graph/edge.d.ts.map +1 -1
  25. package/dist/graph/edge.js +3 -6
  26. package/dist/graph/edge.js.map +1 -1
  27. package/dist/graph/graph.d.ts +2 -3
  28. package/dist/graph/graph.js +8 -9
  29. package/dist/graph/graph.js.map +1 -1
  30. package/dist/graph/node.d.ts +7 -8
  31. package/dist/graph/node.d.ts.map +1 -1
  32. package/dist/graph/node.js +3 -5
  33. package/dist/graph/node.js.map +1 -1
  34. package/dist/graph-style-schema.cdn.d.ts +2 -0
  35. package/dist/graph-style-schema.cdn.js +2 -0
  36. package/dist/graph-style-schema.json +12 -0
  37. package/dist/index.cjs +2821 -549
  38. package/dist/index.cjs.map +4 -4
  39. package/dist/index.d.ts +28 -22
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +25 -21
  42. package/dist/index.js.map +1 -1
  43. package/dist/layers/common-layers/flow-path-layer/flow-path-layer-fragment.glsl.d.ts +0 -1
  44. package/dist/layers/common-layers/flow-path-layer/flow-path-layer-fragment.glsl.js +0 -1
  45. package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex-tf.glsl.d.ts +0 -1
  46. package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex-tf.glsl.js +0 -1
  47. package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex.glsl.d.ts +0 -1
  48. package/dist/layers/common-layers/flow-path-layer/flow-path-layer-vertex.glsl.js +0 -1
  49. package/dist/layers/common-layers/flow-path-layer/flow-path-layer.d.ts +0 -1
  50. package/dist/layers/common-layers/flow-path-layer/flow-path-layer.js +0 -1
  51. package/dist/layers/common-layers/marker-layer/atlas-data-url.d.ts +0 -1
  52. package/dist/layers/common-layers/marker-layer/atlas-data-url.js +0 -1
  53. package/dist/layers/common-layers/marker-layer/marker-layer.d.ts +0 -1
  54. package/dist/layers/common-layers/marker-layer/marker-layer.js +2 -3
  55. package/dist/layers/common-layers/marker-layer/marker-list.d.ts +2 -63
  56. package/dist/layers/common-layers/marker-layer/marker-list.d.ts.map +1 -1
  57. package/dist/layers/common-layers/marker-layer/marker-list.js +1 -65
  58. package/dist/layers/common-layers/marker-layer/marker-list.js.map +1 -1
  59. package/dist/layers/common-layers/marker-layer/marker-mapping.d.ts +0 -1
  60. package/dist/layers/common-layers/marker-layer/marker-mapping.js +0 -1
  61. package/dist/layers/common-layers/spline-layer/spline-layer.d.ts +0 -1
  62. package/dist/layers/common-layers/spline-layer/spline-layer.js +0 -1
  63. package/dist/layers/common-layers/zoomable-text-layer/zoomable-text-layer.d.ts +0 -1
  64. package/dist/layers/common-layers/zoomable-text-layer/zoomable-text-layer.js +0 -1
  65. package/dist/layers/edge-attachment-helper.d.ts +15 -0
  66. package/dist/layers/edge-attachment-helper.d.ts.map +1 -0
  67. package/dist/layers/edge-attachment-helper.js +230 -0
  68. package/dist/layers/edge-attachment-helper.js.map +1 -0
  69. package/dist/layers/edge-layer.d.ts +1 -5
  70. package/dist/layers/edge-layer.d.ts.map +1 -1
  71. package/dist/layers/edge-layer.js +9 -11
  72. package/dist/layers/edge-layer.js.map +1 -1
  73. package/dist/layers/edge-layers/arrow-2d-geometry.d.ts +4 -0
  74. package/dist/layers/edge-layers/arrow-2d-geometry.d.ts.map +1 -0
  75. package/dist/layers/edge-layers/arrow-2d-geometry.js +42 -0
  76. package/dist/layers/edge-layers/arrow-2d-geometry.js.map +1 -0
  77. package/dist/layers/edge-layers/curved-edge-layer.d.ts +1 -2
  78. package/dist/layers/edge-layers/curved-edge-layer.js +1 -2
  79. package/dist/layers/edge-layers/edge-arrow-layer.d.ts +21 -0
  80. package/dist/layers/edge-layers/edge-arrow-layer.d.ts.map +1 -0
  81. package/dist/layers/edge-layers/edge-arrow-layer.js +131 -0
  82. package/dist/layers/edge-layers/edge-arrow-layer.js.map +1 -0
  83. package/dist/layers/edge-layers/edge-label-layer.d.ts +1 -2
  84. package/dist/layers/edge-layers/edge-label-layer.js +1 -2
  85. package/dist/layers/edge-layers/flow-layer.d.ts +1 -2
  86. package/dist/layers/edge-layers/flow-layer.js +1 -2
  87. package/dist/layers/edge-layers/path-edge-layer.d.ts +0 -1
  88. package/dist/layers/edge-layers/path-edge-layer.js +0 -1
  89. package/dist/layers/edge-layers/straight-line-edge-layer.d.ts +0 -1
  90. package/dist/layers/edge-layers/straight-line-edge-layer.js +0 -1
  91. package/dist/layers/graph-layer.d.ts +22 -23
  92. package/dist/layers/graph-layer.d.ts.map +1 -1
  93. package/dist/layers/graph-layer.js +218 -62
  94. package/dist/layers/graph-layer.js.map +1 -1
  95. package/dist/layers/node-layers/circle-layer.d.ts +0 -1
  96. package/dist/layers/node-layers/circle-layer.js +0 -1
  97. package/dist/layers/node-layers/image-layer.d.ts +0 -1
  98. package/dist/layers/node-layers/image-layer.js +0 -1
  99. package/dist/layers/node-layers/label-layer.d.ts +1 -2
  100. package/dist/layers/node-layers/label-layer.js +1 -2
  101. package/dist/layers/node-layers/path-rounded-rectangle-layer.d.ts +0 -1
  102. package/dist/layers/node-layers/path-rounded-rectangle-layer.js +1 -2
  103. package/dist/layers/node-layers/rectangle-layer.d.ts +0 -1
  104. package/dist/layers/node-layers/rectangle-layer.js +0 -1
  105. package/dist/layers/node-layers/rounded-rectangle-layer-fragment.d.ts +0 -1
  106. package/dist/layers/node-layers/rounded-rectangle-layer-fragment.js +0 -1
  107. package/dist/layers/node-layers/rounded-rectangle-layer.d.ts +1 -2
  108. package/dist/layers/node-layers/rounded-rectangle-layer.js +2 -3
  109. package/dist/layers/node-layers/zoomable-marker-layer.d.ts +1 -2
  110. package/dist/layers/node-layers/zoomable-marker-layer.js +1 -2
  111. package/dist/layouts/d3-dag/d3-dag-layout.d.ts +117 -0
  112. package/dist/layouts/d3-dag/d3-dag-layout.d.ts.map +1 -0
  113. package/dist/layouts/d3-dag/d3-dag-layout.js +716 -0
  114. package/dist/layouts/d3-dag/d3-dag-layout.js.map +1 -0
  115. package/dist/layouts/d3-force/d3-force-layout.d.ts +4 -4
  116. package/dist/layouts/d3-force/d3-force-layout.d.ts.map +1 -1
  117. package/dist/layouts/d3-force/d3-force-layout.js +25 -10
  118. package/dist/layouts/d3-force/d3-force-layout.js.map +1 -1
  119. package/dist/layouts/d3-force/worker.d.ts +0 -1
  120. package/dist/layouts/d3-force/worker.js +0 -1
  121. package/dist/layouts/experimental/force-multi-graph-layout.d.ts +9 -8
  122. package/dist/layouts/experimental/force-multi-graph-layout.d.ts.map +1 -1
  123. package/dist/layouts/experimental/force-multi-graph-layout.js +15 -11
  124. package/dist/layouts/experimental/force-multi-graph-layout.js.map +1 -1
  125. package/dist/layouts/experimental/hive-plot-layout.d.ts +11 -8
  126. package/dist/layouts/experimental/hive-plot-layout.d.ts.map +1 -1
  127. package/dist/layouts/experimental/hive-plot-layout.js +12 -7
  128. package/dist/layouts/experimental/hive-plot-layout.js.map +1 -1
  129. package/dist/layouts/experimental/radial-layout.d.ts +10 -7
  130. package/dist/layouts/experimental/radial-layout.d.ts.map +1 -1
  131. package/dist/layouts/experimental/radial-layout.js +11 -6
  132. package/dist/layouts/experimental/radial-layout.js.map +1 -1
  133. package/dist/layouts/gpu-force/gpu-force-layout.d.ts +4 -4
  134. package/dist/layouts/gpu-force/gpu-force-layout.d.ts.map +1 -1
  135. package/dist/layouts/gpu-force/gpu-force-layout.js +18 -9
  136. package/dist/layouts/gpu-force/gpu-force-layout.js.map +1 -1
  137. package/dist/layouts/gpu-force/worker.d.ts +0 -1
  138. package/dist/layouts/gpu-force/worker.js +0 -1
  139. package/dist/layouts/simple-layout.d.ts +19 -12
  140. package/dist/layouts/simple-layout.d.ts.map +1 -1
  141. package/dist/layouts/simple-layout.js +26 -15
  142. package/dist/layouts/simple-layout.js.map +1 -1
  143. package/dist/loaders/create-graph.d.ts +1 -2
  144. package/dist/loaders/create-graph.js +3 -4
  145. package/dist/loaders/edge-parsers.d.ts +1 -2
  146. package/dist/loaders/edge-parsers.js +2 -3
  147. package/dist/loaders/edge-parsers.js.map +1 -1
  148. package/dist/loaders/json-loader.d.ts +2 -3
  149. package/dist/loaders/json-loader.d.ts.map +1 -1
  150. package/dist/loaders/json-loader.js +5 -6
  151. package/dist/loaders/json-loader.js.map +1 -1
  152. package/dist/loaders/node-parsers.d.ts +1 -2
  153. package/dist/loaders/node-parsers.js +2 -3
  154. package/dist/loaders/node-parsers.js.map +1 -1
  155. package/dist/loaders/simple-json-graph-loader.d.ts +0 -1
  156. package/dist/loaders/simple-json-graph-loader.d.ts.map +1 -1
  157. package/dist/loaders/simple-json-graph-loader.js +5 -6
  158. package/dist/loaders/simple-json-graph-loader.js.map +1 -1
  159. package/dist/loaders/table-graph-loader.d.ts +3 -4
  160. package/dist/loaders/table-graph-loader.js +5 -6
  161. package/dist/loaders/table-graph-loader.js.map +1 -1
  162. package/dist/style/graph-layer-stylesheet.d.ts +34 -0
  163. package/dist/style/graph-layer-stylesheet.d.ts.map +1 -0
  164. package/dist/style/graph-layer-stylesheet.js +39 -0
  165. package/dist/style/graph-layer-stylesheet.js.map +1 -0
  166. package/dist/style/graph-style-accessor-map.d.ts +93 -0
  167. package/dist/style/graph-style-accessor-map.d.ts.map +1 -0
  168. package/dist/style/graph-style-accessor-map.js +93 -0
  169. package/dist/style/graph-style-accessor-map.js.map +1 -0
  170. package/dist/style/graph-style-engine.d.ts +10 -0
  171. package/dist/style/graph-style-engine.d.ts.map +1 -0
  172. package/dist/style/graph-style-engine.js +163 -0
  173. package/dist/style/graph-style-engine.js.map +1 -0
  174. package/dist/style/graph-stylesheet.schema.d.ts +310 -0
  175. package/dist/style/graph-stylesheet.schema.d.ts.map +1 -0
  176. package/dist/style/graph-stylesheet.schema.js +237 -0
  177. package/dist/style/graph-stylesheet.schema.js.map +1 -0
  178. package/dist/style/style-engine.d.ts +33 -0
  179. package/dist/style/style-engine.d.ts.map +1 -0
  180. package/dist/style/style-engine.js +121 -0
  181. package/dist/style/style-engine.js.map +1 -0
  182. package/dist/style/style-property.d.ts +2 -3
  183. package/dist/style/style-property.d.ts.map +1 -1
  184. package/dist/style/style-property.js +224 -48
  185. package/dist/style/style-property.js.map +1 -1
  186. package/dist/utils/collapsed-chains.d.ts +17 -0
  187. package/dist/utils/collapsed-chains.d.ts.map +1 -0
  188. package/dist/utils/collapsed-chains.js +197 -0
  189. package/dist/utils/collapsed-chains.js.map +1 -0
  190. package/dist/utils/layer-utils.d.ts +0 -1
  191. package/dist/utils/layer-utils.d.ts.map +1 -1
  192. package/dist/utils/layer-utils.js +0 -1
  193. package/dist/utils/log.d.ts +2 -1
  194. package/dist/utils/log.d.ts.map +1 -1
  195. package/dist/utils/log.js +12 -2
  196. package/dist/utils/log.js.map +1 -1
  197. package/dist/utils/node-boundary.d.ts +10 -0
  198. package/dist/utils/node-boundary.d.ts.map +1 -0
  199. package/dist/utils/node-boundary.js +130 -0
  200. package/dist/utils/node-boundary.js.map +1 -0
  201. package/dist/utils/polygon-calculations.d.ts +0 -1
  202. package/dist/utils/polygon-calculations.js +0 -1
  203. package/dist/widgets/long-press-button.d.ts +0 -1
  204. package/dist/widgets/long-press-button.js +0 -1
  205. package/dist/widgets/view-control-widget.d.ts +4 -5
  206. package/dist/widgets/view-control-widget.d.ts.map +1 -1
  207. package/dist/widgets/view-control-widget.js +10 -8
  208. package/dist/widgets/view-control-widget.js.map +1 -1
  209. package/package.json +23 -7
  210. package/src/_deprecated/old-constants.ts +122 -0
  211. package/src/core/constants.ts +21 -43
  212. package/src/core/graph-engine.ts +24 -9
  213. package/src/core/graph-layout.ts +133 -28
  214. package/src/core/interaction-manager.ts +80 -20
  215. package/src/graph/edge.ts +6 -6
  216. package/src/graph/graph.ts +7 -7
  217. package/src/graph/node.ts +6 -6
  218. package/src/index.ts +31 -6
  219. package/src/layers/common-layers/marker-layer/marker-list.ts +62 -64
  220. package/src/layers/edge-attachment-helper.ts +355 -0
  221. package/src/layers/edge-layer.ts +6 -7
  222. package/src/layers/edge-layers/arrow-2d-geometry.ts +51 -0
  223. package/src/layers/edge-layers/edge-arrow-layer.ts +171 -0
  224. package/src/layers/graph-layer.ts +304 -86
  225. package/src/layouts/d3-dag/d3-dag-layout.ts +969 -0
  226. package/src/layouts/d3-force/d3-force-layout.ts +33 -11
  227. package/src/layouts/experimental/force-multi-graph-layout.ts +22 -13
  228. package/src/layouts/experimental/hive-plot-layout.ts +22 -10
  229. package/src/layouts/experimental/radial-layout.ts +20 -8
  230. package/src/layouts/gpu-force/gpu-force-layout.ts +22 -10
  231. package/src/layouts/simple-layout.ts +48 -25
  232. package/src/loaders/edge-parsers.ts +2 -2
  233. package/src/loaders/json-loader.ts +2 -2
  234. package/src/loaders/node-parsers.ts +2 -2
  235. package/src/loaders/simple-json-graph-loader.ts +2 -2
  236. package/src/loaders/table-graph-loader.ts +2 -2
  237. package/src/style/graph-layer-stylesheet.ts +99 -0
  238. package/src/style/graph-style-accessor-map.ts +103 -0
  239. package/src/style/graph-style-engine.ts +229 -0
  240. package/src/style/graph-stylesheet.schema.ts +344 -0
  241. package/src/style/style-engine.ts +168 -0
  242. package/src/style/style-property.ts +314 -51
  243. package/src/utils/collapsed-chains.ts +261 -0
  244. package/src/utils/log.ts +15 -1
  245. package/src/utils/node-boundary.ts +238 -0
  246. package/src/widgets/view-control-widget.tsx +15 -13
  247. package/dist/style/style-sheet.d.ts +0 -11
  248. package/dist/style/style-sheet.d.ts.map +0 -1
  249. package/dist/style/style-sheet.js +0 -253
  250. package/dist/style/style-sheet.js.map +0 -1
  251. package/src/style/style-sheet.ts +0 -277
@@ -0,0 +1,969 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ /* eslint-disable no-continue, complexity, max-statements */
6
+
7
+ import {GraphLayout, GraphLayoutProps} from '../../core/graph-layout';
8
+ import type {Graph} from '../../graph/graph';
9
+ import {Node} from '../../graph/node';
10
+ import {Edge} from '../../graph/edge';
11
+ import {
12
+ coordCenter,
13
+ coordGreedy,
14
+ coordQuad,
15
+ coordSimplex,
16
+ coordTopological,
17
+ decrossDfs,
18
+ decrossOpt,
19
+ decrossTwoLayer,
20
+ graph as createDagGraph,
21
+ graphConnect,
22
+ graphStratify,
23
+ grid,
24
+ layeringLongestPath,
25
+ layeringSimplex,
26
+ layeringTopological,
27
+ sugiyama,
28
+ zherebko,
29
+ type Coord,
30
+ type Decross,
31
+ type DefaultGrid,
32
+ type DefaultSugiyama,
33
+ type DefaultZherebko,
34
+ type LayoutResult,
35
+ type MutGraph,
36
+ type MutGraphNode,
37
+ type MutGraphLink,
38
+ type NodeSize
39
+ } from 'd3-dag';
40
+ import {log} from '../../utils/log';
41
+
42
+ export type D3DagLayoutBuilderName = 'sugiyama' | 'grid' | 'zherebko';
43
+ export type D3DagLayeringName = 'simplex' | 'longestPath' | 'topological';
44
+ export type D3DagDecrossName = 'twoLayer' | 'opt' | 'dfs';
45
+ export type D3DagCoordName = 'simplex' | 'greedy' | 'quad' | 'center' | 'topological';
46
+ export type D3DagDagBuilderName = 'graph' | 'connect' | 'stratify';
47
+ export type D3DagOrientation = 'TB' | 'BT' | 'LR' | 'RL';
48
+ export type D3DagCenterOption = boolean | {x?: boolean; y?: boolean};
49
+
50
+ export type D3DagLayoutOperator =
51
+ | DefaultSugiyama
52
+ | DefaultGrid
53
+ | DefaultZherebko
54
+ | ((dag: MutGraph<Node, Edge>) => LayoutResult);
55
+
56
+ export type D3DagLayoutOptions = GraphLayoutProps & {
57
+ /** Which high-level layout operator to use. */
58
+ layout?: D3DagLayoutBuilderName | D3DagLayoutOperator;
59
+ /** Layering operator used by sugiyama layouts. */
60
+ layering?: D3DagLayeringName | LayeringOperator;
61
+ /** Decrossing operator used by sugiyama layouts. */
62
+ decross?: D3DagDecrossName | DecrossOperator;
63
+ /** Coordinate assignment operator used by sugiyama layouts. */
64
+ coord?: D3DagCoordName | CoordOperator;
65
+ /** Node sizing accessor passed to the active layout. */
66
+ nodeSize?: NodeSize<Node, Edge>;
67
+ /** Optional gap between nodes. Alias: separation. */
68
+ gap?: readonly [number, number];
69
+ /** Optional gap between nodes. */
70
+ separation?: readonly [number, number];
71
+ /** Orientation transform applied after the layout finishes. */
72
+ orientation?: D3DagOrientation;
73
+ /** Whether to center the layout along each axis. */
74
+ center?: D3DagCenterOption;
75
+ /** How to convert the Graph into a DAG. */
76
+ dagBuilder?: D3DagDagBuilderName | DagBuilder;
77
+ /** Whether to collapse linear chains of nodes into a single representative. */
78
+ collapseLinearChains?: boolean;
79
+ };
80
+
81
+ type DagBuilder = (graph: Graph) => MutGraph<Node, Edge>;
82
+
83
+ type LayeringOperator =
84
+ | ReturnType<typeof layeringSimplex>
85
+ | ReturnType<typeof layeringLongestPath>
86
+ | ReturnType<typeof layeringTopological>;
87
+ type DecrossOperator =
88
+ | ReturnType<typeof decrossTwoLayer>
89
+ | ReturnType<typeof decrossOpt>
90
+ | ReturnType<typeof decrossDfs>;
91
+ type CoordOperator =
92
+ | ReturnType<typeof coordCenter>
93
+ | ReturnType<typeof coordGreedy>
94
+ | ReturnType<typeof coordQuad>
95
+ | ReturnType<typeof coordSimplex>
96
+ | ReturnType<typeof coordTopological>;
97
+
98
+ type LayoutCallable = (dag: MutGraph<Node, Edge>) => LayoutResult;
99
+
100
+ type LayoutWithConfiguration = LayoutCallable & {
101
+ layering?: (layer?: any) => any;
102
+ decross?: (decross?: any) => any;
103
+ coord?: (coord?: any) => any;
104
+ nodeSize?: (size?: NodeSize<Node, Edge>) => any;
105
+ gap?: (gap?: readonly [number, number]) => any;
106
+ };
107
+
108
+ type CollapsedChainDescriptor = {
109
+ id: string;
110
+ nodeIds: (string | number)[];
111
+ edgeIds: (string | number)[];
112
+ representativeId: string | number;
113
+ };
114
+
115
+ type DagBounds = {
116
+ minX: number;
117
+ maxX: number;
118
+ minY: number;
119
+ maxY: number;
120
+ centerX: number;
121
+ centerY: number;
122
+ };
123
+
124
+ const DEFAULT_NODE_SIZE: readonly [number, number] = [140, 120];
125
+ const DEFAULT_GAP: readonly [number, number] = [0, 0];
126
+
127
+ const LAYERING_FACTORIES: Record<D3DagLayeringName, () => LayeringOperator> = {
128
+ simplex: layeringSimplex,
129
+ longestPath: layeringLongestPath,
130
+ topological: layeringTopological
131
+ };
132
+
133
+ const DECROSS_FACTORIES: Record<D3DagDecrossName, () => DecrossOperator> = {
134
+ twoLayer: decrossTwoLayer,
135
+ opt: decrossOpt,
136
+ dfs: decrossDfs
137
+ };
138
+
139
+ const COORD_FACTORIES: Record<D3DagCoordName, () => CoordOperator> = {
140
+ simplex: coordSimplex,
141
+ greedy: coordGreedy,
142
+ quad: coordQuad,
143
+ center: coordCenter,
144
+ topological: coordTopological
145
+ };
146
+
147
+ const LAYOUT_FACTORIES: Record<D3DagLayoutBuilderName, () => LayoutWithConfiguration> = {
148
+ sugiyama: () => sugiyama(),
149
+ grid: () => grid(),
150
+ zherebko: () => zherebko()
151
+ };
152
+
153
+ const DAG_ID_SEPARATOR = '::';
154
+
155
+ /**
156
+ * Layout that orchestrates d3-dag operators from declarative options.
157
+ */
158
+ export class D3DagLayout extends GraphLayout<D3DagLayoutOptions> {
159
+ static defaultProps: Required<Omit<D3DagLayoutOptions, 'layout'>> & {layout: D3DagLayoutBuilderName} = {
160
+ layout: 'sugiyama',
161
+ layering: 'topological',
162
+ decross: 'twoLayer',
163
+ coord: 'greedy',
164
+ nodeSize: DEFAULT_NODE_SIZE,
165
+ gap: DEFAULT_GAP,
166
+ separation: DEFAULT_GAP,
167
+ orientation: 'TB',
168
+ center: true,
169
+ dagBuilder: 'graph',
170
+ collapseLinearChains: false
171
+ };
172
+
173
+ protected readonly _name = 'D3DagLayout';
174
+
175
+ private _graph: Graph | null = null;
176
+ private _dag: MutGraph<Node, Edge> | null = null;
177
+ private _layoutOperator: LayoutWithConfiguration | null = null;
178
+ private _rawNodePositions = new Map<string | number, [number, number]>();
179
+ private _rawEdgePoints = new Map<string | number, [number, number][]>();
180
+ private _nodePositions = new Map<string | number, [number, number]>();
181
+ private _edgePoints = new Map<string | number, [number, number][]>();
182
+ private _edgeControlPoints = new Map<string | number, [number, number][]>();
183
+ private _lockedNodePositions = new Map<string | number, [number, number]>();
184
+ private _dagBounds: DagBounds | null = null;
185
+
186
+ private _nodeLookup = new Map<string | number, Node>();
187
+ private _stringIdLookup = new Map<string, string | number>();
188
+ private _edgeLookup = new Map<string, Edge>();
189
+ private _incomingParentMap = new Map<string | number, (string | number)[]>();
190
+ private _chainDescriptors = new Map<string, CollapsedChainDescriptor>();
191
+ private _nodeToChainId = new Map<string | number, string>();
192
+ private _collapsedChainState = new Map<string, boolean>();
193
+ private _hiddenNodeIds = new Set<string | number>();
194
+
195
+ constructor(options: D3DagLayoutOptions = {}) {
196
+ super({...D3DagLayout.defaultProps, ...options});
197
+ }
198
+
199
+ initializeGraph(graph: Graph): void {
200
+ this.updateGraph(graph);
201
+ }
202
+
203
+ updateGraph(graph: Graph): void {
204
+ this._graph = graph;
205
+ this._nodeLookup = new Map();
206
+ this._stringIdLookup = new Map();
207
+ this._edgeLookup = new Map();
208
+ this._incomingParentMap = new Map();
209
+ this._chainDescriptors = new Map();
210
+ this._nodeToChainId = new Map();
211
+ this._hiddenNodeIds = new Set();
212
+
213
+ for (const node of graph.getNodes()) {
214
+ const id = node.getId();
215
+ const key = this._toDagId(id);
216
+ this._nodeLookup.set(id, node);
217
+ this._stringIdLookup.set(key, id);
218
+ }
219
+
220
+ for (const edge of graph.getEdges()) {
221
+ if (!edge.isDirected()) {
222
+ continue;
223
+ }
224
+ const key = this._edgeKey(edge.getSourceNodeId(), edge.getTargetNodeId());
225
+ this._edgeLookup.set(key, edge);
226
+
227
+ const targetId = edge.getTargetNodeId();
228
+ const parents = this._incomingParentMap.get(targetId) ?? [];
229
+ parents.push(edge.getSourceNodeId());
230
+ this._incomingParentMap.set(targetId, parents);
231
+ }
232
+ }
233
+
234
+ start(): void {
235
+ this._runLayout();
236
+ }
237
+
238
+ update(): void {
239
+ this._runLayout();
240
+ }
241
+
242
+ resume(): void {
243
+ this._runLayout();
244
+ }
245
+
246
+ stop(): void {}
247
+
248
+ toggleCollapsedChain(chainId: string): void {
249
+ if (!this._graph) {
250
+ log.log(1, `D3DagLayout: toggleCollapsedChain(${chainId}) ignored (no graph)`);
251
+ return;
252
+ }
253
+ if (!this._chainDescriptors.has(chainId)) {
254
+ this._refreshCollapsedChains();
255
+ }
256
+ if (!this._chainDescriptors.has(chainId)) {
257
+ log.log(1, `D3DagLayout: toggleCollapsedChain(${chainId}) skipped (unknown chain)`);
258
+ return;
259
+ }
260
+ const collapsed = this._isChainCollapsed(chainId);
261
+ const nextState = !collapsed;
262
+ log.log(
263
+ 0,
264
+ `D3DagLayout: toggleCollapsedChain(${chainId}) -> ${nextState ? 'collapsed' : 'expanded'}`
265
+ );
266
+ // eslint-disable-next-line no-console
267
+ console.log(
268
+ `D3DagLayout: toggleCollapsedChain(${chainId}) -> ${nextState ? 'collapsed' : 'expanded'}`
269
+ );
270
+ this._collapsedChainState.set(chainId, nextState);
271
+ this._runLayout();
272
+ }
273
+
274
+ setCollapsedChains(chainIds: Iterable<string>): void {
275
+ if (!this._graph) {
276
+ log.log(1, 'D3DagLayout: setCollapsedChains ignored (no graph)');
277
+ return;
278
+ }
279
+ if (!this._chainDescriptors.size) {
280
+ this._refreshCollapsedChains();
281
+ }
282
+ const desired = new Set(chainIds);
283
+ log.log(0, `D3DagLayout: setCollapsedChains(${desired.size}) requested`);
284
+ let changed = false;
285
+ for (const chainId of this._chainDescriptors.keys()) {
286
+ const next = desired.has(chainId);
287
+ if (this._isChainCollapsed(chainId) !== next) {
288
+ this._collapsedChainState.set(chainId, next);
289
+ changed = true;
290
+ }
291
+ }
292
+ if (changed) {
293
+ log.log(0, 'D3DagLayout: setCollapsedChains -> changes detected, rerunning layout');
294
+ // eslint-disable-next-line no-console
295
+ console.log('D3DagLayout: setCollapsedChains -> changes detected, rerunning layout');
296
+ this._runLayout();
297
+ } else {
298
+ log.log(1, 'D3DagLayout: setCollapsedChains -> no changes');
299
+ // eslint-disable-next-line no-console
300
+ console.log('D3DagLayout: setCollapsedChains -> no changes');
301
+ }
302
+ }
303
+
304
+ setPipelineOptions(options: Partial<D3DagLayoutOptions>): void {
305
+ this._options = {...this._options, ...options};
306
+ if (
307
+ options.layout !== undefined ||
308
+ options.layering !== undefined ||
309
+ options.decross !== undefined ||
310
+ options.coord !== undefined ||
311
+ options.nodeSize !== undefined ||
312
+ options.gap !== undefined ||
313
+ options.separation !== undefined
314
+ ) {
315
+ this._layoutOperator = null;
316
+ }
317
+ if (options.collapseLinearChains !== undefined && this._graph) {
318
+ this._runLayout();
319
+ }
320
+ }
321
+
322
+ getNodePosition(node: Node): [number, number] | null {
323
+ if (this._shouldSkipNode(node.getId())) {
324
+ return null;
325
+ }
326
+ const mappedId = this._mapNodeId(node.getId());
327
+ return this._nodePositions.get(mappedId) || null;
328
+ }
329
+
330
+ getEdgePosition(edge: Edge):
331
+ | {
332
+ type: string;
333
+ sourcePosition: [number, number];
334
+ targetPosition: [number, number];
335
+ controlPoints: [number, number][];
336
+ }
337
+ | null {
338
+ const mappedSourceId = this._mapNodeId(edge.getSourceNodeId());
339
+ const mappedTargetId = this._mapNodeId(edge.getTargetNodeId());
340
+ if (mappedSourceId === mappedTargetId) {
341
+ return null;
342
+ }
343
+
344
+ const sourcePosition = this._nodePositions.get(mappedSourceId);
345
+ const targetPosition = this._nodePositions.get(mappedTargetId);
346
+ if (!sourcePosition || !targetPosition) {
347
+ return null;
348
+ }
349
+
350
+ if (!this._edgePoints.has(edge.getId())) {
351
+ return null;
352
+ }
353
+
354
+ // const points = this._edgePoints.get(edge.getId()) || [sourcePosition, targetPosition];
355
+ const controlPoints = this._edgeControlPoints.get(edge.getId()) || [];
356
+ const edgeType = controlPoints.length ? 'spline-curve' : 'line';
357
+
358
+ return {
359
+ type: edgeType,
360
+ sourcePosition,
361
+ targetPosition,
362
+ controlPoints
363
+ };
364
+ }
365
+
366
+ getLinkControlPoints(edge: Edge): [number, number][] {
367
+ return this._edgeControlPoints.get(edge.getId()) || [];
368
+ }
369
+
370
+ lockNodePosition(node: Node, x: number, y: number): void {
371
+ this._lockedNodePositions.set(node.getId(), [x, y]);
372
+ this._nodePositions.set(node.getId(), [x, y]);
373
+ this._onLayoutChange();
374
+ this._onLayoutDone();
375
+ }
376
+
377
+ unlockNodePosition(node: Node): void {
378
+ this._lockedNodePositions.delete(node.getId());
379
+ }
380
+
381
+ private _runLayout(): void {
382
+ if (!this._graph) {
383
+ return;
384
+ }
385
+ this._refreshCollapsedChains();
386
+ this._onLayoutStart();
387
+
388
+ try {
389
+ this._dag = this._buildDag();
390
+ const layout = this._getLayoutOperator();
391
+ layout(this._dag);
392
+ this._cacheGeometry();
393
+ this._onLayoutChange();
394
+ this._onLayoutDone();
395
+ } catch (error) {
396
+ this._onLayoutError();
397
+ throw error;
398
+ }
399
+ }
400
+
401
+ private _refreshCollapsedChains(): void {
402
+ const previousChainCount = this._chainDescriptors.size;
403
+ if (!this._graph) {
404
+ if (previousChainCount > 0) {
405
+ log.log(0, 'D3DagLayout: clearing collapsed chains (graph unavailable)');
406
+ // eslint-disable-next-line no-console
407
+ console.log('D3DagLayout: clearing collapsed chains (graph unavailable)');
408
+ }
409
+ this._chainDescriptors.clear();
410
+ this._nodeToChainId.clear();
411
+ this._hiddenNodeIds.clear();
412
+ return;
413
+ }
414
+
415
+ log.log(
416
+ 0,
417
+ `D3DagLayout: refreshing collapsed chains (previous=${previousChainCount})`
418
+ );
419
+ // eslint-disable-next-line no-console
420
+ console.log(
421
+ `D3DagLayout: refreshing collapsed chains (previous=${previousChainCount})`
422
+ );
423
+
424
+ const collapseDefault =
425
+ this._options.collapseLinearChains ?? D3DagLayout.defaultProps.collapseLinearChains;
426
+
427
+ const previousStates = new Map(this._collapsedChainState);
428
+
429
+ this._chainDescriptors.clear();
430
+ this._nodeToChainId.clear();
431
+ this._hiddenNodeIds.clear();
432
+
433
+ const nodes = this._graph.getNodes();
434
+ const candidateNodes = new Set<string | number>();
435
+ const incomingCache = new Map<string | number, Edge[]>();
436
+ const outgoingCache = new Map<string | number, Edge[]>();
437
+
438
+ for (const node of nodes) {
439
+ const incoming = this._getIncomingEdges(node);
440
+ const outgoing = this._getOutgoingEdges(node);
441
+ incomingCache.set(node.getId(), incoming);
442
+ outgoingCache.set(node.getId(), outgoing);
443
+ if (incoming.length <= 1 && outgoing.length <= 1 && incoming.length + outgoing.length > 0) {
444
+ candidateNodes.add(node.getId());
445
+ }
446
+ }
447
+
448
+ const visited = new Set<string | number>();
449
+ for (const node of nodes) {
450
+ const nodeId = node.getId();
451
+ if (!candidateNodes.has(nodeId) || visited.has(nodeId)) {
452
+ continue;
453
+ }
454
+
455
+ const incoming = incomingCache.get(nodeId) ?? [];
456
+ const hasCandidateParent =
457
+ incoming.length === 1 && candidateNodes.has(incoming[0].getSourceNodeId());
458
+ if (hasCandidateParent) {
459
+ continue;
460
+ }
461
+
462
+ const chainNodeIds: (string | number)[] = [];
463
+ const chainEdgeIds: (string | number)[] = [];
464
+ let currentNode: Node | undefined = node;
465
+
466
+ while (currentNode) {
467
+ const currentId = currentNode.getId();
468
+ if (!candidateNodes.has(currentId) || visited.has(currentId)) {
469
+ break;
470
+ }
471
+
472
+ visited.add(currentId);
473
+ chainNodeIds.push(currentId);
474
+
475
+ const outgoing = outgoingCache.get(currentId) ?? [];
476
+ if (outgoing.length !== 1) {
477
+ break;
478
+ }
479
+
480
+ const nextEdge = outgoing[0];
481
+ const nextNodeId = nextEdge.getTargetNodeId();
482
+ if (!candidateNodes.has(nextNodeId)) {
483
+ break;
484
+ }
485
+
486
+ const nextIncoming = incomingCache.get(nextNodeId) ?? [];
487
+ if (nextIncoming.length !== 1) {
488
+ break;
489
+ }
490
+
491
+ chainEdgeIds.push(nextEdge.getId());
492
+ currentNode = this._nodeLookup.get(nextNodeId);
493
+ }
494
+
495
+ if (chainNodeIds.length > 1) {
496
+ const chainId = this._createChainId(chainNodeIds);
497
+ const collapsed = previousStates.has(chainId)
498
+ ? previousStates.get(chainId)
499
+ : collapseDefault;
500
+ this._chainDescriptors.set(chainId, {
501
+ id: chainId,
502
+ nodeIds: chainNodeIds,
503
+ edgeIds: chainEdgeIds,
504
+ representativeId: chainNodeIds[0]
505
+ });
506
+ this._collapsedChainState.set(chainId, collapsed);
507
+ for (const chainNodeId of chainNodeIds) {
508
+ this._nodeToChainId.set(chainNodeId, chainId);
509
+ }
510
+ }
511
+ }
512
+
513
+ for (const key of previousStates.keys()) {
514
+ if (!this._chainDescriptors.has(key)) {
515
+ this._collapsedChainState.delete(key);
516
+ }
517
+ }
518
+
519
+ this._hiddenNodeIds.clear();
520
+ for (const [chainId, descriptor] of this._chainDescriptors) {
521
+ const collapsed = this._isChainCollapsed(chainId);
522
+ if (collapsed) {
523
+ for (const nodeId of descriptor.nodeIds) {
524
+ // eslint-disable-next-line max-depth
525
+ if (nodeId !== descriptor.representativeId) {
526
+ this._hiddenNodeIds.add(nodeId);
527
+ }
528
+ }
529
+ }
530
+ }
531
+
532
+ this._updateCollapsedChainNodeMetadata();
533
+
534
+ let collapsedCount = 0;
535
+ for (const chainId of this._chainDescriptors.keys()) {
536
+ if (this._isChainCollapsed(chainId)) {
537
+ collapsedCount++;
538
+ }
539
+ }
540
+ log.log(
541
+ 0,
542
+ `D3DagLayout: refreshed collapsed chains -> total=${this._chainDescriptors.size}, collapsed=${collapsedCount}`
543
+ );
544
+ }
545
+
546
+ private _buildDag(): MutGraph<Node, Edge> {
547
+ const builder = this._options.dagBuilder ?? D3DagLayout.defaultProps.dagBuilder;
548
+
549
+ if (typeof builder === 'function') {
550
+ const dag = builder(this._graph);
551
+ return this._ensureEdgeData(dag);
552
+ }
553
+
554
+ switch (builder) {
555
+ case 'connect':
556
+ return this._buildDagWithConnect();
557
+ case 'stratify':
558
+ return this._buildDagWithStratify();
559
+ case 'graph':
560
+ default:
561
+ return this._buildDagWithGraph();
562
+ }
563
+ }
564
+
565
+ private _buildDagWithGraph(): MutGraph<Node, Edge> {
566
+ const dag = createDagGraph<Node, Edge>();
567
+ const dagNodeLookup = new Map<string | number, MutGraphNode<Node, Edge>>();
568
+
569
+ for (const node of this._graph.getNodes()) {
570
+ if (this._shouldSkipNode(node.getId())) {
571
+ continue;
572
+ }
573
+ const dagNode = dag.node(node);
574
+ dagNodeLookup.set(node.getId(), dagNode);
575
+ }
576
+
577
+ for (const edge of this._graph.getEdges()) {
578
+ if (!edge.isDirected()) {
579
+ continue;
580
+ }
581
+ const sourceId = this._mapNodeId(edge.getSourceNodeId());
582
+ const targetId = this._mapNodeId(edge.getTargetNodeId());
583
+ if (sourceId === targetId) {
584
+ continue;
585
+ }
586
+ const source = dagNodeLookup.get(sourceId);
587
+ const target = dagNodeLookup.get(targetId);
588
+ if (!source || !target) {
589
+ continue;
590
+ }
591
+ dag.link(source, target, edge);
592
+ }
593
+
594
+ return dag;
595
+ }
596
+
597
+ private _buildDagWithConnect(): MutGraph<Node, Edge> {
598
+ type ConnectDatum = {source: string; target: string; edge: Edge};
599
+
600
+ const connect = graphConnect()
601
+ .sourceId(({source}: ConnectDatum): string => source)
602
+ .targetId(({target}: ConnectDatum): string => target)
603
+ .nodeDatum((id: string): Node => this._nodeLookup.get(this._fromDagId(id)) ?? new Node({id}))
604
+ .single(true);
605
+
606
+ const data: ConnectDatum[] = this._graph
607
+ .getEdges()
608
+ .filter((edge) => edge.isDirected())
609
+ .map((edge) => {
610
+ const sourceId = this._mapNodeId(edge.getSourceNodeId());
611
+ const targetId = this._mapNodeId(edge.getTargetNodeId());
612
+ return {sourceId, targetId, edge};
613
+ })
614
+ .filter(({sourceId, targetId}) => sourceId !== targetId)
615
+ .map(({sourceId, targetId, edge}) => ({
616
+ source: this._toDagId(sourceId),
617
+ target: this._toDagId(targetId),
618
+ edge
619
+ }));
620
+
621
+ const dag = connect(data);
622
+
623
+ const seenIds = new Set<string | number>();
624
+ for (const dagNode of dag.nodes()) {
625
+ const datum = dagNode.data;
626
+ if (datum instanceof Node) {
627
+ seenIds.add(datum.getId());
628
+ }
629
+ }
630
+
631
+ for (const node of this._graph.getNodes()) {
632
+ if (this._shouldSkipNode(node.getId())) {
633
+ continue;
634
+ }
635
+ if (!seenIds.has(node.getId())) {
636
+ dag.node(node);
637
+ }
638
+ }
639
+
640
+ return this._ensureEdgeData(dag);
641
+ }
642
+
643
+ private _buildDagWithStratify(): MutGraph<Node, Edge> {
644
+ const stratify = graphStratify()
645
+ .id((node: Node): string => this._toDagId(node.getId()))
646
+ .parentIds((node: Node): Iterable<string> => {
647
+ const parentIds = this._incomingParentMap.get(node.getId()) ?? [];
648
+ const mapped = new Set<string>();
649
+ for (const parentId of parentIds) {
650
+ if (!this._nodeLookup.has(parentId)) {
651
+ continue;
652
+ }
653
+ const mappedId = this._mapNodeId(parentId);
654
+ if (mappedId === node.getId()) {
655
+ continue;
656
+ }
657
+ mapped.add(this._toDagId(mappedId));
658
+ }
659
+ return mapped;
660
+ });
661
+
662
+ const dag = stratify(this._graph.getNodes().filter((node) => !this._shouldSkipNode(node.getId())));
663
+ return this._ensureEdgeData(dag);
664
+ }
665
+
666
+ private _isChainCollapsed(chainId: string): boolean {
667
+ const collapseDefault =
668
+ this._options.collapseLinearChains ?? D3DagLayout.defaultProps.collapseLinearChains;
669
+ return this._collapsedChainState.get(chainId) ?? collapseDefault;
670
+ }
671
+
672
+ private _shouldSkipNode(nodeId: string | number): boolean {
673
+ return this._hiddenNodeIds.has(nodeId);
674
+ }
675
+
676
+ private _mapNodeId(nodeId: string | number): string | number {
677
+ const chainId = this._nodeToChainId.get(nodeId);
678
+ if (!chainId) {
679
+ return nodeId;
680
+ }
681
+ const descriptor = this._chainDescriptors.get(chainId);
682
+ if (!descriptor) {
683
+ return nodeId;
684
+ }
685
+ return this._isChainCollapsed(chainId) ? descriptor.representativeId : nodeId;
686
+ }
687
+
688
+ private _updateCollapsedChainNodeMetadata(): void {
689
+ if (!this._graph) {
690
+ return;
691
+ }
692
+ for (const node of this._graph.getNodes()) {
693
+ const nodeId = node.getId();
694
+ const chainId = this._nodeToChainId.get(nodeId);
695
+ if (!chainId) {
696
+ node.setDataProperty('collapsedChainId', null);
697
+ node.setDataProperty('collapsedChainLength', 1);
698
+ node.setDataProperty('collapsedNodeIds', []);
699
+ node.setDataProperty('collapsedEdgeIds', []);
700
+ node.setDataProperty('collapsedChainRepresentativeId', null);
701
+ node.setDataProperty('isCollapsedChain', false);
702
+ continue;
703
+ }
704
+ const descriptor = this._chainDescriptors.get(chainId);
705
+ if (!descriptor) {
706
+ node.setDataProperty('collapsedChainId', null);
707
+ node.setDataProperty('collapsedChainLength', 1);
708
+ node.setDataProperty('collapsedNodeIds', []);
709
+ node.setDataProperty('collapsedEdgeIds', []);
710
+ node.setDataProperty('collapsedChainRepresentativeId', null);
711
+ node.setDataProperty('isCollapsedChain', false);
712
+ continue;
713
+ }
714
+ const collapsed = this._isChainCollapsed(chainId);
715
+ node.setDataProperty('collapsedChainId', chainId);
716
+ node.setDataProperty('collapsedChainLength', collapsed ? descriptor.nodeIds.length : 1);
717
+ node.setDataProperty('collapsedNodeIds', descriptor.nodeIds);
718
+ node.setDataProperty('collapsedEdgeIds', descriptor.edgeIds);
719
+ node.setDataProperty('collapsedChainRepresentativeId', descriptor.representativeId);
720
+ node.setDataProperty('isCollapsedChain', collapsed);
721
+ }
722
+ }
723
+
724
+ private _createChainId(nodeIds: (string | number)[]): string {
725
+ return `chain:${nodeIds.map((id) => this._toDagId(id)).join('>')}`;
726
+ }
727
+
728
+ private _getIncomingEdges(node: Node): Edge[] {
729
+ const nodeId = node.getId();
730
+ return node
731
+ .getConnectedEdges()
732
+ .filter((edge) => edge.isDirected() && edge.getTargetNodeId() === nodeId);
733
+ }
734
+
735
+ private _getOutgoingEdges(node: Node): Edge[] {
736
+ const nodeId = node.getId();
737
+ return node
738
+ .getConnectedEdges()
739
+ .filter((edge) => edge.isDirected() && edge.getSourceNodeId() === nodeId);
740
+ }
741
+
742
+ private _ensureEdgeData<T>(dag: MutGraph<Node, T>): MutGraph<Node, Edge> {
743
+ for (const link of dag.links()) {
744
+ if (link.data instanceof Edge) {
745
+ continue;
746
+ }
747
+ const sourceNode = link.source.data;
748
+ const targetNode = link.target.data;
749
+ if (!(sourceNode instanceof Node) || !(targetNode instanceof Node)) {
750
+ continue;
751
+ }
752
+ const key = this._edgeKey(sourceNode.getId(), targetNode.getId());
753
+ const edge = this._edgeLookup.get(key);
754
+ if (edge) {
755
+ (link as unknown as MutGraphLink<Node, Edge>).data = edge;
756
+ }
757
+ }
758
+
759
+ return dag as unknown as MutGraph<Node, Edge>;
760
+ }
761
+
762
+ private _getLayoutOperator(): LayoutWithConfiguration {
763
+ if (this._layoutOperator) {
764
+ return this._layoutOperator;
765
+ }
766
+
767
+ const layoutOption = this._options.layout ?? D3DagLayout.defaultProps.layout;
768
+ let layout: LayoutWithConfiguration;
769
+
770
+ if (typeof layoutOption === 'string') {
771
+ layout = LAYOUT_FACTORIES[layoutOption]();
772
+ } else {
773
+ layout = layoutOption as LayoutWithConfiguration;
774
+ }
775
+
776
+ if (layout.layering && this._options.layering) {
777
+ layout = layout.layering(this._resolveLayering(this._options.layering));
778
+ }
779
+
780
+ if (layout.decross && this._options.decross) {
781
+ layout = layout.decross(this._resolveDecross(this._options.decross));
782
+ }
783
+
784
+ if (layout.coord && this._options.coord) {
785
+ layout = layout.coord(this._resolveCoord(this._options.coord));
786
+ }
787
+
788
+ const nodeSize = this._options.nodeSize ?? DEFAULT_NODE_SIZE;
789
+ if (layout.nodeSize) {
790
+ layout = layout.nodeSize(nodeSize);
791
+ }
792
+
793
+ const gap = this._options.separation ?? this._options.gap ?? DEFAULT_GAP;
794
+ if (layout.gap) {
795
+ layout = layout.gap(gap);
796
+ }
797
+
798
+ this._layoutOperator = layout;
799
+ return layout;
800
+ }
801
+
802
+ private _resolveLayering(option: D3DagLayeringName | LayeringOperator): LayeringOperator {
803
+ if (typeof option === 'string') {
804
+ return LAYERING_FACTORIES[option]();
805
+ }
806
+ return option;
807
+ }
808
+
809
+ private _resolveDecross(option: D3DagDecrossName | DecrossOperator): Decross<Node, Edge> {
810
+ if (typeof option === 'string') {
811
+ return DECROSS_FACTORIES[option]();
812
+ }
813
+ return option;
814
+ }
815
+
816
+ private _resolveCoord(option: D3DagCoordName | CoordOperator): Coord<Node, Edge> {
817
+ if (typeof option === 'string') {
818
+ return COORD_FACTORIES[option]();
819
+ }
820
+ return option;
821
+ }
822
+
823
+ private _cacheGeometry(): void {
824
+ this._rawNodePositions.clear();
825
+ this._rawEdgePoints.clear();
826
+
827
+ if (!this._dag) {
828
+ this._dagBounds = null;
829
+ this._bounds = null;
830
+ return;
831
+ }
832
+
833
+ let minX = Number.POSITIVE_INFINITY;
834
+ let maxX = Number.NEGATIVE_INFINITY;
835
+ let minY = Number.POSITIVE_INFINITY;
836
+ let maxY = Number.NEGATIVE_INFINITY;
837
+
838
+ for (const dagNode of this._dag.nodes()) {
839
+ const node = dagNode.data;
840
+ const id = node.getId();
841
+ const x = dagNode.x ?? 0;
842
+ const y = dagNode.y ?? 0;
843
+
844
+ minX = Math.min(minX, x);
845
+ maxX = Math.max(maxX, x);
846
+ minY = Math.min(minY, y);
847
+ maxY = Math.max(maxY, y);
848
+
849
+ this._rawNodePositions.set(id, [x, y]);
850
+ }
851
+
852
+ if (minX === Number.POSITIVE_INFINITY) {
853
+ this._dagBounds = null;
854
+ this._bounds = null;
855
+ this._nodePositions.clear();
856
+ this._edgePoints.clear();
857
+ this._edgeControlPoints.clear();
858
+ return;
859
+ }
860
+
861
+ this._dagBounds = {
862
+ minX,
863
+ maxX,
864
+ minY,
865
+ maxY,
866
+ centerX: (minX + maxX) / 2,
867
+ centerY: (minY + maxY) / 2
868
+ };
869
+
870
+ for (const link of this._dag.links()) {
871
+ const source = link.source.data;
872
+ const target = link.target.data;
873
+ const edge = link.data instanceof Edge ? link.data : this._edgeLookup.get(this._edgeKey(source.getId(), target.getId()));
874
+ if (!edge) {
875
+ continue;
876
+ }
877
+ const points = (link.points && link.points.length ? link.points : [[link.source.x ?? 0, link.source.y ?? 0], [link.target.x ?? 0, link.target.y ?? 0]]);
878
+ this._rawEdgePoints.set(edge.getId(), points.map((point) => [...point] as [number, number]));
879
+ }
880
+
881
+ this._updateTransformedGeometry();
882
+ }
883
+
884
+ private _updateTransformedGeometry(): void {
885
+ this._nodePositions.clear();
886
+ this._edgePoints.clear();
887
+ this._edgeControlPoints.clear();
888
+
889
+ if (!this._dagBounds) {
890
+ this._bounds = null;
891
+ return;
892
+ }
893
+
894
+ const {offsetX, offsetY} = this._getOffsets();
895
+ const orientation = this._options.orientation ?? D3DagLayout.defaultProps.orientation;
896
+
897
+ const transform = (x: number, y: number): [number, number] => {
898
+ const localX = x - offsetX;
899
+ const localY = y - offsetY;
900
+ switch (orientation) {
901
+ case 'BT':
902
+ return [localX, localY];
903
+ case 'LR':
904
+ return [localY, localX];
905
+ case 'RL':
906
+ return [-localY, localX];
907
+ case 'TB':
908
+ default:
909
+ return [localX, -localY];
910
+ }
911
+ };
912
+
913
+ for (const [id, [x, y]] of this._rawNodePositions) {
914
+ this._nodePositions.set(id, transform(x, y));
915
+ }
916
+
917
+ for (const [edgeId, points] of this._rawEdgePoints) {
918
+ const transformed = points.map(([x, y]) => transform(x, y));
919
+ this._edgePoints.set(edgeId, transformed);
920
+ this._edgeControlPoints.set(
921
+ edgeId,
922
+ transformed.length > 2 ? transformed.slice(1, -1) : []
923
+ );
924
+ }
925
+
926
+ for (const [id, position] of this._lockedNodePositions) {
927
+ this._nodePositions.set(id, position);
928
+ }
929
+
930
+ this._bounds = this._calculateBounds(this._nodePositions.values());
931
+ }
932
+
933
+ private _getOffsets(): {offsetX: number; offsetY: number} {
934
+ if (!this._dagBounds) {
935
+ return {offsetX: 0, offsetY: 0};
936
+ }
937
+ const centerOption = this._options.center ?? true;
938
+ let offsetX = 0;
939
+ let offsetY = 0;
940
+ if (centerOption === true) {
941
+ offsetX = this._dagBounds.centerX;
942
+ offsetY = this._dagBounds.centerY;
943
+ } else if (centerOption && typeof centerOption === 'object') {
944
+ if (centerOption.x) {
945
+ offsetX = this._dagBounds.centerX;
946
+ }
947
+ if (centerOption.y) {
948
+ offsetY = this._dagBounds.centerY;
949
+ }
950
+ }
951
+ return {offsetX, offsetY};
952
+ }
953
+
954
+ protected override _updateBounds(): void {
955
+ this._bounds = this._calculateBounds(this._nodePositions.values());
956
+ }
957
+
958
+ private _edgeKey(sourceId: string | number, targetId: string | number): string {
959
+ return `${this._toDagId(sourceId)}${DAG_ID_SEPARATOR}${this._toDagId(targetId)}`;
960
+ }
961
+
962
+ private _toDagId(id: string | number): string {
963
+ return String(id);
964
+ }
965
+
966
+ private _fromDagId(id: string): string | number {
967
+ return this._stringIdLookup.get(id) ?? id;
968
+ }
969
+ }