@tisoap/react-flow-smart-edge
Version:
Custom React Flow Edge that never intersects with other nodes
408 lines (407 loc) • 10.6 kB
JavaScript
import { jsx as A } from "react/jsx-runtime";
import { BezierEdge as _, BaseEdge as K, useNodes as X, StraightEdge as O, StepEdge as V } from "@xyflow/react";
import { createContext as tt, useContext as et } from "react";
const nt = (t, e, n) => {
const o = new Array(e);
for (let s = 0; s < e; s++) {
const r = new Array(t);
for (let l = 0; l < t; l++) {
const x = !!(n ? n[s]?.[l] : void 0), h = n ? !x : !0;
r[l] = { x: l, y: s, walkable: h };
}
o[s] = r;
}
return o;
}, v = (t, e, n, o) => n >= 0 && n < t && o >= 0 && o < e, R = (t, e, n) => {
const o = nt(t, e, n), s = (h, i) => o[i][h], r = (h, i) => v(t, e, h, i) && o[i][h].walkable;
return {
width: t,
height: e,
nodes: o,
getNodeAt: s,
isWalkableAt: r,
setWalkableAt: (h, i, a) => {
v(t, e, h, i) && (o[i][h].walkable = a);
},
getNeighbors: (h, i) => {
const a = h.x, c = h.y, g = [], m = r(a, c - 1), E = r(a + 1, c), S = r(a, c + 1), M = r(a - 1, c);
m && g.push(s(a, c - 1)), E && g.push(s(a + 1, c)), S && g.push(s(a, c + 1)), M && g.push(s(a - 1, c));
const w = r(a + 1, c - 1), d = r(a + 1, c + 1), f = r(a - 1, c + 1), y = r(a - 1, c - 1);
return i === "Never" || (w && g.push(s(a + 1, c - 1)), d && g.push(s(a + 1, c + 1)), f && g.push(s(a - 1, c + 1)), y && g.push(s(a - 1, c - 1))), g;
},
isInside: (h, i) => v(t, e, h, i),
clone: () => {
const h = o.map(
(i) => i.map((a) => a.walkable ? 0 : 1)
);
return R(t, e, h);
}
};
}, $ = (t, e) => {
switch (e) {
case "top":
return { x: t.x, y: t.y - 1 };
case "bottom":
return { x: t.x, y: t.y + 1 };
case "left":
return { x: t.x - 1, y: t.y };
case "right":
return { x: t.x + 1, y: t.y };
}
}, W = (t, e, n) => {
let o = t.getNodeAt(e.x, e.y);
for (; !o.walkable; ) {
t.setWalkableAt(o.x, o.y, !0);
const s = $(o, n);
o = t.getNodeAt(s.x, s.y);
}
}, P = (t, e, n, o) => {
const s = (t.x - e) / o + 1, r = (t.y - n) / o + 1;
return { x: s, y: r };
}, Y = (t, e, n, o) => {
const s = (t.x - 1) * o + e, r = (t.y - 1) * o + n;
return { x: s, y: r };
}, B = (t, e = 10) => Math.round(t / e) * e, k = (t, e = 10) => Math.floor(t / e) * e, N = (t, e = 10) => Math.ceil(t / e) * e, D = (t, e = 0) => {
let n = Math.max(Math.round(t), e);
return n = Number.isInteger(n) ? n : e, n = n >= e ? n : e, n;
}, ot = (t, e, n, o, s = 2) => {
const { xMin: r, yMin: l, width: u, height: x } = t, h = N(u, s) / s + 1, i = N(x, s) / s + 1, a = R(h, i);
e.forEach((w) => {
const d = P(w.topLeft, r, l, s), f = P(w.bottomRight, r, l, s);
for (let y = d.x; y < f.x; y++)
for (let p = d.y; p < f.y; p++)
a.setWalkableAt(y, p, !1);
});
const c = P(
{
x: B(n.x, s),
y: B(n.y, s)
},
r,
l,
s
), g = P(
{
x: B(o.x, s),
y: B(o.y, s)
},
r,
l,
s
), m = a.getNodeAt(c.x, c.y);
W(a, m, n.position);
const E = a.getNodeAt(g.x, g.y);
W(a, E, o.position);
const S = $(m, n.position), M = $(E, o.position);
return { grid: a, start: S, end: M };
}, T = (t, e, n) => {
let o = `M ${String(t.x)}, ${String(t.y)} `;
return n.forEach((s) => {
const [r, l] = s;
o += `L ${String(r)}, ${String(l)} `;
}), o += `L ${String(e.x)}, ${String(e.y)} `, o;
}, z = (t, e, n) => {
const o = [[t.x, t.y], ...n, [e.x, e.y]];
return st(o);
}, st = (t) => {
let o = t[0];
const s = t[0];
let r = `M${String(s[0])},${String(s[1])}M`;
for (const u of t) {
const x = rt(o[0], o[1], u[0], u[1]);
r += ` ${String(x[0])},${String(x[1])}`, r += `Q${String(u[0])},${String(u[1])}`, o = u;
}
const l = t[t.length - 1];
return r += ` ${String(l[0])},${String(l[1])}`, r;
}, rt = (t, e, n, o) => {
const s = (t - n) / 2 + n, r = (e - o) / 2 + o;
return [s, r];
}, at = (t, e) => t + e, ct = (t, e) => {
const n = Math.SQRT2 - 1;
return t < e ? n * t + e : n * e + t;
}, it = (t) => {
const e = [];
let n = t;
for (; n; )
e.push([n.x, n.y]), n = n.parent;
return e.reverse();
}, lt = (t) => t === "Never" ? at : ct, ht = (t) => {
let e = 0;
for (let n = 1; n < t.length; n++)
(t[n].estimatedTotalCost ?? 1 / 0) < (t[e].estimatedTotalCost ?? 1 / 0) && (e = n);
return t.splice(e, 1)[0];
}, xt = (t, e, n, o, s, r) => {
if (t.closed) return;
const l = Math.abs(t.x - e.x), u = Math.abs(t.y - e.y), x = (e.costFromStart ?? 0) + (l === 0 || u === 0 ? 1 : Math.SQRT2);
(!t.opened || x < (t.costFromStart ?? 1 / 0)) && (t.costFromStart = x, t.heuristicCostToGoal = t.heuristicCostToGoal ?? r * s(Math.abs(t.x - n.x), Math.abs(t.y - n.y)), t.estimatedTotalCost = (t.costFromStart ?? 0) + (t.heuristicCostToGoal ?? 0), t.parent = e, t.opened || (t.opened = !0, o.push(t)));
}, U = (t = {}) => {
const e = t.diagonalMovement ?? "Never", n = t.heuristic ?? lt(e), o = t.weight ?? 1;
return { findPath: (r, l, u, x, h) => {
const i = h.getNodeAt(r, l), a = h.getNodeAt(u, x), c = [];
for (i.costFromStart = 0, i.heuristicCostToGoal = 0, i.estimatedTotalCost = 0, i.opened = !0, c.push(i); c.length > 0; ) {
const g = ht(c);
if (g.closed = !0, g === a)
return it(a);
const m = h.getNeighbors(g, e);
for (const E of m)
xt(E, g, a, c, n, o);
}
return [];
} };
}, F = (t, e, n) => {
try {
const s = U({
diagonalMovement: "Always"
}).findPath(e.x, e.y, n.x, n.y, t);
if (s.length === 0)
throw new Error("No path found");
return s;
} catch (o) {
throw o instanceof Error ? o : new Error(`Unknown error: ${String(o)}`);
}
}, gt = (t, e, n) => {
try {
const s = U({
diagonalMovement: "Never"
}).findPath(e.x, e.y, n.x, n.y, t);
if (s.length === 0)
throw new Error("No path found");
return s;
} catch (o) {
throw o instanceof Error ? o : new Error(`Unknown error: ${String(o)}`);
}
}, ft = (t, e = 2, n = 2) => {
let o = Number.MIN_SAFE_INTEGER, s = Number.MIN_SAFE_INTEGER, r = Number.MAX_SAFE_INTEGER, l = Number.MAX_SAFE_INTEGER;
const u = t.map((S) => {
const M = Math.max(S.measured?.width ?? 0, 1), w = Math.max(S.measured?.height ?? 0, 1), d = {
x: S.position.x,
y: S.position.y
}, f = {
x: d.x - e,
y: d.y - e
}, y = {
x: d.x - e,
y: d.y + w + e
}, p = {
x: d.x + M + e,
y: d.y - e
}, b = {
x: d.x + M + e,
y: d.y + w + e
};
return n > 0 && (f.x = k(f.x, n), f.y = k(f.y, n), y.x = k(y.x, n), y.y = N(y.y, n), p.x = N(p.x, n), p.y = k(p.y, n), b.x = N(b.x, n), b.y = N(b.y, n)), f.y < l && (l = f.y), f.x < r && (r = f.x), b.y > s && (s = b.y), b.x > o && (o = b.x), {
id: S.id,
width: M,
height: w,
topLeft: f,
bottomLeft: y,
topRight: p,
bottomRight: b
};
}), x = e * 2;
o = N(o + x, n), s = N(s + x, n), r = k(r - x, n), l = k(l - x, n);
const h = {
x: r,
y: l
}, i = {
x: r,
y: s
}, a = {
x: o,
y: l
}, c = {
x: o,
y: s
}, g = Math.abs(h.x - a.x), m = Math.abs(h.y - i.y);
return { nodeBoxes: u, graphBox: {
topLeft: h,
bottomLeft: i,
topRight: a,
bottomRight: c,
width: g,
height: m,
xMax: o,
yMax: s,
xMin: r,
yMin: l
} };
}, ut = ({
options: t = {},
nodes: e = [],
sourceX: n,
sourceY: o,
targetX: s,
targetY: r,
sourcePosition: l,
targetPosition: u
}) => {
try {
const {
drawEdge: x = z,
generatePath: h = F
} = t;
let { gridRatio: i = 10, nodePadding: a = 10 } = t;
i = D(i), a = D(a);
const { graphBox: c, nodeBoxes: g } = ft(
e,
a,
i
);
t.debug?.enabled && t.debug.setGraphBox && t.debug.setGraphBox({
x: c.topLeft.x,
y: c.topLeft.y,
width: c.width,
height: c.height
});
const m = {
x: n,
y: o,
position: l
}, E = {
x: s,
y: r,
position: u
}, { grid: S, start: M, end: w } = ot(
c,
g,
m,
E,
i
), f = h(S, M, w), y = f.map((q) => {
const [H, J] = q, L = Y(
{ x: H, y: J },
c.xMin,
c.yMin,
i
);
return [L.x, L.y];
}), p = x(m, E, y), b = Math.floor(f.length / 2), C = f[b], [G, Q] = C, { x: Z, y: j } = Y(
{ x: G, y: Q },
c.xMin,
c.yMin,
i
);
return { svgPathString: p, edgeCenterX: Z, edgeCenterY: j };
} catch (x) {
return x instanceof Error ? x : new Error(`Unknown error: ${String(x)}`);
}
}, yt = tt({
enabled: !1,
graphBox: null,
setGraphBox: () => {
}
}), dt = () => et(yt);
function I({
nodes: t,
options: e,
...n
}) {
const { enabled: o, setGraphBox: s } = dt(), {
sourceX: r,
sourceY: l,
sourcePosition: u,
targetX: x,
targetY: h,
targetPosition: i,
style: a,
label: c,
labelStyle: g,
labelShowBg: m,
labelBgStyle: E,
labelBgPadding: S,
labelBgBorderRadius: M,
markerEnd: w,
markerStart: d,
interactionWidth: f
} = n, y = ut({
sourcePosition: u,
targetPosition: i,
sourceX: r,
sourceY: l,
targetX: x,
targetY: h,
options: {
...e,
debug: { enabled: o, setGraphBox: s }
},
nodes: t
}), p = e.fallback ?? _;
if (y instanceof Error)
return o && console.error(y), /* @__PURE__ */ A(p, { ...n });
const { edgeCenterX: b, edgeCenterY: C, svgPathString: G } = y;
return /* @__PURE__ */ A(
K,
{
path: G,
labelX: b,
labelY: C,
label: c,
labelStyle: g,
labelShowBg: m,
labelBgStyle: E,
labelBgPadding: S,
labelBgBorderRadius: M,
style: a,
markerStart: d,
markerEnd: w,
interactionWidth: f
}
);
}
const mt = {
drawEdge: z,
generatePath: F,
fallback: _
};
function Mt(t) {
const e = X();
return /* @__PURE__ */ A(
I,
{
...t,
options: mt,
nodes: e
}
);
}
const St = {
drawEdge: T,
generatePath: F,
fallback: O
};
function Nt(t) {
const e = X();
return /* @__PURE__ */ A(
I,
{
...t,
options: St,
nodes: e
}
);
}
const pt = {
drawEdge: T,
generatePath: gt,
fallback: V
};
function kt(t) {
const e = X();
return /* @__PURE__ */ A(
I,
{
...t,
options: pt,
nodes: e
}
);
}
export {
Mt as SmartBezierEdge,
kt as SmartStepEdge,
Nt as SmartStraightEdge,
ut as getSmartEdge,
F as pathfindingAStarDiagonal,
z as svgDrawSmoothLinePath
};
//# sourceMappingURL=index.mjs.map