@weasel-js/diagram 1.5.0 → 1.5.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.
- package/README.md +32 -0
- package/dist/chunk-AD7TY6TK.js +862 -0
- package/dist/chunk-AD7TY6TK.js.map +1 -0
- package/dist/index.d.ts +12 -749
- package/dist/index.js +17 -883
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +3 -0
- package/dist/layout.entry-C0twMfuH.d.ts +752 -0
- package/dist/layout.js +3 -0
- package/dist/layout.js.map +1 -0
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -1,442 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { portOf, portsOf, diagramNodeOf, force, tree, layered, forceRelaxation, center, outlinePath } from './chunk-AD7TY6TK.js';
|
|
2
|
+
export { COMPASS, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, force, forceRelaxation, forestOf, graphOrder, isPinned, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, pinnedSet, portOf, portsOf, portsOnOutline, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree } from './chunk-AD7TY6TK.js';
|
|
3
|
+
import { AUTO_POSE_DESCRIPTOR, pointAlongPath, translatePoseViaDescriptor, polylineFromPoints, effectivePose, composeAffordanceLayer, usePoseRun, RECT_POSE_DESCRIPTOR, registerNodeShape } from '@weasel-js/core';
|
|
4
|
+
import { portCurvePoints } from '@weasel-js/geom';
|
|
5
|
+
import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1 } from '@weasel-js/core/math';
|
|
2
6
|
import { useRef, useCallback, useEffect, useMemo } from 'react';
|
|
3
7
|
|
|
4
|
-
// src/outline.ts
|
|
5
|
-
var LEAN = 0.2;
|
|
6
|
-
var KAPPA = 0.5522847498307936;
|
|
7
|
-
function outlinePath(outline, bounds) {
|
|
8
|
-
if (typeof outline !== "string") return outline;
|
|
9
|
-
const { x, y, width: w, height: h } = bounds;
|
|
10
|
-
if (w <= 0 || h <= 0) return rectPath(x, y, Math.max(w, 0), Math.max(h, 0));
|
|
11
|
-
switch (outline) {
|
|
12
|
-
case "rect":
|
|
13
|
-
return rectPath(x, y, w, h);
|
|
14
|
-
case "diamond":
|
|
15
|
-
return polygonFromPoints([
|
|
16
|
-
{ x: x + w / 2, y },
|
|
17
|
-
{ x: x + w, y: y + h / 2 },
|
|
18
|
-
{ x: x + w / 2, y: y + h },
|
|
19
|
-
{ x, y: y + h / 2 }
|
|
20
|
-
]);
|
|
21
|
-
case "parallelogram": {
|
|
22
|
-
const lean = Math.min(w * LEAN, w / 2);
|
|
23
|
-
return polygonFromPoints([
|
|
24
|
-
{ x: x + lean, y },
|
|
25
|
-
{ x: x + w, y },
|
|
26
|
-
{ x: x + w - lean, y: y + h },
|
|
27
|
-
{ x, y: y + h }
|
|
28
|
-
]);
|
|
29
|
-
}
|
|
30
|
-
case "stadium":
|
|
31
|
-
return stadiumPath(x, y, w, h);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function stadiumPath(x, y, w, h) {
|
|
35
|
-
const r = Math.min(w, h) / 2;
|
|
36
|
-
const k = r * KAPPA;
|
|
37
|
-
const b = new PathBuilder();
|
|
38
|
-
if (w >= h) {
|
|
39
|
-
const left2 = x + r;
|
|
40
|
-
const right2 = x + w - r;
|
|
41
|
-
const top2 = y;
|
|
42
|
-
const bottom2 = y + h;
|
|
43
|
-
const midY = y + h / 2;
|
|
44
|
-
return b.moveTo(left2, top2).lineTo(right2, top2).curveTo(right2 + k, top2, x + w, midY - k, x + w, midY).curveTo(x + w, midY + k, right2 + k, bottom2, right2, bottom2).lineTo(left2, bottom2).curveTo(left2 - k, bottom2, x, midY + k, x, midY).curveTo(x, midY - k, left2 - k, top2, left2, top2).close().build();
|
|
45
|
-
}
|
|
46
|
-
const top = y + r;
|
|
47
|
-
const bottom = y + h - r;
|
|
48
|
-
const left = x;
|
|
49
|
-
const right = x + w;
|
|
50
|
-
const midX = x + w / 2;
|
|
51
|
-
return b.moveTo(right, top).lineTo(right, bottom).curveTo(right, bottom + k, midX + k, y + h, midX, y + h).curveTo(midX - k, y + h, left, bottom + k, left, bottom).lineTo(left, top).curveTo(left, top - k, midX - k, y, midX, y).curveTo(midX + k, y, right, top - k, right, top).close().build();
|
|
52
|
-
}
|
|
53
|
-
function contentBox(outline, bounds) {
|
|
54
|
-
if (typeof outline !== "string") return bounds;
|
|
55
|
-
const { x, y, width: w, height: h } = bounds;
|
|
56
|
-
switch (outline) {
|
|
57
|
-
case "rect":
|
|
58
|
-
return bounds;
|
|
59
|
-
case "diamond":
|
|
60
|
-
return { x: x + w / 4, y: y + h / 4, width: w / 2, height: h / 2 };
|
|
61
|
-
case "parallelogram": {
|
|
62
|
-
const lean = Math.min(w * LEAN, w / 2);
|
|
63
|
-
return { x: x + lean, y, width: Math.max(w - lean * 2, 0), height: h };
|
|
64
|
-
}
|
|
65
|
-
case "stadium": {
|
|
66
|
-
const r = Math.min(w, h) / 2;
|
|
67
|
-
return w >= h ? { x: x + r, y, width: Math.max(w - r * 2, 0), height: h } : { x, y: y + r, width: w, height: Math.max(h - r * 2, 0) };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function boxForContent(outline, content) {
|
|
72
|
-
if (typeof outline !== "string") return content;
|
|
73
|
-
switch (outline) {
|
|
74
|
-
case "rect":
|
|
75
|
-
return content;
|
|
76
|
-
case "diamond":
|
|
77
|
-
return { width: content.width * 2, height: content.height * 2 };
|
|
78
|
-
case "parallelogram":
|
|
79
|
-
return { width: content.width / (1 - LEAN * 2), height: content.height };
|
|
80
|
-
case "stadium":
|
|
81
|
-
return { width: content.width + content.height, height: content.height };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
var TOLERANCE = 0.25;
|
|
85
|
-
function outlinePolyline(outline, bounds) {
|
|
86
|
-
const path = outlinePath(outline, bounds);
|
|
87
|
-
return flattenPath(path);
|
|
88
|
-
}
|
|
89
|
-
function flattenPath(path) {
|
|
90
|
-
if (path.kind === "rect") {
|
|
91
|
-
const { x, y, width: w, height: h } = path;
|
|
92
|
-
return [x, y, x + w, y, x + w, y + h, x, y + h, x, y];
|
|
93
|
-
}
|
|
94
|
-
const out = [];
|
|
95
|
-
const { commands, coords } = path;
|
|
96
|
-
let ci = 0;
|
|
97
|
-
let cx = 0;
|
|
98
|
-
let cy = 0;
|
|
99
|
-
let sx = 0;
|
|
100
|
-
let sy = 0;
|
|
101
|
-
for (const cmd of commands) {
|
|
102
|
-
if (cmd === PATH_M) {
|
|
103
|
-
cx = coords[ci];
|
|
104
|
-
cy = coords[ci + 1];
|
|
105
|
-
ci += 2;
|
|
106
|
-
sx = cx;
|
|
107
|
-
sy = cy;
|
|
108
|
-
out.push(cx, cy);
|
|
109
|
-
} else if (cmd === PATH_L) {
|
|
110
|
-
cx = coords[ci];
|
|
111
|
-
cy = coords[ci + 1];
|
|
112
|
-
ci += 2;
|
|
113
|
-
out.push(cx, cy);
|
|
114
|
-
} else if (cmd === PATH_C) {
|
|
115
|
-
const x1 = coords[ci], y1 = coords[ci + 1];
|
|
116
|
-
const x2 = coords[ci + 2], y2 = coords[ci + 3];
|
|
117
|
-
const x3 = coords[ci + 4], y3 = coords[ci + 5];
|
|
118
|
-
ci += 6;
|
|
119
|
-
flattenCubic(cx, cy, x1, y1, x2, y2, x3, y3, TOLERANCE, out);
|
|
120
|
-
cx = x3;
|
|
121
|
-
cy = y3;
|
|
122
|
-
} else if (cmd === PATH_Q) {
|
|
123
|
-
const qx = coords[ci], qy = coords[ci + 1];
|
|
124
|
-
const x3 = coords[ci + 2], y3 = coords[ci + 3];
|
|
125
|
-
ci += 4;
|
|
126
|
-
flattenCubic(
|
|
127
|
-
cx,
|
|
128
|
-
cy,
|
|
129
|
-
cx + 2 / 3 * (qx - cx),
|
|
130
|
-
cy + 2 / 3 * (qy - cy),
|
|
131
|
-
x3 + 2 / 3 * (qx - x3),
|
|
132
|
-
y3 + 2 / 3 * (qy - y3),
|
|
133
|
-
x3,
|
|
134
|
-
y3,
|
|
135
|
-
TOLERANCE,
|
|
136
|
-
out
|
|
137
|
-
);
|
|
138
|
-
cx = x3;
|
|
139
|
-
cy = y3;
|
|
140
|
-
} else if (cmd === PATH_Z) {
|
|
141
|
-
out.push(sx, sy);
|
|
142
|
-
cx = sx;
|
|
143
|
-
cy = sy;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (out.length >= 2 && (out[0] !== out[out.length - 2] || out[1] !== out[out.length - 1])) {
|
|
147
|
-
out.push(out[0], out[1]);
|
|
148
|
-
}
|
|
149
|
-
return out;
|
|
150
|
-
}
|
|
151
|
-
function rayHit(poly, center2, point) {
|
|
152
|
-
const dx = point.x - center2.x;
|
|
153
|
-
const dy = point.y - center2.y;
|
|
154
|
-
if (dx === 0 && dy === 0) return null;
|
|
155
|
-
let bestT = -Infinity;
|
|
156
|
-
let hit = null;
|
|
157
|
-
for (let i = 0; i + 3 < poly.length; i += 2) {
|
|
158
|
-
const ax = poly[i], ay = poly[i + 1];
|
|
159
|
-
const bx = poly[i + 2], by = poly[i + 3];
|
|
160
|
-
const ex = bx - ax;
|
|
161
|
-
const ey = by - ay;
|
|
162
|
-
const denom = dx * ey - dy * ex;
|
|
163
|
-
if (denom === 0) continue;
|
|
164
|
-
const t = ((ax - center2.x) * ey - (ay - center2.y) * ex) / denom;
|
|
165
|
-
const u = ((ax - center2.x) * dy - (ay - center2.y) * dx) / denom;
|
|
166
|
-
if (t < 0 || u < 0 || u > 1) continue;
|
|
167
|
-
if (t > bestT) {
|
|
168
|
-
bestT = t;
|
|
169
|
-
hit = { x: center2.x + dx * t, y: center2.y + dy * t };
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return hit;
|
|
173
|
-
}
|
|
174
|
-
function portsOnOutline(ports, outline, bounds) {
|
|
175
|
-
if (bounds.width <= 0 || bounds.height <= 0) return [...ports];
|
|
176
|
-
const poly = outlinePolyline(outline, bounds);
|
|
177
|
-
const center2 = { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2 };
|
|
178
|
-
return ports.map((port) => {
|
|
179
|
-
const hit = rayHit(poly, center2, port.point);
|
|
180
|
-
return hit === null ? port : { ...port, point: hit };
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// src/trait.ts
|
|
185
|
-
var DIAGRAM_TRAIT_KEY = "diagram";
|
|
186
|
-
var dataKeyReader = (node) => {
|
|
187
|
-
const data = node.data;
|
|
188
|
-
if (data === null || typeof data !== "object") return null;
|
|
189
|
-
const trait = data[DIAGRAM_TRAIT_KEY];
|
|
190
|
-
if (trait === null || typeof trait !== "object") return null;
|
|
191
|
-
if ("from" in trait && "to" in trait) return null;
|
|
192
|
-
return trait;
|
|
193
|
-
};
|
|
194
|
-
function createDiagramNodes(entries) {
|
|
195
|
-
const seen = /* @__PURE__ */ new Set();
|
|
196
|
-
for (const entry of entries) {
|
|
197
|
-
if (seen.has(entry.name)) {
|
|
198
|
-
throw new Error(`createDiagramNodes: duplicate entry name "${entry.name}"`);
|
|
199
|
-
}
|
|
200
|
-
seen.add(entry.name);
|
|
201
|
-
}
|
|
202
|
-
return (node) => {
|
|
203
|
-
const own = dataKeyReader(node);
|
|
204
|
-
if (own !== null) return own;
|
|
205
|
-
for (const entry of entries) {
|
|
206
|
-
if (!entry.matches(node.data)) continue;
|
|
207
|
-
return typeof entry.trait === "function" ? entry.trait(node.data) : entry.trait;
|
|
208
|
-
}
|
|
209
|
-
return null;
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
function diagramNodeOf(node, read = dataKeyReader) {
|
|
213
|
-
return read(node);
|
|
214
|
-
}
|
|
215
|
-
function isPinned(node, read) {
|
|
216
|
-
return diagramNodeOf(node, read)?.pinned === true;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// src/ports.ts
|
|
220
|
-
var COMPASS = {
|
|
221
|
-
nw: { u: 0, v: 0 },
|
|
222
|
-
n: { u: 0.5, v: 0 },
|
|
223
|
-
ne: { u: 1, v: 0 },
|
|
224
|
-
e: { u: 1, v: 0.5 },
|
|
225
|
-
se: { u: 1, v: 1 },
|
|
226
|
-
s: { u: 0.5, v: 1 },
|
|
227
|
-
sw: { u: 0, v: 1 },
|
|
228
|
-
w: { u: 0, v: 0.5 }
|
|
229
|
-
};
|
|
230
|
-
var DEFAULT_PORTS = Object.freeze([
|
|
231
|
-
{ id: "n", at: COMPASS.n },
|
|
232
|
-
{ id: "e", at: COMPASS.e },
|
|
233
|
-
{ id: "s", at: COMPASS.s },
|
|
234
|
-
{ id: "w", at: COMPASS.w }
|
|
235
|
-
]);
|
|
236
|
-
function normalOf(at) {
|
|
237
|
-
const x = at.u * 2 - 1;
|
|
238
|
-
const y = at.v * 2 - 1;
|
|
239
|
-
const len = Math.hypot(x, y);
|
|
240
|
-
if (len === 0) return null;
|
|
241
|
-
return { x: x / len, y: y / len };
|
|
242
|
-
}
|
|
243
|
-
function portsOf(node, pose, opts = {}) {
|
|
244
|
-
const trait = diagramNodeOf(node, opts.read);
|
|
245
|
-
if (trait === null) return [];
|
|
246
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
247
|
-
const bounds = geometry.getBounds(pose);
|
|
248
|
-
const rotation = geometry.getRotation?.(pose) ?? 0;
|
|
249
|
-
const cx = bounds.x + bounds.width / 2;
|
|
250
|
-
const cy = bounds.y + bounds.height / 2;
|
|
251
|
-
const poly = trait.outline === void 0 || bounds.width <= 0 || bounds.height <= 0 ? null : outlinePolyline(trait.outline, bounds);
|
|
252
|
-
return (trait.ports ?? DEFAULT_PORTS).map((spec) => {
|
|
253
|
-
const anchored = {
|
|
254
|
-
x: bounds.x + spec.at.u * bounds.width,
|
|
255
|
-
y: bounds.y + spec.at.v * bounds.height
|
|
256
|
-
};
|
|
257
|
-
const flat = (poly === null ? null : rayHit(poly, { x: cx, y: cy }, anchored)) ?? anchored;
|
|
258
|
-
const normal = spec.normal === void 0 ? normalOf(spec.at) : spec.normal;
|
|
259
|
-
if (rotation === 0) {
|
|
260
|
-
return { id: spec.id, nodeId: node.id, point: flat, normal, ...typeOf(spec) };
|
|
261
|
-
}
|
|
262
|
-
return {
|
|
263
|
-
id: spec.id,
|
|
264
|
-
nodeId: node.id,
|
|
265
|
-
point: rotatePoint(flat.x, flat.y, cx, cy, rotation),
|
|
266
|
-
// A direction rotates about the origin, not about the node's center.
|
|
267
|
-
normal: normal === null ? null : rotatePoint(normal.x, normal.y, 0, 0, rotation),
|
|
268
|
-
...typeOf(spec)
|
|
269
|
-
};
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
function portOf(node, pose, portId, opts = {}) {
|
|
273
|
-
return portsOf(node, pose, opts).find((p) => p.id === portId);
|
|
274
|
-
}
|
|
275
|
-
function typeOf(spec) {
|
|
276
|
-
return spec.type === void 0 ? {} : { type: spec.type };
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// src/body.ts
|
|
280
|
-
var DEFAULT_PADDING = 8;
|
|
281
|
-
var DEFAULT_GAP = 4;
|
|
282
|
-
var FALLBACK_LINE = 16;
|
|
283
|
-
function canvasMeasure(ctx) {
|
|
284
|
-
return (text, style) => {
|
|
285
|
-
const size = style.fontSize ?? FALLBACK_LINE;
|
|
286
|
-
ctx.font = `${style.bold === true ? "bold " : ""}${size}px ${style.fontFamily ?? "sans-serif"}`;
|
|
287
|
-
return { width: ctx.measureText(text).width, height: Math.ceil(size * 1.2) };
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
function measureRow(row, measure) {
|
|
291
|
-
const text = (s, style) => measure === void 0 ? { width: s.length * ((style?.fontSize ?? FALLBACK_LINE) * 0.6), height: FALLBACK_LINE } : measure(s, style ?? {});
|
|
292
|
-
switch (row.kind) {
|
|
293
|
-
case "label":
|
|
294
|
-
return text(row.text, row.style);
|
|
295
|
-
case "field": {
|
|
296
|
-
const l = text(row.label, row.style);
|
|
297
|
-
const v = text(row.value, row.style);
|
|
298
|
-
return { width: l.width + DEFAULT_GAP * 2 + v.width, height: Math.max(l.height, v.height) };
|
|
299
|
-
}
|
|
300
|
-
case "ports": {
|
|
301
|
-
const side = (ports) => (ports ?? []).reduce((w, p) => Math.max(w, p.label === void 0 ? 0 : text(p.label).width), 0);
|
|
302
|
-
const rows = Math.max(row.left?.length ?? 0, row.right?.length ?? 0, 1);
|
|
303
|
-
return {
|
|
304
|
-
width: side(row.left) + DEFAULT_GAP * 2 + side(row.right),
|
|
305
|
-
height: row.height ?? rows * FALLBACK_LINE
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
case "slot":
|
|
309
|
-
return { width: 0, height: row.height };
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
function measureBody(spec, measure) {
|
|
313
|
-
const padding = spec.padding ?? DEFAULT_PADDING;
|
|
314
|
-
const gap = spec.gap ?? DEFAULT_GAP;
|
|
315
|
-
let width = 0;
|
|
316
|
-
let height = 0;
|
|
317
|
-
for (const [i, row] of spec.rows.entries()) {
|
|
318
|
-
const m = measureRow(row, measure);
|
|
319
|
-
width = Math.max(width, m.width);
|
|
320
|
-
height += m.height + (i > 0 ? gap : 0);
|
|
321
|
-
}
|
|
322
|
-
const box = boxForContent(spec.outline, {
|
|
323
|
-
width: width + padding * 2,
|
|
324
|
-
height: height + padding * 2
|
|
325
|
-
});
|
|
326
|
-
return { minWidth: box.width, minHeight: box.height };
|
|
327
|
-
}
|
|
328
|
-
function sizeToBody(pose, floor) {
|
|
329
|
-
const width = Math.max(pose.width, floor.minWidth);
|
|
330
|
-
const height = Math.max(pose.height, floor.minHeight);
|
|
331
|
-
if (width === pose.width && height === pose.height) return pose;
|
|
332
|
-
return { ...pose, width, height };
|
|
333
|
-
}
|
|
334
|
-
function layoutBody(spec, bounds, measure) {
|
|
335
|
-
const padding = spec.padding ?? DEFAULT_PADDING;
|
|
336
|
-
const gap = spec.gap ?? DEFAULT_GAP;
|
|
337
|
-
const inner = contentBox(spec.outline, bounds);
|
|
338
|
-
const width = Math.max(inner.width - padding * 2, 0);
|
|
339
|
-
let y = inner.y + padding;
|
|
340
|
-
return spec.rows.map((row, index) => {
|
|
341
|
-
const h = measureRow(row, measure).height;
|
|
342
|
-
const box = { row, index, x: inner.x + padding, y, width, height: h };
|
|
343
|
-
y += h + gap;
|
|
344
|
-
return box;
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
function layoutRowPorts(row, box, measure) {
|
|
348
|
-
const width = (label) => label === void 0 ? 0 : measureRow({ kind: "label", text: label }, measure).width;
|
|
349
|
-
const slots = Math.max(row.left?.length ?? 0, row.right?.length ?? 0, 1);
|
|
350
|
-
const slotHeight = box.height / slots;
|
|
351
|
-
const out = [];
|
|
352
|
-
const side = (ports, which) => {
|
|
353
|
-
for (const [index, port] of (ports ?? []).entries()) {
|
|
354
|
-
const w = width(port.label);
|
|
355
|
-
out.push({
|
|
356
|
-
port,
|
|
357
|
-
side: which,
|
|
358
|
-
index,
|
|
359
|
-
x: which === "left" ? box.x : box.x + box.width - w,
|
|
360
|
-
y: box.y + index * slotHeight,
|
|
361
|
-
width: w,
|
|
362
|
-
height: slotHeight
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
side(row.left, "left");
|
|
367
|
-
side(row.right, "right");
|
|
368
|
-
return out;
|
|
369
|
-
}
|
|
370
|
-
function bodyTrait(spec, bounds, measure) {
|
|
371
|
-
const outline = spec.outline;
|
|
372
|
-
const compass = [
|
|
373
|
-
{ id: "n", at: COMPASS.n },
|
|
374
|
-
{ id: "e", at: COMPASS.e },
|
|
375
|
-
{ id: "s", at: COMPASS.s },
|
|
376
|
-
{ id: "w", at: COMPASS.w }
|
|
377
|
-
];
|
|
378
|
-
if (bounds.height <= 0) return { outline, ports: compass };
|
|
379
|
-
const rowPorts = [];
|
|
380
|
-
let hasLeft = false;
|
|
381
|
-
let hasRight = false;
|
|
382
|
-
for (const box of layoutBody(spec, bounds, measure)) {
|
|
383
|
-
if (box.row.kind !== "ports") continue;
|
|
384
|
-
for (const pb of layoutRowPorts(box.row, box, measure)) {
|
|
385
|
-
if (pb.side === "left") hasLeft = true;
|
|
386
|
-
else hasRight = true;
|
|
387
|
-
const v = (pb.y + pb.height / 2 - bounds.y) / bounds.height;
|
|
388
|
-
rowPorts.push(withType(
|
|
389
|
-
{ id: pb.port.id, at: { u: pb.side === "left" ? 0 : 1, v } },
|
|
390
|
-
pb.port.type
|
|
391
|
-
));
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
const kept = compass.filter((p) => !(p.id === "w" && hasLeft) && !(p.id === "e" && hasRight));
|
|
395
|
-
return { outline, ports: [...kept, ...rowPorts] };
|
|
396
|
-
}
|
|
397
|
-
function withType(spec, type) {
|
|
398
|
-
return type === void 0 ? spec : { ...spec, type };
|
|
399
|
-
}
|
|
400
|
-
function bodyOutline(spec, bounds) {
|
|
401
|
-
return outlinePath(spec.outline, bounds);
|
|
402
|
-
}
|
|
403
|
-
function buildBody(spec, at, opts) {
|
|
404
|
-
const pose = sizeToBody(at, measureBody(spec, opts.measure));
|
|
405
|
-
const specs = [{
|
|
406
|
-
kind: "container",
|
|
407
|
-
layer: opts.layer,
|
|
408
|
-
pose,
|
|
409
|
-
data: opts.body(bodyTrait(spec, pose, opts.measure), pose),
|
|
410
|
-
...opts.id === void 0 ? {} : { id: opts.id }
|
|
411
|
-
}];
|
|
412
|
-
const leaf = (box, data) => {
|
|
413
|
-
specs.push({
|
|
414
|
-
kind: "leaf",
|
|
415
|
-
layer: opts.layer,
|
|
416
|
-
pose: { ...pose, x: box.x, y: box.y, width: box.width, height: box.height },
|
|
417
|
-
data,
|
|
418
|
-
pickable: false,
|
|
419
|
-
...opts.id === void 0 ? {} : { parent: opts.id }
|
|
420
|
-
});
|
|
421
|
-
};
|
|
422
|
-
const portLabel = opts.portLabel;
|
|
423
|
-
for (const box of layoutBody(spec, pose, opts.measure)) {
|
|
424
|
-
const data = opts.row(rowText(box.row), box);
|
|
425
|
-
if (data !== null) leaf(box, data);
|
|
426
|
-
if (portLabel === void 0 || box.row.kind !== "ports") continue;
|
|
427
|
-
for (const pb of layoutRowPorts(box.row, box, opts.measure)) {
|
|
428
|
-
if (pb.port.label === void 0) continue;
|
|
429
|
-
const labelData = portLabel(pb.port.label, pb);
|
|
430
|
-
if (labelData !== null) leaf(pb, labelData);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return { pose, specs };
|
|
434
|
-
}
|
|
435
|
-
function rowText(row) {
|
|
436
|
-
if (row.kind === "label") return row.text;
|
|
437
|
-
if (row.kind === "field") return `${row.label}: ${row.value}`;
|
|
438
|
-
return "";
|
|
439
|
-
}
|
|
440
8
|
var NAMED = { start: 0, mid: 0.5, end: 1 };
|
|
441
9
|
function diagramLabelOf(node) {
|
|
442
10
|
const data = node.data;
|
|
@@ -505,36 +73,28 @@ var orthogonal = (req) => {
|
|
|
505
73
|
function isHorizontal(normal) {
|
|
506
74
|
return Math.abs(normal?.x ?? 1) >= Math.abs(normal?.y ?? 0);
|
|
507
75
|
}
|
|
508
|
-
var BEZIER_REACH = 0.4;
|
|
509
76
|
var bezier = (req) => {
|
|
510
77
|
const stops = [req.from.point, ...req.waypoints, req.to.point];
|
|
511
78
|
const out = [stops[0]];
|
|
512
79
|
for (let i = 1; i < stops.length; i++) {
|
|
513
80
|
const a = stops[i - 1];
|
|
514
81
|
const b = stops[i];
|
|
515
|
-
const reach = Math.hypot(b.x - a.x, b.y - a.y) * BEZIER_REACH;
|
|
516
82
|
const outN = i === 1 ? req.from.normal : null;
|
|
517
83
|
const inN = i === stops.length - 1 ? req.to.normal : null;
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
84
|
+
const leg = portCurvePoints(
|
|
85
|
+
a.x,
|
|
86
|
+
a.y,
|
|
87
|
+
b.x,
|
|
88
|
+
b.y,
|
|
89
|
+
outN == null ? null : [outN.x, outN.y],
|
|
90
|
+
inN == null ? null : [inN.x, inN.y],
|
|
91
|
+
BEZIER_SAMPLES
|
|
92
|
+
);
|
|
93
|
+
for (const [x, y] of leg) out.push({ x, y });
|
|
523
94
|
}
|
|
524
95
|
return out;
|
|
525
96
|
};
|
|
526
97
|
var BEZIER_SAMPLES = 16;
|
|
527
|
-
function cubicAt(p0, p1, p2, p3, t) {
|
|
528
|
-
const u = 1 - t;
|
|
529
|
-
const a = u * u * u;
|
|
530
|
-
const b = 3 * u * u * t;
|
|
531
|
-
const c = 3 * u * t * t;
|
|
532
|
-
const d = t * t * t;
|
|
533
|
-
return {
|
|
534
|
-
x: a * p0.x + b * p1.x + c * p2.x + d * p3.x,
|
|
535
|
-
y: a * p0.y + b * p1.y + c * p2.y + d * p3.y
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
98
|
var ROUTERS = Object.freeze({
|
|
539
99
|
straight,
|
|
540
100
|
orthogonal,
|
|
@@ -838,7 +398,7 @@ function diagramPorts(opts) {
|
|
|
838
398
|
};
|
|
839
399
|
}
|
|
840
400
|
function buildGraph(source, opts = {}) {
|
|
841
|
-
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
|
|
401
|
+
const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR$1;
|
|
842
402
|
const nodes = [];
|
|
843
403
|
const byId = /* @__PURE__ */ new Map();
|
|
844
404
|
const candidates = [];
|
|
@@ -881,432 +441,6 @@ function push(index, key, edge) {
|
|
|
881
441
|
if (list === void 0) index.set(key, [edge]);
|
|
882
442
|
else list.push(edge);
|
|
883
443
|
}
|
|
884
|
-
|
|
885
|
-
// src/layout.ts
|
|
886
|
-
var DEFAULT_NODE_GAP = 48;
|
|
887
|
-
var DEFAULT_RANK_GAP = 96;
|
|
888
|
-
var DEFAULT_TOLERANCE = 1e-6;
|
|
889
|
-
function axesFor(direction = "down") {
|
|
890
|
-
switch (direction) {
|
|
891
|
-
case "up":
|
|
892
|
-
return { rank: "y", cross: "x", sign: -1 };
|
|
893
|
-
case "right":
|
|
894
|
-
return { rank: "x", cross: "y", sign: 1 };
|
|
895
|
-
case "left":
|
|
896
|
-
return { rank: "x", cross: "y", sign: -1 };
|
|
897
|
-
default:
|
|
898
|
-
return { rank: "y", cross: "x", sign: 1 };
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
function extent(bounds, axis) {
|
|
902
|
-
return axis === "x" ? bounds.width : bounds.height;
|
|
903
|
-
}
|
|
904
|
-
function center(bounds, axis) {
|
|
905
|
-
return axis === "x" ? bounds.x + bounds.width / 2 : bounds.y + bounds.height / 2;
|
|
906
|
-
}
|
|
907
|
-
function pinnedSet(graph, opts = {}) {
|
|
908
|
-
const pinned = new Set(opts.pin ?? []);
|
|
909
|
-
for (const node of graph.nodes) {
|
|
910
|
-
if (node.pinned) pinned.add(node.id);
|
|
911
|
-
}
|
|
912
|
-
return pinned;
|
|
913
|
-
}
|
|
914
|
-
function settle(graph, slots, opts = {}) {
|
|
915
|
-
const axes = axesFor(opts.direction);
|
|
916
|
-
const pinned = pinnedSet(graph, opts);
|
|
917
|
-
const tolerance = opts.tolerance ?? DEFAULT_TOLERANCE;
|
|
918
|
-
const placed = /* @__PURE__ */ new Map();
|
|
919
|
-
for (const node of graph.nodes) {
|
|
920
|
-
const slot = slots.get(node.id);
|
|
921
|
-
if (slot === void 0) continue;
|
|
922
|
-
const along = axes.sign === 1 ? slot.rank : -(slot.rank + extent(node.bounds, axes.rank));
|
|
923
|
-
placed.set(
|
|
924
|
-
node.id,
|
|
925
|
-
axes.rank === "y" ? { x: slot.cross, y: along } : { x: along, y: slot.cross }
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
return translated(graph, placed, pinned, tolerance);
|
|
929
|
-
}
|
|
930
|
-
function translated(graph, placed, pinned, tolerance = DEFAULT_TOLERANCE) {
|
|
931
|
-
const anchor = anchorDelta(graph, placed, pinned);
|
|
932
|
-
const out = /* @__PURE__ */ new Map();
|
|
933
|
-
for (const node of graph.nodes) {
|
|
934
|
-
const at = placed.get(node.id);
|
|
935
|
-
if (at === void 0 || pinned.has(node.id)) continue;
|
|
936
|
-
const x = at.x + anchor.x;
|
|
937
|
-
const y = at.y + anchor.y;
|
|
938
|
-
if (Math.abs(x - node.bounds.x) <= tolerance && Math.abs(y - node.bounds.y) <= tolerance) {
|
|
939
|
-
continue;
|
|
940
|
-
}
|
|
941
|
-
out.set(node.id, { x, y });
|
|
942
|
-
}
|
|
943
|
-
return out;
|
|
944
|
-
}
|
|
945
|
-
function anchorDelta(graph, placed, pinned) {
|
|
946
|
-
for (const node of graph.nodes) {
|
|
947
|
-
if (!pinned.has(node.id)) continue;
|
|
948
|
-
const at = placed.get(node.id);
|
|
949
|
-
if (at !== void 0) return { x: node.bounds.x - at.x, y: node.bounds.y - at.y };
|
|
950
|
-
}
|
|
951
|
-
let minCurX = Infinity;
|
|
952
|
-
let minCurY = Infinity;
|
|
953
|
-
let minNewX = Infinity;
|
|
954
|
-
let minNewY = Infinity;
|
|
955
|
-
for (const node of graph.nodes) {
|
|
956
|
-
const at = placed.get(node.id);
|
|
957
|
-
if (at === void 0) continue;
|
|
958
|
-
minCurX = Math.min(minCurX, node.bounds.x);
|
|
959
|
-
minCurY = Math.min(minCurY, node.bounds.y);
|
|
960
|
-
minNewX = Math.min(minNewX, at.x);
|
|
961
|
-
minNewY = Math.min(minNewY, at.y);
|
|
962
|
-
}
|
|
963
|
-
if (!Number.isFinite(minCurX)) return { x: 0, y: 0 };
|
|
964
|
-
return { x: minCurX - minNewX, y: minCurY - minNewY };
|
|
965
|
-
}
|
|
966
|
-
function packAcross(nodes, axis, gap) {
|
|
967
|
-
const at = /* @__PURE__ */ new Map();
|
|
968
|
-
let cursor = 0;
|
|
969
|
-
for (const node of nodes) {
|
|
970
|
-
at.set(node.id, cursor);
|
|
971
|
-
cursor += extent(node.bounds, axis) + gap;
|
|
972
|
-
}
|
|
973
|
-
return { at, span: nodes.length === 0 ? 0 : cursor - gap };
|
|
974
|
-
}
|
|
975
|
-
function seededOrder(nodes, axis, indexOf) {
|
|
976
|
-
return [...nodes].sort((a, b) => {
|
|
977
|
-
const d = center(a.bounds, axis) - center(b.bounds, axis);
|
|
978
|
-
return d !== 0 ? d : indexOf(a.id) - indexOf(b.id);
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
function graphOrder(graph) {
|
|
982
|
-
const index = /* @__PURE__ */ new Map();
|
|
983
|
-
graph.nodes.forEach((node, i) => index.set(node.id, i));
|
|
984
|
-
return (id) => index.get(id) ?? Number.MAX_SAFE_INTEGER;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
// src/force.ts
|
|
988
|
-
var DEFAULTS = {
|
|
989
|
-
iterations: 300,
|
|
990
|
-
linkDistance: 160,
|
|
991
|
-
linkStrength: 0.35,
|
|
992
|
-
charge: -3e3,
|
|
993
|
-
gravity: 0.02,
|
|
994
|
-
padding: 24
|
|
995
|
-
};
|
|
996
|
-
var GOLDEN = Math.PI * (3 - Math.sqrt(5));
|
|
997
|
-
var NUDGE = 0.5;
|
|
998
|
-
function charge(bodies, strength) {
|
|
999
|
-
return (alpha) => {
|
|
1000
|
-
for (let i = 0; i < bodies.length; i++) {
|
|
1001
|
-
const a = bodies[i];
|
|
1002
|
-
for (let j = i + 1; j < bodies.length; j++) {
|
|
1003
|
-
const b = bodies[j];
|
|
1004
|
-
const dx = b.x - a.x;
|
|
1005
|
-
const dy = b.y - a.y;
|
|
1006
|
-
const d2 = dx * dx + dy * dy;
|
|
1007
|
-
if (d2 === 0) continue;
|
|
1008
|
-
const w = strength * alpha / d2;
|
|
1009
|
-
a.vx += dx * w;
|
|
1010
|
-
a.vy += dy * w;
|
|
1011
|
-
b.vx -= dx * w;
|
|
1012
|
-
b.vy -= dy * w;
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
1017
|
-
function links(bodies, graph, distance, strength) {
|
|
1018
|
-
return (alpha) => {
|
|
1019
|
-
for (const edge of graph.edges) {
|
|
1020
|
-
const a = bodies.get(edge.from);
|
|
1021
|
-
const b = bodies.get(edge.to);
|
|
1022
|
-
if (a === void 0 || b === void 0 || a === b) continue;
|
|
1023
|
-
const dx = b.x - a.x;
|
|
1024
|
-
const dy = b.y - a.y;
|
|
1025
|
-
const d = Math.hypot(dx, dy);
|
|
1026
|
-
if (d === 0) continue;
|
|
1027
|
-
const pull = (d - distance) / d * alpha * strength;
|
|
1028
|
-
a.vx += dx * pull;
|
|
1029
|
-
a.vy += dy * pull;
|
|
1030
|
-
b.vx -= dx * pull;
|
|
1031
|
-
b.vy -= dy * pull;
|
|
1032
|
-
}
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
function separateBoxes(bodies, padding) {
|
|
1036
|
-
return () => {
|
|
1037
|
-
for (let i = 0; i < bodies.length; i++) {
|
|
1038
|
-
const a = bodies[i];
|
|
1039
|
-
for (let j = i + 1; j < bodies.length; j++) {
|
|
1040
|
-
const b = bodies[j];
|
|
1041
|
-
const dx = b.x - a.x;
|
|
1042
|
-
const dy = b.y - a.y;
|
|
1043
|
-
const gapX = a.hw + b.hw + padding - Math.abs(dx);
|
|
1044
|
-
const gapY = a.hh + b.hh + padding - Math.abs(dy);
|
|
1045
|
-
if (gapX <= 0 || gapY <= 0) continue;
|
|
1046
|
-
if (gapX < gapY) {
|
|
1047
|
-
const push2 = (Math.sign(dx) || 1) * gapX * 0.5;
|
|
1048
|
-
a.vx -= push2;
|
|
1049
|
-
b.vx += push2;
|
|
1050
|
-
} else {
|
|
1051
|
-
const push2 = (Math.sign(dy) || 1) * gapY * 0.5;
|
|
1052
|
-
a.vy -= push2;
|
|
1053
|
-
b.vy += push2;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
};
|
|
1058
|
-
}
|
|
1059
|
-
function gravity(bodies, at, strength) {
|
|
1060
|
-
return (alpha) => {
|
|
1061
|
-
for (const body of bodies) {
|
|
1062
|
-
body.vx += (at.x - body.x) * strength * alpha;
|
|
1063
|
-
body.vy += (at.y - body.y) * strength * alpha;
|
|
1064
|
-
}
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1067
|
-
function forceRelaxation(graph, opts = {}) {
|
|
1068
|
-
const {
|
|
1069
|
-
linkDistance = DEFAULTS.linkDistance,
|
|
1070
|
-
linkStrength = DEFAULTS.linkStrength,
|
|
1071
|
-
charge: chargeStrength = DEFAULTS.charge,
|
|
1072
|
-
gravity: gravityStrength = DEFAULTS.gravity,
|
|
1073
|
-
padding = DEFAULTS.padding
|
|
1074
|
-
} = opts;
|
|
1075
|
-
const pinned = pinnedSet(graph, opts);
|
|
1076
|
-
const bodies = graph.nodes.map((node) => {
|
|
1077
|
-
const body = {
|
|
1078
|
-
id: node.id,
|
|
1079
|
-
x: center(node.bounds, "x"),
|
|
1080
|
-
y: center(node.bounds, "y"),
|
|
1081
|
-
hw: node.bounds.width / 2,
|
|
1082
|
-
hh: node.bounds.height / 2
|
|
1083
|
-
};
|
|
1084
|
-
if (pinned.has(node.id)) {
|
|
1085
|
-
body.fx = body.x;
|
|
1086
|
-
body.fy = body.y;
|
|
1087
|
-
}
|
|
1088
|
-
return body;
|
|
1089
|
-
});
|
|
1090
|
-
separate(bodies);
|
|
1091
|
-
const byId = new Map(bodies.map((b) => [b.id, b]));
|
|
1092
|
-
const centroid = {
|
|
1093
|
-
x: bodies.reduce((sum, b) => sum + b.x, 0) / bodies.length,
|
|
1094
|
-
y: bodies.reduce((sum, b) => sum + b.y, 0) / bodies.length
|
|
1095
|
-
};
|
|
1096
|
-
const sim = createSimulation({
|
|
1097
|
-
nodes: bodies,
|
|
1098
|
-
forces: [
|
|
1099
|
-
charge(bodies, chargeStrength),
|
|
1100
|
-
links(byId, graph, linkDistance, linkStrength),
|
|
1101
|
-
gravity(bodies, centroid, gravityStrength),
|
|
1102
|
-
separateBoxes(bodies, padding)
|
|
1103
|
-
]
|
|
1104
|
-
});
|
|
1105
|
-
return {
|
|
1106
|
-
bodies,
|
|
1107
|
-
byId,
|
|
1108
|
-
pinned,
|
|
1109
|
-
sim,
|
|
1110
|
-
placed() {
|
|
1111
|
-
const placed = /* @__PURE__ */ new Map();
|
|
1112
|
-
for (const node of graph.nodes) {
|
|
1113
|
-
const body = byId.get(node.id);
|
|
1114
|
-
placed.set(node.id, {
|
|
1115
|
-
x: body.x - node.bounds.width / 2,
|
|
1116
|
-
y: body.y - node.bounds.height / 2
|
|
1117
|
-
});
|
|
1118
|
-
}
|
|
1119
|
-
return placed;
|
|
1120
|
-
}
|
|
1121
|
-
};
|
|
1122
|
-
}
|
|
1123
|
-
var force = (graph, opts = {}) => {
|
|
1124
|
-
if (graph.nodes.length === 0) return /* @__PURE__ */ new Map();
|
|
1125
|
-
const relaxation = forceRelaxation(graph, opts);
|
|
1126
|
-
relaxation.sim.tick(opts.iterations ?? DEFAULTS.iterations);
|
|
1127
|
-
return translated(graph, relaxation.placed(), relaxation.pinned, opts.tolerance);
|
|
1128
|
-
};
|
|
1129
|
-
function separate(bodies) {
|
|
1130
|
-
const seen = /* @__PURE__ */ new Map();
|
|
1131
|
-
bodies.forEach((body, i) => {
|
|
1132
|
-
const key = `${body.x},${body.y}`;
|
|
1133
|
-
const nth = seen.get(key) ?? 0;
|
|
1134
|
-
seen.set(key, nth + 1);
|
|
1135
|
-
if (nth === 0 || body.fx != null || body.fy != null) return;
|
|
1136
|
-
const angle = GOLDEN * i;
|
|
1137
|
-
const radius = NUDGE * nth;
|
|
1138
|
-
body.x += Math.cos(angle) * radius;
|
|
1139
|
-
body.y += Math.sin(angle) * radius;
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
// src/layered.ts
|
|
1144
|
-
function backEdges(graph) {
|
|
1145
|
-
const back = /* @__PURE__ */ new Set();
|
|
1146
|
-
const done = /* @__PURE__ */ new Set();
|
|
1147
|
-
const onStack = /* @__PURE__ */ new Set();
|
|
1148
|
-
const walk = (id) => {
|
|
1149
|
-
if (done.has(id)) return;
|
|
1150
|
-
onStack.add(id);
|
|
1151
|
-
for (const edge of graph.outgoing(id)) {
|
|
1152
|
-
if (onStack.has(edge.to)) back.add(edge.id);
|
|
1153
|
-
else walk(edge.to);
|
|
1154
|
-
}
|
|
1155
|
-
onStack.delete(id);
|
|
1156
|
-
done.add(id);
|
|
1157
|
-
};
|
|
1158
|
-
for (const node of graph.nodes) walk(node.id);
|
|
1159
|
-
return back;
|
|
1160
|
-
}
|
|
1161
|
-
function ranksOf(graph, back) {
|
|
1162
|
-
const ranks = /* @__PURE__ */ new Map();
|
|
1163
|
-
const resolving = /* @__PURE__ */ new Set();
|
|
1164
|
-
const rankOf = (id) => {
|
|
1165
|
-
const known = ranks.get(id);
|
|
1166
|
-
if (known !== void 0) return known;
|
|
1167
|
-
if (resolving.has(id)) return 0;
|
|
1168
|
-
resolving.add(id);
|
|
1169
|
-
let rank = 0;
|
|
1170
|
-
for (const edge of graph.incoming(id)) {
|
|
1171
|
-
if (back.has(edge.id)) continue;
|
|
1172
|
-
rank = Math.max(rank, rankOf(edge.from) + 1);
|
|
1173
|
-
}
|
|
1174
|
-
resolving.delete(id);
|
|
1175
|
-
ranks.set(id, rank);
|
|
1176
|
-
return rank;
|
|
1177
|
-
};
|
|
1178
|
-
for (const node of graph.nodes) rankOf(node.id);
|
|
1179
|
-
return ranks;
|
|
1180
|
-
}
|
|
1181
|
-
var layered = (graph, opts = {}) => {
|
|
1182
|
-
const axes = axesFor(opts.direction);
|
|
1183
|
-
const nodeGap = opts.nodeGap ?? DEFAULT_NODE_GAP;
|
|
1184
|
-
const rankGap = opts.rankGap ?? DEFAULT_RANK_GAP;
|
|
1185
|
-
const order = graphOrder(graph);
|
|
1186
|
-
const ranks = ranksOf(graph, backEdges(graph));
|
|
1187
|
-
const byRank = /* @__PURE__ */ new Map();
|
|
1188
|
-
for (const node of graph.nodes) {
|
|
1189
|
-
const rank = ranks.get(node.id) ?? 0;
|
|
1190
|
-
const list = byRank.get(rank);
|
|
1191
|
-
if (list === void 0) byRank.set(rank, [node]);
|
|
1192
|
-
else list.push(node);
|
|
1193
|
-
}
|
|
1194
|
-
const rows = [...byRank.keys()].sort((a, b) => a - b);
|
|
1195
|
-
const packed = /* @__PURE__ */ new Map();
|
|
1196
|
-
let widest = 0;
|
|
1197
|
-
for (const rank of rows) {
|
|
1198
|
-
const row = seededOrder(byRank.get(rank), axes.cross, order);
|
|
1199
|
-
const p = packAcross(row, axes.cross, nodeGap);
|
|
1200
|
-
packed.set(rank, p);
|
|
1201
|
-
widest = Math.max(widest, p.span);
|
|
1202
|
-
}
|
|
1203
|
-
const slots = /* @__PURE__ */ new Map();
|
|
1204
|
-
let along = 0;
|
|
1205
|
-
for (const rank of rows) {
|
|
1206
|
-
const row = byRank.get(rank);
|
|
1207
|
-
const p = packed.get(rank);
|
|
1208
|
-
const indent = (widest - p.span) / 2;
|
|
1209
|
-
const depth = Math.max(0, ...row.map((n) => extent(n.bounds, axes.rank)));
|
|
1210
|
-
for (const node of row) {
|
|
1211
|
-
slots.set(node.id, {
|
|
1212
|
-
cross: indent + p.at.get(node.id),
|
|
1213
|
-
// Centered in its rank's band, so a tall node does not push its
|
|
1214
|
-
// neighbors' edges off the line they read along.
|
|
1215
|
-
rank: along + (depth - extent(node.bounds, axes.rank)) / 2
|
|
1216
|
-
});
|
|
1217
|
-
}
|
|
1218
|
-
along += depth + rankGap;
|
|
1219
|
-
}
|
|
1220
|
-
return settle(graph, slots, opts);
|
|
1221
|
-
};
|
|
1222
|
-
|
|
1223
|
-
// src/tree.ts
|
|
1224
|
-
function forestOf(graph, crossAxis) {
|
|
1225
|
-
const order = graphOrder(graph);
|
|
1226
|
-
const children = /* @__PURE__ */ new Map();
|
|
1227
|
-
const depth = /* @__PURE__ */ new Map();
|
|
1228
|
-
const roots = [];
|
|
1229
|
-
const claim = (node, at) => {
|
|
1230
|
-
depth.set(node.id, at);
|
|
1231
|
-
const kids = [];
|
|
1232
|
-
for (const edge of graph.outgoing(node.id)) {
|
|
1233
|
-
if (edge.to === node.id || depth.has(edge.to)) continue;
|
|
1234
|
-
const child = graph.node(edge.to);
|
|
1235
|
-
if (child === void 0) continue;
|
|
1236
|
-
depth.set(child.id, at + 1);
|
|
1237
|
-
kids.push(child);
|
|
1238
|
-
}
|
|
1239
|
-
const ordered = seededOrder(kids, crossAxis, order);
|
|
1240
|
-
children.set(node.id, ordered);
|
|
1241
|
-
for (const child of ordered) claim(child, at + 1);
|
|
1242
|
-
};
|
|
1243
|
-
const rootish = graph.nodes.filter((node) => graph.incoming(node.id).every((edge) => edge.from === node.id));
|
|
1244
|
-
for (const node of seededOrder(rootish, crossAxis, order)) {
|
|
1245
|
-
if (depth.has(node.id)) continue;
|
|
1246
|
-
roots.push(node);
|
|
1247
|
-
claim(node, 0);
|
|
1248
|
-
}
|
|
1249
|
-
for (const node of graph.nodes) {
|
|
1250
|
-
if (depth.has(node.id)) continue;
|
|
1251
|
-
roots.push(node);
|
|
1252
|
-
claim(node, 0);
|
|
1253
|
-
}
|
|
1254
|
-
return { roots, children, depth };
|
|
1255
|
-
}
|
|
1256
|
-
var tree = (graph, opts = {}) => {
|
|
1257
|
-
const axes = axesFor(opts.direction);
|
|
1258
|
-
const nodeGap = opts.nodeGap ?? DEFAULT_NODE_GAP;
|
|
1259
|
-
const rankGap = opts.rankGap ?? DEFAULT_RANK_GAP;
|
|
1260
|
-
const { roots, children, depth } = forestOf(graph, axes.cross);
|
|
1261
|
-
const bands = /* @__PURE__ */ new Map();
|
|
1262
|
-
for (const node of graph.nodes) {
|
|
1263
|
-
const at = depth.get(node.id) ?? 0;
|
|
1264
|
-
bands.set(at, Math.max(bands.get(at) ?? 0, extent(node.bounds, axes.rank)));
|
|
1265
|
-
}
|
|
1266
|
-
const bandStart = /* @__PURE__ */ new Map();
|
|
1267
|
-
let along = 0;
|
|
1268
|
-
for (const at of [...bands.keys()].sort((a, b) => a - b)) {
|
|
1269
|
-
bandStart.set(at, along);
|
|
1270
|
-
along += bands.get(at) + rankGap;
|
|
1271
|
-
}
|
|
1272
|
-
const slots = /* @__PURE__ */ new Map();
|
|
1273
|
-
const measured = /* @__PURE__ */ new Map();
|
|
1274
|
-
const measure = (node) => {
|
|
1275
|
-
const known = measured.get(node.id);
|
|
1276
|
-
if (known !== void 0) return known;
|
|
1277
|
-
const kids = children.get(node.id) ?? [];
|
|
1278
|
-
let width = 0;
|
|
1279
|
-
for (const child of kids) width += measure(child);
|
|
1280
|
-
if (kids.length > 0) width += nodeGap * (kids.length - 1);
|
|
1281
|
-
const block = Math.max(extent(node.bounds, axes.cross), width);
|
|
1282
|
-
measured.set(node.id, block);
|
|
1283
|
-
return block;
|
|
1284
|
-
};
|
|
1285
|
-
const place = (node, from) => {
|
|
1286
|
-
const kids = children.get(node.id) ?? [];
|
|
1287
|
-
const block = measure(node);
|
|
1288
|
-
const width = kids.reduce((sum, k) => sum + measure(k), 0) + Math.max(0, kids.length - 1) * nodeGap;
|
|
1289
|
-
let cursor2 = from + (block - width) / 2;
|
|
1290
|
-
for (const child of kids) {
|
|
1291
|
-
place(child, cursor2);
|
|
1292
|
-
cursor2 += measure(child) + nodeGap;
|
|
1293
|
-
}
|
|
1294
|
-
const own = extent(node.bounds, axes.cross);
|
|
1295
|
-
const at = depth.get(node.id) ?? 0;
|
|
1296
|
-
slots.set(node.id, {
|
|
1297
|
-
cross: from + (block - own) / 2,
|
|
1298
|
-
rank: bandStart.get(at) + (bands.get(at) - extent(node.bounds, axes.rank)) / 2
|
|
1299
|
-
});
|
|
1300
|
-
};
|
|
1301
|
-
let cursor = 0;
|
|
1302
|
-
for (const root of roots) {
|
|
1303
|
-
place(root, cursor);
|
|
1304
|
-
cursor += measure(root) + nodeGap;
|
|
1305
|
-
}
|
|
1306
|
-
return settle(graph, slots, opts);
|
|
1307
|
-
};
|
|
1308
|
-
|
|
1309
|
-
// src/layoutAction.ts
|
|
1310
444
|
var LAYOUTS = Object.freeze({
|
|
1311
445
|
layered,
|
|
1312
446
|
tree,
|
|
@@ -1553,6 +687,6 @@ function registerDiagramShape(opts = {}) {
|
|
|
1553
687
|
return registerNodeShape(diagramShape(opts), { priority: "high" });
|
|
1554
688
|
}
|
|
1555
689
|
|
|
1556
|
-
export {
|
|
690
|
+
export { CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramShape, easedProducer, edgeDerivePath, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, withDiagramRegistry };
|
|
1557
691
|
//# sourceMappingURL=index.js.map
|
|
1558
692
|
//# sourceMappingURL=index.js.map
|