avoid-nodes-edge 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,558 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
- var _chunkTO34Q3IDcjs = require('./chunk-TO34Q3ID.cjs');
11
-
12
- // src/router.ts
13
- var LIBAVOID_WASM_URL = "/libavoid.wasm";
14
- var WASM_RETRY_DELAY_MS = 2e3;
15
- var WASM_MAX_RETRIES = 5;
16
- var _AvoidRouter = class _AvoidRouter {
17
- // Loads the libavoid WASM module with retry logic.
18
- // Retries up to WASM_MAX_RETRIES times with a delay between attempts.
19
- // Returns true if the library loaded successfully, false otherwise.
20
- static async load(wasmUrl = LIBAVOID_WASM_URL) {
21
- if (_AvoidRouter.lib != null) return true;
22
- if (typeof globalThis === "undefined") return false;
23
- for (let attempt = 1; attempt <= WASM_MAX_RETRIES; attempt++) {
24
- const ok = await _AvoidRouter.loadOnce(wasmUrl);
25
- if (ok) return true;
26
- if (attempt < WASM_MAX_RETRIES) {
27
- await new Promise((r) => setTimeout(r, WASM_RETRY_DELAY_MS));
28
- }
29
- }
30
- return false;
31
- }
32
- // Single attempt to load the WASM module.
33
- // Resolves the WASM URL to an absolute path, dynamically imports libavoid-js,
34
- // calls its load() to initialize the WASM, then stores the library instance.
35
- static async loadOnce(wasmUrl) {
36
- const origin = _optionalChain([globalThis, 'access', _ => _.location, 'optionalAccess', _2 => _2.origin]);
37
- const absoluteWasmUrl = origin && wasmUrl.startsWith("/") ? `${origin}${wasmUrl}` : wasmUrl;
38
- try {
39
- const mod = await Promise.resolve().then(() => _interopRequireWildcard(require("libavoid-js")));
40
- const AvoidLib = _nullishCoalesce(mod.AvoidLib, () => ( mod.default));
41
- if (!_optionalChain([AvoidLib, 'optionalAccess', _3 => _3.load])) return false;
42
- await AvoidLib.load(absoluteWasmUrl);
43
- const lib = _optionalChain([AvoidLib, 'access', _4 => _4.getInstance, 'optionalCall', _5 => _5()]);
44
- if (lib == null) return false;
45
- _AvoidRouter.lib = lib;
46
- return true;
47
- } catch (e2) {
48
- return false;
49
- }
50
- }
51
- // Returns the singleton AvoidRouter instance.
52
- // Throws if the WASM library hasn't been loaded yet via load().
53
- static getInstance() {
54
- if (_AvoidRouter.instance == null) _AvoidRouter.instance = new _AvoidRouter();
55
- if (_AvoidRouter.lib == null) throw new Error("AvoidRouter.load() must be called first.");
56
- return _AvoidRouter.instance;
57
- }
58
- // Main routing method — takes all nodes and edges, computes obstacle-avoiding
59
- // orthogonal paths for every edge, and returns a map of edgeId -> AvoidRoute.
60
- //
61
- // Steps:
62
- // 1. Filter out group nodes (only real nodes are obstacles)
63
- // 2. Create a libavoid Router and configure its parameters
64
- // 3. Register each node as a rectangular obstacle shape with connection pins
65
- // 4. Create a connector (ConnRef) for each edge between source/target pins
66
- // 5. Run the routing algorithm (processTransaction)
67
- // 6. Extract the routed polyline for each edge and convert to SVG path
68
- // 7. Clean up all libavoid objects to free WASM memory
69
- routeAll(nodes, edges, options) {
70
- const Avoid = _AvoidRouter.lib;
71
- if (!Avoid) return {};
72
- const shapeBuffer = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _6 => _6.shapeBufferDistance]), () => ( 8));
73
- const idealNudging = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _7 => _7.idealNudgingDistance]), () => ( 10));
74
- const cornerRadius = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _8 => _8.edgeRounding]), () => ( 0));
75
- const gridSize = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _9 => _9.diagramGridSize]), () => ( 0));
76
- const obstacleNodes = nodes.filter((n) => n.type !== "group");
77
- const nodeById = new Map(nodes.map((n) => [n.id, n]));
78
- const nodeBounds = new Map(obstacleNodes.map((n) => [n.id, this.getNodeBoundsAbsolute(n, nodeById)]));
79
- const router = new Avoid.Router(Avoid.OrthogonalRouting);
80
- router.setRoutingParameter(Avoid.shapeBufferDistance, shapeBuffer);
81
- router.setRoutingParameter(Avoid.idealNudgingDistance, idealNudging);
82
- router.setRoutingOption(Avoid.nudgeOrthogonalSegmentsConnectedToShapes, true);
83
- router.setRoutingOption(Avoid.nudgeSharedPathsWithCommonEndPoint, true);
84
- router.setRoutingOption(Avoid.performUnifyingNudgingPreprocessingStep, true);
85
- const PIN_CENTER = 1;
86
- const PIN_TOP = 2;
87
- const PIN_BOTTOM = 3;
88
- const PIN_LEFT = 4;
89
- const PIN_RIGHT = 5;
90
- const pinIdForPosition = {
91
- top: PIN_TOP,
92
- bottom: PIN_BOTTOM,
93
- left: PIN_LEFT,
94
- right: PIN_RIGHT
95
- };
96
- const pinProportions = {
97
- [PIN_CENTER]: { x: 0.5, y: 0.5, dir: Avoid.ConnDirAll },
98
- [PIN_TOP]: { x: 0.5, y: 0, dir: Avoid.ConnDirUp },
99
- [PIN_BOTTOM]: { x: 0.5, y: 1, dir: Avoid.ConnDirDown },
100
- [PIN_LEFT]: { x: 0, y: 0.5, dir: Avoid.ConnDirLeft },
101
- [PIN_RIGHT]: { x: 1, y: 0.5, dir: Avoid.ConnDirRight }
102
- };
103
- const shapeRefMap = /* @__PURE__ */ new Map();
104
- const shapeRefs = [];
105
- for (const node of obstacleNodes) {
106
- const b = nodeBounds.get(node.id);
107
- const topLeft = new Avoid.Point(b.x, b.y);
108
- const bottomRight = new Avoid.Point(b.x + b.w, b.y + b.h);
109
- const rect = new Avoid.Rectangle(topLeft, bottomRight);
110
- const shapeRef = new Avoid.ShapeRef(router, rect);
111
- shapeRefs.push({ ref: shapeRef });
112
- shapeRefMap.set(node.id, shapeRef);
113
- for (const pinId of [PIN_CENTER, PIN_TOP, PIN_BOTTOM, PIN_LEFT, PIN_RIGHT]) {
114
- const p = pinProportions[pinId];
115
- const pin = new Avoid.ShapeConnectionPin(shapeRef, pinId, p.x, p.y, true, 0, p.dir);
116
- pin.setExclusive(false);
117
- }
118
- }
119
- const connRefs = [];
120
- for (const edge of edges) {
121
- const src = nodeById.get(edge.source);
122
- const tgt = nodeById.get(edge.target);
123
- if (!src || !tgt) continue;
124
- const srcShapeRef = shapeRefMap.get(edge.source);
125
- const tgtShapeRef = shapeRefMap.get(edge.target);
126
- const sourcePos = this.getHandlePosition(src, "source");
127
- const targetPos = this.getHandlePosition(tgt, "target");
128
- let srcEnd;
129
- let tgtEnd;
130
- if (srcShapeRef) {
131
- const pinId = _nullishCoalesce(pinIdForPosition[sourcePos], () => ( PIN_CENTER));
132
- srcEnd = new Avoid.ConnEnd(srcShapeRef, pinId);
133
- } else {
134
- const sb = this.getNodeBoundsAbsolute(src, nodeById);
135
- const sourcePt = this.getHandlePoint(sb, sourcePos);
136
- srcEnd = new Avoid.ConnEnd(new Avoid.Point(sourcePt.x, sourcePt.y));
137
- }
138
- if (tgtShapeRef) {
139
- const pinId = _nullishCoalesce(pinIdForPosition[targetPos], () => ( PIN_CENTER));
140
- tgtEnd = new Avoid.ConnEnd(tgtShapeRef, pinId);
141
- } else {
142
- const tb = this.getNodeBoundsAbsolute(tgt, nodeById);
143
- const targetPt = this.getHandlePoint(tb, targetPos);
144
- tgtEnd = new Avoid.ConnEnd(new Avoid.Point(targetPt.x, targetPt.y));
145
- }
146
- const connRef = new Avoid.ConnRef(router, srcEnd, tgtEnd);
147
- connRef.setRoutingType(Avoid.ConnType_Orthogonal);
148
- connRefs.push({ edgeId: edge.id, connRef });
149
- }
150
- try {
151
- router.processTransaction();
152
- } catch (e3) {
153
- this.cleanup(router, connRefs, shapeRefs);
154
- return {};
155
- }
156
- const result = {};
157
- for (const { edgeId, connRef } of connRefs) {
158
- try {
159
- const route = connRef.displayRoute();
160
- const size = route.size();
161
- if (size < 2) continue;
162
- const path = this.polylineToPath(size, (i) => {
163
- const p = route.get_ps(i);
164
- return { x: p.x, y: p.y };
165
- }, { gridSize: gridSize || void 0, cornerRadius });
166
- const mid = Math.floor(size / 2);
167
- const midP = route.get_ps(mid);
168
- const labelP = gridSize > 0 ? this.snapToGrid(midP.x, midP.y, gridSize) : { x: midP.x, y: midP.y };
169
- result[edgeId] = { path, labelX: labelP.x, labelY: labelP.y };
170
- } catch (e4) {
171
- }
172
- }
173
- this.cleanup(router, connRefs, shapeRefs);
174
- return result;
175
- }
176
- // Gets the local position and dimensions of a node.
177
- // Tries measured size first, then explicit width/height, then style, with fallback defaults.
178
- getNodeBounds(node) {
179
- const x = _nullishCoalesce(_optionalChain([node, 'access', _10 => _10.position, 'optionalAccess', _11 => _11.x]), () => ( 0));
180
- const y = _nullishCoalesce(_optionalChain([node, 'access', _12 => _12.position, 'optionalAccess', _13 => _13.y]), () => ( 0));
181
- const w = Number(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([node, 'access', _14 => _14.measured, 'optionalAccess', _15 => _15.width]), () => ( node.width)), () => ( _optionalChain([node, 'access', _16 => _16.style, 'optionalAccess', _17 => _17.width]))), () => ( 150)));
182
- const h = Number(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([node, 'access', _18 => _18.measured, 'optionalAccess', _19 => _19.height]), () => ( node.height)), () => ( _optionalChain([node, 'access', _20 => _20.style, 'optionalAccess', _21 => _21.height]))), () => ( 50)));
183
- return { x, y, w, h };
184
- }
185
- // Computes the absolute (world-space) bounds of a node by walking up
186
- // the parent chain and accumulating parent positions. This is needed
187
- // because child nodes have positions relative to their parent.
188
- getNodeBoundsAbsolute(node, nodeById) {
189
- const b = this.getNodeBounds(node);
190
- let current = node;
191
- while (_optionalChain([current, 'optionalAccess', _22 => _22.parentId])) {
192
- const parent = nodeById.get(current.parentId);
193
- if (!parent) break;
194
- b.x += _nullishCoalesce(_optionalChain([parent, 'access', _23 => _23.position, 'optionalAccess', _24 => _24.x]), () => ( 0));
195
- b.y += _nullishCoalesce(_optionalChain([parent, 'access', _25 => _25.position, 'optionalAccess', _26 => _26.y]), () => ( 0));
196
- current = parent;
197
- }
198
- return b;
199
- }
200
- // Determines which side of a node a handle is on (left/right/top/bottom).
201
- // Checks both the node's direct properties and its data object.
202
- // Defaults to "right" for source handles and "left" for target handles.
203
- getHandlePosition(node, kind) {
204
- const raw = kind === "source" ? _nullishCoalesce(node.sourcePosition, () => ( _optionalChain([node, 'access', _27 => _27.data, 'optionalAccess', _28 => _28.sourcePosition]))) : _nullishCoalesce(node.targetPosition, () => ( _optionalChain([node, 'access', _29 => _29.data, 'optionalAccess', _30 => _30.targetPosition])));
205
- const s = String(_nullishCoalesce(raw, () => ( ""))).toLowerCase();
206
- if (s === "left" || s === "right" || s === "top" || s === "bottom") return s;
207
- return kind === "source" ? "right" : "left";
208
- }
209
- // Converts a handle position (left/right/top/bottom) into an actual x/y coordinate
210
- // on the node's boundary. The point is at the center of the respective edge.
211
- getHandlePoint(bounds, position) {
212
- const { x, y, w, h } = bounds;
213
- const cx = x + w / 2;
214
- const cy = y + h / 2;
215
- switch (position) {
216
- case "left":
217
- return { x, y: cy };
218
- case "right":
219
- return { x: x + w, y: cy };
220
- case "top":
221
- return { x: cx, y };
222
- case "bottom":
223
- return { x: cx, y: y + h };
224
- default:
225
- return { x: x + w, y: cy };
226
- }
227
- }
228
- // Snaps x/y coordinates to the nearest grid point (rounds to nearest multiple of gridSize)
229
- snapToGrid(x, y, gridSize) {
230
- if (gridSize <= 0) return { x, y };
231
- return { x: Math.round(x / gridSize) * gridSize, y: Math.round(y / gridSize) * gridSize };
232
- }
233
- // Converts a polyline (series of waypoints) into an SVG path string.
234
- // If cornerRadius > 0, adds quadratic bezier curves (Q commands) at each bend
235
- // to create smooth rounded corners instead of sharp right angles.
236
- // If cornerRadius is 0, produces a simple M/L path with straight segments.
237
- polylineToPath(size, getPoint, options = {}) {
238
- if (size < 2) return "";
239
- const gridSize = _nullishCoalesce(options.gridSize, () => ( 0));
240
- const r = Math.max(0, _nullishCoalesce(options.cornerRadius, () => ( 0)));
241
- const pt = (i) => {
242
- const p = getPoint(i);
243
- return gridSize > 0 ? this.snapToGrid(p.x, p.y, gridSize) : p;
244
- };
245
- if (r <= 0) {
246
- let d2 = `M ${pt(0).x} ${pt(0).y}`;
247
- for (let i = 1; i < size; i++) {
248
- const p = pt(i);
249
- d2 += ` L ${p.x} ${p.y}`;
250
- }
251
- return d2;
252
- }
253
- const dist = (a, b) => Math.hypot(b.x - a.x, b.y - a.y);
254
- const unit = (a, b) => {
255
- const d2 = dist(a, b);
256
- if (d2 < 1e-6) return { x: 0, y: 0 };
257
- return { x: (b.x - a.x) / d2, y: (b.y - a.y) / d2 };
258
- };
259
- let d = `M ${pt(0).x} ${pt(0).y}`;
260
- for (let i = 1; i < size - 1; i++) {
261
- const prev = pt(i - 1);
262
- const curr = pt(i);
263
- const next = pt(i + 1);
264
- const dirIn = unit(curr, prev);
265
- const dirOut = unit(curr, next);
266
- const lenIn = dist(curr, prev);
267
- const lenOut = dist(curr, next);
268
- const rIn = Math.min(r, lenIn / 2, lenOut / 2);
269
- const rOut = Math.min(r, lenIn / 2, lenOut / 2);
270
- const endPrev = { x: curr.x + dirIn.x * rIn, y: curr.y + dirIn.y * rIn };
271
- const startNext = { x: curr.x + dirOut.x * rOut, y: curr.y + dirOut.y * rOut };
272
- d += ` L ${endPrev.x} ${endPrev.y} Q ${curr.x} ${curr.y} ${startNext.x} ${startNext.y}`;
273
- }
274
- const last = pt(size - 1);
275
- d += ` L ${last.x} ${last.y}`;
276
- return d;
277
- }
278
- // Frees all libavoid objects (connectors and shapes) from the router.
279
- // This is important to prevent WASM memory leaks since libavoid
280
- // allocates memory in the WASM heap that isn't garbage collected by JS.
281
- cleanup(router, connRefs, shapeRefs) {
282
- try {
283
- for (const { connRef } of connRefs) router.deleteConnector(connRef);
284
- for (const { ref } of shapeRefs) router.deleteShape(ref);
285
- } catch (e5) {
286
- }
287
- }
288
- };
289
- // Singleton references — the WASM library and the single router instance
290
- _chunkTO34Q3IDcjs.__publicField.call(void 0, _AvoidRouter, "lib", null);
291
- _chunkTO34Q3IDcjs.__publicField.call(void 0, _AvoidRouter, "instance", null);
292
- var AvoidRouter = _AvoidRouter;
293
- async function loadAvoidRouter() {
294
- return AvoidRouter.load();
295
- }
296
- function routeAll(nodes, edges, options) {
297
- try {
298
- return AvoidRouter.getInstance().routeAll(nodes, edges, options);
299
- } catch (e6) {
300
- return {};
301
- }
302
- }
303
-
304
- // src/useAvoidNodesRouterFromWorker.ts
305
- var _react = require('react');
306
-
307
- // src/useAvoidWorker.ts
308
-
309
-
310
- // src/worker-listener.ts
311
- function attachAvoidWorkerListener(worker, options = {}) {
312
- const { onRouted, onLoaded } = options;
313
- const setLoaded = _chunkTO34Q3IDcjs.useAvoidRoutesStore.getState().setLoaded;
314
- const setRoutes = _chunkTO34Q3IDcjs.useAvoidRoutesStore.getState().setRoutes;
315
- const handler = (e) => {
316
- const msg = e.data;
317
- if (!msg || typeof msg !== "object" || !("command" in msg)) return;
318
- switch (msg.command) {
319
- case "loaded":
320
- setLoaded(msg.success);
321
- _optionalChain([onLoaded, 'optionalCall', _31 => _31(msg.success)]);
322
- break;
323
- case "routed":
324
- setRoutes(msg.routes);
325
- _optionalChain([onRouted, 'optionalCall', _32 => _32(msg.routes)]);
326
- break;
327
- default:
328
- break;
329
- }
330
- };
331
- worker.addEventListener("message", handler);
332
- return () => worker.removeEventListener("message", handler);
333
- }
334
-
335
- // src/useAvoidWorker.ts
336
- function useAvoidWorker(options) {
337
- const workerRef = _react.useRef.call(void 0, null);
338
- const [workerLoaded, setWorkerLoaded] = _react.useState.call(void 0, false);
339
- const onRoutedRef = _react.useRef.call(void 0, _optionalChain([options, 'optionalAccess', _33 => _33.onRouted]));
340
- const onLoadedRef = _react.useRef.call(void 0, _optionalChain([options, 'optionalAccess', _34 => _34.onLoaded]));
341
- onRoutedRef.current = _optionalChain([options, 'optionalAccess', _35 => _35.onRouted]);
342
- onLoadedRef.current = _optionalChain([options, 'optionalAccess', _36 => _36.onLoaded]);
343
- const createWorker = _optionalChain([options, 'optionalAccess', _37 => _37.create]) !== false;
344
- _react.useEffect.call(void 0, () => {
345
- if (!createWorker) {
346
- workerRef.current = null;
347
- setWorkerLoaded(false);
348
- return;
349
- }
350
- let worker;
351
- try {
352
- worker = new Worker(new URL("./workers/avoid-router.worker.js", import.meta.url), { type: "module" });
353
- } catch (e) {
354
- console.error("[avoid-worker] Failed to create worker:", e);
355
- return;
356
- }
357
- workerRef.current = worker;
358
- worker.addEventListener("error", (e) => {
359
- console.error("[avoid-worker] Worker error:", e.message);
360
- });
361
- const cleanup = attachAvoidWorkerListener(worker, {
362
- onRouted: (routes) => _optionalChain([onRoutedRef, 'access', _38 => _38.current, 'optionalCall', _39 => _39(routes)]),
363
- onLoaded: (success) => {
364
- setWorkerLoaded(success);
365
- _optionalChain([onLoadedRef, 'access', _40 => _40.current, 'optionalCall', _41 => _41(success)]);
366
- }
367
- });
368
- return () => {
369
- cleanup();
370
- worker.postMessage({ command: "close" });
371
- worker.terminate();
372
- workerRef.current = null;
373
- setWorkerLoaded(false);
374
- };
375
- }, [createWorker]);
376
- const post = _react.useCallback.call(void 0, (cmd) => {
377
- if (workerRef.current) {
378
- workerRef.current.postMessage(cmd);
379
- }
380
- }, []);
381
- const close = _react.useCallback.call(void 0, () => {
382
- if (workerRef.current) {
383
- workerRef.current.postMessage({ command: "close" });
384
- workerRef.current.terminate();
385
- workerRef.current = null;
386
- setWorkerLoaded(false);
387
- }
388
- }, []);
389
- return { workerLoaded, post, close };
390
- }
391
-
392
- // src/useAvoidNodesRouterFromWorker.ts
393
- var DEFAULT_OPTIONS = {
394
- edgeToEdgeSpacing: 10,
395
- edgeToNodeSpacing: 8
396
- };
397
- function toRouterOptions(opts) {
398
- return {
399
- idealNudgingDistance: _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _42 => _42.edgeToEdgeSpacing]), () => ( DEFAULT_OPTIONS.edgeToEdgeSpacing)),
400
- shapeBufferDistance: _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _43 => _43.edgeToNodeSpacing]), () => ( DEFAULT_OPTIONS.edgeToNodeSpacing)),
401
- edgeRounding: _optionalChain([opts, 'optionalAccess', _44 => _44.edgeRounding]),
402
- diagramGridSize: _optionalChain([opts, 'optionalAccess', _45 => _45.diagramGridSize]),
403
- shouldSplitEdgesNearHandle: _optionalChain([opts, 'optionalAccess', _46 => _46.shouldSplitEdgesNearHandle])
404
- };
405
- }
406
- function useAvoidNodesRouterFromWorker(nodes, edges, options) {
407
- const nodesRef = _react.useRef.call(void 0, nodes);
408
- const edgesRef = _react.useRef.call(void 0, edges);
409
- const opts = toRouterOptions(options);
410
- const optsRef = _react.useRef.call(void 0, opts);
411
- _react.useEffect.call(void 0, () => {
412
- nodesRef.current = nodes;
413
- }, [nodes]);
414
- _react.useEffect.call(void 0, () => {
415
- edgesRef.current = edges;
416
- }, [edges]);
417
- _react.useEffect.call(void 0, () => {
418
- optsRef.current = opts;
419
- });
420
- const setRoutes = _chunkTO34Q3IDcjs.useAvoidRoutesStore.call(void 0, (s) => s.setRoutes);
421
- const setActions = _chunkTO34Q3IDcjs.useAvoidRouterActionsStore.call(void 0, (s) => s.setActions);
422
- const { post, workerLoaded } = useAvoidWorker({ create: true });
423
- const didResetRef = _react.useRef.call(void 0, false);
424
- const nodesMeasuredRef = _react.useRef.call(void 0, false);
425
- const sendReset = _react.useCallback.call(void 0, () => {
426
- if (!workerLoaded) return;
427
- const nodes2 = nodesRef.current;
428
- const hasMeasured = nodes2.length === 0 || nodes2.some((n) => _optionalChain([n, 'access', _47 => _47.measured, 'optionalAccess', _48 => _48.width]) != null);
429
- if (!hasMeasured) return;
430
- nodesMeasuredRef.current = true;
431
- const avoidEdges = edgesRef.current.filter((e) => e.type === "avoidNodes");
432
- if (avoidEdges.length === 0) {
433
- setRoutes({});
434
- return;
435
- }
436
- post({
437
- command: "reset",
438
- nodes: nodes2,
439
- edges: avoidEdges,
440
- options: optsRef.current
441
- });
442
- didResetRef.current = true;
443
- }, [post, setRoutes, workerLoaded]);
444
- const sendIncrementalChanges = _react.useCallback.call(void 0,
445
- (nodeIds) => {
446
- if (!workerLoaded || !didResetRef.current) return;
447
- const nodeMap = new Map(nodesRef.current.map((n) => [n.id, n]));
448
- const changedNodes = nodeIds.map((id) => nodeMap.get(id)).filter((n) => n != null);
449
- if (changedNodes.length === 0) return;
450
- post({ command: "updateNodes", nodes: changedNodes });
451
- },
452
- [post, workerLoaded]
453
- );
454
- const debounceRef = _react.useRef.call(void 0, null);
455
- const pendingChangeIdsRef = _react.useRef.call(void 0, /* @__PURE__ */ new Set());
456
- const resetRouting = _react.useCallback.call(void 0, () => {
457
- sendReset();
458
- }, [sendReset]);
459
- const refreshRouting = _react.useCallback.call(void 0, () => {
460
- sendReset();
461
- }, [sendReset]);
462
- const updateRoutingForNodeIds = _react.useCallback.call(void 0,
463
- (nodeIds) => {
464
- sendIncrementalChanges(nodeIds);
465
- },
466
- [sendIncrementalChanges]
467
- );
468
- const updateRoutingOnNodesChange = _react.useCallback.call(void 0,
469
- (changes) => {
470
- if (!workerLoaded) return;
471
- let hasPosition = false;
472
- let hasDimensions = false;
473
- let hasAddOrRemove = false;
474
- for (const c of changes) {
475
- if (c.type === "position") {
476
- hasPosition = true;
477
- pendingChangeIdsRef.current.add(c.id);
478
- } else if (c.type === "dimensions") {
479
- hasDimensions = true;
480
- pendingChangeIdsRef.current.add(c.id);
481
- } else if (c.type === "add" || c.type === "remove") {
482
- hasAddOrRemove = true;
483
- }
484
- }
485
- if (!hasPosition && !hasDimensions && !hasAddOrRemove) return;
486
- const needsFullReset = hasAddOrRemove || hasDimensions && !nodesMeasuredRef.current;
487
- if (needsFullReset) {
488
- if (debounceRef.current) {
489
- clearTimeout(debounceRef.current);
490
- debounceRef.current = null;
491
- }
492
- pendingChangeIdsRef.current.clear();
493
- debounceRef.current = setTimeout(() => {
494
- debounceRef.current = null;
495
- requestAnimationFrame(() => sendReset());
496
- }, _chunkTO34Q3IDcjs.DEBOUNCE_ROUTING_MS);
497
- return;
498
- }
499
- if (!didResetRef.current) return;
500
- if (debounceRef.current) clearTimeout(debounceRef.current);
501
- debounceRef.current = setTimeout(() => {
502
- debounceRef.current = null;
503
- requestAnimationFrame(() => {
504
- const ids = Array.from(pendingChangeIdsRef.current);
505
- pendingChangeIdsRef.current.clear();
506
- if (ids.length > 0) {
507
- sendIncrementalChanges(ids);
508
- }
509
- });
510
- }, _chunkTO34Q3IDcjs.DEBOUNCE_ROUTING_MS);
511
- },
512
- [workerLoaded, sendReset, sendIncrementalChanges]
513
- );
514
- _react.useEffect.call(void 0, () => {
515
- setActions({
516
- resetRouting,
517
- updateRoutesForNodeId: (nodeId) => updateRoutingForNodeIds([nodeId])
518
- });
519
- return () => setActions({ resetRouting: () => {
520
- }, updateRoutesForNodeId: () => {
521
- } });
522
- }, [resetRouting, updateRoutingForNodeIds, setActions]);
523
- _react.useEffect.call(void 0, () => {
524
- if (workerLoaded) sendReset();
525
- }, [
526
- workerLoaded,
527
- nodes.length,
528
- edges.length,
529
- opts.shapeBufferDistance,
530
- opts.idealNudgingDistance,
531
- opts.edgeRounding,
532
- opts.diagramGridSize,
533
- opts.shouldSplitEdgesNearHandle,
534
- sendReset
535
- ]);
536
- return {
537
- updateRoutingOnNodesChange,
538
- resetRouting,
539
- refreshRouting,
540
- updateRoutingForNodeIds
541
- };
542
- }
543
-
544
-
545
-
546
-
547
-
548
-
549
-
550
-
551
-
552
-
553
-
554
-
555
-
556
-
557
- exports.AvoidRouter = AvoidRouter; exports.DEBOUNCE_ROUTING_MS = _chunkTO34Q3IDcjs.DEBOUNCE_ROUTING_MS; exports.DEV_LOG_WEB_WORKER_MESSAGES = _chunkTO34Q3IDcjs.DEV_LOG_WEB_WORKER_MESSAGES; exports.EDGE_BORDER_RADIUS = _chunkTO34Q3IDcjs.EDGE_BORDER_RADIUS; exports.SHOULD_START_EDGE_AT_HANDLE_BORDER = _chunkTO34Q3IDcjs.SHOULD_START_EDGE_AT_HANDLE_BORDER; exports.attachAvoidWorkerListener = attachAvoidWorkerListener; exports.loadAvoidRouter = loadAvoidRouter; exports.routeAll = routeAll; exports.useAvoidNodesPath = _chunkTO34Q3IDcjs.useAvoidNodesPath; exports.useAvoidNodesRouterFromWorker = useAvoidNodesRouterFromWorker; exports.useAvoidRouterActionsStore = _chunkTO34Q3IDcjs.useAvoidRouterActionsStore; exports.useAvoidRoutesStore = _chunkTO34Q3IDcjs.useAvoidRoutesStore; exports.useAvoidWorker = useAvoidWorker;
558
- //# sourceMappingURL=index.cjs.map
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkHK5MLBM7cjs = require('./chunk-HK5MLBM7.cjs');var ee="/libavoid.wasm";var y=class y{static async load(o=ee){if(y.lib!=null)return!0;if(typeof globalThis>"u")return!1;for(let s=1;s<=5;s++){if(await y.loadOnce(o))return!0;s<5&&await new Promise(e=>setTimeout(e,2e3))}return!1}static async loadOnce(o){let s=_optionalChain([globalThis, 'access', _2 => _2.location, 'optionalAccess', _3 => _3.origin]),t=s&&o.startsWith("/")?`${s}${o}`:o;try{let e=await Promise.resolve().then(() => _interopRequireWildcard(require("libavoid-js"))),n=_nullishCoalesce(e.AvoidLib, () => (e.default));if(!_optionalChain([n, 'optionalAccess', _4 => _4.load]))return!1;await n.load(t);let a=_optionalChain([n, 'access', _5 => _5.getInstance, 'optionalCall', _6 => _6()]);return a==null?!1:(y.lib=a,!0)}catch (e2){return!1}}static getInstance(){if(y.instance==null&&(y.instance=new y),y.lib==null)throw new Error("AvoidRouter.load() must be called first.");return y.instance}routeAll(o,s,t){let e=y.lib;if(!e)return{};let n=_nullishCoalesce(_optionalChain([t, 'optionalAccess', _7 => _7.shapeBufferDistance]), () => (8)),a=_nullishCoalesce(_optionalChain([t, 'optionalAccess', _8 => _8.idealNudgingDistance]), () => (10)),b=_nullishCoalesce(_optionalChain([t, 'optionalAccess', _9 => _9.edgeRounding]), () => (0)),c=_nullishCoalesce(_optionalChain([t, 'optionalAccess', _10 => _10.diagramGridSize]), () => (0)),p=o.filter(r=>r.type!=="group"),g=new Map(o.map(r=>[r.id,r])),i=new Map(p.map(r=>[r.id,this.getNodeBoundsAbsolute(r,g)])),u=new e.Router(e.OrthogonalRouting);u.setRoutingParameter(e.shapeBufferDistance,n),u.setRoutingParameter(e.idealNudgingDistance,a),u.setRoutingOption(e.nudgeOrthogonalSegmentsConnectedToShapes,!0),u.setRoutingOption(e.nudgeSharedPathsWithCommonEndPoint,!0),u.setRoutingOption(e.performUnifyingNudgingPreprocessingStep,!0);let d=1,w=2,R=3,A=4,k=5,T={top:w,bottom:R,left:A,right:k},P={[d]:{x:.5,y:.5,dir:e.ConnDirAll},[w]:{x:.5,y:0,dir:e.ConnDirUp},[R]:{x:.5,y:1,dir:e.ConnDirDown},[A]:{x:0,y:.5,dir:e.ConnDirLeft},[k]:{x:1,y:.5,dir:e.ConnDirRight}},E=new Map,l=[];for(let r of p){let v=i.get(r.id),f=new e.Point(v.x,v.y),O=new e.Point(v.x+v.w,v.y+v.h),L=new e.Rectangle(f,O),C=new e.ShapeRef(u,L);l.push({ref:C}),E.set(r.id,C);for(let N of[d,w,R,A,k]){let S=P[N];new e.ShapeConnectionPin(C,N,S.x,S.y,!0,0,S.dir).setExclusive(!1)}}let h=[];for(let r of s){let v=g.get(r.source),f=g.get(r.target);if(!v||!f)continue;let O=E.get(r.source),L=E.get(r.target),C=this.getHandlePosition(v,"source"),N=this.getHandlePosition(f,"target"),S,D;if(O){let W=_nullishCoalesce(T[C], () => (d));S=new e.ConnEnd(O,W)}else{let W=this.getNodeBoundsAbsolute(v,g),B=this.getHandlePoint(W,C);S=new e.ConnEnd(new e.Point(B.x,B.y))}if(L){let W=_nullishCoalesce(T[N], () => (d));D=new e.ConnEnd(L,W)}else{let W=this.getNodeBoundsAbsolute(f,g),B=this.getHandlePoint(W,N);D=new e.ConnEnd(new e.Point(B.x,B.y))}let U=new e.ConnRef(u,S,D);U.setRoutingType(e.ConnType_Orthogonal),h.push({edgeId:r.id,connRef:U})}try{u.processTransaction()}catch (e3){return this.cleanup(u,h,l),{}}let x={};for(let{edgeId:r,connRef:v}of h)try{let f=v.displayRoute(),O=f.size();if(O<2)continue;let L=this.polylineToPath(O,D=>{let U=f.get_ps(D);return{x:U.x,y:U.y}},{gridSize:c||void 0,cornerRadius:b}),C=Math.floor(O/2),N=f.get_ps(C),S=c>0?this.snapToGrid(N.x,N.y,c):{x:N.x,y:N.y};x[r]={path:L,labelX:S.x,labelY:S.y}}catch (e4){}return this.cleanup(u,h,l),x}getNodeBounds(o){let s=_nullishCoalesce(_optionalChain([o, 'access', _11 => _11.position, 'optionalAccess', _12 => _12.x]), () => (0)),t=_nullishCoalesce(_optionalChain([o, 'access', _13 => _13.position, 'optionalAccess', _14 => _14.y]), () => (0)),e=Number(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([o, 'access', _15 => _15.measured, 'optionalAccess', _16 => _16.width]), () => (o.width)), () => (_optionalChain([o, 'access', _17 => _17.style, 'optionalAccess', _18 => _18.width]))), () => (150))),n=Number(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([o, 'access', _19 => _19.measured, 'optionalAccess', _20 => _20.height]), () => (o.height)), () => (_optionalChain([o, 'access', _21 => _21.style, 'optionalAccess', _22 => _22.height]))), () => (50)));return{x:s,y:t,w:e,h:n}}getNodeBoundsAbsolute(o,s){let t=this.getNodeBounds(o),e=o;for(;_optionalChain([e, 'optionalAccess', _23 => _23.parentId]);){let n=s.get(e.parentId);if(!n)break;t.x+=_nullishCoalesce(_optionalChain([n, 'access', _24 => _24.position, 'optionalAccess', _25 => _25.x]), () => (0)),t.y+=_nullishCoalesce(_optionalChain([n, 'access', _26 => _26.position, 'optionalAccess', _27 => _27.y]), () => (0)),e=n}return t}getHandlePosition(o,s){let t=s==="source"?_nullishCoalesce(o.sourcePosition, () => (_optionalChain([o, 'access', _28 => _28.data, 'optionalAccess', _29 => _29.sourcePosition]))):_nullishCoalesce(o.targetPosition, () => (_optionalChain([o, 'access', _30 => _30.data, 'optionalAccess', _31 => _31.targetPosition]))),e=String(_nullishCoalesce(t, () => (""))).toLowerCase();return e==="left"||e==="right"||e==="top"||e==="bottom"?e:s==="source"?"right":"left"}getHandlePoint(o,s){let{x:t,y:e,w:n,h:a}=o,b=t+n/2,c=e+a/2;switch(s){case"left":return{x:t,y:c};case"right":return{x:t+n,y:c};case"top":return{x:b,y:e};case"bottom":return{x:b,y:e+a};default:return{x:t+n,y:c}}}snapToGrid(o,s,t){return t<=0?{x:o,y:s}:{x:Math.round(o/t)*t,y:Math.round(s/t)*t}}polylineToPath(o,s,t={}){if(o<2)return"";let e=_nullishCoalesce(t.gridSize, () => (0)),n=Math.max(0,_nullishCoalesce(t.cornerRadius, () => (0))),a=i=>{let u=s(i);return e>0?this.snapToGrid(u.x,u.y,e):u};if(n<=0){let i=`M ${a(0).x} ${a(0).y}`;for(let u=1;u<o;u++){let d=a(u);i+=` L ${d.x} ${d.y}`}return i}let b=(i,u)=>Math.hypot(u.x-i.x,u.y-i.y),c=(i,u)=>{let d=b(i,u);return d<1e-6?{x:0,y:0}:{x:(u.x-i.x)/d,y:(u.y-i.y)/d}},p=`M ${a(0).x} ${a(0).y}`;for(let i=1;i<o-1;i++){let u=a(i-1),d=a(i),w=a(i+1),R=c(d,u),A=c(d,w),k=b(d,u),T=b(d,w),P=Math.min(n,k/2,T/2),E=Math.min(n,k/2,T/2),l={x:d.x+R.x*P,y:d.y+R.y*P},h={x:d.x+A.x*E,y:d.y+A.y*E};p+=` L ${l.x} ${l.y} Q ${d.x} ${d.y} ${h.x} ${h.y}`}let g=a(o-1);return p+=` L ${g.x} ${g.y}`,p}cleanup(o,s,t){try{for(let{connRef:e}of s)o.deleteConnector(e);for(let{ref:e}of t)o.deleteShape(e)}catch (e5){}}};_chunkHK5MLBM7cjs.a.call(void 0, y,"lib",null),_chunkHK5MLBM7cjs.a.call(void 0, y,"instance",null);var H=y;async function oe(){return H.load()}function te(m,o,s){try{return H.getInstance().routeAll(m,o,s)}catch (e6){return{}}}var _react = require('react');function Y(m,o={}){let{onRouted:s,onLoaded:t}=o,e=_chunkHK5MLBM7cjs.b.getState().setLoaded,n=_chunkHK5MLBM7cjs.b.getState().setRoutes,a=b=>{let c=b.data;if(!(!c||typeof c!="object"||!("command"in c)))switch(c.command){case"loaded":e(c.success),_optionalChain([t, 'optionalCall', _32 => _32(c.success)]);break;case"routed":n(c.routes),_optionalChain([s, 'optionalCall', _33 => _33(c.routes)]);break;default:break}};return m.addEventListener("message",a),()=>m.removeEventListener("message",a)}function j(m){let o=_react.useRef.call(void 0, null),[s,t]=_react.useState.call(void 0, !1),e=_react.useRef.call(void 0, _optionalChain([m, 'optionalAccess', _34 => _34.onRouted])),n=_react.useRef.call(void 0, _optionalChain([m, 'optionalAccess', _35 => _35.onLoaded]));e.current=_optionalChain([m, 'optionalAccess', _36 => _36.onRouted]),n.current=_optionalChain([m, 'optionalAccess', _37 => _37.onLoaded]);let a=_optionalChain([m, 'optionalAccess', _38 => _38.create])!==!1;_react.useEffect.call(void 0, ()=>{if(!a){o.current=null,t(!1);return}let p;try{p=new Worker(new URL("./workers/avoid-router.worker.js",import.meta.url),{type:"module"})}catch(i){console.error("[avoid-worker] Failed to create worker:",i);return}o.current=p,p.addEventListener("error",i=>{console.error("[avoid-worker] Worker error:",i.message)});let g=Y(p,{onRouted:i=>_optionalChain([e, 'access', _39 => _39.current, 'optionalCall', _40 => _40(i)]),onLoaded:i=>{t(i),_optionalChain([n, 'access', _41 => _41.current, 'optionalCall', _42 => _42(i)])}});return()=>{g(),p.postMessage({command:"close"}),p.terminate(),o.current=null,t(!1)}},[a]);let b=_react.useCallback.call(void 0, p=>{o.current&&o.current.postMessage(p)},[]),c=_react.useCallback.call(void 0, ()=>{o.current&&(o.current.postMessage({command:"close"}),o.current.terminate(),o.current=null,t(!1))},[]);return{workerLoaded:s,post:b,close:c}}var V={edgeToEdgeSpacing:10,edgeToNodeSpacing:8};function se(m){return{idealNudgingDistance:_nullishCoalesce(_optionalChain([m, 'optionalAccess', _43 => _43.edgeToEdgeSpacing]), () => (V.edgeToEdgeSpacing)),shapeBufferDistance:_nullishCoalesce(_optionalChain([m, 'optionalAccess', _44 => _44.edgeToNodeSpacing]), () => (V.edgeToNodeSpacing)),edgeRounding:_optionalChain([m, 'optionalAccess', _45 => _45.edgeRounding]),diagramGridSize:_optionalChain([m, 'optionalAccess', _46 => _46.diagramGridSize]),shouldSplitEdgesNearHandle:_optionalChain([m, 'optionalAccess', _47 => _47.shouldSplitEdgesNearHandle])}}function ie(m,o,s){let t=_react.useRef.call(void 0, m),e=_react.useRef.call(void 0, o),n=se(s),a=_react.useRef.call(void 0, n);_react.useEffect.call(void 0, ()=>{t.current=m},[m]),_react.useEffect.call(void 0, ()=>{e.current=o},[o]),_react.useEffect.call(void 0, ()=>{a.current=n});let b=_chunkHK5MLBM7cjs.b.call(void 0, l=>l.setRoutes),c=_chunkHK5MLBM7cjs.c.call(void 0, l=>l.setActions),{post:p,workerLoaded:g}=j({create:!0}),i=_react.useRef.call(void 0, !1),u=_react.useRef.call(void 0, !1),d=_react.useCallback.call(void 0, ()=>{if(!g)return;let l=t.current;if(!(l.length===0||l.some(r=>_optionalChain([r, 'access', _48 => _48.measured, 'optionalAccess', _49 => _49.width])!=null)))return;u.current=!0;let x=e.current.filter(r=>r.type==="avoidNodes");if(x.length===0){b({});return}p({command:"reset",nodes:l,edges:x,options:a.current}),i.current=!0},[p,b,g]),w=_react.useCallback.call(void 0, l=>{if(!g||!i.current)return;let h=new Map(t.current.map(r=>[r.id,r])),x=l.map(r=>h.get(r)).filter(r=>r!=null);x.length!==0&&p({command:"updateNodes",nodes:x})},[p,g]),R=_react.useRef.call(void 0, null),A=_react.useRef.call(void 0, new Set),k=_react.useCallback.call(void 0, ()=>{d()},[d]),T=_react.useCallback.call(void 0, ()=>{d()},[d]),P=_react.useCallback.call(void 0, l=>{w(l)},[w]),E=_react.useCallback.call(void 0, l=>{if(!g)return;let h=!1,x=!1,r=!1;for(let f of l)f.type==="position"?(h=!0,A.current.add(f.id)):f.type==="dimensions"?(x=!0,A.current.add(f.id)):(f.type==="add"||f.type==="remove")&&(r=!0);if(!h&&!x&&!r)return;if(r||x&&!u.current){R.current&&(clearTimeout(R.current),R.current=null),A.current.clear(),R.current=setTimeout(()=>{R.current=null,requestAnimationFrame(()=>d())},0);return}i.current&&(R.current&&clearTimeout(R.current),R.current=setTimeout(()=>{R.current=null,requestAnimationFrame(()=>{let f=Array.from(A.current);A.current.clear(),f.length>0&&w(f)})},0))},[g,d,w]);return _react.useEffect.call(void 0, ()=>(c({resetRouting:k,updateRoutesForNodeId:l=>P([l])}),()=>c({resetRouting:()=>{},updateRoutesForNodeId:()=>{}})),[k,P,c]),_react.useEffect.call(void 0, ()=>{g&&d()},[g,m.length,o.length,n.shapeBufferDistance,n.idealNudgingDistance,n.edgeRounding,n.diagramGridSize,n.shouldSplitEdgesNearHandle,d]),{updateRoutingOnNodesChange:E,resetRouting:k,refreshRouting:T,updateRoutingForNodeIds:P}}exports.AvoidRouter = H; exports.DEBOUNCE_ROUTING_MS = _chunkHK5MLBM7cjs.e; exports.DEV_LOG_WEB_WORKER_MESSAGES = _chunkHK5MLBM7cjs.d; exports.EDGE_BORDER_RADIUS = _chunkHK5MLBM7cjs.g; exports.SHOULD_START_EDGE_AT_HANDLE_BORDER = _chunkHK5MLBM7cjs.f; exports.attachAvoidWorkerListener = Y; exports.loadAvoidRouter = oe; exports.routeAll = te; exports.useAvoidNodesPath = _chunkHK5MLBM7cjs.h; exports.useAvoidNodesRouterFromWorker = ie; exports.useAvoidRouterActionsStore = _chunkHK5MLBM7cjs.c; exports.useAvoidRoutesStore = _chunkHK5MLBM7cjs.b; exports.useAvoidWorker = j;