@statelyai/layout 0.1.3 → 0.3.0

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 (85) hide show
  1. package/NOTICE.md +43 -0
  2. package/README.md +153 -6
  3. package/dist/elkjs/index.cjs +9 -0
  4. package/dist/elkjs/index.d.cts +155 -0
  5. package/dist/elkjs/index.d.mts +51 -39
  6. package/dist/elkjs/index.mjs +3 -1484
  7. package/dist/elkjs/main.cjs +17 -0
  8. package/dist/elkjs/main.d.cts +10 -0
  9. package/dist/elkjs/main.d.mts +11 -0
  10. package/dist/elkjs/main.mjs +17 -0
  11. package/dist/elkjs/worker-api.cjs +3 -0
  12. package/dist/elkjs/worker-api.d.cts +3 -0
  13. package/dist/elkjs/worker-api.d.mts +3 -0
  14. package/dist/elkjs/worker-api.mjs +3 -0
  15. package/dist/elkjs/worker.cjs +86 -0
  16. package/dist/elkjs/worker.d.cts +17 -0
  17. package/dist/elkjs/worker.d.mts +17 -0
  18. package/dist/elkjs/worker.mjs +86 -0
  19. package/dist/elkjs-DaHMISnQ.mjs +1677 -0
  20. package/dist/elkjs-dMghiiWW.cjs +1706 -0
  21. package/dist/index-BEA_pzeT.d.cts +72 -0
  22. package/dist/{index-8xYkohbz.d.mts → index-D9dhxrRl.d.cts} +111 -5
  23. package/dist/index-DNTLRsDl.d.mts +72 -0
  24. package/dist/index-DNixtE3h.d.mts +1318 -0
  25. package/dist/index.cjs +215 -0
  26. package/dist/index.d.cts +78 -0
  27. package/dist/index.d.mts +4 -3
  28. package/dist/index.mjs +25 -25
  29. package/dist/layered/index.cjs +38 -0
  30. package/dist/layered/index.d.cts +3 -0
  31. package/dist/layered/index.d.mts +3 -2
  32. package/dist/layered/index.mjs +1 -1
  33. package/dist/layered-B2buJqMf.cjs +12009 -0
  34. package/dist/{layered-CoeH9LRO.mjs → layered-DGFTeQSl.mjs} +2323 -149
  35. package/dist/{spore-BtTSZbb6.mjs → layout-engine-Cs5tatA6.mjs} +159 -5
  36. package/dist/layout-engine-Dn1PiFwP.cjs +712 -0
  37. package/dist/public-types-9NCPoqbp.d.mts +109 -0
  38. package/dist/public-types-BcRZoJE3.d.cts +109 -0
  39. package/dist/routing/index.cjs +28 -0
  40. package/dist/routing/index.d.cts +3 -0
  41. package/dist/routing/index.d.mts +3 -0
  42. package/dist/routing/index.mjs +3 -0
  43. package/dist/routing-A2hrK9MP.mjs +2396 -0
  44. package/dist/routing-ujEP7Qh6.cjs +2606 -0
  45. package/dist/types-CT2akdK0.d.mts +133 -0
  46. package/dist/types-uxPlGRof.d.cts +133 -0
  47. package/dist/worker-client-BNjwT1AJ.mjs +92 -0
  48. package/dist/worker-client-BwGGRD5y.d.mts +19 -0
  49. package/dist/worker-client-D4oEvhAl.cjs +98 -0
  50. package/dist/worker-client-D4zNyPEI.d.cts +19 -0
  51. package/package.json +45 -10
  52. package/src/authoring/coordinates.ts +114 -0
  53. package/src/authoring/partial.ts +584 -0
  54. package/src/authoring/routing.ts +211 -0
  55. package/src/box.ts +22 -0
  56. package/src/constraints.ts +222 -0
  57. package/src/elkjs/index.ts +104 -39
  58. package/src/elkjs/main.ts +19 -0
  59. package/src/elkjs/public-types.ts +125 -0
  60. package/src/elkjs/routes.ts +147 -0
  61. package/src/elkjs/worker-api.ts +29 -0
  62. package/src/elkjs/worker-client.ts +138 -0
  63. package/src/elkjs/worker.ts +117 -0
  64. package/src/index.ts +19 -0
  65. package/src/internal/elkjs-compatibility.ts +17 -0
  66. package/src/internal/layout-engine.ts +126 -0
  67. package/src/layered/compound.ts +417 -0
  68. package/src/layered/index.ts +58 -105
  69. package/src/layered/types.ts +21 -0
  70. package/src/layout.ts +35 -28
  71. package/src/packing.ts +22 -0
  72. package/src/random.ts +75 -3
  73. package/src/routing/adapters.ts +111 -0
  74. package/src/routing/algorithms.ts +676 -0
  75. package/src/routing/coordination.ts +86 -0
  76. package/src/routing/engine.ts +473 -0
  77. package/src/routing/index.ts +68 -0
  78. package/src/routing/layout-cache.ts +65 -0
  79. package/src/routing/model.ts +218 -0
  80. package/src/routing/path.ts +324 -0
  81. package/src/routing/persistent.ts +117 -0
  82. package/src/routing/search.ts +276 -0
  83. package/src/routing/spatial.ts +78 -0
  84. package/src/routing/types.ts +146 -0
  85. package/src/types.ts +17 -2
@@ -0,0 +1,712 @@
1
+ const require_layered = require('./layered-B2buJqMf.cjs');
2
+ let _statelyai_graph_layout = require("@statelyai/graph/layout");
3
+
4
+ //#region src/fixed.ts
5
+ function defaultRoute(source, target) {
6
+ return [{
7
+ x: source.x + source.width / 2,
8
+ y: source.y + source.height / 2
9
+ }, {
10
+ x: target.x + target.width / 2,
11
+ y: target.y + target.height / 2
12
+ }];
13
+ }
14
+ /** Preserve authored positions and routes while completing visual geometry. */
15
+ function getFixedLayout(graph, options = {}) {
16
+ const direction = options.direction ?? graph.direction ?? "down";
17
+ const nodes = graph.nodes.map((node) => {
18
+ const size = (0, _statelyai_graph_layout.getNodeSize)(node, options);
19
+ const rect = {
20
+ x: node.x ?? 0,
21
+ y: node.y ?? 0,
22
+ ...size
23
+ };
24
+ const ports = require_layered.placePorts(node.ports, rect, direction);
25
+ return {
26
+ ...node,
27
+ ...rect,
28
+ ...ports === void 0 ? {} : { ports }
29
+ };
30
+ });
31
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
32
+ const edges = graph.edges.map((edge) => {
33
+ const source = nodeById.get(edge.sourceId);
34
+ const target = nodeById.get(edge.targetId);
35
+ const points = edge.points ? [...edge.points] : source && target ? defaultRoute(source, target) : [];
36
+ const midpoint = require_layered.getPolylineMidpoint(points);
37
+ const width = edge.width ?? 0;
38
+ const height = edge.height ?? 0;
39
+ return {
40
+ ...edge,
41
+ x: edge.x ?? midpoint.x - width / 2,
42
+ y: edge.y ?? midpoint.y - height / 2,
43
+ width,
44
+ height,
45
+ points,
46
+ routing: edge.routing ?? "polyline"
47
+ };
48
+ });
49
+ return {
50
+ ...graph,
51
+ direction,
52
+ nodes,
53
+ edges
54
+ };
55
+ }
56
+ const fixedAlgorithm = {
57
+ id: "fixed",
58
+ capabilities: {
59
+ full: true,
60
+ incremental: false,
61
+ partial: false,
62
+ routeOnly: false,
63
+ hierarchy: true,
64
+ ports: true
65
+ },
66
+ layout(graph, options) {
67
+ return getFixedLayout(graph, options ?? {});
68
+ }
69
+ };
70
+
71
+ //#endregion
72
+ //#region src/internal/elkjs-compatibility.ts
73
+ const elkjs0111ResultPolicy = Symbol("elkjs0111ResultPolicy");
74
+ function setElkjs0111ResultPolicy(graph, policy) {
75
+ Object.defineProperty(graph, elkjs0111ResultPolicy, { value: policy });
76
+ return graph;
77
+ }
78
+
79
+ //#endregion
80
+ //#region src/box.ts
81
+ function getPadding$3(value) {
82
+ if (typeof value === "number") return {
83
+ top: value,
84
+ right: value,
85
+ bottom: value,
86
+ left: value
87
+ };
88
+ return {
89
+ top: value?.top ?? 15,
90
+ right: value?.right ?? 15,
91
+ bottom: value?.bottom ?? 15,
92
+ left: value?.left ?? 15
93
+ };
94
+ }
95
+ function standardDeviation(values, mean) {
96
+ if (values.length < 2) return 0;
97
+ const variance = values.reduce((sum, value) => sum + (value - mean) ** 2, 0);
98
+ return Math.sqrt(variance / (values.length - 1));
99
+ }
100
+ /** ELK Box SIMPLE packing translated onto `@statelyai/graph`. */
101
+ function getBoxLayout(graph, options = {}) {
102
+ const spacing = Math.fround(options.spacing ?? 15);
103
+ const padding = getPadding$3(options.padding);
104
+ const aspectRatio = (options.aspectRatio ?? 1.3) > 0 ? options.aspectRatio ?? 1.3 : 1.3;
105
+ const sizes = new Map(graph.nodes.map((node) => [node.id, (0, _statelyai_graph_layout.getNodeSize)(node, options)]));
106
+ const sortedNodes = [...graph.nodes].sort((first, second) => {
107
+ const priorityDifference = (options.priority?.(second) ?? 0) - (options.priority?.(first) ?? 0);
108
+ if (priorityDifference !== 0) return priorityDifference;
109
+ if (options.interactive) {
110
+ const yDifference = (first.y ?? 0) - (second.y ?? 0);
111
+ if (yDifference !== 0) return yDifference;
112
+ const xDifference = (first.x ?? 0) - (second.x ?? 0);
113
+ if (xDifference !== 0) return xDifference;
114
+ }
115
+ const firstSize = sizes.get(first.id) ?? {
116
+ width: 0,
117
+ height: 0
118
+ };
119
+ const secondSize = sizes.get(second.id) ?? {
120
+ width: 0,
121
+ height: 0
122
+ };
123
+ return firstSize.width * firstSize.height - secondSize.width * secondSize.height;
124
+ });
125
+ const areas = sortedNodes.map((node) => {
126
+ const size = sizes.get(node.id) ?? {
127
+ width: 0,
128
+ height: 0
129
+ };
130
+ return size.width * size.height;
131
+ });
132
+ let totalArea = areas.reduce((sum, area) => sum + area, 0);
133
+ const mean = areas.length === 0 ? 0 : totalArea / areas.length;
134
+ totalArea += areas.length * standardDeviation(areas, mean);
135
+ totalArea += Math.sqrt(totalArea) * (padding.bottom + padding.top);
136
+ totalArea += Math.sqrt(totalArea) * padding.right;
137
+ const widestNode = Math.max(0, ...sortedNodes.map((node) => sizes.get(node.id)?.width ?? 0));
138
+ const maximumRowWidth = Math.max(widestNode, Math.sqrt(totalArea * aspectRatio)) + padding.left;
139
+ let x = padding.left;
140
+ let y = padding.top;
141
+ let rowHeight = 0;
142
+ let rowStart = 0;
143
+ const rows = [];
144
+ const nodes = [];
145
+ for (const node of sortedNodes) {
146
+ const size = sizes.get(node.id) ?? {
147
+ width: 0,
148
+ height: 0
149
+ };
150
+ if (x + size.width > maximumRowWidth) {
151
+ rows.push({
152
+ start: rowStart,
153
+ end: nodes.length,
154
+ height: rowHeight
155
+ });
156
+ rowStart = nodes.length;
157
+ x = padding.left;
158
+ y += rowHeight + spacing;
159
+ rowHeight = 0;
160
+ }
161
+ nodes.push({
162
+ ...node,
163
+ x,
164
+ y,
165
+ ...size
166
+ });
167
+ x += size.width + spacing;
168
+ rowHeight = Math.max(rowHeight, size.height);
169
+ }
170
+ rows.push({
171
+ start: rowStart,
172
+ end: nodes.length,
173
+ height: rowHeight
174
+ });
175
+ if (options.expandNodes) {
176
+ const broadestRow = Math.max(padding.left + padding.right, ...nodes.map((node) => node.x + node.width + padding.right));
177
+ for (const row of rows) for (let index = row.start; index < row.end; index++) {
178
+ const node = nodes[index];
179
+ if (!node) continue;
180
+ node.height = row.height;
181
+ if (index === row.end - 1) node.width = broadestRow - node.x - padding.right;
182
+ }
183
+ }
184
+ const placedById = new Map(nodes.map((node) => [node.id, node]));
185
+ return getFixedLayout({
186
+ ...graph,
187
+ nodes: graph.nodes.map((node) => placedById.get(node.id) ?? node)
188
+ }, { direction: options.direction ?? graph.direction });
189
+ }
190
+ const boxAlgorithm = {
191
+ id: "box",
192
+ capabilities: {
193
+ full: true,
194
+ incremental: false,
195
+ partial: false,
196
+ routeOnly: false,
197
+ hierarchy: false,
198
+ ports: true
199
+ },
200
+ layout(graph, options) {
201
+ return getBoxLayout(graph, options ?? {});
202
+ }
203
+ };
204
+ /** Pinned behavior used only by the elkjs 0.11.1 compatibility adapter. */
205
+ const elkjs0111BoxAlgorithm = {
206
+ ...boxAlgorithm,
207
+ layout(graph, options = {}) {
208
+ const result = getBoxLayout(graph, options);
209
+ const padding = getPadding$3(options.padding);
210
+ return setElkjs0111ResultPolicy(result, {
211
+ providerBounds: result.nodes.length === 0 ? {
212
+ width: 0,
213
+ height: 0
214
+ } : {
215
+ width: Math.max(...result.nodes.map((node) => node.x + node.width)) + padding.right,
216
+ height: Math.max(...result.nodes.map((node) => node.y + node.height)) + padding.bottom
217
+ },
218
+ preserveEdgeSections: true,
219
+ skipBoundsNormalization: true
220
+ });
221
+ }
222
+ };
223
+
224
+ //#endregion
225
+ //#region src/packing.ts
226
+ function getPadding$2(value) {
227
+ if (typeof value === "number") return {
228
+ top: value,
229
+ right: value,
230
+ bottom: value,
231
+ left: value
232
+ };
233
+ return {
234
+ top: value?.top ?? 0,
235
+ right: value?.right ?? 0,
236
+ bottom: value?.bottom ?? 0,
237
+ left: value?.left ?? 0
238
+ };
239
+ }
240
+ /** Deterministic shelf-based rectangle packing for `@statelyai/graph`. */
241
+ function getRectanglePackingLayout(graph, options = {}) {
242
+ const spacing = options.spacing ?? 20;
243
+ const padding = getPadding$2(options.padding);
244
+ const sizes = new Map(graph.nodes.map((node) => [node.id, (0, _statelyai_graph_layout.getNodeSize)(node, options)]));
245
+ const totalArea = [...sizes.values()].reduce((area, size) => area + (size.width + spacing) * (size.height + spacing), 0);
246
+ const targetWidth = options.targetWidth ?? Math.max(1, Math.sqrt(totalArea) * 1.5);
247
+ let x = padding.left;
248
+ let y = padding.top;
249
+ let rowHeight = 0;
250
+ const nodes = [];
251
+ for (const node of graph.nodes) {
252
+ const size = sizes.get(node.id) ?? {
253
+ width: 0,
254
+ height: 0
255
+ };
256
+ if (x > padding.left && x + size.width > padding.left + targetWidth) {
257
+ x = padding.left;
258
+ y += rowHeight + spacing;
259
+ rowHeight = 0;
260
+ }
261
+ nodes.push({
262
+ ...node,
263
+ x,
264
+ y,
265
+ ...size
266
+ });
267
+ x += size.width + spacing;
268
+ rowHeight = Math.max(rowHeight, size.height);
269
+ }
270
+ return getFixedLayout({
271
+ ...graph,
272
+ nodes
273
+ }, { direction: options.direction ?? graph.direction });
274
+ }
275
+ const rectanglePackingAlgorithm = {
276
+ id: "rectpacking",
277
+ capabilities: {
278
+ full: true,
279
+ incremental: false,
280
+ partial: false,
281
+ routeOnly: false,
282
+ hierarchy: false,
283
+ ports: true
284
+ },
285
+ layout(graph, options) {
286
+ return getRectanglePackingLayout(graph, options ?? {});
287
+ }
288
+ };
289
+ /** Pinned output policy used only by the elkjs 0.11.1 compatibility adapter. */
290
+ const elkjs0111RectanglePackingAlgorithm = {
291
+ ...rectanglePackingAlgorithm,
292
+ layout(graph, options = {}) {
293
+ const result = getRectanglePackingLayout(graph, options);
294
+ const padding = getPadding$2(options.padding);
295
+ return setElkjs0111ResultPolicy(result, {
296
+ providerBounds: result.nodes.length === 0 ? {
297
+ width: 0,
298
+ height: 0
299
+ } : {
300
+ width: Math.max(...result.nodes.map((node) => node.x + node.width)) + padding.right,
301
+ height: Math.max(...result.nodes.map((node) => node.y + node.height)) + padding.bottom
302
+ },
303
+ preserveEdgeSections: true,
304
+ skipBoundsNormalization: true
305
+ });
306
+ }
307
+ };
308
+
309
+ //#endregion
310
+ //#region src/random.ts
311
+ const nativeBehavior = {
312
+ elkjs0111EdgeTargetBug: false,
313
+ preserveProviderBounds: false
314
+ };
315
+ const elkjs0111Behavior = {
316
+ elkjs0111EdgeTargetBug: true,
317
+ preserveProviderBounds: true
318
+ };
319
+ function getPadding$1(value) {
320
+ if (typeof value === "number") return {
321
+ top: value,
322
+ right: value,
323
+ bottom: value,
324
+ left: value
325
+ };
326
+ return {
327
+ top: value?.top ?? 15,
328
+ right: value?.right ?? 15,
329
+ bottom: value?.bottom ?? 15,
330
+ left: value?.left ?? 15
331
+ };
332
+ }
333
+ function borderPoint(source, target) {
334
+ const sourceCenter = {
335
+ x: source.x + source.width / 2,
336
+ y: source.y + source.height / 2
337
+ };
338
+ const targetCenter = {
339
+ x: target.x + target.width / 2,
340
+ y: target.y + target.height / 2
341
+ };
342
+ const dx = targetCenter.x - sourceCenter.x;
343
+ const dy = targetCenter.y - sourceCenter.y;
344
+ if (dx === 0 && dy === 0) return {
345
+ x: source.x + source.width,
346
+ y: sourceCenter.y
347
+ };
348
+ const scale = Math.min(dx === 0 ? Infinity : source.width / 2 / Math.abs(dx), dy === 0 ? Infinity : source.height / 2 / Math.abs(dy));
349
+ return {
350
+ x: sourceCenter.x + dx * scale,
351
+ y: sourceCenter.y + dy * scale
352
+ };
353
+ }
354
+ /** Seeded random distribution using Java's 48-bit `Random` sequence. */
355
+ function getRandomLayout(graph, options = {}) {
356
+ return getRandomLayoutWithBehavior(graph, options, nativeBehavior);
357
+ }
358
+ function getRandomLayoutWithBehavior(graph, options, behavior) {
359
+ if (graph.nodes.length === 0) return getFixedLayout(graph, options);
360
+ const random = new require_layered.JavaRandom(options.seed && options.seed !== 0 ? options.seed : Date.now());
361
+ const aspectRatio = Math.fround(options.aspectRatio ?? 1.6);
362
+ const spacing = Math.fround(options.spacing ?? 15);
363
+ const padding = getPadding$1(options.padding);
364
+ const sizes = new Map(graph.nodes.map((node) => [node.id, (0, _statelyai_graph_layout.getNodeSize)(node, options)]));
365
+ const nodeArea = [...sizes.values()].reduce((sum, size) => sum + size.width * size.height, 0);
366
+ const maximumWidth = Math.max(...[...sizes.values()].map((size) => size.width));
367
+ const maximumHeight = Math.max(...[...sizes.values()].map((size) => size.height));
368
+ const edgeFactor = 1 + graph.edges.length;
369
+ const drawArea = nodeArea + 2 * spacing * spacing * edgeFactor * graph.nodes.length;
370
+ const areaRoot = Math.sqrt(drawArea);
371
+ const drawWidth = Math.max(areaRoot * aspectRatio, maximumWidth);
372
+ const drawHeight = Math.max(areaRoot / aspectRatio, maximumHeight);
373
+ const nodes = graph.nodes.map((node) => {
374
+ const size = sizes.get(node.id) ?? {
375
+ width: 0,
376
+ height: 0
377
+ };
378
+ return {
379
+ ...node,
380
+ x: padding.left + random.nextDouble() * (drawWidth - size.width),
381
+ y: padding.left + random.nextDouble() * (drawHeight - size.height),
382
+ ...size
383
+ };
384
+ });
385
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
386
+ const totalWidth = drawWidth + padding.left + padding.right;
387
+ const totalHeight = drawHeight + padding.top + padding.bottom;
388
+ const layoutEdge = (edge) => {
389
+ const source = nodeById.get(edge.sourceId);
390
+ const target = nodeById.get(edge.targetId);
391
+ if (!source || !target) return edge;
392
+ if (behavior.elkjs0111EdgeTargetBug) {
393
+ const start$1 = {
394
+ x: source.x + source.width,
395
+ y: source.y + source.height / 2
396
+ };
397
+ const end$1 = {
398
+ x: source.x + source.width / 2,
399
+ y: source.y + source.height
400
+ };
401
+ const bendCount$1 = random.nextInt(5) + 1;
402
+ const xDifference = end$1.x - start$1.x;
403
+ const yDifference = end$1.y - start$1.y;
404
+ const maximumDeviation$1 = Math.hypot(xDifference, yDifference) * Math.fround(.2);
405
+ const xIncrement = xDifference / (bendCount$1 + 1);
406
+ const yIncrement = yDifference / (bendCount$1 + 1);
407
+ let x = start$1.x;
408
+ let y = start$1.y;
409
+ const points$1 = [start$1];
410
+ for (let index = 0; index < bendCount$1; index++) {
411
+ x += xIncrement;
412
+ y += yIncrement;
413
+ let randomX = x + random.nextFloat() * maximumDeviation$1 - maximumDeviation$1 / 2;
414
+ if (randomX < 0) randomX = 1;
415
+ else if (randomX > totalWidth) randomX = totalWidth - 1;
416
+ let randomY = y + random.nextFloat() * maximumDeviation$1 - maximumDeviation$1 / 2;
417
+ if (randomY < 0) randomY = 1;
418
+ else if (randomY > totalHeight) randomY = totalHeight - 1;
419
+ points$1.push({
420
+ x: randomX,
421
+ y: randomY
422
+ });
423
+ }
424
+ points$1.push(end$1);
425
+ return {
426
+ ...edge,
427
+ points: points$1,
428
+ routing: "polyline"
429
+ };
430
+ }
431
+ const start = borderPoint(source, target);
432
+ const end = borderPoint(target, source);
433
+ const bendCount = random.nextInt(5) + (source === target ? 1 : 0);
434
+ const maximumDeviation = Math.hypot(end.x - start.x, end.y - start.y) * .2;
435
+ const points = [start];
436
+ for (let index = 1; index <= bendCount; index++) {
437
+ const progress = index / (bendCount + 1);
438
+ points.push({
439
+ x: Math.min(totalWidth - 1, Math.max(1, start.x + (end.x - start.x) * progress + random.nextFloat() * maximumDeviation - maximumDeviation / 2)),
440
+ y: Math.min(totalHeight - 1, Math.max(1, start.y + (end.y - start.y) * progress + random.nextFloat() * maximumDeviation - maximumDeviation / 2))
441
+ });
442
+ }
443
+ points.push(end);
444
+ return {
445
+ ...edge,
446
+ points,
447
+ routing: "polyline"
448
+ };
449
+ };
450
+ const edges = behavior.elkjs0111EdgeTargetBug ? graph.nodes.flatMap((node) => graph.edges.filter((edge) => edge.sourceId === node.id).map(layoutEdge)) : graph.edges.map(layoutEdge);
451
+ const result = getFixedLayout({
452
+ ...graph,
453
+ nodes,
454
+ edges
455
+ }, { direction: options.direction ?? graph.direction });
456
+ if (behavior.preserveProviderBounds) setElkjs0111ResultPolicy(result, { providerBounds: {
457
+ width: drawWidth + 2 * (padding.left + padding.right),
458
+ height: drawHeight + 2 * (padding.top + padding.bottom)
459
+ } });
460
+ return result;
461
+ }
462
+ const randomAlgorithm = {
463
+ id: "random",
464
+ capabilities: {
465
+ full: true,
466
+ incremental: false,
467
+ partial: false,
468
+ routeOnly: false,
469
+ hierarchy: false,
470
+ ports: false
471
+ },
472
+ layout(graph, options) {
473
+ return getRandomLayout(graph, options ?? {});
474
+ }
475
+ };
476
+ /** Pinned behavior used only by the elkjs 0.11.1 compatibility adapter. */
477
+ const elkjs0111RandomAlgorithm = {
478
+ ...randomAlgorithm,
479
+ layout(graph, options) {
480
+ return getRandomLayoutWithBehavior(graph, options ?? {}, elkjs0111Behavior);
481
+ }
482
+ };
483
+
484
+ //#endregion
485
+ //#region src/spore.ts
486
+ function getPadding(value) {
487
+ if (typeof value === "number") return {
488
+ top: value,
489
+ right: value,
490
+ bottom: value,
491
+ left: value
492
+ };
493
+ return {
494
+ top: value?.top ?? 0,
495
+ right: value?.right ?? 0,
496
+ bottom: value?.bottom ?? 0,
497
+ left: value?.left ?? 0
498
+ };
499
+ }
500
+ function getSporeLayout(graph, options, compact) {
501
+ const spacing = options.spacing ?? 20;
502
+ const padding = getPadding(options.padding);
503
+ const nodes = [];
504
+ graph.nodes.forEach((node, index) => {
505
+ const size = (0, _statelyai_graph_layout.getNodeSize)(node, options);
506
+ const previous = graph.nodes[index - 1];
507
+ const placedPrevious = nodes[index - 1];
508
+ if (!previous || !placedPrevious) {
509
+ nodes.push({
510
+ ...node,
511
+ x: padding.left,
512
+ y: padding.top,
513
+ ...size
514
+ });
515
+ return;
516
+ }
517
+ const deltaX = (node.x ?? 0) - (previous.x ?? 0);
518
+ const deltaY = (node.y ?? 0) - (previous.y ?? 0);
519
+ const requiredX = placedPrevious.width + spacing;
520
+ const requiredY = placedPrevious.height + spacing;
521
+ const distance = (delta, required) => {
522
+ if (delta === 0) return 0;
523
+ const magnitude = compact ? required : Math.max(Math.abs(delta), required);
524
+ return Math.sign(delta) * magnitude;
525
+ };
526
+ nodes.push({
527
+ ...node,
528
+ x: placedPrevious.x + distance(deltaX, requiredX),
529
+ y: placedPrevious.y + distance(deltaY, requiredY),
530
+ ...size
531
+ });
532
+ });
533
+ return getFixedLayout({
534
+ ...graph,
535
+ nodes
536
+ }, { direction: options.direction ?? graph.direction });
537
+ }
538
+ /** Compact an existing layout while preserving its relative directions. */
539
+ function getSporeCompactionLayout(graph, options = {}) {
540
+ return getSporeLayout(graph, options, true);
541
+ }
542
+ /** Remove overlap while preserving existing distances that already fit. */
543
+ function getSporeOverlapRemovalLayout(graph, options = {}) {
544
+ return getSporeLayout(graph, options, false);
545
+ }
546
+ function algorithm(id, compact) {
547
+ return {
548
+ id,
549
+ capabilities: {
550
+ full: true,
551
+ incremental: false,
552
+ partial: false,
553
+ routeOnly: false,
554
+ hierarchy: false,
555
+ ports: true
556
+ },
557
+ layout(graph, options) {
558
+ return getSporeLayout(graph, options ?? {}, compact);
559
+ }
560
+ };
561
+ }
562
+ const sporeCompactionAlgorithm = algorithm("sporeCompaction", true);
563
+ const sporeOverlapRemovalAlgorithm = algorithm("sporeOverlap", false);
564
+
565
+ //#endregion
566
+ //#region src/internal/layout-engine.ts
567
+ const builtInLayoutAlgorithms = {
568
+ box: boxAlgorithm,
569
+ fixed: fixedAlgorithm,
570
+ layered: require_layered.layeredAlgorithm,
571
+ random: randomAlgorithm,
572
+ rectpacking: rectanglePackingAlgorithm,
573
+ sporeCompaction: sporeCompactionAlgorithm,
574
+ sporeOverlap: sporeOverlapRemovalAlgorithm
575
+ };
576
+ const elkjs0111LayoutAlgorithms = {
577
+ ...builtInLayoutAlgorithms,
578
+ box: elkjs0111BoxAlgorithm,
579
+ random: elkjs0111RandomAlgorithm,
580
+ rectpacking: elkjs0111RectanglePackingAlgorithm
581
+ };
582
+ function getBuiltInLayoutAlgorithm(id) {
583
+ if (!Object.hasOwn(builtInLayoutAlgorithms, id)) return void 0;
584
+ return builtInLayoutAlgorithms[id];
585
+ }
586
+ /** Execute one algorithm through the shared engine seam. */
587
+ function executeLayoutAlgorithm({ algorithm: algorithm$1, graph, options, context }) {
588
+ return algorithm$1.layout(graph, options, context);
589
+ }
590
+ function createDirectExecutionContext() {
591
+ return {
592
+ scope: { mode: "full" },
593
+ diagnostics: [],
594
+ measurePhase(_id, run) {
595
+ return run();
596
+ },
597
+ throwIfAborted() {}
598
+ };
599
+ }
600
+ /** Execute through the pinned elkjs 0.11.1 compatibility policy. */
601
+ function executeElkjs0111Layout({ algorithm: algorithm$1, graph, options }) {
602
+ return executeLayoutAlgorithm({
603
+ algorithm: elkjs0111LayoutAlgorithms[algorithm$1],
604
+ graph,
605
+ options,
606
+ context: createDirectExecutionContext()
607
+ });
608
+ }
609
+
610
+ //#endregion
611
+ Object.defineProperty(exports, 'boxAlgorithm', {
612
+ enumerable: true,
613
+ get: function () {
614
+ return boxAlgorithm;
615
+ }
616
+ });
617
+ Object.defineProperty(exports, 'builtInLayoutAlgorithms', {
618
+ enumerable: true,
619
+ get: function () {
620
+ return builtInLayoutAlgorithms;
621
+ }
622
+ });
623
+ Object.defineProperty(exports, 'elkjs0111ResultPolicy', {
624
+ enumerable: true,
625
+ get: function () {
626
+ return elkjs0111ResultPolicy;
627
+ }
628
+ });
629
+ Object.defineProperty(exports, 'executeElkjs0111Layout', {
630
+ enumerable: true,
631
+ get: function () {
632
+ return executeElkjs0111Layout;
633
+ }
634
+ });
635
+ Object.defineProperty(exports, 'executeLayoutAlgorithm', {
636
+ enumerable: true,
637
+ get: function () {
638
+ return executeLayoutAlgorithm;
639
+ }
640
+ });
641
+ Object.defineProperty(exports, 'fixedAlgorithm', {
642
+ enumerable: true,
643
+ get: function () {
644
+ return fixedAlgorithm;
645
+ }
646
+ });
647
+ Object.defineProperty(exports, 'getBoxLayout', {
648
+ enumerable: true,
649
+ get: function () {
650
+ return getBoxLayout;
651
+ }
652
+ });
653
+ Object.defineProperty(exports, 'getBuiltInLayoutAlgorithm', {
654
+ enumerable: true,
655
+ get: function () {
656
+ return getBuiltInLayoutAlgorithm;
657
+ }
658
+ });
659
+ Object.defineProperty(exports, 'getFixedLayout', {
660
+ enumerable: true,
661
+ get: function () {
662
+ return getFixedLayout;
663
+ }
664
+ });
665
+ Object.defineProperty(exports, 'getRandomLayout', {
666
+ enumerable: true,
667
+ get: function () {
668
+ return getRandomLayout;
669
+ }
670
+ });
671
+ Object.defineProperty(exports, 'getRectanglePackingLayout', {
672
+ enumerable: true,
673
+ get: function () {
674
+ return getRectanglePackingLayout;
675
+ }
676
+ });
677
+ Object.defineProperty(exports, 'getSporeCompactionLayout', {
678
+ enumerable: true,
679
+ get: function () {
680
+ return getSporeCompactionLayout;
681
+ }
682
+ });
683
+ Object.defineProperty(exports, 'getSporeOverlapRemovalLayout', {
684
+ enumerable: true,
685
+ get: function () {
686
+ return getSporeOverlapRemovalLayout;
687
+ }
688
+ });
689
+ Object.defineProperty(exports, 'randomAlgorithm', {
690
+ enumerable: true,
691
+ get: function () {
692
+ return randomAlgorithm;
693
+ }
694
+ });
695
+ Object.defineProperty(exports, 'rectanglePackingAlgorithm', {
696
+ enumerable: true,
697
+ get: function () {
698
+ return rectanglePackingAlgorithm;
699
+ }
700
+ });
701
+ Object.defineProperty(exports, 'sporeCompactionAlgorithm', {
702
+ enumerable: true,
703
+ get: function () {
704
+ return sporeCompactionAlgorithm;
705
+ }
706
+ });
707
+ Object.defineProperty(exports, 'sporeOverlapRemovalAlgorithm', {
708
+ enumerable: true,
709
+ get: function () {
710
+ return sporeOverlapRemovalAlgorithm;
711
+ }
712
+ });