UNPKG

@tisoap/react-flow-smart-edge

Version:

Custom React Flow Edge that never intersects with other nodes

408 lines (407 loc) 10.6 kB
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