@weasel-js/diagram 1.13.0 → 1.14.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 (35) hide show
  1. package/README.md +122 -17
  2. package/dist/{chunk-Y72FK3Y7.js → chunk-5VNPLOIX.js} +94 -6
  3. package/dist/chunk-5VNPLOIX.js.map +1 -0
  4. package/dist/{chunk-ESFIS7BB.js → chunk-62SMY45E.js} +918 -574
  5. package/dist/chunk-62SMY45E.js.map +1 -0
  6. package/dist/{chunk-FAQXYGMX.js → chunk-TBG6DFB2.js} +3 -3
  7. package/dist/{chunk-FAQXYGMX.js.map → chunk-TBG6DFB2.js.map} +1 -1
  8. package/dist/{chunk-L4H444QI.js → chunk-VB6HUKNG.js} +659 -185
  9. package/dist/chunk-VB6HUKNG.js.map +1 -0
  10. package/dist/{chunk-QU4QZUC6.js → chunk-ZWXM5F4I.js} +141 -39
  11. package/dist/chunk-ZWXM5F4I.js.map +1 -0
  12. package/dist/{dataTypes-DW-MljuN.d.ts → dataTypes-BIkhAOc4.d.ts} +13 -2
  13. package/dist/effect-audio.d.ts +2 -1
  14. package/dist/effect-audio.js +6 -5
  15. package/dist/effect-audio.js.map +1 -1
  16. package/dist/effect.d.ts +46 -16
  17. package/dist/effect.js +257 -102
  18. package/dist/effect.js.map +1 -1
  19. package/dist/icons.table.js +262 -0
  20. package/dist/icons.table.js.map +1 -1
  21. package/dist/icons.table.json +262 -0
  22. package/dist/index.d.ts +491 -167
  23. package/dist/index.js +586 -36
  24. package/dist/index.js.map +1 -1
  25. package/dist/labelTrait-BBjRgxbz.d.ts +44 -0
  26. package/dist/layout.d.ts +4 -3
  27. package/dist/{layout.entry-CQoPNfzA.d.ts → layout.entry-Ca6pVvqo.d.ts} +23 -2
  28. package/dist/layout.js +2 -2
  29. package/dist/{stageTypes-DvnR8cKQ.d.ts → stageTypes-UiO25hcn.d.ts} +7 -0
  30. package/dist/{types-vbW21fHm.d.ts → types-B0n2CNTW.d.ts} +108 -81
  31. package/package.json +11 -9
  32. package/dist/chunk-ESFIS7BB.js.map +0 -1
  33. package/dist/chunk-L4H444QI.js.map +0 -1
  34. package/dist/chunk-QU4QZUC6.js.map +0 -1
  35. package/dist/chunk-Y72FK3Y7.js.map +0 -1
@@ -1,57 +1,100 @@
1
- import { AUTO_POSE_DESCRIPTOR, rotatePoint, rectPath, polygonFromPoints, PATH_M, PATH_L, PATH_C, flattenCubic, PATH_Q, PATH_Z, PathBuilder, pointAlongPath } from '@weasel-js/core/math';
2
- import { transformPath, ellipsePath, PATH_M as PATH_M$1, PATH_CMD_LENGTHS, measurePath, forEachSegment, PATH_L as PATH_L$1, nearestOnLine, PATH_Q as PATH_Q$1, nearestOnQuadratic, PATH_C as PATH_C$1, nearestOnCubic } from '@weasel-js/geom';
1
+ import { boundsOfPath, transformPath, ellipsePath, polylineFromPoints, pathFromD, worldCoords, PATH_M as PATH_M$1, PATH_CMD_LENGTHS, measurePath, forEachSegment, PATH_L as PATH_L$1, nearestOnLine, PATH_Q as PATH_Q$1, nearestOnQuadratic, PATH_C as PATH_C$1, nearestOnCubic } from '@weasel-js/geom';
2
+ import { PathBuilder, rectPath, polygonFromPoints, AUTO_POSE_DESCRIPTOR, rotatePoint, PATH_M, PATH_L, PATH_C, flattenCubic, PATH_Q, PATH_Z, pointAlongPath } from '@weasel-js/core/math';
3
3
  import { cssFamilyName } from '@weasel-js/font/metrics';
4
4
  import { createSimulation } from '@weasel-js/simulation';
5
5
 
6
- // src/outline.ts
6
+ // src/outlines/fromD.ts
7
+ var unitMatrix = (b) => [b.width, 0, 0, b.height, b.x, b.y];
8
+ function outlineFromD(d, opts = {}) {
9
+ const path = pathFromD(d);
10
+ const detail = opts.detail === void 0 ? void 0 : pathFromD(opts.detail);
11
+ const unit = opts.content ?? { x: 0, y: 0, width: 1, height: 1 };
12
+ return {
13
+ path: (b) => transformPath(path, unitMatrix(b)),
14
+ ...detail ? { detail: (b) => transformPath(detail, unitMatrix(b)) } : {},
15
+ content: (b) => ({
16
+ x: b.x + unit.x * b.width,
17
+ y: b.y + unit.y * b.height,
18
+ width: unit.width * b.width,
19
+ height: unit.height * b.height
20
+ }),
21
+ boxForContent: (c) => ({ width: c.width / unit.width, height: c.height / unit.height })
22
+ };
23
+ }
7
24
  var LEAN = 0.2;
8
25
  var POINT = 0.2;
9
26
  var KAPPA = 0.5522847498307936;
10
- function outlinePath(outline, bounds) {
11
- const { x, y, width: w, height: h } = bounds;
12
- if (w <= 0 || h <= 0) return rectPath(x, y, Math.max(w, 0), Math.max(h, 0));
13
- if (typeof outline !== "string") return transformPath(outline, [w, 0, 0, h, x, y]);
14
- switch (outline) {
15
- case "rect":
16
- return rectPath(x, y, w, h);
17
- case "diamond":
18
- return polygonFromPoints([
19
- { x: x + w / 2, y },
20
- { x: x + w, y: y + h / 2 },
21
- { x: x + w / 2, y: y + h },
22
- { x, y: y + h / 2 }
23
- ]);
24
- case "parallelogram": {
25
- const lean = Math.min(w * LEAN, w / 2);
26
- return polygonFromPoints([
27
- { x: x + lean, y },
28
- { x: x + w, y },
29
- { x: x + w - lean, y: y + h },
30
- { x, y: y + h }
31
- ]);
32
- }
33
- case "trapezoid":
34
- return polygonFromPoints([
35
- { x, y: y + h / 4 },
36
- { x: x + w, y },
37
- { x: x + w, y: y + h },
38
- { x, y: y + h * 3 / 4 }
39
- ]);
40
- case "hexagon":
41
- return polygonFromPoints([
42
- { x, y: y + h / 2 },
43
- { x: x + w * POINT, y },
44
- { x: x + w * (1 - POINT), y },
45
- { x: x + w, y: y + h / 2 },
46
- { x: x + w * (1 - POINT), y: y + h },
47
- { x: x + w * POINT, y: y + h }
48
- ]);
49
- case "stadium":
50
- return stadiumPath(x, y, w, h);
51
- case "ellipse":
52
- return ellipsePath(bounds);
53
- }
54
- }
27
+ var rect = {
28
+ path: ({ x, y, width, height }) => rectPath(x, y, width, height)
29
+ };
30
+ var diamond = {
31
+ path: ({ x, y, width: w, height: h }) => polygonFromPoints([
32
+ { x: x + w / 2, y },
33
+ { x: x + w, y: y + h / 2 },
34
+ { x: x + w / 2, y: y + h },
35
+ { x, y: y + h / 2 }
36
+ ]),
37
+ // Half the width and half the height, centered: the inscribed rect.
38
+ content: ({ x, y, width: w, height: h }) => ({ x: x + w / 4, y: y + h / 4, width: w / 2, height: h / 2 }),
39
+ boxForContent: (c) => ({ width: c.width * 2, height: c.height * 2 })
40
+ };
41
+ var leanOf = (w) => Math.min(w * LEAN, w / 2);
42
+ var parallelogram = {
43
+ path: ({ x, y, width: w, height: h }) => {
44
+ const lean = leanOf(w);
45
+ return polygonFromPoints([
46
+ { x: x + lean, y },
47
+ { x: x + w, y },
48
+ { x: x + w - lean, y: y + h },
49
+ { x, y: y + h }
50
+ ]);
51
+ },
52
+ content: ({ x, y, width: w, height: h }) => {
53
+ const lean = leanOf(w);
54
+ return { x: x + lean, y, width: Math.max(w - lean * 2, 0), height: h };
55
+ },
56
+ boxForContent: (c) => ({ width: c.width / (1 - LEAN * 2), height: c.height })
57
+ };
58
+ var trapezoid = {
59
+ path: ({ x, y, width: w, height: h }) => polygonFromPoints([
60
+ { x, y: y + h / 4 },
61
+ { x: x + w, y },
62
+ { x: x + w, y: y + h },
63
+ { x, y: y + h * 3 / 4 }
64
+ ]),
65
+ content: ({ x, y, width: w, height: h }) => ({ x, y: y + h / 4, width: w, height: h / 2 }),
66
+ boxForContent: (c) => ({ width: c.width, height: c.height * 2 })
67
+ };
68
+ var hexagon = {
69
+ path: ({ x, y, width: w, height: h }) => polygonFromPoints([
70
+ { x, y: y + h / 2 },
71
+ { x: x + w * POINT, y },
72
+ { x: x + w * (1 - POINT), y },
73
+ { x: x + w, y: y + h / 2 },
74
+ { x: x + w * (1 - POINT), y: y + h },
75
+ { x: x + w * POINT, y: y + h }
76
+ ]),
77
+ content: ({ x, y, width: w, height: h }) => ({ x: x + w * POINT, y, width: w * (1 - POINT * 2), height: h }),
78
+ boxForContent: (c) => ({ width: c.width / (1 - POINT * 2), height: c.height })
79
+ };
80
+ var ellipse = {
81
+ path: ellipsePath,
82
+ // The inscribed rect of an ellipse is its box over root two, centered.
83
+ content: ({ x, y, width: w, height: h }) => {
84
+ const cw = w / Math.SQRT2;
85
+ const ch = h / Math.SQRT2;
86
+ return { x: x + (w - cw) / 2, y: y + (h - ch) / 2, width: cw, height: ch };
87
+ },
88
+ boxForContent: (c) => ({ width: c.width * Math.SQRT2, height: c.height * Math.SQRT2 })
89
+ };
90
+ var stadium = {
91
+ path: ({ x, y, width: w, height: h }) => stadiumPath(x, y, w, h),
92
+ content: ({ x, y, width: w, height: h }) => {
93
+ const r = Math.min(w, h) / 2;
94
+ 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) };
95
+ },
96
+ boxForContent: (c) => ({ width: c.width + c.height, height: c.height })
97
+ };
55
98
  function stadiumPath(x, y, w, h) {
56
99
  const r = Math.min(w, h) / 2;
57
100
  const k = r * KAPPA;
@@ -71,51 +114,462 @@ function stadiumPath(x, y, w, h) {
71
114
  const midX = x + w / 2;
72
115
  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();
73
116
  }
117
+ var FLOWCHART_OUTLINES = { rect, diamond, stadium, ellipse, parallelogram, trapezoid, hexagon };
118
+ var capOf = (w, h) => Math.min(h * 0.15, w * 0.25);
119
+ var cylinder = {
120
+ path: ({ x, y, width: w, height: h }) => {
121
+ const ry = capOf(w, h);
122
+ const rx = w / 2;
123
+ const cx = x + rx;
124
+ const top = y + ry;
125
+ const bottom = y + h - ry;
126
+ return new PathBuilder().moveTo(x, top).curveTo(x, top - ry * KAPPA, cx - rx * KAPPA, y, cx, y).curveTo(cx + rx * KAPPA, y, x + w, top - ry * KAPPA, x + w, top).lineTo(x + w, bottom).curveTo(x + w, bottom + ry * KAPPA, cx + rx * KAPPA, y + h, cx, y + h).curveTo(cx - rx * KAPPA, y + h, x, bottom + ry * KAPPA, x, bottom).close().build();
127
+ },
128
+ detail: ({ x, y, width: w, height: h }) => {
129
+ const ry = capOf(w, h);
130
+ const rx = w / 2;
131
+ const cx = x + rx;
132
+ const top = y + ry;
133
+ return new PathBuilder().moveTo(x, top).curveTo(x, top + ry * KAPPA, cx - rx * KAPPA, top + ry, cx, top + ry).curveTo(cx + rx * KAPPA, top + ry, x + w, top + ry * KAPPA, x + w, top).build();
134
+ },
135
+ content: ({ x, y, width: w, height: h }) => {
136
+ const ry = capOf(w, h);
137
+ return { x, y: y + ry * 2, width: w, height: Math.max(h - ry * 3, 0) };
138
+ },
139
+ boxForContent: (c) => ({ width: c.width, height: c.height / 0.55 })
140
+ };
141
+ var WAVE = 0.1;
142
+ var WAVE_CONTROL = 6 / Math.sqrt(3);
143
+ var document = {
144
+ path: ({ x, y, width: w, height: h }) => {
145
+ const a = h * WAVE;
146
+ const mid = y + h - a;
147
+ const k = a * WAVE_CONTROL;
148
+ return new PathBuilder().moveTo(x, y).lineTo(x + w, y).lineTo(x + w, mid).curveTo(x + w * 2 / 3, mid - k, x + w / 3, mid + k, x, mid).close().build();
149
+ },
150
+ content: ({ x, y, width: w, height: h }) => ({ x, y, width: w, height: h * (1 - WAVE * 2) }),
151
+ boxForContent: (c) => ({ width: c.width, height: c.height / (1 - WAVE * 2) })
152
+ };
153
+ var MAX_FOLD = 20;
154
+ var foldOf = (w, h) => Math.min(MAX_FOLD, Math.min(w, h) / 4);
155
+ var note = {
156
+ path: ({ x, y, width: w, height: h }) => {
157
+ const f = foldOf(w, h);
158
+ return polygonFromPoints([
159
+ { x, y },
160
+ { x: x + w - f, y },
161
+ { x: x + w, y: y + f },
162
+ { x: x + w, y: y + h },
163
+ { x, y: y + h }
164
+ ]);
165
+ },
166
+ detail: ({ x, y, width: w, height: h }) => {
167
+ const f = foldOf(w, h);
168
+ return polylineFromPoints([{ x: x + w - f, y }, { x: x + w - f, y: y + f }, { x: x + w, y: y + f }]);
169
+ },
170
+ content: ({ x, y, width: w, height: h }) => {
171
+ const f = foldOf(w, h);
172
+ return { x, y: y + f, width: w, height: h - f };
173
+ },
174
+ boxForContent: (c) => ({ width: c.width, height: c.height + MAX_FOLD })
175
+ };
176
+ var MAX_TAB = 16;
177
+ var tabOf = (w, h) => {
178
+ const t = Math.min(MAX_TAB, h * 0.2);
179
+ return { t, tw: Math.max(Math.min(w * 0.4, w - t * 2), 0) };
180
+ };
181
+ var folder = {
182
+ path: ({ x, y, width: w, height: h }) => {
183
+ const { t, tw } = tabOf(w, h);
184
+ return polygonFromPoints([
185
+ { x, y },
186
+ { x: x + tw, y },
187
+ { x: x + tw + t, y: y + t },
188
+ { x: x + w, y: y + t },
189
+ { x: x + w, y: y + h },
190
+ { x, y: y + h }
191
+ ]);
192
+ },
193
+ detail: ({ x, y, width: w, height: h }) => {
194
+ const { t, tw } = tabOf(w, h);
195
+ return polylineFromPoints([{ x, y: y + t }, { x: x + tw + t, y: y + t }]);
196
+ },
197
+ content: ({ x, y, width: w, height: h }) => {
198
+ const { t } = tabOf(w, h);
199
+ return { x, y: y + t, width: w, height: h - t };
200
+ },
201
+ boxForContent: (c) => ({ width: c.width, height: c.height + MAX_TAB })
202
+ };
203
+ var DEPTH = 0.15;
204
+ function insetByShorter(c, k) {
205
+ const d = k * Math.min(c.width, c.height) / (1 - k);
206
+ return { width: c.width + d, height: c.height + d };
207
+ }
208
+ var cube = {
209
+ path: ({ x, y, width: w, height: h }) => {
210
+ const d = Math.min(w, h) * DEPTH;
211
+ return polygonFromPoints([
212
+ { x, y: y + d },
213
+ { x: x + d, y },
214
+ { x: x + w, y },
215
+ { x: x + w, y: y + h - d },
216
+ { x: x + w - d, y: y + h },
217
+ { x, y: y + h }
218
+ ]);
219
+ },
220
+ detail: ({ x, y, width: w, height: h }) => {
221
+ const d = Math.min(w, h) * DEPTH;
222
+ return new PathBuilder().moveTo(x, y + d).lineTo(x + w - d, y + d).lineTo(x + w, y).moveTo(x + w - d, y + d).lineTo(x + w - d, y + h).build();
223
+ },
224
+ content: ({ x, y, width: w, height: h }) => {
225
+ const d = Math.min(w, h) * DEPTH;
226
+ return { x, y: y + d, width: w - d, height: h - d };
227
+ },
228
+ boxForContent: (c) => insetByShorter(c, DEPTH)
229
+ };
230
+ var STORAGE_OUTLINES = { cylinder, document, note, folder, cube };
231
+
232
+ // src/outlines/figures.ts
233
+ var MAX_RADIUS = 10;
234
+ var radiusOf = (w, h) => Math.min(MAX_RADIUS, w / 6, h / 6);
235
+ var BITE = 1 - Math.SQRT1_2;
236
+ var roundRect = {
237
+ path: ({ x, y, width: w, height: h }) => {
238
+ const r = radiusOf(w, h);
239
+ const k = r * KAPPA;
240
+ return new PathBuilder().moveTo(x + r, y).lineTo(x + w - r, y).curveTo(x + w - r + k, y, x + w, y + r - k, x + w, y + r).lineTo(x + w, y + h - r).curveTo(x + w, y + h - r + k, x + w - r + k, y + h, x + w - r, y + h).lineTo(x + r, y + h).curveTo(x + r - k, y + h, x, y + h - r + k, x, y + h - r).lineTo(x, y + r).curveTo(x, y + r - k, x + r - k, y, x + r, y).close().build();
241
+ },
242
+ content: ({ x, y, width: w, height: h }) => {
243
+ const i = radiusOf(w, h) * BITE;
244
+ return { x: x + i, y: y + i, width: w - i * 2, height: h - i * 2 };
245
+ },
246
+ boxForContent: (c) => ({ width: c.width + MAX_RADIUS * BITE * 2, height: c.height + MAX_RADIUS * BITE * 2 })
247
+ };
248
+ var triangle = {
249
+ path: ({ x, y, width: w, height: h }) => polygonFromPoints([
250
+ { x: x + w / 2, y },
251
+ { x: x + w, y: y + h },
252
+ { x, y: y + h }
253
+ ]),
254
+ // The largest rect in a triangle: half its width and half its height, on the base.
255
+ content: ({ x, y, width: w, height: h }) => ({ x: x + w / 4, y: y + h / 2, width: w / 2, height: h / 2 }),
256
+ boxForContent: (c) => ({ width: c.width * 2, height: c.height * 2 })
257
+ };
258
+ var CUT = 1 - Math.SQRT1_2;
259
+ var octagon = {
260
+ path: ({ x, y, width: w, height: h }) => {
261
+ const c = Math.min(w, h) * CUT;
262
+ return polygonFromPoints([
263
+ { x: x + c, y },
264
+ { x: x + w - c, y },
265
+ { x: x + w, y: y + c },
266
+ { x: x + w, y: y + h - c },
267
+ { x: x + w - c, y: y + h },
268
+ { x: x + c, y: y + h },
269
+ { x, y: y + h - c },
270
+ { x, y: y + c }
271
+ ]);
272
+ },
273
+ // Its corners sit on the cuts' midpoints.
274
+ content: ({ x, y, width: w, height: h }) => {
275
+ const c = Math.min(w, h) * CUT;
276
+ return { x: x + c / 2, y: y + c / 2, width: w - c, height: h - c };
277
+ },
278
+ boxForContent: (c) => insetByShorter(c, CUT)
279
+ };
280
+ function arc(b, cx, cy, r, from, sweep) {
281
+ const n = Math.ceil(Math.abs(sweep) / (Math.PI / 2));
282
+ const step = sweep / n;
283
+ const k = 4 / 3 * Math.tan(step / 4) * r;
284
+ for (let i = 0; i < n; i++) {
285
+ const a0 = from + step * i;
286
+ const a1 = a0 + step;
287
+ const x0 = cx + r * Math.cos(a0), y0 = cy + r * Math.sin(a0);
288
+ const x1 = cx + r * Math.cos(a1), y1 = cy + r * Math.sin(a1);
289
+ b.curveTo(x0 - k * Math.sin(a0), y0 + k * Math.cos(a0), x1 + k * Math.sin(a1), y1 - k * Math.cos(a1), x1, y1);
290
+ }
291
+ }
292
+ var CLOUD_JOINS = [178, 222, 268, 318, 2, 46, 92, 136];
293
+ var CLOUD_RX = 0.36;
294
+ var CLOUD_RY = 0.3;
295
+ var CLOUD = (() => {
296
+ const pts = CLOUD_JOINS.map((deg) => {
297
+ const t = deg * Math.PI / 180;
298
+ return { x: 0.5 + CLOUD_RX * Math.cos(t), y: 0.5 + CLOUD_RY * Math.sin(t) };
299
+ });
300
+ const b = new PathBuilder().moveTo(pts[0].x, pts[0].y);
301
+ pts.forEach((p, i) => {
302
+ const q = pts[(i + 1) % pts.length];
303
+ const mx = (p.x + q.x) / 2, my = (p.y + q.y) / 2;
304
+ const r = Math.hypot(q.x - p.x, q.y - p.y) / 2;
305
+ const from = Math.atan2(p.y - my, p.x - mx);
306
+ const out = (s) => Math.hypot(mx + r * Math.cos(from + s) - 0.5, my + r * Math.sin(from + s) - 0.5);
307
+ arc(b, mx, my, r, from, out(Math.PI / 2) > out(-Math.PI / 2) ? Math.PI : -Math.PI);
308
+ });
309
+ const raw = b.close().build();
310
+ const box = boundsOfPath(raw);
311
+ const fit = (p) => ({ x: (p.x - box.x) / box.width, y: (p.y - box.y) / box.height });
312
+ const lo = fit({ x: 0.5 - CLOUD_RX * 0.85 / Math.SQRT2, y: 0.5 - CLOUD_RY * 0.85 / Math.SQRT2 });
313
+ const hi = fit({ x: 0.5 + CLOUD_RX * 0.85 / Math.SQRT2, y: 0.5 + CLOUD_RY * 0.85 / Math.SQRT2 });
314
+ return {
315
+ path: transformPath(raw, [1 / box.width, 0, 0, 1 / box.height, -box.x / box.width, -box.y / box.height]),
316
+ content: { x: lo.x, y: lo.y, width: hi.x - lo.x, height: hi.y - lo.y }
317
+ };
318
+ })();
319
+ var cloud = {
320
+ path: ({ x, y, width: w, height: h }) => transformPath(CLOUD.path, [w, 0, 0, h, x, y]),
321
+ content: ({ x, y, width: w, height: h }) => ({
322
+ x: x + CLOUD.content.x * w,
323
+ y: y + CLOUD.content.y * h,
324
+ width: CLOUD.content.width * w,
325
+ height: CLOUD.content.height * h
326
+ }),
327
+ boxForContent: (c) => ({ width: c.width / CLOUD.content.width, height: c.height / CLOUD.content.height })
328
+ };
329
+ function figureOver(def, aspect, share) {
330
+ const figureBox = ({ x, y, width: w, height: h }) => {
331
+ const s = Math.min(h * share, w / aspect);
332
+ return { x: x + (w - s * aspect) / 2, y: y + h * share - s, width: s * aspect, height: s };
333
+ };
334
+ const detail = def.detail;
335
+ return {
336
+ path: (b) => def.path(figureBox(b)),
337
+ ...detail ? { detail: (b) => detail(figureBox(b)) } : {},
338
+ content: ({ x, y, width: w, height: h }) => ({ x, y: y + h * share, width: w, height: h * (1 - share) }),
339
+ boxForContent: (c) => ({ width: c.width, height: c.height / (1 - share) })
340
+ };
341
+ }
342
+ var ACTOR_FIGURE = {
343
+ path: ({ x, y, height: s }) => {
344
+ const at2 = (u, v) => [x + u * s, y + v * s];
345
+ const r = 0.13 * s;
346
+ const b = new PathBuilder().moveTo(x + 0.3 * s + r, y + r);
347
+ arc(b, x + 0.3 * s, y + r, r, 0, Math.PI * 2);
348
+ b.close();
349
+ for (const [from, to] of [
350
+ [at2(0.3, 0.26), at2(0.3, 0.62)],
351
+ [at2(0.02, 0.38), at2(0.58, 0.38)],
352
+ [at2(0.3, 0.62), at2(0.04, 1)],
353
+ [at2(0.3, 0.62), at2(0.56, 1)]
354
+ ]) b.moveTo(...from).lineTo(...to);
355
+ return b.build();
356
+ }
357
+ };
358
+ var actor = figureOver(ACTOR_FIGURE, 0.6, 0.75);
359
+ var RING = 3;
360
+ var ringOf = (w, h) => Math.min(RING, w / 4, h / 4);
361
+ var doubleEllipse = {
362
+ path: ellipsePath,
363
+ detail: ({ x, y, width: w, height: h }) => {
364
+ const g = ringOf(w, h);
365
+ return ellipsePath({ x: x + g, y: y + g, width: w - g * 2, height: h - g * 2 });
366
+ },
367
+ content: ({ x, y, width: w, height: h }) => {
368
+ const g = ringOf(w, h);
369
+ const cw = (w - g * 2) / Math.SQRT2;
370
+ const ch = (h - g * 2) / Math.SQRT2;
371
+ return { x: x + (w - cw) / 2, y: y + (h - ch) / 2, width: cw, height: ch };
372
+ },
373
+ boxForContent: (c) => ({ width: c.width * Math.SQRT2 + RING * 2, height: c.height * Math.SQRT2 + RING * 2 })
374
+ };
375
+ var FIGURE_OUTLINES = { roundRect, triangle, octagon, cloud, actor, doubleEllipse };
376
+
377
+ // src/outlines/bpmn.ts
378
+ var STRIP = 32;
379
+ var EVENT = 40;
380
+ var GATEWAY = 50;
381
+ var EVENT_SIZE = { width: 90, height: EVENT + STRIP };
382
+ var GATEWAY_SIZE = { width: 100, height: GATEWAY + STRIP };
383
+ var event = figureOver(FLOWCHART_OUTLINES.ellipse, 1, EVENT / (EVENT + STRIP));
384
+ var intermediateEvent = figureOver(FIGURE_OUTLINES.doubleEllipse, 1, EVENT / (EVENT + STRIP));
385
+ var DIAMOND = "M0.5 0L1 0.5L0.5 1L0 0.5Z";
386
+ var gateway = (marker) => figureOver(outlineFromD(DIAMOND, { detail: marker }), 1, GATEWAY / (GATEWAY + STRIP));
387
+ var ring = (r) => `M${0.5 + r} 0.5A${r} ${r} 0 1 1 ${0.5 - r} 0.5A${r} ${r} 0 1 1 ${0.5 + r} 0.5`;
388
+ var PENTAGON = (() => {
389
+ const r = 0.13;
390
+ const pts = [0, 1, 2, 3, 4].map((k) => {
391
+ const t = -Math.PI / 2 + k * 2 * Math.PI / 5;
392
+ return `${(0.5 + r * Math.cos(t)).toFixed(4)} ${(0.5 + r * Math.sin(t)).toFixed(4)}`;
393
+ });
394
+ return `M${pts.join("L")}Z`;
395
+ })();
396
+ var exclusiveGateway = gateway("M0.37 0.37L0.63 0.63M0.63 0.37L0.37 0.63");
397
+ var parallelGateway = gateway("M0.5 0.3V0.7M0.3 0.5H0.7");
398
+ var inclusiveGateway = gateway(ring(0.2));
399
+ var eventGateway = gateway(ring(0.22) + PENTAGON);
400
+ var MARKER = 14;
401
+ var MARKER_GAP = 4;
402
+ var markerOf = (w, h) => Math.min(MARKER, w / 4, h / 3);
403
+ var roundRect2 = FIGURE_OUTLINES.roundRect;
404
+ var subprocess = {
405
+ path: roundRect2.path,
406
+ detail: ({ x, y, width: w, height: h }) => {
407
+ const m = markerOf(w, h);
408
+ const left = x + (w - m) / 2;
409
+ const top = y + h - MARKER_GAP - m;
410
+ const arm = m * 0.25;
411
+ return new PathBuilder().moveTo(left, top).lineTo(left + m, top).lineTo(left + m, top + m).lineTo(left, top + m).close().moveTo(left + m / 2, top + arm).lineTo(left + m / 2, top + m - arm).moveTo(left + arm, top + m / 2).lineTo(left + m - arm, top + m / 2).build();
412
+ },
413
+ content: (b) => {
414
+ const c = roundRect2.content(b);
415
+ const foot = b.y + b.height - MARKER_GAP - markerOf(b.width, b.height);
416
+ return { ...c, height: Math.max(Math.min(c.y + c.height, foot) - c.y, 0) };
417
+ },
418
+ boxForContent: (c) => {
419
+ const box = roundRect2.boxForContent(c);
420
+ return { width: box.width, height: box.height + MARKER + MARKER_GAP };
421
+ }
422
+ };
423
+ var BPMN_OUTLINES = {
424
+ event,
425
+ intermediateEvent,
426
+ exclusiveGateway,
427
+ parallelGateway,
428
+ inclusiveGateway,
429
+ eventGateway,
430
+ subprocess
431
+ };
432
+
433
+ // src/outlines/network.ts
434
+ var GLYPH = 56;
435
+ var STRIP2 = 32;
436
+ var DEVICE_SIZE = { width: 96, height: GLYPH + STRIP2 };
437
+ var SHARE = GLYPH / (GLYPH + STRIP2);
438
+ var device = (d, detail) => figureOver(outlineFromD(d, { detail }), 1, SHARE);
439
+ var user = figureOver(ACTOR_FIGURE, 0.6, SHARE);
440
+ var ring2 = (cx, cy, r) => `M${cx + r} ${cy}A${r} ${r} 0 1 1 ${cx - r} ${cy}A${r} ${r} 0 1 1 ${cx + r} ${cy}`;
441
+ var server = device(
442
+ "M0.33 0H0.67Q0.7 0 0.7 0.03V0.97Q0.7 1 0.67 1H0.33Q0.3 1 0.3 0.97V0.03Q0.3 0 0.33 0Z",
443
+ `M0.37 0.16H0.63M0.37 0.28H0.63M0.37 0.4H0.63${ring2(0.5, 0.8, 0.045)}`
444
+ );
445
+ var router = device(
446
+ "M0.08 0.46A0.42 0.14 0 0 1 0.92 0.46V0.86A0.42 0.14 0 0 1 0.08 0.86Z",
447
+ "M0.08 0.46A0.42 0.14 0 0 0 0.92 0.46M0.38 0.68L0.62 0.92M0.53 0.92H0.62V0.83M0.38 0.92L0.62 0.68M0.53 0.68H0.62V0.77"
448
+ );
449
+ var switchDevice = device(
450
+ "M0.09 0.56H0.91Q0.94 0.56 0.94 0.59V0.97Q0.94 1 0.91 1H0.09Q0.06 1 0.06 0.97V0.59Q0.06 0.56 0.09 0.56Z",
451
+ "M0.24 0.69H0.76M0.715 0.645L0.76 0.69L0.715 0.735M0.76 0.87H0.24M0.285 0.825L0.24 0.87L0.285 0.915"
452
+ );
453
+ var firewall = device(
454
+ "M0.1 0.3H0.9V1H0.1Z",
455
+ "M0.1 0.475H0.9M0.1 0.65H0.9M0.1 0.825H0.9M0.37 0.3V0.475M0.63 0.3V0.475M0.24 0.475V0.65M0.5 0.475V0.65M0.76 0.475V0.65M0.37 0.65V0.825M0.63 0.65V0.825M0.24 0.825V1M0.5 0.825V1M0.76 0.825V1"
456
+ );
457
+ var laptop = device(
458
+ "M0.2 0.84V0.43Q0.2 0.4 0.23 0.4H0.77Q0.8 0.4 0.8 0.43V0.84L0.94 0.96Q0.96 1 0.92 1H0.08Q0.04 1 0.06 0.96Z",
459
+ "M0.26 0.46H0.74V0.78H0.26ZM0.2 0.84H0.8M0.43 0.92H0.57"
460
+ );
461
+ var desktop = device(
462
+ "M0.13 0.3H0.87Q0.9 0.3 0.9 0.33V0.79Q0.9 0.82 0.87 0.82H0.56V0.93H0.7V1H0.3V0.93H0.44V0.82H0.13Q0.1 0.82 0.1 0.79V0.33Q0.1 0.3 0.13 0.3Z",
463
+ "M0.16 0.36H0.84V0.76H0.16Z"
464
+ );
465
+ var phone = device(
466
+ "M0.37 0.4H0.63Q0.68 0.4 0.68 0.45V0.95Q0.68 1 0.63 1H0.37Q0.32 1 0.32 0.95V0.45Q0.32 0.4 0.37 0.4Z",
467
+ "M0.38 0.47H0.62V0.86H0.38ZM0.46 0.93H0.54"
468
+ );
469
+ var printer = device(
470
+ "M0.28 0.55V0.36H0.72V0.55H0.86Q0.9 0.55 0.9 0.59V0.84Q0.9 0.88 0.86 0.88H0.74V1H0.26V0.88H0.14Q0.1 0.88 0.1 0.84V0.59Q0.1 0.55 0.14 0.55Z",
471
+ `M0.26 0.88H0.74M0.34 0.94H0.66M0.2 0.78H0.8${ring2(0.78, 0.66, 0.025)}`
472
+ );
473
+ var globe = device(
474
+ ring2(0.5, 0.6, 0.4),
475
+ "M0.1 0.6H0.9M0.1536 0.4H0.8464M0.1536 0.8H0.8464M0.5 0.2V1M0.5 0.2A0.17 0.4 0 0 0 0.5 1A0.17 0.4 0 0 0 0.5 0.2"
476
+ );
477
+ var NETWORK_OUTLINES = {
478
+ server,
479
+ router,
480
+ switch: switchDevice,
481
+ firewall,
482
+ laptop,
483
+ desktop,
484
+ phone,
485
+ printer,
486
+ user,
487
+ globe
488
+ };
489
+ var ICON = { width: 14, height: 12, tab: { width: 6, height: 3 }, margin: 6 };
490
+ var ICON_SIDE = ICON.margin * 2 + ICON.width + ICON.tab.width / 2;
491
+ var component = {
492
+ path: ({ x, y, width: w, height: h }) => rectPath(x, y, w, h),
493
+ // The UML component glyph in the top-right corner: a body with two tabs across its left edge,
494
+ // the edge broken where each tab crosses it.
495
+ detail: ({ x, y, width: w }) => {
496
+ const left = x + w - ICON.margin - ICON.width;
497
+ const top = y + ICON.margin;
498
+ const { width: tw, height: th } = ICON.tab;
499
+ const tabs = [top + 2, top + ICON.height - 2 - th];
500
+ const b = new PathBuilder().moveTo(left, tabs[0]).lineTo(left, top).lineTo(left + ICON.width, top).lineTo(left + ICON.width, top + ICON.height).lineTo(left, top + ICON.height).lineTo(left, tabs[1] + th).moveTo(left, tabs[0] + th).lineTo(left, tabs[1]);
501
+ for (const t of tabs) b.moveTo(left - tw / 2, t).lineTo(left + tw / 2, t).lineTo(left + tw / 2, t + th).lineTo(left - tw / 2, t + th).close();
502
+ return b.build();
503
+ },
504
+ content: ({ x, y, width: w, height: h }) => {
505
+ const side = Math.min(ICON_SIDE, w / 4);
506
+ return { x: x + side, y, width: w - side * 2, height: h };
507
+ },
508
+ boxForContent: (c) => ({ width: c.width + ICON_SIDE * 2, height: c.height })
509
+ };
510
+ var GAP = 4;
511
+ var gapOf = (w, h) => Math.min(GAP, w / 4, h / 4);
512
+ var inset = ({ x, y, width: w, height: h }, g) => ({ x: x + g, y: y + g, width: w - g * 2, height: h - g * 2 });
513
+ var doubleRect = {
514
+ path: ({ x, y, width: w, height: h }) => rectPath(x, y, w, h),
515
+ detail: (b) => {
516
+ const i = inset(b, gapOf(b.width, b.height));
517
+ return rectPath(i.x, i.y, i.width, i.height);
518
+ },
519
+ content: (b) => inset(b, gapOf(b.width, b.height)),
520
+ boxForContent: (c) => ({ width: c.width + GAP * 2, height: c.height + GAP * 2 })
521
+ };
522
+ var MODELING_OUTLINES = { component, doubleRect };
523
+
524
+ // src/outlines/table.ts
525
+ var BUILTINS = {
526
+ ...FLOWCHART_OUTLINES,
527
+ ...FIGURE_OUTLINES,
528
+ ...STORAGE_OUTLINES,
529
+ ...NETWORK_OUTLINES,
530
+ ...BPMN_OUTLINES,
531
+ ...MODELING_OUTLINES
532
+ };
533
+ var table = new Map(
534
+ Object.entries(BUILTINS).map(([name, def]) => [name, [def]])
535
+ );
536
+ var warned = /* @__PURE__ */ new Set();
537
+ function registerOutline(name, def) {
538
+ const stack = table.get(name) ?? [];
539
+ stack.push(def);
540
+ table.set(name, stack);
541
+ return () => {
542
+ const i = stack.lastIndexOf(def);
543
+ if (i >= 0) stack.splice(i, 1);
544
+ if (stack.length === 0) table.delete(name);
545
+ };
546
+ }
547
+ function resolveOutline(name) {
548
+ const def = table.get(name)?.at(-1);
549
+ if (def) return def;
550
+ if (!warned.has(name)) {
551
+ warned.add(name);
552
+ console.warn(`@weasel-js/diagram: no outline named '${name}' is registered; painting it as 'rect'`);
553
+ }
554
+ return FLOWCHART_OUTLINES.rect;
555
+ }
556
+ function outlinePath(outline, bounds) {
557
+ const { x, y, width: w, height: h } = bounds;
558
+ if (w <= 0 || h <= 0) return rectPath(x, y, Math.max(w, 0), Math.max(h, 0));
559
+ if (typeof outline !== "string") return transformPath(outline, [w, 0, 0, h, x, y]);
560
+ return resolveOutline(outline).path(bounds);
561
+ }
562
+ function outlineDetail(outline, bounds) {
563
+ if (typeof outline !== "string" || bounds.width <= 0 || bounds.height <= 0) return null;
564
+ return resolveOutline(outline).detail?.(bounds) ?? null;
565
+ }
74
566
  function contentBox(outline, bounds) {
75
567
  if (typeof outline !== "string") return bounds;
76
- const { x, y, width: w, height: h } = bounds;
77
- switch (outline) {
78
- case "rect":
79
- return bounds;
80
- case "diamond":
81
- return { x: x + w / 4, y: y + h / 4, width: w / 2, height: h / 2 };
82
- case "parallelogram": {
83
- const lean = Math.min(w * LEAN, w / 2);
84
- return { x: x + lean, y, width: Math.max(w - lean * 2, 0), height: h };
85
- }
86
- case "trapezoid":
87
- return { x, y: y + h / 4, width: w, height: h / 2 };
88
- case "hexagon":
89
- return { x: x + w * POINT, y, width: w * (1 - POINT * 2), height: h };
90
- case "ellipse": {
91
- const cw = w / Math.SQRT2;
92
- const ch = h / Math.SQRT2;
93
- return { x: x + (w - cw) / 2, y: y + (h - ch) / 2, width: cw, height: ch };
94
- }
95
- case "stadium": {
96
- const r = Math.min(w, h) / 2;
97
- 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) };
98
- }
99
- }
568
+ return resolveOutline(outline).content?.(bounds) ?? bounds;
100
569
  }
101
570
  function boxForContent(outline, content) {
102
571
  if (typeof outline !== "string") return content;
103
- switch (outline) {
104
- case "rect":
105
- return content;
106
- case "diamond":
107
- return { width: content.width * 2, height: content.height * 2 };
108
- case "parallelogram":
109
- return { width: content.width / (1 - LEAN * 2), height: content.height };
110
- case "trapezoid":
111
- return { width: content.width, height: content.height * 2 };
112
- case "hexagon":
113
- return { width: content.width / (1 - POINT * 2), height: content.height };
114
- case "stadium":
115
- return { width: content.width + content.height, height: content.height };
116
- case "ellipse":
117
- return { width: content.width * Math.SQRT2, height: content.height * Math.SQRT2 };
118
- }
572
+ return resolveOutline(outline).boxForContent?.(content) ?? content;
119
573
  }
120
574
  var TOLERANCE = 0.25;
121
575
  function outlinePolyline(outline, bounds) {
@@ -128,7 +582,8 @@ function flattenPath(path) {
128
582
  return [x, y, x + w, y, x + w, y + h, x, y + h, x, y];
129
583
  }
130
584
  const out = [];
131
- const { commands, coords } = path;
585
+ const { commands } = path;
586
+ const coords = worldCoords(path);
132
587
  let ci = 0;
133
588
  let cx = 0;
134
589
  let cy = 0;
@@ -367,10 +822,11 @@ function withLabels(ports, labels) {
367
822
  var DEFAULT_PADDING = 8;
368
823
  var DEFAULT_GAP = 4;
369
824
  var FALLBACK_LINE = 16;
825
+ var DIVIDER_HEIGHT = 4;
370
826
  function canvasMeasure(ctx) {
371
827
  return (text, style) => {
372
828
  const size = style.fontSize ?? FALLBACK_LINE;
373
- ctx.font = `${style.bold === true ? "bold " : ""}${size}px ${cssFamilyName(style.fontFamily ?? "sans-serif")}`;
829
+ ctx.font = `${style.italic === true ? "italic " : ""}${style.bold === true ? "bold " : ""}${size}px ${cssFamilyName(style.fontFamily ?? "sans-serif")}`;
374
830
  return { width: ctx.measureText(text).width, height: Math.ceil(size * 1.2) };
375
831
  };
376
832
  }
@@ -398,8 +854,13 @@ function measureRow(row, measure) {
398
854
  }
399
855
  case "slot":
400
856
  return { width: row.width ?? 0, height: row.height };
857
+ case "divider":
858
+ return { width: 0, height: DIVIDER_HEIGHT };
401
859
  }
402
860
  }
861
+ function rowHeights(spec, measure) {
862
+ return spec.rows.map((row) => measureRow(row, measure).height);
863
+ }
403
864
  function measureBody(spec, measure) {
404
865
  const padding = spec.padding ?? DEFAULT_PADDING;
405
866
  const gap = spec.gap ?? DEFAULT_GAP;
@@ -427,19 +888,22 @@ function layoutBody(spec, bounds, measure) {
427
888
  const gap = spec.gap ?? DEFAULT_GAP;
428
889
  const inner = contentBox(spec.outline, bounds);
429
890
  const width = Math.max(inner.width - padding * 2, 0);
430
- const heights = spec.rows.map((row) => measureRow(row, measure).height);
891
+ const heights = rowHeights(spec, measure);
431
892
  const used = heights.reduce((sum, h) => sum + h, 0) + gap * Math.max(heights.length - 1, 0);
432
893
  const slack = Math.max(inner.height - padding * 2 - used, 0);
433
894
  const share = { top: 0, center: 0.5, bottom: 1 }[spec.verticalAlign ?? "center"];
434
895
  let y = inner.y + padding + slack * share;
435
896
  return spec.rows.map((row, index) => {
436
897
  const h = heights[index];
437
- const w = row.kind === "slot" && row.width !== void 0 ? Math.min(row.width, width) : width;
438
- const box = { row, index, x: inner.x + padding + (width - w) / 2, y, width: w, height: h };
898
+ const box = row.kind === "divider" ? { row, index, x: inner.x + (row.inset ?? 0), y, width: Math.max(inner.width - 2 * (row.inset ?? 0), 0), height: h } : rowBox(row, index, inner.x + padding, width, y, h);
439
899
  y += h + gap;
440
900
  return box;
441
901
  });
442
902
  }
903
+ function rowBox(row, index, x, width, y, height) {
904
+ const w = row.kind === "slot" && row.width !== void 0 ? Math.min(row.width, width) : width;
905
+ return { row, index, x: x + (width - w) / 2, y, width: w, height };
906
+ }
443
907
  function layoutRowPorts(row, box, measure) {
444
908
  const width = (label) => label === void 0 ? 0 : measureRow({ kind: "label", text: label }, measure).width;
445
909
  const slots = Math.max(row.left?.length ?? 0, row.right?.length ?? 0, 1);
@@ -727,28 +1191,28 @@ function graphOrder(graph) {
727
1191
  }
728
1192
 
729
1193
  // src/cluster.ts
730
- function insetOf(inset) {
731
- if (typeof inset === "number") return { top: inset, right: inset, bottom: inset, left: inset };
1194
+ function insetOf(inset2) {
1195
+ if (typeof inset2 === "number") return { top: inset2, right: inset2, bottom: inset2, left: inset2 };
732
1196
  return {
733
- top: inset?.top ?? DEFAULT_GROUP_INSET,
734
- right: inset?.right ?? DEFAULT_GROUP_INSET,
735
- bottom: inset?.bottom ?? DEFAULT_GROUP_INSET,
736
- left: inset?.left ?? DEFAULT_GROUP_INSET
1197
+ top: inset2?.top ?? DEFAULT_GROUP_INSET,
1198
+ right: inset2?.right ?? DEFAULT_GROUP_INSET,
1199
+ bottom: inset2?.bottom ?? DEFAULT_GROUP_INSET,
1200
+ left: inset2?.left ?? DEFAULT_GROUP_INSET
737
1201
  };
738
1202
  }
739
1203
  var DEFAULT_GROUP_INSET = 12;
740
- function canonical(inset, axes) {
1204
+ function canonical(inset2, axes) {
741
1205
  if (axes.rank === "y") {
742
- return axes.sign === 1 ? { crossLo: inset.left, crossHi: inset.right, rankLo: inset.top, rankHi: inset.bottom } : { crossLo: inset.left, crossHi: inset.right, rankLo: inset.bottom, rankHi: inset.top };
1206
+ return axes.sign === 1 ? { crossLo: inset2.left, crossHi: inset2.right, rankLo: inset2.top, rankHi: inset2.bottom } : { crossLo: inset2.left, crossHi: inset2.right, rankLo: inset2.bottom, rankHi: inset2.top };
743
1207
  }
744
- return axes.sign === 1 ? { crossLo: inset.top, crossHi: inset.bottom, rankLo: inset.left, rankHi: inset.right } : { crossLo: inset.top, crossHi: inset.bottom, rankLo: inset.right, rankHi: inset.left };
1208
+ return axes.sign === 1 ? { crossLo: inset2.top, crossHi: inset2.bottom, rankLo: inset2.left, rankHi: inset2.right } : { crossLo: inset2.top, crossHi: inset2.bottom, rankLo: inset2.right, rankHi: inset2.left };
745
1209
  }
746
- function crossInset(inset, axes) {
747
- const c = canonical(inset, axes);
1210
+ function crossInset(inset2, axes) {
1211
+ const c = canonical(inset2, axes);
748
1212
  return { lo: c.crossLo, hi: c.crossHi };
749
1213
  }
750
- function rankInset(inset, axes) {
751
- const c = canonical(inset, axes);
1214
+ function rankInset(inset2, axes) {
1215
+ const c = canonical(inset2, axes);
752
1216
  return { lo: c.rankLo, hi: c.rankHi };
753
1217
  }
754
1218
  function rankGaps(graph, rows, axes, rankGap) {
@@ -863,8 +1327,8 @@ function packClusters(graph, rows, preferred2, axes, nodeGap) {
863
1327
  return block;
864
1328
  };
865
1329
  function outer(span) {
866
- const inset = insets.get(span);
867
- return blockOf(span).width + inset.crossLo + inset.crossHi;
1330
+ const inset2 = insets.get(span);
1331
+ return blockOf(span).width + inset2.crossLo + inset2.crossHi;
868
1332
  }
869
1333
  const roots = spans.filter((s) => s.parent === void 0);
870
1334
  const start = new Map(roots.map((s) => [s, centerOf.get(s) - outer(s) / 2]));
@@ -878,8 +1342,8 @@ function packClusters(graph, rows, preferred2, axes, nodeGap) {
878
1342
  for (const root of roots) place(root, start.get(root));
879
1343
  return at2;
880
1344
  }
881
- function widen(block, group, inset, axes) {
882
- const floor = axes.cross === "x" ? (group.minWidth ?? 0) - inset.crossLo - inset.crossHi : 0;
1345
+ function widen(block, group, inset2, axes) {
1346
+ const floor = axes.cross === "x" ? (group.minWidth ?? 0) - inset2.crossLo - inset2.crossHi : 0;
883
1347
  const slack = Math.max(0, floor - block.width) / 2;
884
1348
  block.width += 2 * slack;
885
1349
  for (const [child, local] of block.childAt) block.childAt.set(child, local + slack);
@@ -950,7 +1414,13 @@ function subpathOf(path, index) {
950
1414
  if (i === commands.length || cmd === PATH_M$1) {
951
1415
  if (seen === index) {
952
1416
  if (start === 0 && i === commands.length) return path;
953
- return { kind: "polygon", fillRule: path.fillRule, commands: commands.slice(start, i), coords: coords.slice(startCoord, coord2) };
1417
+ return {
1418
+ kind: "polygon",
1419
+ fillRule: path.fillRule,
1420
+ commands: commands.slice(start, i),
1421
+ coords: coords.slice(startCoord, coord2),
1422
+ ...path.origin ? { origin: path.origin } : {}
1423
+ };
954
1424
  }
955
1425
  seen++;
956
1426
  start = i;
@@ -963,7 +1433,8 @@ function subpathOf(path, index) {
963
1433
  var NAMED = { start: 0, mid: 0.5, end: 1 };
964
1434
  function nearestStation(path, near) {
965
1435
  if (path.kind !== "polygon") return null;
966
- const { commands, coords } = path;
1436
+ const { commands } = path;
1437
+ const coords = worldCoords(path);
967
1438
  let best = null;
968
1439
  let heading = { x: 1, y: 0 };
969
1440
  forEachSegment(commands, coords, (cmd, ci, x0, y0) => {
@@ -1167,8 +1638,8 @@ function boxOf(g) {
1167
1638
  x1 = Math.max(x1, b.x1);
1168
1639
  y1 = Math.max(y1, b.y1);
1169
1640
  }
1170
- const { inset } = g.group;
1171
- return { x0: x0 - inset.left, y0: y0 - inset.top, x1: x1 + inset.right, y1: y1 + inset.bottom };
1641
+ const { inset: inset2 } = g.group;
1642
+ return { x0: x0 - inset2.left, y0: y0 - inset2.top, x1: x1 + inset2.right, y1: y1 + inset2.bottom };
1172
1643
  }
1173
1644
  function clearance(a, b, padding) {
1174
1645
  const gapX = Math.min(a.x1, b.x1) - Math.max(a.x0, b.x0) + padding;
@@ -1522,9 +1993,9 @@ function longestPath(count, arcs) {
1522
1993
  const rank = new Array(count).fill(0);
1523
1994
  const pending = new Array(count).fill(0);
1524
1995
  const out = Array.from({ length: count }, () => []);
1525
- for (const arc of arcs) {
1526
- pending[arc.head]++;
1527
- out[arc.tail].push(arc.head);
1996
+ for (const arc2 of arcs) {
1997
+ pending[arc2.head]++;
1998
+ out[arc2.tail].push(arc2.head);
1528
1999
  }
1529
2000
  const ready = [];
1530
2001
  for (let i = 0; i < count; i++) if (pending[i] === 0) ready.push(i);
@@ -1539,9 +2010,9 @@ function longestPath(count, arcs) {
1539
2010
  }
1540
2011
  function components(count, arcs) {
1541
2012
  const near = Array.from({ length: count }, () => []);
1542
- for (const arc of arcs) {
1543
- near[arc.tail].push(arc.head);
1544
- near[arc.head].push(arc.tail);
2013
+ for (const arc2 of arcs) {
2014
+ near[arc2.tail].push(arc2.head);
2015
+ near[arc2.head].push(arc2.tail);
1545
2016
  }
1546
2017
  const part = new Array(count).fill(-1);
1547
2018
  const parts = [];
@@ -1562,14 +2033,14 @@ function components(count, arcs) {
1562
2033
  }
1563
2034
  const local = new Array(count);
1564
2035
  for (const p of parts) p.nodes.forEach((g, i) => local[g] = i);
1565
- for (const arc of arcs) {
1566
- parts[part[arc.tail]].arcs.push({ tail: local[arc.tail], head: local[arc.head] });
2036
+ for (const arc2 of arcs) {
2037
+ parts[part[arc2.tail]].arcs.push({ tail: local[arc2.tail], head: local[arc2.head] });
1567
2038
  }
1568
2039
  return parts;
1569
2040
  }
1570
- var slackOf = (arc, rank) => rank[arc.head] - rank[arc.tail] - 1;
2041
+ var slackOf = (arc2, rank) => rank[arc2.head] - rank[arc2.tail] - 1;
1571
2042
  function simplex(arcs, rank) {
1572
- if (arcs.length === 0 || arcs.some((arc) => slackOf(arc, rank) < 0)) return;
2043
+ if (arcs.length === 0 || arcs.some((arc2) => slackOf(arc2, rank) < 0)) return;
1573
2044
  const inTree = feasibleTree(arcs, rank);
1574
2045
  const cap = 4 * arcs.length + 100;
1575
2046
  for (let round = 0; round < cap; round++) {
@@ -1588,10 +2059,10 @@ function simplex(arcs, rank) {
1588
2059
  const inside = insideOf(tree2, childOf(arcs[leave], leave, tree2));
1589
2060
  let enter = -1;
1590
2061
  for (let a = 0; a < arcs.length; a++) {
1591
- const arc = arcs[a];
2062
+ const arc2 = arcs[a];
1592
2063
  if (inTree[a]) continue;
1593
- const crosses = tailInside ? !inside(arc.tail) && inside(arc.head) : inside(arc.tail) && !inside(arc.head);
1594
- if (crosses && (enter === -1 || slackOf(arc, rank) < slackOf(arcs[enter], rank))) enter = a;
2064
+ const crosses = tailInside ? !inside(arc2.tail) && inside(arc2.head) : inside(arc2.tail) && !inside(arc2.head);
2065
+ if (crosses && (enter === -1 || slackOf(arc2, rank) < slackOf(arcs[enter], rank))) enter = a;
1595
2066
  }
1596
2067
  if (enter === -1) return;
1597
2068
  inTree[leave] = false;
@@ -1607,10 +2078,10 @@ function feasibleTree(arcs, rank) {
1607
2078
  const grow = () => {
1608
2079
  for (let changed = true; changed; ) {
1609
2080
  changed = false;
1610
- arcs.forEach((arc, a) => {
1611
- if (has[arc.tail] === has[arc.head] || slackOf(arc, rank) !== 0) return;
2081
+ arcs.forEach((arc2, a) => {
2082
+ if (has[arc2.tail] === has[arc2.head] || slackOf(arc2, rank) !== 0) return;
1612
2083
  inTree[a] = true;
1613
- has[arc.tail] = has[arc.head] = true;
2084
+ has[arc2.tail] = has[arc2.head] = true;
1614
2085
  size++;
1615
2086
  changed = true;
1616
2087
  });
@@ -1619,12 +2090,12 @@ function feasibleTree(arcs, rank) {
1619
2090
  grow();
1620
2091
  while (size < rank.length) {
1621
2092
  let best = -1;
1622
- arcs.forEach((arc2, a) => {
1623
- if (has[arc2.tail] === has[arc2.head]) return;
1624
- if (best === -1 || slackOf(arc2, rank) < slackOf(arcs[best], rank)) best = a;
2093
+ arcs.forEach((arc3, a) => {
2094
+ if (has[arc3.tail] === has[arc3.head]) return;
2095
+ if (best === -1 || slackOf(arc3, rank) < slackOf(arcs[best], rank)) best = a;
1625
2096
  });
1626
- const arc = arcs[best];
1627
- const delta = has[arc.head] ? -slackOf(arc, rank) : slackOf(arc, rank);
2097
+ const arc2 = arcs[best];
2098
+ const delta = has[arc2.head] ? -slackOf(arc2, rank) : slackOf(arc2, rank);
1628
2099
  for (let v = 0; v < rank.length; v++) if (has[v]) rank[v] += delta;
1629
2100
  grow();
1630
2101
  }
@@ -1632,10 +2103,10 @@ function feasibleTree(arcs, rank) {
1632
2103
  }
1633
2104
  function treeAdjacency(count, arcs, inTree) {
1634
2105
  const adj = Array.from({ length: count }, () => []);
1635
- arcs.forEach((arc, a) => {
2106
+ arcs.forEach((arc2, a) => {
1636
2107
  if (!inTree[a]) return;
1637
- adj[arc.tail].push(a);
1638
- adj[arc.head].push(a);
2108
+ adj[arc2.tail].push(a);
2109
+ adj[arc2.head].push(a);
1639
2110
  });
1640
2111
  return adj;
1641
2112
  }
@@ -1656,16 +2127,16 @@ function postorder(count, arcs, inTree) {
1656
2127
  continue;
1657
2128
  }
1658
2129
  if (a === parentArc[top.v]) continue;
1659
- const arc = arcs[a];
1660
- const w = arc.tail === top.v ? arc.head : arc.tail;
2130
+ const arc2 = arcs[a];
2131
+ const w = arc2.tail === top.v ? arc2.head : arc2.tail;
1661
2132
  parentArc[w] = a;
1662
2133
  low[w] = next;
1663
2134
  stack.push({ v: w, i: 0 });
1664
2135
  }
1665
2136
  return { low, lim, parentArc };
1666
2137
  }
1667
- function childOf(arc, a, tree2) {
1668
- return tree2.parentArc[arc.tail] === a ? arc.tail : arc.head;
2138
+ function childOf(arc2, a, tree2) {
2139
+ return tree2.parentArc[arc2.tail] === a ? arc2.tail : arc2.head;
1669
2140
  }
1670
2141
  function insideOf(tree2, child) {
1671
2142
  const lo = tree2.low[child];
@@ -1678,9 +2149,9 @@ function cutValue(arcs, a, tree2) {
1678
2149
  const inside = insideOf(tree2, child);
1679
2150
  const tailInside = child === treeArc.tail;
1680
2151
  let value = 0;
1681
- for (const arc of arcs) {
1682
- const from = inside(arc.tail);
1683
- if (from === inside(arc.head)) continue;
2152
+ for (const arc2 of arcs) {
2153
+ const from = inside(arc2.tail);
2154
+ if (from === inside(arc2.head)) continue;
1684
2155
  value += from === tailInside ? 1 : -1;
1685
2156
  }
1686
2157
  return { value, tailInside };
@@ -1693,11 +2164,11 @@ function tighten(rank, arcs, inTree) {
1693
2164
  for (let k = 0; k < queue.length; k++) {
1694
2165
  const v = queue[k];
1695
2166
  for (const a of adj[v]) {
1696
- const arc = arcs[a];
1697
- const w = arc.tail === v ? arc.head : arc.tail;
2167
+ const arc2 = arcs[a];
2168
+ const w = arc2.tail === v ? arc2.head : arc2.tail;
1698
2169
  if (seen[w]) continue;
1699
2170
  seen[w] = true;
1700
- rank[w] = arc.tail === v ? rank[v] + 1 : rank[v] - 1;
2171
+ rank[w] = arc2.tail === v ? rank[v] + 1 : rank[v] - 1;
1701
2172
  queue.push(w);
1702
2173
  }
1703
2174
  }
@@ -1709,18 +2180,18 @@ var isPlaceholder = (id) => id.startsWith(PLACEHOLDER);
1709
2180
 
1710
2181
  // src/layered/expand.ts
1711
2182
  function labelRowsOf(drawn, ranks, span) {
1712
- const gapOf = /* @__PURE__ */ new Map();
2183
+ const gapOf2 = /* @__PURE__ */ new Map();
1713
2184
  for (const d of drawn) {
1714
2185
  if (d.label === void 0) continue;
1715
2186
  const a = span(d.back ? d.to : d.from, ranks);
1716
2187
  const b = span(d.back ? d.from : d.to, ranks);
1717
2188
  if (a === null || b === null || a[1] >= b[0]) continue;
1718
- gapOf.set(d.id, a[1] + Math.floor((b[0] - a[1] - 1) / 2));
2189
+ gapOf2.set(d.id, a[1] + Math.floor((b[0] - a[1] - 1) / 2));
1719
2190
  }
1720
- const gaps = [...new Set(gapOf.values())];
2191
+ const gaps = [...new Set(gapOf2.values())];
1721
2192
  const rowOf = (r) => r + gaps.filter((g) => g < r).length;
1722
2193
  const rows = new Map([...ranks].map(([id, r]) => [id, rowOf(r)]));
1723
- return { rows, labelRow: new Map([...gapOf].map(([id, g]) => [id, rowOf(g) + 1])) };
2194
+ return { rows, labelRow: new Map([...gapOf2].map(([id, g]) => [id, rowOf(g) + 1])) };
1724
2195
  }
1725
2196
  function labelRoom(room, axes, back, nodeGap) {
1726
2197
  const across = extent({ width: room.width, height: room.height }, axes.cross);
@@ -3177,6 +3648,28 @@ function forestOf(graph, crossAxis) {
3177
3648
  }
3178
3649
  return { roots, children, depth };
3179
3650
  }
3651
+ function subtreeBlocks(children, axis, gap) {
3652
+ const measured = /* @__PURE__ */ new Map();
3653
+ const kidsSpan = (kids) => kids.reduce((sum, k) => sum + span(k), 0) + Math.max(0, kids.length - 1) * gap;
3654
+ const span = (node) => {
3655
+ const known = measured.get(node.id);
3656
+ if (known !== void 0) return known;
3657
+ const block = Math.max(extent(node.bounds, axis), kidsSpan(children.get(node.id) ?? []));
3658
+ measured.set(node.id, block);
3659
+ return block;
3660
+ };
3661
+ const lay = (node, from, into) => {
3662
+ const kids = children.get(node.id) ?? [];
3663
+ const block = span(node);
3664
+ let cursor = from + (block - kidsSpan(kids)) / 2;
3665
+ for (const child of kids) {
3666
+ lay(child, cursor, into);
3667
+ cursor += span(child) + gap;
3668
+ }
3669
+ into.set(node.id, from + (block - extent(node.bounds, axis)) / 2);
3670
+ };
3671
+ return { span, lay };
3672
+ }
3180
3673
  var tree = (graph, opts = {}) => (graph.groups?.length ?? 0) > 0 ? untilSettled(treeOnce, graph, opts) : treeOnce(graph, opts);
3181
3674
  var treeOnce = (graph, opts = {}) => {
3182
3675
  const axes = axesFor(opts.direction);
@@ -3199,48 +3692,29 @@ var treeOnce = (graph, opts = {}) => {
3199
3692
  along += bands.get(at2) + (gaps[i] ?? 0);
3200
3693
  }
3201
3694
  const slots = /* @__PURE__ */ new Map();
3202
- const measured = /* @__PURE__ */ new Map();
3203
- const measure = (node) => {
3204
- const known = measured.get(node.id);
3205
- if (known !== void 0) return known;
3206
- const kids = children.get(node.id) ?? [];
3207
- let width = 0;
3208
- for (const child of kids) width += measure(child);
3209
- if (kids.length > 0) width += nodeGap * (kids.length - 1);
3210
- const block = Math.max(extent(node.bounds, axes.cross), width);
3211
- measured.set(node.id, block);
3212
- return block;
3213
- };
3214
- const place = (node, from) => {
3215
- const kids = children.get(node.id) ?? [];
3216
- const block = measure(node);
3217
- const width = kids.reduce((sum, k) => sum + measure(k), 0) + Math.max(0, kids.length - 1) * nodeGap;
3218
- let cursor2 = from + (block - width) / 2;
3219
- for (const child of kids) {
3220
- place(child, cursor2);
3221
- cursor2 += measure(child) + nodeGap;
3222
- }
3223
- const own = extent(node.bounds, axes.cross);
3695
+ const blocks = subtreeBlocks(children, axes.cross, nodeGap);
3696
+ const crossAt = /* @__PURE__ */ new Map();
3697
+ let cursor = 0;
3698
+ for (const root of roots) {
3699
+ blocks.lay(root, cursor, crossAt);
3700
+ cursor += blocks.span(root) + nodeGap;
3701
+ }
3702
+ for (const node of graph.nodes) {
3224
3703
  const at2 = depth.get(node.id) ?? 0;
3225
3704
  slots.set(node.id, {
3226
- cross: from + (block - own) / 2,
3705
+ cross: crossAt.get(node.id),
3227
3706
  rank: bandStart.get(at2) + (bands.get(at2) - extent(node.bounds, axes.rank)) / 2
3228
3707
  });
3229
- };
3230
- let cursor = 0;
3231
- for (const root of roots) {
3232
- place(root, cursor);
3233
- cursor += measure(root) + nodeGap;
3234
3708
  }
3235
3709
  if (grouped) {
3236
3710
  const preferred2 = new Map([...slots].map(([id, slot]) => [id, slot.cross]));
3237
3711
  const ordered = rowsAt.map((row) => [...row].sort((a, b) => preferred2.get(a.id) - preferred2.get(b.id)));
3238
- const crossAt = packClusters(graph, ordered, preferred2, axes, nodeGap);
3239
- for (const [id, slot] of slots) slots.set(id, { ...slot, cross: crossAt.get(id) ?? slot.cross });
3712
+ const crossAt2 = packClusters(graph, ordered, preferred2, axes, nodeGap);
3713
+ for (const [id, slot] of slots) slots.set(id, { ...slot, cross: crossAt2.get(id) ?? slot.cross });
3240
3714
  }
3241
3715
  return settle(graph, slots, opts);
3242
3716
  };
3243
3717
 
3244
- export { COMPASS, DEFAULT_GROUP_INSET, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, PORT_LABEL_GAP, SIDE_TRACK_GAP, anchorOffset, authoredOver, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, center, chain, contentBox, createDiagramNodes, dataKeyReader, diagramEdgeOf, diagramNodeOf, expandGroupEdges, extent, footprintNode, force, forceRelaxation, forestOf, graphOrder, grownBy, insetOf, isPinned, isPromise, labelCenter, labelSpotAt, layered, layeredPlan, layeredRoutes, layoutBody, layoutOwnsPort, layoutOwnsRoute, layoutRowPorts, layoutWritesRoute, marginOf, measureBody, outlinePath, outlinePolyline, packAcross, packClusters, pinnedSet, planOf, plannerPlan, portLabelBox, portLabelsOf, portOf, portSitesOf, portsOf, portsOnOutline, rankGaps, ranksByLength, ranksOf, rayHit, routeHolds, samePoints, seededOrder, settle, sizeToBody, subpathOf, translated, tree, untilSettled, widenForMinWidth };
3245
- //# sourceMappingURL=chunk-L4H444QI.js.map
3246
- //# sourceMappingURL=chunk-L4H444QI.js.map
3718
+ export { COMPASS, DEFAULT_GROUP_INSET, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DEVICE_SIZE, DIAGRAM_TRAIT_KEY, DIVIDER_HEIGHT, EVENT_SIZE, GATEWAY_SIZE, PORT_LABEL_GAP, SIDE_TRACK_GAP, anchorOffset, authoredOver, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, center, chain, contentBox, createDiagramNodes, dataKeyReader, diagramEdgeOf, diagramNodeOf, expandGroupEdges, extent, footprintNode, force, forceRelaxation, forestOf, graphOrder, grownBy, insetOf, isPinned, isPromise, labelCenter, labelSpotAt, layered, layeredPlan, layeredRoutes, layoutBody, layoutOwnsPort, layoutOwnsRoute, layoutRowPorts, layoutWritesRoute, marginOf, measureBody, outlineDetail, outlineFromD, outlinePath, outlinePolyline, packAcross, packClusters, pinnedSet, planOf, plannerPlan, portLabelBox, portLabelsOf, portOf, portSitesOf, portsOf, portsOnOutline, rankGaps, ranksByLength, ranksOf, rayHit, registerOutline, resolveOutline, routeHolds, rowHeights, samePoints, seededOrder, settle, sizeToBody, subpathOf, subtreeBlocks, translated, tree, untilSettled, widenForMinWidth };
3719
+ //# sourceMappingURL=chunk-VB6HUKNG.js.map
3720
+ //# sourceMappingURL=chunk-VB6HUKNG.js.map