@skygraph/core 0.0.0-placeholder.0 → 0.5.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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -6
  3. package/dist/calendar.cjs +404 -0
  4. package/dist/calendar.cjs.map +1 -0
  5. package/dist/calendar.d.cts +209 -0
  6. package/dist/calendar.d.ts +209 -0
  7. package/dist/calendar.js +12 -0
  8. package/dist/calendar.js.map +1 -0
  9. package/dist/chunk-5CCD5Q4B.js +36 -0
  10. package/dist/chunk-5CCD5Q4B.js.map +1 -0
  11. package/dist/chunk-7FA2TBSZ.js +1211 -0
  12. package/dist/chunk-7FA2TBSZ.js.map +1 -0
  13. package/dist/chunk-AB3RLBLW.js +357 -0
  14. package/dist/chunk-AB3RLBLW.js.map +1 -0
  15. package/dist/chunk-GEMALROQ.js +258 -0
  16. package/dist/chunk-GEMALROQ.js.map +1 -0
  17. package/dist/chunk-IY6LJU3L.js +256 -0
  18. package/dist/chunk-IY6LJU3L.js.map +1 -0
  19. package/dist/chunk-KMGRNPLG.js +453 -0
  20. package/dist/chunk-KMGRNPLG.js.map +1 -0
  21. package/dist/chunk-Y7FTBBYX.js +397 -0
  22. package/dist/chunk-Y7FTBBYX.js.map +1 -0
  23. package/dist/chunk-ZWB7JGAJ.js +488 -0
  24. package/dist/chunk-ZWB7JGAJ.js.map +1 -0
  25. package/dist/form.cjs +498 -0
  26. package/dist/form.cjs.map +1 -0
  27. package/dist/form.d.cts +130 -0
  28. package/dist/form.d.ts +130 -0
  29. package/dist/form.js +8 -0
  30. package/dist/form.js.map +1 -0
  31. package/dist/graph.cjs +1269 -0
  32. package/dist/graph.cjs.map +1 -0
  33. package/dist/graph.d.cts +671 -0
  34. package/dist/graph.d.ts +671 -0
  35. package/dist/graph.js +34 -0
  36. package/dist/graph.js.map +1 -0
  37. package/dist/index.cjs +3860 -0
  38. package/dist/index.cjs.map +1 -0
  39. package/dist/index.d.cts +144 -0
  40. package/dist/index.d.ts +144 -0
  41. package/dist/index.js +477 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/runtime-internal.cjs +339 -0
  44. package/dist/runtime-internal.cjs.map +1 -0
  45. package/dist/runtime-internal.d.cts +130 -0
  46. package/dist/runtime-internal.d.ts +130 -0
  47. package/dist/runtime-internal.js +67 -0
  48. package/dist/runtime-internal.js.map +1 -0
  49. package/dist/table.cjs +537 -0
  50. package/dist/table.cjs.map +1 -0
  51. package/dist/table.d.cts +215 -0
  52. package/dist/table.d.ts +215 -0
  53. package/dist/table.js +16 -0
  54. package/dist/table.js.map +1 -0
  55. package/dist/tree.cjs +442 -0
  56. package/dist/tree.cjs.map +1 -0
  57. package/dist/tree.d.cts +76 -0
  58. package/dist/tree.d.ts +76 -0
  59. package/dist/tree.js +8 -0
  60. package/dist/tree.js.map +1 -0
  61. package/dist/types-B-rKQJ4R.d.cts +35 -0
  62. package/dist/types-B-rKQJ4R.d.ts +35 -0
  63. package/dist/virtual.cjs +283 -0
  64. package/dist/virtual.cjs.map +1 -0
  65. package/dist/virtual.d.cts +96 -0
  66. package/dist/virtual.d.ts +96 -0
  67. package/dist/virtual.js +9 -0
  68. package/dist/virtual.js.map +1 -0
  69. package/package.json +98 -18
  70. package/index.js +0 -3
@@ -0,0 +1,1211 @@
1
+ import {
2
+ GRAPH_PREFIX
3
+ } from "./chunk-5CCD5Q4B.js";
4
+
5
+ // src/engines/graph/history.ts
6
+ var HISTORY_LIMIT = 100;
7
+ function createHistory(limit = HISTORY_LIMIT) {
8
+ const undoStack = [];
9
+ const redoStack = [];
10
+ function trim(stack) {
11
+ while (stack.length > limit) stack.shift();
12
+ }
13
+ return {
14
+ push(entry) {
15
+ undoStack.push(entry);
16
+ trim(undoStack);
17
+ },
18
+ popUndo() {
19
+ return undoStack.pop();
20
+ },
21
+ pushRedo(entry) {
22
+ redoStack.push(entry);
23
+ trim(redoStack);
24
+ },
25
+ popRedo() {
26
+ return redoStack.pop();
27
+ },
28
+ clearRedo() {
29
+ redoStack.length = 0;
30
+ },
31
+ canUndo() {
32
+ return undoStack.length > 0;
33
+ },
34
+ canRedo() {
35
+ return redoStack.length > 0;
36
+ },
37
+ clear() {
38
+ undoStack.length = 0;
39
+ redoStack.length = 0;
40
+ },
41
+ relabelTop(label) {
42
+ const top = undoStack[undoStack.length - 1];
43
+ if (!top) return;
44
+ undoStack[undoStack.length - 1] = { snapshot: top.snapshot, label };
45
+ },
46
+ topLabel() {
47
+ return undoStack[undoStack.length - 1]?.label;
48
+ },
49
+ size() {
50
+ return { undo: undoStack.length, redo: redoStack.length };
51
+ }
52
+ };
53
+ }
54
+
55
+ // src/engines/graph/GraphEngine.ts
56
+ var engineCounter = 0;
57
+ var DEFAULT_OUTLINE = { kind: "rect", w: 100, h: 60 };
58
+ var DEFAULT_TRANSFORM = { x: 0, y: 0 };
59
+ var DEFAULT_ANCHOR_POLICY = { kind: "perEdge", k: 1 };
60
+ function createGraph(core, options) {
61
+ const engineId = `g${engineCounter++}${options?.name ? `-${options.name}` : ""}`;
62
+ const prefix = GRAPH_PREFIX;
63
+ const nodes = /* @__PURE__ */ new Map();
64
+ const edges = /* @__PURE__ */ new Map();
65
+ const edgesByNode = /* @__PURE__ */ new Map();
66
+ let nodeCounter = 0;
67
+ let edgeCounter = 0;
68
+ const localSubscribers = /* @__PURE__ */ new Set();
69
+ function publishSnapshot() {
70
+ core.set(`${prefix}snapshot.${engineId}`, {
71
+ nodes: Array.from(nodes.values()),
72
+ edges: Array.from(edges.values())
73
+ });
74
+ for (const cb of localSubscribers) cb();
75
+ }
76
+ function subscribeLocal(cb) {
77
+ localSubscribers.add(cb);
78
+ return () => {
79
+ localSubscribers.delete(cb);
80
+ };
81
+ }
82
+ publishSnapshot();
83
+ const history = createHistory(HISTORY_LIMIT);
84
+ let transactionDepth = 0;
85
+ let pendingEntry = null;
86
+ let isRestoring = false;
87
+ function _captureState() {
88
+ return {
89
+ nodes: Array.from(nodes.values()),
90
+ edges: Array.from(edges.values()),
91
+ nodeCounter,
92
+ edgeCounter
93
+ };
94
+ }
95
+ function _restoreState(snapshot) {
96
+ nodes.clear();
97
+ edges.clear();
98
+ edgesByNode.clear();
99
+ for (const n of snapshot.nodes) {
100
+ nodes.set(n.id, n);
101
+ edgesByNode.set(n.id, /* @__PURE__ */ new Set());
102
+ }
103
+ for (const e of snapshot.edges) {
104
+ edges.set(e.id, e);
105
+ ensureBucket(endpointNode(e.from)).add(e.id);
106
+ ensureBucket(endpointNode(e.to)).add(e.id);
107
+ }
108
+ nodeCounter = snapshot.nodeCounter;
109
+ edgeCounter = snapshot.edgeCounter;
110
+ }
111
+ function _withHistory(fn, label) {
112
+ if (isRestoring) return fn();
113
+ const wasOuter = transactionDepth === 0;
114
+ if (wasOuter) {
115
+ pendingEntry = { snapshot: _captureState(), label };
116
+ } else if (label !== void 0 && pendingEntry && pendingEntry.label === void 0) {
117
+ pendingEntry = { snapshot: pendingEntry.snapshot, label };
118
+ }
119
+ transactionDepth++;
120
+ try {
121
+ return fn();
122
+ } finally {
123
+ transactionDepth--;
124
+ if (transactionDepth === 0 && wasOuter) {
125
+ if (pendingEntry !== null) {
126
+ history.push(pendingEntry);
127
+ history.clearRedo();
128
+ }
129
+ pendingEntry = null;
130
+ }
131
+ }
132
+ }
133
+ function newNodeId() {
134
+ return `${engineId}-n${nodeCounter++}`;
135
+ }
136
+ function newEdgeId() {
137
+ return `${engineId}-e${edgeCounter++}`;
138
+ }
139
+ function ensureBucket(nodeId) {
140
+ let bucket = edgesByNode.get(nodeId);
141
+ if (!bucket) {
142
+ bucket = /* @__PURE__ */ new Set();
143
+ edgesByNode.set(nodeId, bucket);
144
+ }
145
+ return bucket;
146
+ }
147
+ function endpointNode(ep) {
148
+ return ep.node;
149
+ }
150
+ function bumpRevision(node) {
151
+ return { ...node, geomRevision: node.geomRevision + 1 };
152
+ }
153
+ function _addNodeImpl(init = {}) {
154
+ const id = init.id ?? newNodeId();
155
+ if (nodes.has(id)) {
156
+ throw new Error(`[skygraph/graph] node id "${id}" already exists`);
157
+ }
158
+ const node = {
159
+ id,
160
+ parentId: init.parentId ?? null,
161
+ transform: init.transform ?? { ...DEFAULT_TRANSFORM },
162
+ outline: init.outline ?? DEFAULT_OUTLINE,
163
+ anchorPolicy: init.anchorPolicy ?? DEFAULT_ANCHOR_POLICY,
164
+ data: init.data,
165
+ geomRevision: 0
166
+ };
167
+ nodes.set(id, node);
168
+ edgesByNode.set(id, /* @__PURE__ */ new Set());
169
+ publishSnapshot();
170
+ return id;
171
+ }
172
+ function addNode(init = {}) {
173
+ return _withHistory(() => _addNodeImpl(init));
174
+ }
175
+ function _removeNodeImpl(id) {
176
+ if (!nodes.has(id)) return;
177
+ const incident = edgesByNode.get(id);
178
+ if (incident) {
179
+ for (const eid of incident) removeEdge(eid);
180
+ }
181
+ for (const [otherId, n] of nodes) {
182
+ if (n.parentId === id) {
183
+ nodes.set(otherId, { ...n, parentId: null });
184
+ }
185
+ }
186
+ nodes.delete(id);
187
+ edgesByNode.delete(id);
188
+ publishSnapshot();
189
+ }
190
+ function removeNode(id) {
191
+ _withHistory(() => _removeNodeImpl(id));
192
+ }
193
+ function _updateNodeImpl(id, patch) {
194
+ const node = nodes.get(id);
195
+ if (!node) {
196
+ throw new Error(`[skygraph/graph] node "${id}" not found`);
197
+ }
198
+ const next = {
199
+ ...node,
200
+ transform: patch.transform ? { ...node.transform, ...patch.transform } : node.transform,
201
+ outline: patch.outline ?? node.outline,
202
+ anchorPolicy: patch.anchorPolicy ?? node.anchorPolicy,
203
+ data: patch.data === void 0 ? node.data : patch.data
204
+ };
205
+ const geomChanged = patch.transform !== void 0 || patch.outline !== void 0 || patch.anchorPolicy !== void 0;
206
+ nodes.set(id, geomChanged ? bumpRevision(next) : next);
207
+ publishSnapshot();
208
+ }
209
+ function updateNode(id, patch) {
210
+ _withHistory(() => _updateNodeImpl(id, patch));
211
+ }
212
+ function moveNode(id, x, y) {
213
+ _withHistory(() => _updateNodeImpl(id, { transform: { x, y } }));
214
+ }
215
+ function _setParentImpl(id, parentId) {
216
+ const node = nodes.get(id);
217
+ if (!node) {
218
+ throw new Error(`[skygraph/graph] node "${id}" not found`);
219
+ }
220
+ if (parentId !== null && !nodes.has(parentId)) {
221
+ throw new Error(`[skygraph/graph] parent node "${parentId}" not found`);
222
+ }
223
+ if (parentId === id) {
224
+ throw new Error(`[skygraph/graph] node cannot be its own parent ("${id}")`);
225
+ }
226
+ let cursor = parentId;
227
+ while (cursor !== null) {
228
+ if (cursor === id) {
229
+ throw new Error(`[skygraph/graph] setParent would create a cycle ("${id}" \u2192 "${parentId}")`);
230
+ }
231
+ cursor = nodes.get(cursor)?.parentId ?? null;
232
+ }
233
+ nodes.set(id, { ...node, parentId });
234
+ publishSnapshot();
235
+ }
236
+ function setParent(id, parentId) {
237
+ _withHistory(() => _setParentImpl(id, parentId));
238
+ }
239
+ function _addEdgeImpl(init) {
240
+ const id = init.id ?? newEdgeId();
241
+ if (edges.has(id)) {
242
+ throw new Error(`[skygraph/graph] edge id "${id}" already exists`);
243
+ }
244
+ const fromId = endpointNode(init.from);
245
+ const toId = endpointNode(init.to);
246
+ if (!nodes.has(fromId)) {
247
+ throw new Error(`[skygraph/graph] addEdge: from-node "${fromId}" not found`);
248
+ }
249
+ if (!nodes.has(toId)) {
250
+ throw new Error(`[skygraph/graph] addEdge: to-node "${toId}" not found`);
251
+ }
252
+ const edge = {
253
+ id,
254
+ from: init.from,
255
+ to: init.to,
256
+ routing: init.routing ?? "straight",
257
+ waypoints: init.waypoints,
258
+ data: init.data
259
+ };
260
+ edges.set(id, edge);
261
+ ensureBucket(fromId).add(id);
262
+ ensureBucket(toId).add(id);
263
+ publishSnapshot();
264
+ return id;
265
+ }
266
+ function addEdge(init) {
267
+ return _withHistory(() => _addEdgeImpl(init));
268
+ }
269
+ function _removeEdgeImpl(id) {
270
+ const edge = edges.get(id);
271
+ if (!edge) return;
272
+ edgesByNode.get(endpointNode(edge.from))?.delete(id);
273
+ edgesByNode.get(endpointNode(edge.to))?.delete(id);
274
+ edges.delete(id);
275
+ publishSnapshot();
276
+ }
277
+ function removeEdge(id) {
278
+ _withHistory(() => _removeEdgeImpl(id));
279
+ }
280
+ function getNode(id) {
281
+ return nodes.get(id);
282
+ }
283
+ function getEdge(id) {
284
+ return edges.get(id);
285
+ }
286
+ function getState() {
287
+ const byNode = /* @__PURE__ */ new Map();
288
+ for (const [nid, set] of edgesByNode) byNode.set(nid, Array.from(set));
289
+ return {
290
+ nodes: new Map(nodes),
291
+ edges: new Map(edges),
292
+ edgesByNode: byNode
293
+ };
294
+ }
295
+ function childrenOf(id) {
296
+ const out = [];
297
+ for (const n of nodes.values()) {
298
+ if (n.parentId === id) out.push(n.id);
299
+ }
300
+ return out;
301
+ }
302
+ function edgesOf(id) {
303
+ return Array.from(edgesByNode.get(id) ?? []);
304
+ }
305
+ function rectCornerId(corner) {
306
+ return corner;
307
+ }
308
+ function rectAnchors(w, h, policy) {
309
+ const corners = [
310
+ { id: rectCornerId("nw"), s: 0, point: [0, 0] },
311
+ { id: rectCornerId("ne"), s: 0.25, point: [w, 0] },
312
+ { id: rectCornerId("se"), s: 0.5, point: [w, h] },
313
+ { id: rectCornerId("sw"), s: 0.75, point: [0, h] }
314
+ ];
315
+ if (policy.kind === "manual") {
316
+ return policy.anchors.map((a) => ({
317
+ id: a.id,
318
+ s: a.s,
319
+ point: pointOnRectPerimeter(w, h, a.s)
320
+ }));
321
+ }
322
+ const k = policy.kind === "perEdge" ? Math.max(0, Math.floor(policy.k)) : null;
323
+ const density = policy.kind === "byLength" ? policy.density : null;
324
+ const out = [...corners];
325
+ const edgesGeom = [
326
+ { start: 0, end: 0.25 },
327
+ // top
328
+ { start: 0.25, end: 0.5 },
329
+ // right
330
+ { start: 0.5, end: 0.75 },
331
+ // bottom
332
+ { start: 0.75, end: 1 }
333
+ // left
334
+ ];
335
+ edgesGeom.forEach((edge, edgeIdx) => {
336
+ let count = 0;
337
+ if (k !== null) count = k;
338
+ if (density !== null) {
339
+ const len = edgeIdx % 2 === 0 ? w : h;
340
+ count = Math.max(0, Math.floor(len / density) - 1);
341
+ }
342
+ for (let i = 1; i <= count; i++) {
343
+ const t = i / (count + 1);
344
+ const s = edge.start + (edge.end - edge.start) * t;
345
+ out.push({
346
+ id: `edge${edgeIdx}-${i}of${count}`,
347
+ s,
348
+ point: pointOnRectPerimeter(w, h, s)
349
+ });
350
+ }
351
+ });
352
+ out.sort((a, b) => a.s - b.s);
353
+ return out;
354
+ }
355
+ function pointOnRectPerimeter(w, h, s) {
356
+ const ss = (s % 1 + 1) % 1;
357
+ if (ss < 0.25) return [w * (ss / 0.25), 0];
358
+ if (ss < 0.5) return [w, h * ((ss - 0.25) / 0.25)];
359
+ if (ss < 0.75) return [w * (1 - (ss - 0.5) / 0.25), h];
360
+ return [0, h * (1 - (ss - 0.75) / 0.25)];
361
+ }
362
+ function polygonAnchors(verts, policy) {
363
+ if (verts.length === 0) return [];
364
+ const segLengths = [];
365
+ let total = 0;
366
+ for (let i = 0; i < verts.length; i++) {
367
+ const a = verts[i];
368
+ const b = verts[(i + 1) % verts.length];
369
+ const dx = b[0] - a[0];
370
+ const dy = b[1] - a[1];
371
+ const len = Math.hypot(dx, dy);
372
+ segLengths.push(len);
373
+ total += len;
374
+ }
375
+ const cumS = [0];
376
+ for (let i = 0; i < segLengths.length; i++) {
377
+ cumS.push(cumS[i] + segLengths[i] / total);
378
+ }
379
+ function pointAt(s) {
380
+ const ss = (s % 1 + 1) % 1;
381
+ let i = 0;
382
+ while (i < cumS.length - 1 && cumS[i + 1] <= ss) i++;
383
+ const a = verts[i % verts.length];
384
+ const b = verts[(i + 1) % verts.length];
385
+ const segS0 = cumS[i];
386
+ const segS1 = cumS[i + 1];
387
+ const t = segS1 === segS0 ? 0 : (ss - segS0) / (segS1 - segS0);
388
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
389
+ }
390
+ if (policy.kind === "manual") {
391
+ return policy.anchors.map((a) => ({ id: a.id, s: a.s, point: pointAt(a.s) }));
392
+ }
393
+ const out = [];
394
+ for (let i = 0; i < verts.length; i++) {
395
+ out.push({
396
+ id: `v${i}`,
397
+ s: cumS[i],
398
+ point: [verts[i][0], verts[i][1]]
399
+ });
400
+ }
401
+ const k = policy.kind === "perEdge" ? Math.max(0, Math.floor(policy.k)) : null;
402
+ const density = policy.kind === "byLength" ? policy.density : null;
403
+ for (let i = 0; i < verts.length; i++) {
404
+ const segLen = segLengths[i];
405
+ let count = 0;
406
+ if (k !== null) count = k;
407
+ if (density !== null) count = Math.max(0, Math.floor(segLen / density) - 1);
408
+ const s0 = cumS[i];
409
+ const s1 = cumS[i + 1];
410
+ for (let j = 1; j <= count; j++) {
411
+ const t = j / (count + 1);
412
+ const s = s0 + (s1 - s0) * t;
413
+ out.push({ id: `v${i}-${j}of${count}`, s, point: pointAt(s) });
414
+ }
415
+ }
416
+ out.sort((a, b) => a.s - b.s);
417
+ return out;
418
+ }
419
+ function ellipseAnchors(rx, ry, policy) {
420
+ function pointAt(s) {
421
+ const angle = (s % 1 + 1) % 1 * Math.PI * 2;
422
+ return [rx * Math.cos(angle), ry * Math.sin(angle)];
423
+ }
424
+ if (policy.kind === "manual") {
425
+ return policy.anchors.map((a) => ({ id: a.id, s: a.s, point: pointAt(a.s) }));
426
+ }
427
+ const k = policy.kind === "perEdge" ? Math.max(1, Math.floor(policy.k)) : null;
428
+ const totalSamples = k !== null ? Math.max(4, k * 4) : Math.max(4, Math.floor(2 * Math.PI * Math.max(rx, ry) / policy.density));
429
+ const out = [];
430
+ for (let i = 0; i < totalSamples; i++) {
431
+ const s = i / totalSamples;
432
+ out.push({ id: `el${i}of${totalSamples}`, s, point: pointAt(s) });
433
+ }
434
+ return out;
435
+ }
436
+ function anchorsOf(id) {
437
+ const node = nodes.get(id);
438
+ if (!node) return [];
439
+ const o = node.outline;
440
+ if (o.kind === "rect") return rectAnchors(o.w, o.h, node.anchorPolicy);
441
+ if (o.kind === "polygon") return polygonAnchors(o.verts, node.anchorPolicy);
442
+ if (o.kind === "ellipse") return ellipseAnchors(o.rx, o.ry, node.anchorPolicy);
443
+ if (o.kind === "path") return polygonAnchors(o.flatten, node.anchorPolicy);
444
+ return [];
445
+ }
446
+ function localBounds(node) {
447
+ const o = node.outline;
448
+ if (o.kind === "rect") return { x0: 0, y0: 0, x1: o.w, y1: o.h };
449
+ if (o.kind === "ellipse") return { x0: -o.rx, y0: -o.ry, x1: o.rx, y1: o.ry };
450
+ const verts = o.kind === "polygon" ? o.verts : o.flatten;
451
+ if (verts.length === 0) return { x0: 0, y0: 0, x1: 0, y1: 0 };
452
+ let x0 = verts[0][0];
453
+ let x1 = verts[0][0];
454
+ let y0 = verts[0][1];
455
+ let y1 = verts[0][1];
456
+ for (const [x, y] of verts) {
457
+ if (x < x0) x0 = x;
458
+ if (x > x1) x1 = x;
459
+ if (y < y0) y0 = y;
460
+ if (y > y1) y1 = y;
461
+ }
462
+ return { x0, y0, x1, y1 };
463
+ }
464
+ function worldOriginOf(id) {
465
+ let cursor = id;
466
+ let x = 0;
467
+ let y = 0;
468
+ let scale = 1;
469
+ while (cursor !== null) {
470
+ const n = nodes.get(cursor);
471
+ if (!n) break;
472
+ const s = n.transform.scale ?? 1;
473
+ x = n.transform.x + x * s;
474
+ y = n.transform.y + y * s;
475
+ scale *= s;
476
+ cursor = n.parentId;
477
+ }
478
+ return { x, y, scale };
479
+ }
480
+ function boundsOf(id) {
481
+ const node = nodes.get(id);
482
+ if (!node) return { x: 0, y: 0, w: 0, h: 0 };
483
+ const lb = localBounds(node);
484
+ const origin = worldOriginOf(id);
485
+ const w = (lb.x1 - lb.x0) * origin.scale;
486
+ const h = (lb.y1 - lb.y0) * origin.scale;
487
+ return {
488
+ x: origin.x + lb.x0 * origin.scale,
489
+ y: origin.y + lb.y0 * origin.scale,
490
+ w,
491
+ h
492
+ };
493
+ }
494
+ function getNodeOBB(id) {
495
+ const node = nodes.get(id);
496
+ if (!node) return { center: [0, 0], halfWidth: 0, halfHeight: 0, angle: 0 };
497
+ const lb = localBounds(node);
498
+ const origin = worldOriginOf(id);
499
+ const localCx = (lb.x0 + lb.x1) / 2;
500
+ const localCy = (lb.y0 + lb.y1) / 2;
501
+ const halfWidth = Math.abs((lb.x1 - lb.x0) / 2 * origin.scale);
502
+ const halfHeight = Math.abs((lb.y1 - lb.y0) / 2 * origin.scale);
503
+ let angle = 0;
504
+ let cursor = id;
505
+ while (cursor !== null) {
506
+ const n = nodes.get(cursor);
507
+ if (!n) break;
508
+ angle += n.transform.rot ?? 0;
509
+ cursor = n.parentId;
510
+ }
511
+ const center = [
512
+ origin.x + localCx * origin.scale,
513
+ origin.y + localCy * origin.scale
514
+ ];
515
+ return { center, halfWidth, halfHeight, angle };
516
+ }
517
+ function clear() {
518
+ nodes.clear();
519
+ edges.clear();
520
+ edgesByNode.clear();
521
+ nodeCounter = 0;
522
+ edgeCounter = 0;
523
+ history.clear();
524
+ pendingEntry = null;
525
+ transactionDepth = 0;
526
+ isRestoring = false;
527
+ publishSnapshot();
528
+ }
529
+ function transaction(fn, label) {
530
+ _withHistory(fn, label);
531
+ }
532
+ function pushHistory(label) {
533
+ if (transactionDepth > 0) {
534
+ if (pendingEntry !== null) {
535
+ history.push({ snapshot: pendingEntry.snapshot, label: pendingEntry.label ?? label });
536
+ history.clearRedo();
537
+ }
538
+ pendingEntry = { snapshot: _captureState(), label: void 0 };
539
+ return;
540
+ }
541
+ history.relabelTop(label);
542
+ }
543
+ function undo() {
544
+ if (!history.canUndo()) return false;
545
+ const entry = history.popUndo();
546
+ history.pushRedo({ snapshot: _captureState(), label: entry.label });
547
+ isRestoring = true;
548
+ try {
549
+ _restoreState(entry.snapshot);
550
+ } finally {
551
+ isRestoring = false;
552
+ }
553
+ publishSnapshot();
554
+ return true;
555
+ }
556
+ function redo() {
557
+ if (!history.canRedo()) return false;
558
+ const entry = history.popRedo();
559
+ history.push({ snapshot: _captureState(), label: entry.label });
560
+ isRestoring = true;
561
+ try {
562
+ _restoreState(entry.snapshot);
563
+ } finally {
564
+ isRestoring = false;
565
+ }
566
+ publishSnapshot();
567
+ return true;
568
+ }
569
+ function canUndo() {
570
+ return history.canUndo();
571
+ }
572
+ function canRedo() {
573
+ return history.canRedo();
574
+ }
575
+ function clearHistory() {
576
+ history.clear();
577
+ }
578
+ return {
579
+ addNode,
580
+ removeNode,
581
+ updateNode,
582
+ moveNode,
583
+ setParent,
584
+ addEdge,
585
+ removeEdge,
586
+ getNode,
587
+ getEdge,
588
+ getState,
589
+ childrenOf,
590
+ edgesOf,
591
+ anchorsOf,
592
+ boundsOf,
593
+ getNodeOBB,
594
+ subscribe: subscribeLocal,
595
+ transaction,
596
+ pushHistory,
597
+ undo,
598
+ redo,
599
+ canUndo,
600
+ canRedo,
601
+ clearHistory,
602
+ clear
603
+ };
604
+ }
605
+
606
+ // src/engines/graph/router.ts
607
+ function routeOrthogonal(start, end, options) {
608
+ const opts = typeof options === "string" ? { preferred: options } : options ?? {};
609
+ const gridSize = opts.gridSize ?? 10;
610
+ const stubLength = opts.stubLength ?? Math.max(20, gridSize);
611
+ const stepPosition = opts.stepPosition ?? 0.5;
612
+ let sExit = null;
613
+ let sStub = null;
614
+ let sCorner = null;
615
+ let sSide = null;
616
+ if (opts.sourceBounds) {
617
+ const exitInfo = exitOnNearestSide(opts.sourceBounds, end, start);
618
+ sExit = exitInfo.point;
619
+ sSide = exitInfo.side;
620
+ sStub = extrudePoint(exitInfo.point, exitInfo.side, stubLength);
621
+ sCorner = orthoBridge(start, sExit, exitInfo.side);
622
+ }
623
+ let tExit = null;
624
+ let tStub = null;
625
+ let tCorner = null;
626
+ let tSide = null;
627
+ if (opts.targetBounds) {
628
+ const enterInfo = exitOnNearestSide(opts.targetBounds, start, end);
629
+ tExit = enterInfo.point;
630
+ tSide = enterInfo.side;
631
+ tStub = extrudePoint(enterInfo.point, enterInfo.side, stubLength);
632
+ tCorner = orthoBridge(end, tExit, enterInfo.side);
633
+ }
634
+ const coreStart = sStub ?? start;
635
+ const coreEnd = tStub ?? end;
636
+ const obstacles = opts.obstacles ?? [];
637
+ const filteredObstacles = obstacles.length > 0 && (opts.sourceBounds || opts.targetBounds) ? obstacles.filter((o) => o !== opts.sourceBounds && o !== opts.targetBounds) : obstacles;
638
+ let core;
639
+ if (filteredObstacles.length === 0) {
640
+ if (sSide && tSide) {
641
+ core = smoothStepRoute(coreStart, sSide, coreEnd, tSide, stepPosition);
642
+ } else {
643
+ const snap2 = opts.snap ?? gridSize;
644
+ core = lRoute(coreStart, coreEnd, opts.preferred ?? "auto", snap2);
645
+ }
646
+ } else {
647
+ const inflate = opts.inflate ?? 0;
648
+ const maxNodes = opts.maxNodes ?? 5e3;
649
+ const aStar = aStarRoute(coreStart, coreEnd, filteredObstacles, gridSize, inflate, maxNodes);
650
+ core = aStar ?? lRoute(coreStart, coreEnd, opts.preferred ?? "auto", opts.snap ?? gridSize);
651
+ }
652
+ const points = [];
653
+ pushIfDistinct(points, start);
654
+ if (sCorner && !pointsEqual(sCorner, start)) pushIfDistinct(points, sCorner);
655
+ if (sExit && !pointsEqual(sExit, points[points.length - 1])) pushIfDistinct(points, sExit);
656
+ if (sStub && !pointsEqual(sStub, points[points.length - 1])) pushIfDistinct(points, sStub);
657
+ for (let i = 0; i < core.length; i++) {
658
+ const p = core[i];
659
+ if (i === 0 && sStub && pointsEqual(p, sStub)) continue;
660
+ if (i === core.length - 1 && tStub && pointsEqual(p, tStub)) continue;
661
+ pushIfDistinct(points, p);
662
+ }
663
+ if (tStub && !pointsEqual(tStub, points[points.length - 1])) pushIfDistinct(points, tStub);
664
+ if (tExit && !pointsEqual(tExit, points[points.length - 1])) pushIfDistinct(points, tExit);
665
+ if (tCorner && !pointsEqual(tCorner, points[points.length - 1])) pushIfDistinct(points, tCorner);
666
+ if (!pointsEqual(end, points[points.length - 1])) pushIfDistinct(points, end);
667
+ return compressCollinear(points);
668
+ }
669
+ function smoothStepRoute(coreStart, sSide, coreEnd, tSide, stepPosition) {
670
+ const [sx, sy] = coreStart;
671
+ const [tx, ty] = coreEnd;
672
+ const sAxis = sideAxis(sSide);
673
+ const tAxis = sideAxis(tSide);
674
+ const sDir = sideDir(sSide);
675
+ const tDir = sideDir(tSide);
676
+ if (sAxis === tAxis) {
677
+ if (sDir + tDir === 0) {
678
+ if (sAxis === "x") {
679
+ const cx = sx + (tx - sx) * stepPosition;
680
+ return [
681
+ [sx, sy],
682
+ [cx, sy],
683
+ [cx, ty],
684
+ [tx, ty]
685
+ ];
686
+ }
687
+ const cy = sy + (ty - sy) * stepPosition;
688
+ return [
689
+ [sx, sy],
690
+ [sx, cy],
691
+ [tx, cy],
692
+ [tx, ty]
693
+ ];
694
+ }
695
+ if (sAxis === "x") {
696
+ const extX = sDir > 0 ? Math.max(sx, tx) : Math.min(sx, tx);
697
+ return [
698
+ [sx, sy],
699
+ [extX, sy],
700
+ [extX, ty],
701
+ [tx, ty]
702
+ ];
703
+ }
704
+ const extY = sDir > 0 ? Math.max(sy, ty) : Math.min(sy, ty);
705
+ return [
706
+ [sx, sy],
707
+ [sx, extY],
708
+ [tx, extY],
709
+ [tx, ty]
710
+ ];
711
+ }
712
+ if (sAxis === "x") {
713
+ return [
714
+ [sx, sy],
715
+ [tx, sy],
716
+ [tx, ty]
717
+ ];
718
+ }
719
+ return [
720
+ [sx, sy],
721
+ [sx, ty],
722
+ [tx, ty]
723
+ ];
724
+ }
725
+ function sideAxis(s) {
726
+ return s === "left" || s === "right" ? "x" : "y";
727
+ }
728
+ function sideDir(s) {
729
+ return s === "right" || s === "bottom" ? 1 : -1;
730
+ }
731
+ function pointsToPath(points) {
732
+ if (points.length === 0) return "";
733
+ const [head, ...rest] = points;
734
+ return `M ${head[0]} ${head[1]} ${rest.map((p) => `L ${p[0]} ${p[1]}`).join(" ")}`;
735
+ }
736
+ function pointsToRoundedPath(points, radius) {
737
+ if (points.length === 0) return "";
738
+ if (points.length < 3 || radius <= 0) return pointsToPath(points);
739
+ let d = `M ${points[0][0]} ${points[0][1]}`;
740
+ for (let i = 1; i < points.length - 1; i++) {
741
+ const a = points[i - 1];
742
+ const b = points[i];
743
+ const c = points[i + 1];
744
+ const lenAB = Math.hypot(b[0] - a[0], b[1] - a[1]);
745
+ const lenBC = Math.hypot(c[0] - b[0], c[1] - b[1]);
746
+ const r = Math.min(radius, lenAB / 2, lenBC / 2);
747
+ const ux = (b[0] - a[0]) / (lenAB || 1);
748
+ const uy = (b[1] - a[1]) / (lenAB || 1);
749
+ const vx = (c[0] - b[0]) / (lenBC || 1);
750
+ const vy = (c[1] - b[1]) / (lenBC || 1);
751
+ const cross = ux * vy - uy * vx;
752
+ if (r < 0.5 || Math.abs(cross) < 1e-9) {
753
+ d += ` L ${b[0]} ${b[1]}`;
754
+ continue;
755
+ }
756
+ const p1x = b[0] - ux * r;
757
+ const p1y = b[1] - uy * r;
758
+ const p2x = b[0] + vx * r;
759
+ const p2y = b[1] + vy * r;
760
+ d += ` L ${p1x} ${p1y} Q ${b[0]} ${b[1]} ${p2x} ${p2y}`;
761
+ }
762
+ const tail = points[points.length - 1];
763
+ d += ` L ${tail[0]} ${tail[1]}`;
764
+ return d;
765
+ }
766
+ function controlOffset(distance, curvature) {
767
+ if (distance >= 0) return 0.5 * distance;
768
+ return curvature * 25 * Math.sqrt(-distance);
769
+ }
770
+ function controlPoint(side, x1, y1, x2, y2, curvature) {
771
+ switch (side) {
772
+ case "left":
773
+ return [x1 - controlOffset(x1 - x2, curvature), y1];
774
+ case "right":
775
+ return [x1 + controlOffset(x2 - x1, curvature), y1];
776
+ case "top":
777
+ return [x1, y1 - controlOffset(y1 - y2, curvature)];
778
+ case "bottom":
779
+ return [x1, y1 + controlOffset(y2 - y1, curvature)];
780
+ }
781
+ }
782
+ function getBezierPath(opts) {
783
+ const curvature = opts.curvature ?? 0.25;
784
+ const [sx, sy] = opts.source;
785
+ const [tx, ty] = opts.target;
786
+ const [c1x, c1y] = controlPoint(opts.sourceSide, sx, sy, tx, ty, curvature);
787
+ const [c2x, c2y] = controlPoint(opts.targetSide, tx, ty, sx, sy, curvature);
788
+ return `M ${sx} ${sy} C ${c1x} ${c1y} ${c2x} ${c2y} ${tx} ${ty}`;
789
+ }
790
+ function floatingAnchor(box, opposite) {
791
+ const cx = box.x + box.w / 2;
792
+ const cy = box.y + box.h / 2;
793
+ const dx = opposite[0] - cx;
794
+ const dy = opposite[1] - cy;
795
+ if (dx === 0 && dy === 0) {
796
+ return { point: [box.x + box.w, cy], side: "right" };
797
+ }
798
+ const hw = box.w / 2;
799
+ const hh = box.h / 2;
800
+ const tx = dx === 0 ? Infinity : hw / Math.abs(dx);
801
+ const ty = dy === 0 ? Infinity : hh / Math.abs(dy);
802
+ if (tx < ty) {
803
+ const sign2 = dx > 0 ? 1 : -1;
804
+ return {
805
+ point: [cx + sign2 * hw, cy + sign2 * (hw / Math.abs(dx)) * dy],
806
+ side: sign2 > 0 ? "right" : "left"
807
+ };
808
+ }
809
+ const sign = dy > 0 ? 1 : -1;
810
+ return {
811
+ point: [cx + sign * (hh / Math.abs(dy)) * dx, cy + sign * hh],
812
+ side: sign > 0 ? "bottom" : "top"
813
+ };
814
+ }
815
+ function nearestSide(box, target) {
816
+ const cx = box.x + box.w / 2;
817
+ const cy = box.y + box.h / 2;
818
+ const dx = target[0] - cx;
819
+ const dy = target[1] - cy;
820
+ if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? "right" : "left";
821
+ return dy >= 0 ? "bottom" : "top";
822
+ }
823
+ function getNodeIntersection(intersectionBox, targetCenter) {
824
+ const w = intersectionBox.w / 2;
825
+ const h = intersectionBox.h / 2;
826
+ const x2 = intersectionBox.x + w;
827
+ const y2 = intersectionBox.y + h;
828
+ if (w === 0 || h === 0) return [x2, y2];
829
+ const x1 = targetCenter[0];
830
+ const y1 = targetCenter[1];
831
+ const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
832
+ const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
833
+ const denom = Math.abs(xx1) + Math.abs(yy1);
834
+ if (denom === 0) {
835
+ return [intersectionBox.x + intersectionBox.w, y2];
836
+ }
837
+ const a = 1 / denom;
838
+ const xx3 = a * xx1;
839
+ const yy3 = a * yy1;
840
+ const x = w * (xx3 + yy3) + x2;
841
+ const y = h * (-xx3 + yy3) + y2;
842
+ return [x, y];
843
+ }
844
+ function getEdgePosition(box, intersectionPoint) {
845
+ const nx = Math.round(box.x);
846
+ const ny = Math.round(box.y);
847
+ const px = Math.round(intersectionPoint[0]);
848
+ const py = Math.round(intersectionPoint[1]);
849
+ if (px <= nx + 1) return "left";
850
+ if (px >= nx + box.w - 1) return "right";
851
+ if (py <= ny + 1) return "top";
852
+ if (py >= ny + box.h - 1) return "bottom";
853
+ return "top";
854
+ }
855
+ function resolveEdgeEndpoint(sourceBox, targetBox, padding = 0) {
856
+ const targetCenter = [targetBox.x + targetBox.w / 2, targetBox.y + targetBox.h / 2];
857
+ const raw = getNodeIntersection(sourceBox, targetCenter);
858
+ const side = getEdgePosition(sourceBox, raw);
859
+ if (padding === 0) return { point: raw, side };
860
+ let point = raw;
861
+ switch (side) {
862
+ case "right":
863
+ point = [raw[0] + padding, raw[1]];
864
+ break;
865
+ case "left":
866
+ point = [raw[0] - padding, raw[1]];
867
+ break;
868
+ case "bottom":
869
+ point = [raw[0], raw[1] + padding];
870
+ break;
871
+ case "top":
872
+ point = [raw[0], raw[1] - padding];
873
+ break;
874
+ }
875
+ return { point, side };
876
+ }
877
+ function inferSide(box, anchor, opposite, tolerance = 1) {
878
+ const onLeft = Math.abs(anchor[0] - box.x) <= tolerance;
879
+ const onRight = Math.abs(anchor[0] - (box.x + box.w)) <= tolerance;
880
+ const onTop = Math.abs(anchor[1] - box.y) <= tolerance;
881
+ const onBottom = Math.abs(anchor[1] - (box.y + box.h)) <= tolerance;
882
+ const horizontalHit = onLeft || onRight;
883
+ const verticalHit = onTop || onBottom;
884
+ if (horizontalHit && verticalHit) {
885
+ const cx = box.x + box.w / 2;
886
+ const cy = box.y + box.h / 2;
887
+ const dx = opposite[0] - cx;
888
+ const dy = opposite[1] - cy;
889
+ if (Math.abs(dx) >= Math.abs(dy)) {
890
+ return { side: onRight ? "right" : "left", confident: true };
891
+ }
892
+ return { side: onBottom ? "bottom" : "top", confident: true };
893
+ }
894
+ if (onLeft) return { side: "left", confident: true };
895
+ if (onRight) return { side: "right", confident: true };
896
+ if (onTop) return { side: "top", confident: true };
897
+ if (onBottom) return { side: "bottom", confident: true };
898
+ return { side: nearestSide(box, opposite), confident: false };
899
+ }
900
+ function exitOnNearestSide(box, target, anchor) {
901
+ const cx = box.x + box.w / 2;
902
+ const cy = box.y + box.h / 2;
903
+ if (anchor) {
904
+ const tolerance = 1;
905
+ const onLeft = Math.abs(anchor[0] - box.x) <= tolerance;
906
+ const onRight = Math.abs(anchor[0] - (box.x + box.w)) <= tolerance;
907
+ const onTop = Math.abs(anchor[1] - box.y) <= tolerance;
908
+ const onBottom = Math.abs(anchor[1] - (box.y + box.h)) <= tolerance;
909
+ const horizontalHit = onLeft || onRight;
910
+ const verticalHit = onTop || onBottom;
911
+ if (horizontalHit && verticalHit) {
912
+ const dx2 = target[0] - cx;
913
+ const dy2 = target[1] - cy;
914
+ if (Math.abs(dx2) >= Math.abs(dy2)) {
915
+ return onRight ? { point: [box.x + box.w, cy], side: "right" } : { point: [box.x, cy], side: "left" };
916
+ }
917
+ return onBottom ? { point: [cx, box.y + box.h], side: "bottom" } : { point: [cx, box.y], side: "top" };
918
+ }
919
+ if (onRight) return { point: [box.x + box.w, cy], side: "right" };
920
+ if (onLeft) return { point: [box.x, cy], side: "left" };
921
+ if (onBottom) return { point: [cx, box.y + box.h], side: "bottom" };
922
+ if (onTop) return { point: [cx, box.y], side: "top" };
923
+ }
924
+ const dx = target[0] - cx;
925
+ const dy = target[1] - cy;
926
+ if (Math.abs(dx) >= Math.abs(dy)) {
927
+ if (dx >= 0) return { point: [box.x + box.w, cy], side: "right" };
928
+ return { point: [box.x, cy], side: "left" };
929
+ }
930
+ if (dy >= 0) return { point: [cx, box.y + box.h], side: "bottom" };
931
+ return { point: [cx, box.y], side: "top" };
932
+ }
933
+ function extrudePoint(point, side, distance) {
934
+ switch (side) {
935
+ case "top":
936
+ return [point[0], point[1] - distance];
937
+ case "right":
938
+ return [point[0] + distance, point[1]];
939
+ case "bottom":
940
+ return [point[0], point[1] + distance];
941
+ case "left":
942
+ return [point[0] - distance, point[1]];
943
+ }
944
+ }
945
+ function orthoBridge(start, exit, side) {
946
+ switch (side) {
947
+ case "right":
948
+ case "left":
949
+ return [exit[0], start[1]];
950
+ case "top":
951
+ case "bottom":
952
+ return [start[0], exit[1]];
953
+ }
954
+ }
955
+ function pointsEqual(a, b) {
956
+ return a[0] === b[0] && a[1] === b[1];
957
+ }
958
+ function pushIfDistinct(arr, p) {
959
+ if (arr.length === 0) {
960
+ arr.push(p);
961
+ return;
962
+ }
963
+ const last = arr[arr.length - 1];
964
+ if (!pointsEqual(last, p)) arr.push(p);
965
+ }
966
+ function compressCollinear(points) {
967
+ if (points.length <= 2) return points.slice();
968
+ const out = [points[0], points[1]];
969
+ for (let i = 2; i < points.length; i++) {
970
+ const cur = points[i];
971
+ const a = out[out.length - 2];
972
+ const b = out[out.length - 1];
973
+ const dx1 = b[0] - a[0];
974
+ const dy1 = b[1] - a[1];
975
+ const dx2 = cur[0] - b[0];
976
+ const dy2 = cur[1] - b[1];
977
+ const cross = dx1 * dy2 - dy1 * dx2;
978
+ if (cross === 0 && Math.sign(dx1) === Math.sign(dx2) && Math.sign(dy1) === Math.sign(dy2)) {
979
+ out[out.length - 1] = cur;
980
+ } else {
981
+ out.push(cur);
982
+ }
983
+ }
984
+ return out;
985
+ }
986
+ function lRoute(start, end, preferred, _snapStep) {
987
+ const [sx, sy] = start;
988
+ const [ex, ey] = end;
989
+ if (sx === ex || sy === ey) return [start, end];
990
+ const dx = Math.abs(ex - sx);
991
+ const dy = Math.abs(ey - sy);
992
+ const useHV = preferred === "hv" ? true : preferred === "vh" ? false : dx >= dy;
993
+ if (useHV) return [start, [ex, sy], end];
994
+ return [start, [sx, ey], end];
995
+ }
996
+ function inflateObstacles(obstacles, pad) {
997
+ return obstacles.map((o) => ({
998
+ x0: o.x - pad,
999
+ y0: o.y - pad,
1000
+ x1: o.x + o.w + pad,
1001
+ y1: o.y + o.h + pad
1002
+ }));
1003
+ }
1004
+ function pointBlocked(x, y, rects) {
1005
+ for (const r of rects) {
1006
+ if (x > r.x0 && x < r.x1 && y > r.y0 && y < r.y1) return true;
1007
+ }
1008
+ return false;
1009
+ }
1010
+ function snap(value, step) {
1011
+ return Math.round(value / step) * step;
1012
+ }
1013
+ function key(x, y) {
1014
+ return `${x},${y}`;
1015
+ }
1016
+ function manhattan(ax, ay, bx, by) {
1017
+ return Math.abs(ax - bx) + Math.abs(ay - by);
1018
+ }
1019
+ function reconstruct(cameFrom, endKey) {
1020
+ const reversed = [];
1021
+ let cursor = endKey;
1022
+ while (cursor !== void 0) {
1023
+ const [xs, ys] = cursor.split(",");
1024
+ reversed.push([Number(xs), Number(ys)]);
1025
+ cursor = cameFrom.get(cursor);
1026
+ }
1027
+ reversed.reverse();
1028
+ const out = [];
1029
+ for (let i = 0; i < reversed.length; i++) {
1030
+ const cur = reversed[i];
1031
+ if (out.length < 2) {
1032
+ out.push(cur);
1033
+ continue;
1034
+ }
1035
+ const a = out[out.length - 2];
1036
+ const b = out[out.length - 1];
1037
+ const dx1 = b[0] - a[0];
1038
+ const dy1 = b[1] - a[1];
1039
+ const dx2 = cur[0] - b[0];
1040
+ const dy2 = cur[1] - b[1];
1041
+ if (Math.sign(dx1) === Math.sign(dx2) && Math.sign(dy1) === Math.sign(dy2)) {
1042
+ out[out.length - 1] = cur;
1043
+ } else {
1044
+ out.push(cur);
1045
+ }
1046
+ }
1047
+ return out;
1048
+ }
1049
+ var MinHeap = class {
1050
+ data = [];
1051
+ get size() {
1052
+ return this.data.length;
1053
+ }
1054
+ push(entry) {
1055
+ this.data.push(entry);
1056
+ this.bubbleUp(this.data.length - 1);
1057
+ }
1058
+ pop() {
1059
+ if (this.data.length === 0) return void 0;
1060
+ const head = this.data[0];
1061
+ const tail = this.data.pop();
1062
+ if (this.data.length > 0) {
1063
+ this.data[0] = tail;
1064
+ this.bubbleDown(0);
1065
+ }
1066
+ return head;
1067
+ }
1068
+ bubbleUp(i) {
1069
+ while (i > 0) {
1070
+ const parent = i - 1 >> 1;
1071
+ if (this.data[i].f < this.data[parent].f) {
1072
+ const tmp = this.data[i];
1073
+ this.data[i] = this.data[parent];
1074
+ this.data[parent] = tmp;
1075
+ i = parent;
1076
+ } else {
1077
+ return;
1078
+ }
1079
+ }
1080
+ }
1081
+ bubbleDown(i) {
1082
+ const n = this.data.length;
1083
+ while (true) {
1084
+ const l = i * 2 + 1;
1085
+ const r = l + 1;
1086
+ let smallest = i;
1087
+ if (l < n && this.data[l].f < this.data[smallest].f) smallest = l;
1088
+ if (r < n && this.data[r].f < this.data[smallest].f) smallest = r;
1089
+ if (smallest === i) return;
1090
+ const tmp = this.data[i];
1091
+ this.data[i] = this.data[smallest];
1092
+ this.data[smallest] = tmp;
1093
+ i = smallest;
1094
+ }
1095
+ }
1096
+ };
1097
+ function aStarRoute(start, end, obstacles, gridSize, inflate, maxNodes) {
1098
+ const rects = inflateObstacles(obstacles, inflate);
1099
+ const sx = snap(start[0], gridSize);
1100
+ const sy = snap(start[1], gridSize);
1101
+ const ex = snap(end[0], gridSize);
1102
+ const ey = snap(end[1], gridSize);
1103
+ if (pointBlocked(sx, sy, rects) || pointBlocked(ex, ey, rects)) return null;
1104
+ const startKey = key(sx, sy);
1105
+ const endKey = key(ex, ey);
1106
+ const gScore = /* @__PURE__ */ new Map([[startKey, 0]]);
1107
+ const cameFrom = /* @__PURE__ */ new Map();
1108
+ const cameDir = /* @__PURE__ */ new Map([[startKey, null]]);
1109
+ const open = new MinHeap();
1110
+ open.push({ k: startKey, x: sx, y: sy, f: manhattan(sx, sy, ex, ey) });
1111
+ const TURN_PENALTY = gridSize;
1112
+ let expansions = 0;
1113
+ while (open.size > 0) {
1114
+ if (expansions++ > maxNodes) return null;
1115
+ const current = open.pop();
1116
+ const bestG = gScore.get(current.k) ?? Infinity;
1117
+ const expectedF = bestG + manhattan(current.x, current.y, ex, ey);
1118
+ if (current.f > expectedF) continue;
1119
+ if (current.k === endKey) {
1120
+ const path = reconstruct(cameFrom, endKey);
1121
+ if (path.length > 0) {
1122
+ path[0] = start;
1123
+ path[path.length - 1] = end;
1124
+ }
1125
+ return path;
1126
+ }
1127
+ const incomingDir = cameDir.get(current.k) ?? null;
1128
+ const neighbours = [
1129
+ [gridSize, 0, "h"],
1130
+ [-gridSize, 0, "h"],
1131
+ [0, gridSize, "v"],
1132
+ [0, -gridSize, "v"]
1133
+ ];
1134
+ for (const [dx, dy, dir] of neighbours) {
1135
+ const nx = current.x + dx;
1136
+ const ny = current.y + dy;
1137
+ if (pointBlocked(nx, ny, rects)) continue;
1138
+ const turn = incomingDir !== null && incomingDir !== dir;
1139
+ const tentativeG = bestG + gridSize + (turn ? TURN_PENALTY : 0);
1140
+ const nk = key(nx, ny);
1141
+ const prevG = gScore.get(nk) ?? Infinity;
1142
+ if (tentativeG < prevG) {
1143
+ cameFrom.set(nk, current.k);
1144
+ cameDir.set(nk, dir);
1145
+ gScore.set(nk, tentativeG);
1146
+ const f = tentativeG + manhattan(nx, ny, ex, ey);
1147
+ open.push({ k: nk, x: nx, y: ny, f });
1148
+ }
1149
+ }
1150
+ }
1151
+ return null;
1152
+ }
1153
+
1154
+ // src/engines/graph/obb.ts
1155
+ function obbCorners(obb) {
1156
+ const cos = Math.cos(obb.angle);
1157
+ const sin = Math.sin(obb.angle);
1158
+ const { halfWidth: hw, halfHeight: hh } = obb;
1159
+ const [cx, cy] = obb.center;
1160
+ const lx = [-hw, hw, hw, -hw];
1161
+ const ly = [-hh, -hh, hh, hh];
1162
+ const out = [];
1163
+ for (let i = 0; i < 4; i++) {
1164
+ const x = lx[i];
1165
+ const y = ly[i];
1166
+ out.push([cx + x * cos - y * sin, cy + x * sin + y * cos]);
1167
+ }
1168
+ return out;
1169
+ }
1170
+ function aabbFromOBB(obb) {
1171
+ const corners = obbCorners(obb);
1172
+ let x0 = corners[0][0];
1173
+ let x1 = corners[0][0];
1174
+ let y0 = corners[0][1];
1175
+ let y1 = corners[0][1];
1176
+ for (let i = 1; i < 4; i++) {
1177
+ const [x, y] = corners[i];
1178
+ if (x < x0) x0 = x;
1179
+ if (x > x1) x1 = x;
1180
+ if (y < y0) y0 = y;
1181
+ if (y > y1) y1 = y;
1182
+ }
1183
+ return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
1184
+ }
1185
+ function obbContainsPoint(obb, p) {
1186
+ const cos = Math.cos(-obb.angle);
1187
+ const sin = Math.sin(-obb.angle);
1188
+ const dx = p[0] - obb.center[0];
1189
+ const dy = p[1] - obb.center[1];
1190
+ const lx = dx * cos - dy * sin;
1191
+ const ly = dx * sin + dy * cos;
1192
+ return Math.abs(lx) <= obb.halfWidth && Math.abs(ly) <= obb.halfHeight;
1193
+ }
1194
+
1195
+ export {
1196
+ createGraph,
1197
+ routeOrthogonal,
1198
+ pointsToPath,
1199
+ pointsToRoundedPath,
1200
+ getBezierPath,
1201
+ floatingAnchor,
1202
+ nearestSide,
1203
+ getNodeIntersection,
1204
+ getEdgePosition,
1205
+ resolveEdgeEndpoint,
1206
+ inferSide,
1207
+ obbCorners,
1208
+ aabbFromOBB,
1209
+ obbContainsPoint
1210
+ };
1211
+ //# sourceMappingURL=chunk-7FA2TBSZ.js.map