avoid-nodes-edge 0.1.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.
- package/README.md +501 -0
- package/dist/chunk-TO34Q3ID.cjs +87 -0
- package/dist/chunk-TO34Q3ID.cjs.map +1 -0
- package/dist/chunk-VPHZVUPR.js +87 -0
- package/dist/chunk-VPHZVUPR.js.map +1 -0
- package/dist/edge.cjs +235 -0
- package/dist/edge.cjs.map +1 -0
- package/dist/edge.d.cts +34 -0
- package/dist/edge.d.ts +34 -0
- package/dist/edge.js +235 -0
- package/dist/edge.js.map +1 -0
- package/dist/index.cjs +558 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +202 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.js +558 -0
- package/dist/index.js.map +1 -0
- package/dist/workers/avoid-router.worker.js +348 -0
- package/dist/workers/avoid-router.worker.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
// src/workers/avoid-router.worker.ts
|
|
2
|
+
if (typeof window === "undefined" && typeof self !== "undefined") {
|
|
3
|
+
self.window = self;
|
|
4
|
+
}
|
|
5
|
+
var LIBAVOID_WASM_URL = "/libavoid.wasm";
|
|
6
|
+
var WASM_RETRY_DELAY_MS = 2e3;
|
|
7
|
+
var WASM_MAX_RETRIES = 5;
|
|
8
|
+
var avoidLib = null;
|
|
9
|
+
async function loadWasm() {
|
|
10
|
+
if (avoidLib != null) return true;
|
|
11
|
+
for (let attempt = 1; attempt <= WASM_MAX_RETRIES; attempt++) {
|
|
12
|
+
const origin = globalThis.location?.origin;
|
|
13
|
+
const absoluteUrl = origin && LIBAVOID_WASM_URL.startsWith("/") ? `${origin}${LIBAVOID_WASM_URL}` : LIBAVOID_WASM_URL;
|
|
14
|
+
try {
|
|
15
|
+
const mod = await import("libavoid-js");
|
|
16
|
+
const AvoidLib = mod.AvoidLib ?? mod.default;
|
|
17
|
+
if (!AvoidLib?.load) return false;
|
|
18
|
+
await AvoidLib.load(absoluteUrl);
|
|
19
|
+
const lib = AvoidLib.getInstance?.();
|
|
20
|
+
if (lib == null) return false;
|
|
21
|
+
avoidLib = lib;
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
if (attempt < WASM_MAX_RETRIES) {
|
|
25
|
+
await new Promise((r) => setTimeout(r, WASM_RETRY_DELAY_MS));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function getNodeBounds(node) {
|
|
32
|
+
const x = node.position?.x ?? 0;
|
|
33
|
+
const y = node.position?.y ?? 0;
|
|
34
|
+
const w = Number(node.measured?.width ?? node.width ?? node.style?.width ?? 150);
|
|
35
|
+
const h = Number(node.measured?.height ?? node.height ?? node.style?.height ?? 50);
|
|
36
|
+
return { x, y, w, h };
|
|
37
|
+
}
|
|
38
|
+
function getNodeBoundsAbsolute(node, nodeById) {
|
|
39
|
+
const b = getNodeBounds(node);
|
|
40
|
+
let current = node;
|
|
41
|
+
while (current?.parentId) {
|
|
42
|
+
const parent = nodeById.get(current.parentId);
|
|
43
|
+
if (!parent) break;
|
|
44
|
+
b.x += parent.position?.x ?? 0;
|
|
45
|
+
b.y += parent.position?.y ?? 0;
|
|
46
|
+
current = parent;
|
|
47
|
+
}
|
|
48
|
+
return b;
|
|
49
|
+
}
|
|
50
|
+
function getHandlePosition(node, kind) {
|
|
51
|
+
const raw = kind === "source" ? node.sourcePosition ?? node.data?.sourcePosition : node.targetPosition ?? node.data?.targetPosition;
|
|
52
|
+
const s = String(raw ?? "").toLowerCase();
|
|
53
|
+
if (s === "left" || s === "right" || s === "top" || s === "bottom") return s;
|
|
54
|
+
return kind === "source" ? "right" : "left";
|
|
55
|
+
}
|
|
56
|
+
function getHandlePoint(bounds, position) {
|
|
57
|
+
const { x, y, w, h } = bounds;
|
|
58
|
+
const cx = x + w / 2;
|
|
59
|
+
const cy = y + h / 2;
|
|
60
|
+
switch (position) {
|
|
61
|
+
case "left":
|
|
62
|
+
return { x, y: cy };
|
|
63
|
+
case "right":
|
|
64
|
+
return { x: x + w, y: cy };
|
|
65
|
+
case "top":
|
|
66
|
+
return { x: cx, y };
|
|
67
|
+
case "bottom":
|
|
68
|
+
return { x: cx, y: y + h };
|
|
69
|
+
default:
|
|
70
|
+
return { x: x + w, y: cy };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function snapToGrid(x, y, gridSize) {
|
|
74
|
+
if (gridSize <= 0) return { x, y };
|
|
75
|
+
return { x: Math.round(x / gridSize) * gridSize, y: Math.round(y / gridSize) * gridSize };
|
|
76
|
+
}
|
|
77
|
+
function polylineToPath(size, getPoint, options = {}) {
|
|
78
|
+
if (size < 2) return "";
|
|
79
|
+
const gridSize = options.gridSize ?? 0;
|
|
80
|
+
const r = Math.max(0, options.cornerRadius ?? 0);
|
|
81
|
+
const pt = (i) => {
|
|
82
|
+
const p = getPoint(i);
|
|
83
|
+
return gridSize > 0 ? snapToGrid(p.x, p.y, gridSize) : p;
|
|
84
|
+
};
|
|
85
|
+
if (r <= 0) {
|
|
86
|
+
let d2 = `M ${pt(0).x} ${pt(0).y}`;
|
|
87
|
+
for (let i = 1; i < size; i++) {
|
|
88
|
+
const p = pt(i);
|
|
89
|
+
d2 += ` L ${p.x} ${p.y}`;
|
|
90
|
+
}
|
|
91
|
+
return d2;
|
|
92
|
+
}
|
|
93
|
+
const dist = (a, b) => Math.hypot(b.x - a.x, b.y - a.y);
|
|
94
|
+
const unit = (a, b) => {
|
|
95
|
+
const d2 = dist(a, b);
|
|
96
|
+
if (d2 < 1e-6) return { x: 0, y: 0 };
|
|
97
|
+
return { x: (b.x - a.x) / d2, y: (b.y - a.y) / d2 };
|
|
98
|
+
};
|
|
99
|
+
let d = `M ${pt(0).x} ${pt(0).y}`;
|
|
100
|
+
for (let i = 1; i < size - 1; i++) {
|
|
101
|
+
const prev = pt(i - 1);
|
|
102
|
+
const curr = pt(i);
|
|
103
|
+
const next = pt(i + 1);
|
|
104
|
+
const dirIn = unit(curr, prev);
|
|
105
|
+
const dirOut = unit(curr, next);
|
|
106
|
+
const lenIn = dist(curr, prev);
|
|
107
|
+
const lenOut = dist(curr, next);
|
|
108
|
+
const rIn = Math.min(r, lenIn / 2, lenOut / 2);
|
|
109
|
+
const rOut = Math.min(r, lenIn / 2, lenOut / 2);
|
|
110
|
+
const endPrev = { x: curr.x + dirIn.x * rIn, y: curr.y + dirIn.y * rIn };
|
|
111
|
+
const startNext = { x: curr.x + dirOut.x * rOut, y: curr.y + dirOut.y * rOut };
|
|
112
|
+
d += ` L ${endPrev.x} ${endPrev.y} Q ${curr.x} ${curr.y} ${startNext.x} ${startNext.y}`;
|
|
113
|
+
}
|
|
114
|
+
const last = pt(size - 1);
|
|
115
|
+
d += ` L ${last.x} ${last.y}`;
|
|
116
|
+
return d;
|
|
117
|
+
}
|
|
118
|
+
function routeAll(nodes, edges, options) {
|
|
119
|
+
const Avoid = avoidLib;
|
|
120
|
+
if (!Avoid) return {};
|
|
121
|
+
const shapeBuffer = options?.shapeBufferDistance ?? 8;
|
|
122
|
+
const idealNudging = options?.idealNudgingDistance ?? 10;
|
|
123
|
+
const cornerRadius = options?.edgeRounding ?? 0;
|
|
124
|
+
const gridSize = options?.diagramGridSize ?? 0;
|
|
125
|
+
const obstacleNodes = nodes.filter((n) => n.type !== "group");
|
|
126
|
+
const nodeById = new Map(nodes.map((n) => [n.id, n]));
|
|
127
|
+
const nodeBounds = new Map(obstacleNodes.map((n) => [n.id, getNodeBoundsAbsolute(n, nodeById)]));
|
|
128
|
+
const router = new Avoid.Router(Avoid.OrthogonalRouting);
|
|
129
|
+
router.setRoutingParameter(Avoid.shapeBufferDistance, shapeBuffer);
|
|
130
|
+
router.setRoutingParameter(Avoid.idealNudgingDistance, idealNudging);
|
|
131
|
+
router.setRoutingOption(Avoid.nudgeOrthogonalSegmentsConnectedToShapes, true);
|
|
132
|
+
router.setRoutingOption(Avoid.nudgeSharedPathsWithCommonEndPoint, true);
|
|
133
|
+
router.setRoutingOption(Avoid.performUnifyingNudgingPreprocessingStep, true);
|
|
134
|
+
const PIN_CENTER = 1, PIN_TOP = 2, PIN_BOTTOM = 3, PIN_LEFT = 4, PIN_RIGHT = 5;
|
|
135
|
+
const pinIdForPosition = { top: PIN_TOP, bottom: PIN_BOTTOM, left: PIN_LEFT, right: PIN_RIGHT };
|
|
136
|
+
const pinProportions = {
|
|
137
|
+
[PIN_CENTER]: { x: 0.5, y: 0.5, dir: Avoid.ConnDirAll },
|
|
138
|
+
[PIN_TOP]: { x: 0.5, y: 0, dir: Avoid.ConnDirUp },
|
|
139
|
+
[PIN_BOTTOM]: { x: 0.5, y: 1, dir: Avoid.ConnDirDown },
|
|
140
|
+
[PIN_LEFT]: { x: 0, y: 0.5, dir: Avoid.ConnDirLeft },
|
|
141
|
+
[PIN_RIGHT]: { x: 1, y: 0.5, dir: Avoid.ConnDirRight }
|
|
142
|
+
};
|
|
143
|
+
const shapeRefMap = /* @__PURE__ */ new Map();
|
|
144
|
+
const shapeRefs = [];
|
|
145
|
+
for (const node of obstacleNodes) {
|
|
146
|
+
const b = nodeBounds.get(node.id);
|
|
147
|
+
const topLeft = new Avoid.Point(b.x, b.y);
|
|
148
|
+
const bottomRight = new Avoid.Point(b.x + b.w, b.y + b.h);
|
|
149
|
+
const rect = new Avoid.Rectangle(topLeft, bottomRight);
|
|
150
|
+
const shapeRef = new Avoid.ShapeRef(router, rect);
|
|
151
|
+
shapeRefs.push({ ref: shapeRef });
|
|
152
|
+
shapeRefMap.set(node.id, shapeRef);
|
|
153
|
+
for (const pinId of [PIN_CENTER, PIN_TOP, PIN_BOTTOM, PIN_LEFT, PIN_RIGHT]) {
|
|
154
|
+
const p = pinProportions[pinId];
|
|
155
|
+
const pin = new Avoid.ShapeConnectionPin(shapeRef, pinId, p.x, p.y, true, 0, p.dir);
|
|
156
|
+
pin.setExclusive(false);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const connRefs = [];
|
|
160
|
+
for (const edge of edges) {
|
|
161
|
+
const src = nodeById.get(edge.source);
|
|
162
|
+
const tgt = nodeById.get(edge.target);
|
|
163
|
+
if (!src || !tgt) continue;
|
|
164
|
+
const srcShapeRef = shapeRefMap.get(edge.source);
|
|
165
|
+
const tgtShapeRef = shapeRefMap.get(edge.target);
|
|
166
|
+
const sourcePos = getHandlePosition(src, "source");
|
|
167
|
+
const targetPos = getHandlePosition(tgt, "target");
|
|
168
|
+
let srcEnd;
|
|
169
|
+
let tgtEnd;
|
|
170
|
+
if (srcShapeRef) {
|
|
171
|
+
srcEnd = new Avoid.ConnEnd(srcShapeRef, pinIdForPosition[sourcePos] ?? PIN_CENTER);
|
|
172
|
+
} else {
|
|
173
|
+
const sb = getNodeBoundsAbsolute(src, nodeById);
|
|
174
|
+
const sourcePt = getHandlePoint(sb, sourcePos);
|
|
175
|
+
srcEnd = new Avoid.ConnEnd(new Avoid.Point(sourcePt.x, sourcePt.y));
|
|
176
|
+
}
|
|
177
|
+
if (tgtShapeRef) {
|
|
178
|
+
tgtEnd = new Avoid.ConnEnd(tgtShapeRef, pinIdForPosition[targetPos] ?? PIN_CENTER);
|
|
179
|
+
} else {
|
|
180
|
+
const tb = getNodeBoundsAbsolute(tgt, nodeById);
|
|
181
|
+
const targetPt = getHandlePoint(tb, targetPos);
|
|
182
|
+
tgtEnd = new Avoid.ConnEnd(new Avoid.Point(targetPt.x, targetPt.y));
|
|
183
|
+
}
|
|
184
|
+
const connRef = new Avoid.ConnRef(router, srcEnd, tgtEnd);
|
|
185
|
+
connRef.setRoutingType(Avoid.ConnType_Orthogonal);
|
|
186
|
+
connRefs.push({ edgeId: edge.id, connRef });
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
router.processTransaction();
|
|
190
|
+
} catch {
|
|
191
|
+
for (const { connRef } of connRefs) try {
|
|
192
|
+
router.deleteConnector(connRef);
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
for (const { ref } of shapeRefs) try {
|
|
196
|
+
router.deleteShape(ref);
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
201
|
+
const result = {};
|
|
202
|
+
for (const { edgeId, connRef } of connRefs) {
|
|
203
|
+
try {
|
|
204
|
+
const route = connRef.displayRoute();
|
|
205
|
+
const size = route.size();
|
|
206
|
+
if (size < 2) continue;
|
|
207
|
+
const path = polylineToPath(size, (i) => {
|
|
208
|
+
const p = route.get_ps(i);
|
|
209
|
+
return { x: p.x, y: p.y };
|
|
210
|
+
}, { gridSize: gridSize || void 0, cornerRadius });
|
|
211
|
+
const mid = Math.floor(size / 2);
|
|
212
|
+
const midP = route.get_ps(mid);
|
|
213
|
+
const labelP = gridSize > 0 ? snapToGrid(midP.x, midP.y, gridSize) : { x: midP.x, y: midP.y };
|
|
214
|
+
result[edgeId] = { path, labelX: labelP.x, labelY: labelP.y };
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (const { connRef } of connRefs) try {
|
|
219
|
+
router.deleteConnector(connRef);
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
for (const { ref } of shapeRefs) try {
|
|
223
|
+
router.deleteShape(ref);
|
|
224
|
+
} catch {
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
var routerLoaded = loadWasm();
|
|
229
|
+
routerLoaded.then((success) => {
|
|
230
|
+
postMessage({ command: "loaded", success });
|
|
231
|
+
}).catch(() => {
|
|
232
|
+
postMessage({ command: "loaded", success: false });
|
|
233
|
+
});
|
|
234
|
+
var currentNodes = [];
|
|
235
|
+
var currentEdges = [];
|
|
236
|
+
var currentOptions = {};
|
|
237
|
+
function isNode(cell) {
|
|
238
|
+
return "position" in cell && ("width" in cell || "measured" in cell || !("source" in cell));
|
|
239
|
+
}
|
|
240
|
+
function doRoute() {
|
|
241
|
+
const avoidEdges = currentEdges.filter((e) => e.type === "avoidNodes");
|
|
242
|
+
if (avoidEdges.length === 0) return {};
|
|
243
|
+
try {
|
|
244
|
+
return routeAll(currentNodes, avoidEdges, currentOptions);
|
|
245
|
+
} catch {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
var DEBOUNCE_MS = 0;
|
|
250
|
+
var debounceTimer = null;
|
|
251
|
+
function isPending() {
|
|
252
|
+
return debounceTimer != null;
|
|
253
|
+
}
|
|
254
|
+
function cancelDebounce() {
|
|
255
|
+
if (debounceTimer != null) {
|
|
256
|
+
clearTimeout(debounceTimer);
|
|
257
|
+
debounceTimer = null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function debouncedRoute() {
|
|
261
|
+
cancelDebounce();
|
|
262
|
+
debounceTimer = setTimeout(() => {
|
|
263
|
+
debounceTimer = null;
|
|
264
|
+
const routes = doRoute();
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
if (!isPending()) {
|
|
267
|
+
postMessage({ command: "routed", routes });
|
|
268
|
+
}
|
|
269
|
+
}, 0);
|
|
270
|
+
}, DEBOUNCE_MS);
|
|
271
|
+
}
|
|
272
|
+
onmessage = async (e) => {
|
|
273
|
+
await routerLoaded;
|
|
274
|
+
const msg = e.data;
|
|
275
|
+
if (!msg || typeof msg !== "object" || !("command" in msg)) return;
|
|
276
|
+
switch (msg.command) {
|
|
277
|
+
case "reset":
|
|
278
|
+
currentNodes = msg.nodes ?? [];
|
|
279
|
+
currentEdges = msg.edges ?? [];
|
|
280
|
+
if (msg.options) currentOptions = msg.options;
|
|
281
|
+
debouncedRoute();
|
|
282
|
+
break;
|
|
283
|
+
case "change": {
|
|
284
|
+
const cell = msg.cell;
|
|
285
|
+
if (isNode(cell)) {
|
|
286
|
+
const i = currentNodes.findIndex((n) => n.id === cell.id);
|
|
287
|
+
if (i >= 0) currentNodes[i] = { ...currentNodes[i], ...cell };
|
|
288
|
+
else currentNodes.push(cell);
|
|
289
|
+
} else {
|
|
290
|
+
const i = currentEdges.findIndex((ed) => ed.id === cell.id);
|
|
291
|
+
if (i >= 0) currentEdges[i] = { ...currentEdges[i], ...cell };
|
|
292
|
+
else currentEdges.push(cell);
|
|
293
|
+
}
|
|
294
|
+
debouncedRoute();
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case "remove": {
|
|
298
|
+
const id = msg.id;
|
|
299
|
+
currentNodes = currentNodes.filter((n) => n.id !== id);
|
|
300
|
+
currentEdges = currentEdges.filter((ed) => ed.id !== id);
|
|
301
|
+
debouncedRoute();
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
case "add": {
|
|
305
|
+
const cell = msg.cell;
|
|
306
|
+
if (isNode(cell)) {
|
|
307
|
+
if (!currentNodes.some((n) => n.id === cell.id)) currentNodes.push(cell);
|
|
308
|
+
} else {
|
|
309
|
+
if (!currentEdges.some((ed) => ed.id === cell.id)) currentEdges.push(cell);
|
|
310
|
+
}
|
|
311
|
+
debouncedRoute();
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
case "updateNodes": {
|
|
315
|
+
const updatedNodes = msg.nodes ?? [];
|
|
316
|
+
for (const updated of updatedNodes) {
|
|
317
|
+
const i = currentNodes.findIndex((n) => n.id === updated.id);
|
|
318
|
+
if (i >= 0) currentNodes[i] = { ...currentNodes[i], ...updated };
|
|
319
|
+
else currentNodes.push(updated);
|
|
320
|
+
}
|
|
321
|
+
debouncedRoute();
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case "route": {
|
|
325
|
+
const routeNodes = msg.nodes ?? [];
|
|
326
|
+
const routeEdges = (msg.edges ?? []).filter((ed) => ed.type === "avoidNodes");
|
|
327
|
+
const routeOptions = msg.options ?? currentOptions;
|
|
328
|
+
if (routeEdges.length === 0) {
|
|
329
|
+
postMessage({ command: "routed", routes: {} });
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
const routes = routeAll(routeNodes, routeEdges, routeOptions);
|
|
334
|
+
postMessage({ command: "routed", routes });
|
|
335
|
+
} catch {
|
|
336
|
+
postMessage({ command: "routed", routes: {} });
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
case "close":
|
|
341
|
+
cancelDebounce();
|
|
342
|
+
self.close();
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
//# sourceMappingURL=avoid-router.worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/workers/avoid-router.worker.ts"],"sourcesContent":["/**\n * Web Worker: loads AvoidRouter (WASM) and handles routing commands.\n * WASM loads exclusively in this worker — never on the main thread.\n */\n\n// Polyfill: some WASM libraries reference `window` which doesn't exist in workers\nif (typeof window === \"undefined\" && typeof self !== \"undefined\") {\n (self as unknown as Record<string, unknown>).window = self;\n}\n\n// ---- Types (inlined to keep worker self-contained) ----\n\ntype AvoidRoute = { path: string; labelX: number; labelY: number };\n\ntype AvoidRouterOptions = {\n shapeBufferDistance?: number;\n idealNudgingDistance?: number;\n edgeRounding?: number;\n diagramGridSize?: number;\n shouldSplitEdgesNearHandle?: boolean;\n};\n\ntype FlowNode = {\n id: string;\n position: { x: number; y: number };\n width?: number;\n height?: number;\n measured?: { width?: number; height?: number };\n style?: { width?: number; height?: number };\n type?: string;\n parentId?: string;\n sourcePosition?: string;\n targetPosition?: string;\n data?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\ntype FlowEdge = {\n id: string;\n source: string;\n target: string;\n type?: string;\n [key: string]: unknown;\n};\n\ntype HandlePosition = \"left\" | \"right\" | \"top\" | \"bottom\";\n\ntype AvoidRouterWorkerCommand =\n | { command: \"reset\"; nodes?: FlowNode[]; edges?: FlowEdge[]; options?: AvoidRouterOptions }\n | { command: \"change\"; cell: FlowNode | FlowEdge }\n | { command: \"remove\"; id: string }\n | { command: \"add\"; cell: FlowNode | FlowEdge }\n | { command: \"updateNodes\"; nodes?: FlowNode[] }\n | { command: \"route\"; nodes?: FlowNode[]; edges?: FlowEdge[]; options?: AvoidRouterOptions }\n | { command: \"close\" };\n\n// ---- WASM Library Types ----\n\ntype AvoidLibInstance = {\n Router: new (flags: number) => unknown;\n Point: new (x: number, y: number) => { x: number; y: number };\n Rectangle: new (a: unknown, b: unknown) => unknown;\n ShapeRef: new (router: unknown, poly: unknown) => unknown;\n ShapeConnectionPin: new (\n shapeRef: unknown,\n classId: number,\n xProportion: number,\n yProportion: number,\n proportional: boolean,\n insideOffset: number,\n directions: number\n ) => { setExclusive: (exclusive: boolean) => void };\n ConnEnd: new (shapeRefOrPoint: unknown, pinClassId?: number) => unknown;\n ConnRef: new (router: unknown, src?: unknown, dst?: unknown) => {\n setRoutingType: (t: number) => void;\n displayRoute: () => { size: () => number; get_ps: (i: number) => { x: number; y: number } };\n };\n OrthogonalRouting: number;\n ConnType_Orthogonal: number;\n ConnDirUp: number;\n ConnDirDown: number;\n ConnDirLeft: number;\n ConnDirRight: number;\n ConnDirAll: number;\n shapeBufferDistance: number;\n idealNudgingDistance: number;\n nudgeOrthogonalSegmentsConnectedToShapes: number;\n nudgeSharedPathsWithCommonEndPoint: number;\n performUnifyingNudgingPreprocessingStep: number;\n};\n\n// ---- WASM Loading ----\n\nconst LIBAVOID_WASM_URL = \"/libavoid.wasm\";\nconst WASM_RETRY_DELAY_MS = 2000;\nconst WASM_MAX_RETRIES = 5;\n\nlet avoidLib: AvoidLibInstance | null = null;\n\nasync function loadWasm(): Promise<boolean> {\n if (avoidLib != null) return true;\n for (let attempt = 1; attempt <= WASM_MAX_RETRIES; attempt++) {\n const origin = (globalThis as unknown as { location?: { origin?: string } }).location?.origin;\n const absoluteUrl = origin && LIBAVOID_WASM_URL.startsWith(\"/\") ? `${origin}${LIBAVOID_WASM_URL}` : LIBAVOID_WASM_URL;\n try {\n const mod = (await import(\"libavoid-js\")) as unknown as {\n default?: { load?: (filePath?: string) => Promise<void>; getInstance?: () => AvoidLibInstance };\n AvoidLib?: { load?: (filePath?: string) => Promise<void>; getInstance?: () => AvoidLibInstance };\n };\n const AvoidLib = mod.AvoidLib ?? mod.default;\n if (!AvoidLib?.load) return false;\n await AvoidLib.load(absoluteUrl);\n const lib = AvoidLib.getInstance?.();\n if (lib == null) return false;\n avoidLib = lib;\n return true;\n } catch {\n if (attempt < WASM_MAX_RETRIES) {\n await new Promise((r) => setTimeout(r, WASM_RETRY_DELAY_MS));\n }\n }\n }\n return false;\n}\n\n// ---- Routing Logic ----\n\nfunction getNodeBounds(node: FlowNode): { x: number; y: number; w: number; h: number } {\n const x = node.position?.x ?? 0;\n const y = node.position?.y ?? 0;\n const w = Number((node.measured?.width ?? node.width ?? (node.style as { width?: number })?.width) ?? 150);\n const h = Number((node.measured?.height ?? node.height ?? (node.style as { height?: number })?.height) ?? 50);\n return { x, y, w, h };\n}\n\nfunction getNodeBoundsAbsolute(node: FlowNode, nodeById: Map<string, FlowNode>): { x: number; y: number; w: number; h: number } {\n const b = getNodeBounds(node);\n let current: FlowNode | undefined = node;\n while (current?.parentId) {\n const parent = nodeById.get(current.parentId);\n if (!parent) break;\n b.x += parent.position?.x ?? 0;\n b.y += parent.position?.y ?? 0;\n current = parent;\n }\n return b;\n}\n\nfunction getHandlePosition(node: FlowNode, kind: \"source\" | \"target\"): HandlePosition {\n const raw =\n kind === \"source\"\n ? (node.sourcePosition as string | undefined) ?? (node as { data?: { sourcePosition?: string } }).data?.sourcePosition\n : (node.targetPosition as string | undefined) ?? (node as { data?: { targetPosition?: string } }).data?.targetPosition;\n const s = String(raw ?? \"\").toLowerCase();\n if (s === \"left\" || s === \"right\" || s === \"top\" || s === \"bottom\") return s;\n return kind === \"source\" ? \"right\" : \"left\";\n}\n\nfunction getHandlePoint(bounds: { x: number; y: number; w: number; h: number }, position: HandlePosition): { x: number; y: number } {\n const { x, y, w, h } = bounds;\n const cx = x + w / 2;\n const cy = y + h / 2;\n switch (position) {\n case \"left\": return { x, y: cy };\n case \"right\": return { x: x + w, y: cy };\n case \"top\": return { x: cx, y };\n case \"bottom\": return { x: cx, y: y + h };\n default: return { x: x + w, y: cy };\n }\n}\n\nfunction snapToGrid(x: number, y: number, gridSize: number): { x: number; y: number } {\n if (gridSize <= 0) return { x, y };\n return { x: Math.round(x / gridSize) * gridSize, y: Math.round(y / gridSize) * gridSize };\n}\n\nfunction polylineToPath(\n size: number,\n getPoint: (i: number) => { x: number; y: number },\n options: { gridSize?: number; cornerRadius?: number } = {}\n): string {\n if (size < 2) return \"\";\n const gridSize = options.gridSize ?? 0;\n const r = Math.max(0, options.cornerRadius ?? 0);\n const pt = (i: number) => {\n const p = getPoint(i);\n return gridSize > 0 ? snapToGrid(p.x, p.y, gridSize) : p;\n };\n if (r <= 0) {\n let d = `M ${pt(0).x} ${pt(0).y}`;\n for (let i = 1; i < size; i++) {\n const p = pt(i);\n d += ` L ${p.x} ${p.y}`;\n }\n return d;\n }\n const dist = (a: { x: number; y: number }, b: { x: number; y: number }) =>\n Math.hypot(b.x - a.x, b.y - a.y);\n const unit = (a: { x: number; y: number }, b: { x: number; y: number }) => {\n const d = dist(a, b);\n if (d < 1e-6) return { x: 0, y: 0 };\n return { x: (b.x - a.x) / d, y: (b.y - a.y) / d };\n };\n let d = `M ${pt(0).x} ${pt(0).y}`;\n for (let i = 1; i < size - 1; i++) {\n const prev = pt(i - 1);\n const curr = pt(i);\n const next = pt(i + 1);\n const dirIn = unit(curr, prev);\n const dirOut = unit(curr, next);\n const lenIn = dist(curr, prev);\n const lenOut = dist(curr, next);\n const rIn = Math.min(r, lenIn / 2, lenOut / 2);\n const rOut = Math.min(r, lenIn / 2, lenOut / 2);\n const endPrev = { x: curr.x + dirIn.x * rIn, y: curr.y + dirIn.y * rIn };\n const startNext = { x: curr.x + dirOut.x * rOut, y: curr.y + dirOut.y * rOut };\n d += ` L ${endPrev.x} ${endPrev.y} Q ${curr.x} ${curr.y} ${startNext.x} ${startNext.y}`;\n }\n const last = pt(size - 1);\n d += ` L ${last.x} ${last.y}`;\n return d;\n}\n\nfunction routeAll(nodes: FlowNode[], edges: FlowEdge[], options?: AvoidRouterOptions): Record<string, AvoidRoute> {\n const Avoid = avoidLib;\n if (!Avoid) return {};\n\n const shapeBuffer = options?.shapeBufferDistance ?? 8;\n const idealNudging = options?.idealNudgingDistance ?? 10;\n const cornerRadius = options?.edgeRounding ?? 0;\n const gridSize = options?.diagramGridSize ?? 0;\n\n const obstacleNodes = nodes.filter((n) => n.type !== \"group\");\n const nodeById = new Map(nodes.map((n) => [n.id, n]));\n const nodeBounds = new Map(obstacleNodes.map((n) => [n.id, getNodeBoundsAbsolute(n, nodeById)]));\n\n const router = new Avoid.Router(Avoid.OrthogonalRouting) as {\n setRoutingParameter: (p: number, v: number) => void;\n setRoutingOption: (o: number, v: boolean) => void;\n processTransaction: () => void;\n deleteConnector: (c: unknown) => void;\n deleteShape: (s: unknown) => void;\n };\n router.setRoutingParameter(Avoid.shapeBufferDistance, shapeBuffer);\n router.setRoutingParameter(Avoid.idealNudgingDistance, idealNudging);\n router.setRoutingOption(Avoid.nudgeOrthogonalSegmentsConnectedToShapes, true);\n router.setRoutingOption(Avoid.nudgeSharedPathsWithCommonEndPoint, true);\n router.setRoutingOption(Avoid.performUnifyingNudgingPreprocessingStep, true);\n\n const PIN_CENTER = 1, PIN_TOP = 2, PIN_BOTTOM = 3, PIN_LEFT = 4, PIN_RIGHT = 5;\n const pinIdForPosition: Record<HandlePosition, number> = { top: PIN_TOP, bottom: PIN_BOTTOM, left: PIN_LEFT, right: PIN_RIGHT };\n const pinProportions: Record<number, { x: number; y: number; dir: number }> = {\n [PIN_CENTER]: { x: 0.5, y: 0.5, dir: Avoid.ConnDirAll },\n [PIN_TOP]: { x: 0.5, y: 0, dir: Avoid.ConnDirUp },\n [PIN_BOTTOM]: { x: 0.5, y: 1, dir: Avoid.ConnDirDown },\n [PIN_LEFT]: { x: 0, y: 0.5, dir: Avoid.ConnDirLeft },\n [PIN_RIGHT]: { x: 1, y: 0.5, dir: Avoid.ConnDirRight },\n };\n\n const shapeRefMap = new Map<string, unknown>();\n const shapeRefs: { ref: unknown }[] = [];\n for (const node of obstacleNodes) {\n const b = nodeBounds.get(node.id)!;\n const topLeft = new Avoid.Point(b.x, b.y);\n const bottomRight = new Avoid.Point(b.x + b.w, b.y + b.h);\n const rect = new Avoid.Rectangle(topLeft, bottomRight);\n const shapeRef = new Avoid.ShapeRef(router, rect);\n shapeRefs.push({ ref: shapeRef });\n shapeRefMap.set(node.id, shapeRef);\n for (const pinId of [PIN_CENTER, PIN_TOP, PIN_BOTTOM, PIN_LEFT, PIN_RIGHT]) {\n const p = pinProportions[pinId];\n const pin = new Avoid.ShapeConnectionPin(shapeRef, pinId, p.x, p.y, true, 0, p.dir);\n pin.setExclusive(false);\n }\n }\n\n const connRefs: { edgeId: string; connRef: unknown }[] = [];\n for (const edge of edges) {\n const src = nodeById.get(edge.source);\n const tgt = nodeById.get(edge.target);\n if (!src || !tgt) continue;\n const srcShapeRef = shapeRefMap.get(edge.source);\n const tgtShapeRef = shapeRefMap.get(edge.target);\n const sourcePos = getHandlePosition(src, \"source\");\n const targetPos = getHandlePosition(tgt, \"target\");\n let srcEnd: unknown;\n let tgtEnd: unknown;\n if (srcShapeRef) {\n srcEnd = new Avoid.ConnEnd(srcShapeRef, pinIdForPosition[sourcePos] ?? PIN_CENTER);\n } else {\n const sb = getNodeBoundsAbsolute(src, nodeById);\n const sourcePt = getHandlePoint(sb, sourcePos);\n srcEnd = new Avoid.ConnEnd(new Avoid.Point(sourcePt.x, sourcePt.y));\n }\n if (tgtShapeRef) {\n tgtEnd = new Avoid.ConnEnd(tgtShapeRef, pinIdForPosition[targetPos] ?? PIN_CENTER);\n } else {\n const tb = getNodeBoundsAbsolute(tgt, nodeById);\n const targetPt = getHandlePoint(tb, targetPos);\n tgtEnd = new Avoid.ConnEnd(new Avoid.Point(targetPt.x, targetPt.y));\n }\n const connRef = new Avoid.ConnRef(router, srcEnd, tgtEnd);\n connRef.setRoutingType(Avoid.ConnType_Orthogonal);\n connRefs.push({ edgeId: edge.id, connRef });\n }\n\n try {\n router.processTransaction();\n } catch {\n for (const { connRef } of connRefs) try { router.deleteConnector(connRef); } catch { /* */ }\n for (const { ref } of shapeRefs) try { router.deleteShape(ref); } catch { /* */ }\n return {};\n }\n\n const result: Record<string, AvoidRoute> = {};\n for (const { edgeId, connRef } of connRefs) {\n try {\n const route = (connRef as { displayRoute(): { size(): number; get_ps(i: number): { x: number; y: number } } }).displayRoute();\n const size = route.size();\n if (size < 2) continue;\n const path = polylineToPath(size, (i) => {\n const p = route.get_ps(i);\n return { x: p.x, y: p.y };\n }, { gridSize: gridSize || undefined, cornerRadius });\n const mid = Math.floor(size / 2);\n const midP = route.get_ps(mid);\n const labelP = gridSize > 0 ? snapToGrid(midP.x, midP.y, gridSize) : { x: midP.x, y: midP.y };\n result[edgeId] = { path, labelX: labelP.x, labelY: labelP.y };\n } catch {\n // skip\n }\n }\n\n for (const { connRef } of connRefs) try { router.deleteConnector(connRef); } catch { /* */ }\n for (const { ref } of shapeRefs) try { router.deleteShape(ref); } catch { /* */ }\n return result;\n}\n\n// ---- Load WASM ----\nconst routerLoaded = loadWasm();\nrouterLoaded.then((success) => {\n postMessage({ command: \"loaded\", success } as const);\n}).catch(() => {\n postMessage({ command: \"loaded\", success: false } as const);\n});\n\n// ---- Internal model ----\nlet currentNodes: FlowNode[] = [];\nlet currentEdges: FlowEdge[] = [];\nlet currentOptions: AvoidRouterOptions = {};\n\nfunction isNode(cell: FlowNode | FlowEdge): cell is FlowNode {\n return \"position\" in cell && (\"width\" in cell || \"measured\" in cell || !(\"source\" in cell));\n}\n\nfunction doRoute(): Record<string, AvoidRoute> {\n const avoidEdges = currentEdges.filter((e) => e.type === \"avoidNodes\");\n if (avoidEdges.length === 0) return {};\n try {\n return routeAll(currentNodes, avoidEdges, currentOptions);\n } catch {\n return {};\n }\n}\n\n// ---- Debounce ----\nconst DEBOUNCE_MS = 0;\nlet debounceTimer: ReturnType<typeof setTimeout> | null = null;\nfunction isPending() { return debounceTimer != null; }\nfunction cancelDebounce() {\n if (debounceTimer != null) { clearTimeout(debounceTimer); debounceTimer = null; }\n}\n\nfunction debouncedRoute() {\n cancelDebounce();\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n const routes = doRoute();\n setTimeout(() => {\n if (!isPending()) {\n postMessage({ command: \"routed\", routes } as const);\n }\n }, 0);\n }, DEBOUNCE_MS);\n}\n\n// ---- Message handler ----\nonmessage = async (e: MessageEvent<AvoidRouterWorkerCommand>) => {\n await routerLoaded;\n\n const msg = e.data;\n if (!msg || typeof msg !== \"object\" || !(\"command\" in msg)) return;\n\n switch (msg.command) {\n case \"reset\":\n currentNodes = msg.nodes ?? [];\n currentEdges = msg.edges ?? [];\n if (msg.options) currentOptions = msg.options;\n debouncedRoute();\n break;\n\n case \"change\": {\n const cell = msg.cell;\n if (isNode(cell)) {\n const i = currentNodes.findIndex((n) => n.id === cell.id);\n if (i >= 0) currentNodes[i] = { ...currentNodes[i], ...cell };\n else currentNodes.push(cell);\n } else {\n const i = currentEdges.findIndex((ed) => ed.id === cell.id);\n if (i >= 0) currentEdges[i] = { ...currentEdges[i], ...cell };\n else currentEdges.push(cell);\n }\n debouncedRoute();\n break;\n }\n\n case \"remove\": {\n const id = msg.id;\n currentNodes = currentNodes.filter((n) => n.id !== id);\n currentEdges = currentEdges.filter((ed) => ed.id !== id);\n debouncedRoute();\n break;\n }\n\n case \"add\": {\n const cell = msg.cell;\n if (isNode(cell)) {\n if (!currentNodes.some((n) => n.id === cell.id)) currentNodes.push(cell);\n } else {\n if (!currentEdges.some((ed) => ed.id === cell.id)) currentEdges.push(cell);\n }\n debouncedRoute();\n break;\n }\n\n case \"updateNodes\": {\n const updatedNodes = msg.nodes ?? [];\n for (const updated of updatedNodes) {\n const i = currentNodes.findIndex((n) => n.id === updated.id);\n if (i >= 0) currentNodes[i] = { ...currentNodes[i], ...updated };\n else currentNodes.push(updated);\n }\n debouncedRoute();\n break;\n }\n\n case \"route\": {\n const routeNodes = msg.nodes ?? [];\n const routeEdges = (msg.edges ?? []).filter((ed: FlowEdge) => ed.type === \"avoidNodes\");\n const routeOptions = msg.options ?? currentOptions;\n if (routeEdges.length === 0) {\n postMessage({ command: \"routed\", routes: {} } as const);\n break;\n }\n try {\n const routes = routeAll(routeNodes, routeEdges, routeOptions);\n postMessage({ command: \"routed\", routes } as const);\n } catch {\n postMessage({ command: \"routed\", routes: {} } as const);\n }\n break;\n }\n\n case \"close\":\n cancelDebounce();\n self.close();\n break;\n\n default:\n break;\n }\n};\n"],"mappings":"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","names":["d"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "avoid-nodes-edge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Orthogonal edge routing for React Flow — edges avoid overlapping nodes via libavoid-js WASM running in a Web Worker.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./edge": {
|
|
16
|
+
"types": "./dist/edge.d.ts",
|
|
17
|
+
"import": "./dist/edge.js",
|
|
18
|
+
"require": "./dist/edge.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"dev": "tsup --watch",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@xyflow/react": ">=12.0.0",
|
|
33
|
+
"react": ">=18.0.0",
|
|
34
|
+
"react-dom": ">=18.0.0",
|
|
35
|
+
"zustand": ">=4.0.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"libavoid-js": "^0.4.5"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/react": "^18.2.15",
|
|
42
|
+
"@xyflow/react": "^12.7.0",
|
|
43
|
+
"react": "^18.2.0",
|
|
44
|
+
"react-dom": "^18.2.0",
|
|
45
|
+
"tsup": "^8.0.0",
|
|
46
|
+
"typescript": "^5.0.2",
|
|
47
|
+
"zustand": "^5.0.10"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"react-flow",
|
|
51
|
+
"xyflow",
|
|
52
|
+
"edge-routing",
|
|
53
|
+
"orthogonal",
|
|
54
|
+
"libavoid",
|
|
55
|
+
"wasm",
|
|
56
|
+
"web-worker",
|
|
57
|
+
"avoid-nodes"
|
|
58
|
+
],
|
|
59
|
+
"license": "MIT"
|
|
60
|
+
}
|