@knowvah/dot-engine 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/api/geometry.d.ts +5 -2
  2. package/dist/api/geometry.d.ts.map +1 -1
  3. package/dist/api.js +2 -4
  4. package/dist/api.js.map +2 -2
  5. package/dist/cdt/splay.d.ts.map +1 -1
  6. package/dist/common/htmltable-emit-rules.d.ts.map +1 -1
  7. package/dist/common/shape-clip.d.ts +17 -0
  8. package/dist/common/shape-clip.d.ts.map +1 -0
  9. package/dist/index.js +599 -578
  10. package/dist/index.js.map +4 -4
  11. package/dist/layout/dot/edge-route.d.ts.map +1 -1
  12. package/dist/layout/dot/pack-components.d.ts.map +1 -1
  13. package/dist/layout/dot/sameport.d.ts +1 -13
  14. package/dist/layout/dot/sameport.d.ts.map +1 -1
  15. package/dist/layout/dot/self-loop.d.ts.map +1 -1
  16. package/dist/layout/dot/splines-groups.d.ts.map +1 -1
  17. package/dist/layout/neato/index.d.ts.map +1 -1
  18. package/dist/layout/neato/init.d.ts.map +1 -1
  19. package/dist/ortho/ortho-route.d.ts.map +1 -1
  20. package/dist/render/dot.d.ts.map +1 -1
  21. package/dist/render.js +1209 -1186
  22. package/dist/render.js.map +4 -4
  23. package/package.json +14 -5
  24. package/src/api/geometry.ts +8 -7
  25. package/src/cdt/splay.ts +0 -2
  26. package/src/common/arm-pow.ts +3 -3
  27. package/src/common/htmltable-emit-rules.ts +0 -10
  28. package/src/common/random.ts +2 -2
  29. package/src/common/shape-clip.ts +48 -0
  30. package/src/common/spline-midpoint.ts +1 -1
  31. package/src/layout/circo/circular.ts +1 -1
  32. package/src/layout/dot/edge-route.ts +18 -6
  33. package/src/layout/dot/ns.ts +1 -1
  34. package/src/layout/dot/pack-components.ts +0 -1
  35. package/src/layout/dot/sameport.ts +1 -25
  36. package/src/layout/dot/self-loop.ts +4 -2
  37. package/src/layout/dot/splines-flat-labeled.ts +1 -1
  38. package/src/layout/dot/splines-groups.ts +8 -1
  39. package/src/layout/neato/cdt-surface.ts +1 -1
  40. package/src/layout/neato/index.ts +0 -3
  41. package/src/layout/neato/init.ts +0 -1
  42. package/src/ortho/ortho-route.ts +1 -3
  43. package/src/ortho/trap-query.ts +1 -1
  44. package/src/render/dot.ts +3 -10
  45. package/src/vpsc/Solver.ts +1 -1
package/dist/render.js CHANGED
@@ -21470,7 +21470,7 @@ function leaveEdge(ctx) {
21470
21470
  }
21471
21471
  if (j > 0) {
21472
21472
  ctx.sI = 0;
21473
- [rv, cnt] = leaveEdgeScan(ctx, j, cnt, rv);
21473
+ [rv] = leaveEdgeScan(ctx, j, cnt, rv);
21474
21474
  }
21475
21475
  return rv;
21476
21476
  }
@@ -24494,204 +24494,591 @@ function dotPosition(g) {
24494
24494
  return 0;
24495
24495
  }
24496
24496
 
24497
- // src/pathplan/solvers.ts
24498
- var EPS2 = 1e-7;
24499
- var Solver = class _Solver {
24500
- static aeq0(x) {
24501
- return x < EPS2 && x > -EPS2;
24497
+ // src/common/shape-clip.ts
24498
+ function shapeClip0(ctx, insidefn, n, curve, leftInside) {
24499
+ const saveRealSize = n.info.rw;
24500
+ const c = curve.map((p) => ({ x: p.x - n.info.coord.x, y: p.y - n.info.coord.y }));
24501
+ bezierClip(ctx, insidefn, c, leftInside);
24502
+ for (let i = 0; i < 4; i++) {
24503
+ curve[i] = { x: c[i].x + n.info.coord.x, y: c[i].y + n.info.coord.y };
24502
24504
  }
24503
- static solve1(c, r) {
24504
- if (_Solver.aeq0(c[1])) return _Solver.aeq0(c[0]) ? 4 : 0;
24505
- r[0] = -c[0] / c[1];
24506
- return 1;
24505
+ n.info.rw = saveRealSize;
24506
+ }
24507
+ function shapeClip(n, curve) {
24508
+ const insidefn = n.info.shape?.fns?.insidefn ?? null;
24509
+ if (insidefn === null) return;
24510
+ const ctx = { nodeCoord: n.info.coord, rw: n.info.rw, bp: null, node: n };
24511
+ const saveRealSize = n.info.rw;
24512
+ const leftInside = insidefn(ctx, { x: curve[0].x - n.info.coord.x, y: curve[0].y - n.info.coord.y });
24513
+ n.info.rw = saveRealSize;
24514
+ shapeClip0(ctx, insidefn, n, curve, leftInside);
24515
+ }
24516
+
24517
+ // src/layout/dot/sameport.ts
24518
+ function sameedge(same, e, id) {
24519
+ for (const grp of same) {
24520
+ if (grp.id === id) {
24521
+ grp.edges.push(e);
24522
+ return;
24523
+ }
24507
24524
  }
24508
- static solve2(c, r) {
24509
- if (_Solver.aeq0(c[2])) return _Solver.solve1(c, r);
24510
- const b2a = c[1] / (2 * c[2]);
24511
- const disc = b2a * b2a - c[0] / c[2];
24512
- if (disc < 0) return 0;
24513
- if (disc > 0) {
24514
- r[0] = -b2a + Math.sqrt(disc);
24515
- r[1] = -2 * b2a - r[0];
24516
- return 2;
24525
+ same.push({ id, edges: [e] });
24526
+ }
24527
+ function computeBoundaryOffset(u, x, y, ranksep) {
24528
+ const r = Math.max(
24529
+ (u.info.lw ?? 0) + (u.info.rw ?? 0),
24530
+ (u.info.ht ?? 0) + ranksep
24531
+ );
24532
+ const x2 = x * r + u.info.coord.x;
24533
+ const y2 = y * r + u.info.coord.y;
24534
+ const curve = [
24535
+ { x: u.info.coord.x, y: u.info.coord.y },
24536
+ { x: (2 * u.info.coord.x + x2) / 3, y: (2 * u.info.coord.y + y2) / 3 },
24537
+ { x: (2 * x2 + u.info.coord.x) / 3, y: (2 * y2 + u.info.coord.y) / 3 },
24538
+ { x: x2, y: y2 }
24539
+ ];
24540
+ shapeClip(u, curve);
24541
+ return {
24542
+ x1: curve[0].x - u.info.coord.x,
24543
+ y1: curve[0].y - u.info.coord.y
24544
+ };
24545
+ }
24546
+ function averageDirection(u, edges) {
24547
+ let sx = 0, sy = 0;
24548
+ for (const e of edges) {
24549
+ const v = e.head === u ? e.tail : e.head;
24550
+ const dx = v.info.coord.x - u.info.coord.x;
24551
+ const dy = v.info.coord.y - u.info.coord.y;
24552
+ const r2 = Math.hypot(dx, dy);
24553
+ if (r2 !== 0) {
24554
+ sx += dx / r2;
24555
+ sy += dy / r2;
24517
24556
  }
24518
- r[0] = -b2a;
24519
- return 1;
24520
24557
  }
24521
- static solve3neg(disc, q, b3a, r) {
24522
- const rr = 0.5 * Math.sqrt(-disc + q * q);
24523
- const theta = Math.atan2(Math.sqrt(-disc), -q);
24524
- const temp = 2 * Math.cbrt(rr);
24525
- r[0] = temp * Math.cos(theta / 3) - b3a;
24526
- r[1] = temp * Math.cos((theta + 2 * Math.PI) / 3) - b3a;
24527
- r[2] = temp * Math.cos((theta - 2 * Math.PI) / 3) - b3a;
24528
- return 3;
24558
+ const r = Math.hypot(sx, sy);
24559
+ return r !== 0 ? { x: sx / r, y: sy / r } : { x: 0, y: 0 };
24560
+ }
24561
+ function buildSharedPort(u, edges, ranksep) {
24562
+ const dir = averageDirection(u, edges);
24563
+ const { x1, y1 } = computeBoundaryOffset(u, dir.x, dir.y, ranksep);
24564
+ const px = cround(x1);
24565
+ const py = cround(y1);
24566
+ const lw = u.info.lw ?? 0;
24567
+ const rw = u.info.rw ?? 0;
24568
+ const order = lw + rw !== 0 ? MC_SCALE * (lw + px) / (lw + rw) : 0;
24569
+ return {
24570
+ p: { x: px, y: py },
24571
+ theta: 0,
24572
+ bp: null,
24573
+ defined: true,
24574
+ constrained: false,
24575
+ clip: false,
24576
+ dyna: false,
24577
+ order,
24578
+ side: 0,
24579
+ name: null
24580
+ };
24581
+ }
24582
+ function assignHeadIfMatch(f, u, prt) {
24583
+ if (f.head === u) f.info.head_port = prt;
24584
+ if (f.tail === u) f.info.tail_port = prt;
24585
+ }
24586
+ function assignPortForwardChain(e, u, prt) {
24587
+ for (let f = e; f !== void 0; f = (f.info.edge_type ?? 0) === VIRTUAL && (f.head.info.node_type ?? 0) === VIRTUAL && (f.head.info.out?.size ?? 0) === 1 ? f.head.info.out.list[0] : void 0) {
24588
+ assignHeadIfMatch(f, u, prt);
24529
24589
  }
24530
- static solve3pos(disc, q, b3a, r) {
24531
- const al = 0.5 * (Math.sqrt(disc) - q);
24532
- r[0] = Math.cbrt(al) + Math.cbrt(-q - al) - b3a;
24533
- if (disc > 0) return 1;
24534
- r[1] = r[2] = -0.5 * (r[0] + b3a) - b3a;
24535
- return 3;
24590
+ }
24591
+ function assignPortBackwardChain(e, u, prt) {
24592
+ for (let f = e; f !== void 0; f = (f.info.edge_type ?? 0) === VIRTUAL && (f.tail.info.node_type ?? 0) === VIRTUAL && (f.tail.info.in?.size ?? 0) === 1 ? f.tail.info.in.list[0] : void 0) {
24593
+ assignHeadIfMatch(f, u, prt);
24536
24594
  }
24537
- static solve3(c, r) {
24538
- const a = c[3], b = c[2], cv = c[1], d = c[0];
24539
- if (_Solver.aeq0(a)) return _Solver.solve2(c, r);
24540
- const b3a = b / (3 * a), p0 = b3a * b3a;
24541
- const q = 2 * b3a * p0 - b3a * cv / a + d / a;
24542
- const p = cv / (3 * a) - p0;
24543
- const disc = q * q + 4 * p * p * p;
24544
- return disc < 0 ? _Solver.solve3neg(disc, q, b3a, r) : _Solver.solve3pos(disc, q, b3a, r);
24595
+ }
24596
+ function assignPortToAllVirts(e, u, prt) {
24597
+ for (let virt = e; virt !== void 0; virt = virt.info.to_virt) {
24598
+ assignPortForwardChain(virt, u, prt);
24599
+ assignPortBackwardChain(virt, u, prt);
24545
24600
  }
24546
- };
24547
- function solve3(coeff, roots) {
24548
- return Solver.solve3(coeff, roots);
24549
24601
  }
24550
-
24551
- // src/pathplan/route.ts
24552
- var EPSILON1 = 1e-3;
24553
- var EPSILON2 = 1e-6;
24554
- var RouteHelper = class _RouteHelper {
24555
- static B0(t) {
24556
- const u = 1 - t;
24557
- return u * u * u;
24602
+ function sameport(u, edges, ranksep) {
24603
+ if (edges.length < 2) return;
24604
+ const prt = buildSharedPort(u, edges, ranksep);
24605
+ for (const e of edges) assignPortToAllVirts(e, u, prt);
24606
+ u.info.has_port = true;
24607
+ }
24608
+ function graphHasSamehead(g) {
24609
+ for (const e of g.edges) {
24610
+ if (e.info.samehead !== void 0) return true;
24558
24611
  }
24559
- static B1(t) {
24560
- const u = 1 - t;
24561
- return 3 * t * u * u;
24612
+ return false;
24613
+ }
24614
+ function graphHasSametail(g) {
24615
+ for (const e of g.edges) {
24616
+ if (e.info.sametail !== void 0) return true;
24562
24617
  }
24563
- static B2(t) {
24564
- const u = 1 - t;
24565
- return 3 * t * t * u;
24618
+ return false;
24619
+ }
24620
+ function collectSamehead(n) {
24621
+ const same = [];
24622
+ for (const e of n.root.edges) {
24623
+ if (e.head === n && e.tail === n) continue;
24624
+ const id = e.head === n ? e.info.samehead : void 0;
24625
+ if (id && id !== "") sameedge(same, e, id);
24566
24626
  }
24567
- static B3(t) {
24568
- return t * t * t;
24627
+ return same;
24628
+ }
24629
+ function collectSametail(n) {
24630
+ const same = [];
24631
+ for (const e of n.root.edges) {
24632
+ if (e.head === n && e.tail === n) continue;
24633
+ const id = e.tail === n ? e.info.sametail : void 0;
24634
+ if (id && id !== "") sameedge(same, e, id);
24569
24635
  }
24570
- static B01(t) {
24571
- const u = 1 - t;
24572
- return u * u * (u + 3 * t);
24636
+ return same;
24637
+ }
24638
+ function applyGroupsToNode(n, groups, ranksep) {
24639
+ for (const grp of groups) {
24640
+ if (grp.edges.length > 1) sameport(n, grp.edges, ranksep);
24573
24641
  }
24574
- static B23(t) {
24575
- const u = 1 - t;
24576
- return t * t * (3 * u + t);
24642
+ }
24643
+ function dotSameports(g) {
24644
+ const hasSH = graphHasSamehead(g);
24645
+ const hasST = graphHasSametail(g);
24646
+ if (!hasSH && !hasST) return;
24647
+ const ranksep = g.info.ranksep ?? 0;
24648
+ for (const n of nodesInSeq(g)) {
24649
+ if (hasSH) applyGroupsToNode(n, collectSamehead(n), ranksep);
24650
+ if (hasST) applyGroupsToNode(n, collectSametail(n), ranksep);
24577
24651
  }
24578
- static normv(v) {
24579
- const d = v.x * v.x + v.y * v.y;
24580
- if (d > 1e-6) {
24581
- const s = Math.sqrt(d);
24582
- return { x: v.x / s, y: v.y / s };
24652
+ }
24653
+
24654
+ // src/common/spline-midpoint.ts
24655
+ function dist2(p, q) {
24656
+ const dx = p.x - q.x;
24657
+ const dy = p.y - q.y;
24658
+ return dx * dx + dy * dy;
24659
+ }
24660
+ function distPt(p, q) {
24661
+ return Math.sqrt(dist2(p, q));
24662
+ }
24663
+ function evalBez4(pts, t) {
24664
+ const u = 1 - t;
24665
+ const [p0, p1, p2, p3] = pts;
24666
+ return {
24667
+ x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
24668
+ y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
24669
+ };
24670
+ }
24671
+ function dotneatoClosest(spl, pt) {
24672
+ let besti = 0;
24673
+ let bestj = -1;
24674
+ let bestd2 = Number.MAX_VALUE;
24675
+ for (let i = 0; i < spl.list.length; i++) {
24676
+ const bz2 = spl.list[i];
24677
+ const n = bz2.size ?? bz2.list.length;
24678
+ for (let j2 = 0; j2 < n; j2++) {
24679
+ const d2 = dist2(bz2.list[j2], pt);
24680
+ if (bestj === -1 || d2 < bestd2) {
24681
+ besti = i;
24682
+ bestj = j2;
24683
+ bestd2 = d2;
24684
+ }
24583
24685
  }
24584
- return v;
24585
24686
  }
24586
- static ptAdd(a, b) {
24587
- return { x: a.x + b.x, y: a.y + b.y };
24687
+ const bz = spl.list[besti];
24688
+ const bzn = bz.size ?? bz.list.length;
24689
+ let j = bestj === bzn - 1 ? bestj - 1 : bestj;
24690
+ j = 3 * Math.floor(j / 3);
24691
+ const c = [bz.list[j], bz.list[j + 1], bz.list[j + 2], bz.list[j + 3]];
24692
+ let lo = 0;
24693
+ let hi = 1;
24694
+ let dlo2 = dist2(c[0], pt);
24695
+ let dhi2 = dist2(c[3], pt);
24696
+ let pt2;
24697
+ for (; ; ) {
24698
+ const t = (lo + hi) / 2;
24699
+ pt2 = evalBez4(c, t);
24700
+ if (Math.abs(dlo2 - dhi2) < 1 || Math.abs(hi - lo) < 1e-5) break;
24701
+ if (dlo2 < dhi2) {
24702
+ hi = t;
24703
+ dhi2 = dist2(pt2, pt);
24704
+ } else {
24705
+ lo = t;
24706
+ dlo2 = dist2(pt2, pt);
24707
+ }
24588
24708
  }
24589
- static ptSub(a, b) {
24590
- return { x: a.x - b.x, y: a.y - b.y };
24709
+ return pt2;
24710
+ }
24711
+ function polylineMidpoint(spl) {
24712
+ let total = 0;
24713
+ for (const bz of spl.list) {
24714
+ for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3)
24715
+ total += distPt(bz.list[j], bz.list[k]);
24591
24716
  }
24592
- static ptScale(p, c) {
24593
- return { x: p.x * c, y: p.y * c };
24717
+ total /= 2;
24718
+ for (const bz of spl.list) {
24719
+ for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3) {
24720
+ const pf = bz.list[j];
24721
+ const qf = bz.list[k];
24722
+ const d = distPt(pf, qf);
24723
+ if (d >= total) {
24724
+ return {
24725
+ x: fma(qf.x, total, pf.x * (d - total)) / d,
24726
+ y: fma(qf.y, total, pf.y * (d - total)) / d
24727
+ };
24728
+ }
24729
+ total -= d;
24730
+ }
24594
24731
  }
24595
- static ptDot(a, b) {
24596
- return a.x * b.x + a.y * b.y;
24732
+ const first = spl.list[0];
24733
+ return first.sflag ? first.sp : first.list[0];
24734
+ }
24735
+ function splEndPoints(spl) {
24736
+ const first = spl.list[0];
24737
+ const p = first.sflag ? first.sp : first.list[0];
24738
+ const last = spl.list[spl.list.length - 1];
24739
+ const q = last.eflag ? last.ep : last.list[last.list.length - 1];
24740
+ return { p, q };
24741
+ }
24742
+
24743
+ // src/common/splines-path-shared.ts
24744
+ function addBox(P, b) {
24745
+ if (b.ll.x < b.ur.x && b.ll.y < b.ur.y) {
24746
+ P.boxes[P.nbox++] = b;
24597
24747
  }
24598
- static ptDist(a, b) {
24599
- return Math.hypot(a.x - b.x, a.y - b.y);
24748
+ }
24749
+ function concSlope(n) {
24750
+ const inE = n.info.in;
24751
+ const outE = n.info.out;
24752
+ const cntIn = inE?.size ?? 0;
24753
+ const cntOut = outE?.size ?? 0;
24754
+ let sIn = 0;
24755
+ let sOut = 0;
24756
+ for (let i = 0; i < cntIn; i++) sIn += inE.list[i].tail.info.coord.x;
24757
+ for (let i = 0; i < cntOut; i++) sOut += outE.list[i].head.info.coord.x;
24758
+ const x1 = n.info.coord.x - sIn / cntIn;
24759
+ const y1 = n.info.coord.y - inE.list[0].tail.info.coord.y;
24760
+ const x2 = sOut / cntOut - n.info.coord.x;
24761
+ const y2 = outE.list[0].head.info.coord.y - n.info.coord.y;
24762
+ return (Math.atan2(y1, x1) + Math.atan2(y2, x2)) / 2;
24763
+ }
24764
+ var SIDE_PORT = ["s", "e", "n", "w"];
24765
+ var ALL_SIDES2 = TOP | BOTTOM | LEFT | RIGHT;
24766
+ function cvtPt(p, rankdir) {
24767
+ switch (rankdir) {
24768
+ case RANKDIR_BT:
24769
+ return { x: p.x, y: -p.y };
24770
+ case RANKDIR_LR:
24771
+ return { x: -p.y, y: p.x };
24772
+ case RANKDIR_RL:
24773
+ return { x: p.y, y: p.x };
24774
+ default:
24775
+ return { x: p.x, y: p.y };
24600
24776
  }
24601
- static distN(pts) {
24602
- let rv = 0;
24603
- for (let i = 1; i < pts.length; i++) rv += _RouteHelper.ptDist(pts[i], pts[i - 1]);
24604
- return rv;
24777
+ }
24778
+ function nodeBoxLocal(n, flip) {
24779
+ const lw = n.info.lw, ht22 = n.info.ht / 2;
24780
+ return flip ? { ll: { x: -ht22, y: -lw }, ur: { x: ht22, y: lw } } : { ll: { x: -lw, y: -ht22 }, ur: { x: lw, y: ht22 } };
24781
+ }
24782
+ function faceMidpoint(b, ix) {
24783
+ const cx = (b.ll.x + b.ur.x) / 2, cy = (b.ll.y + b.ur.y) / 2;
24784
+ if (ix === BOTTOM_IX) return { x: cx, y: b.ll.y };
24785
+ if (ix === RIGHT_IX) return { x: b.ur.x, y: cy };
24786
+ if (ix === TOP_IX) return { x: cx, y: b.ur.y };
24787
+ return { x: b.ll.x, y: cy };
24788
+ }
24789
+ function closestSide(n, other, port) {
24790
+ const sides = port.side;
24791
+ if (sides === 0 || sides === ALL_SIDES2) return null;
24792
+ const rkd = (n.root.info.rankdir ?? 0) & 3;
24793
+ const pt = cvtPt(n.info.coord, rkd);
24794
+ const opt = cvtPt(other.info.coord, rkd);
24795
+ const b = port.bp ?? nodeBoxLocal(n, rkd === RANKDIR_LR || rkd === RANKDIR_RL);
24796
+ let rv = null;
24797
+ let mind = 0;
24798
+ for (let i = 0; i < 4; i++) {
24799
+ if ((sides & 1 << i) === 0) continue;
24800
+ const m = faceMidpoint(b, i);
24801
+ const d = dist2({ x: m.x + pt.x, y: m.y + pt.y }, opt);
24802
+ if (rv === null || d < mind) {
24803
+ mind = d;
24804
+ rv = SIDE_PORT[i];
24805
+ }
24605
24806
  }
24606
- static points2coeff(v0, v1, v2, v3, c) {
24607
- c[3] = v3 + 3 * v1 - (v0 + 3 * v2);
24608
- c[2] = 3 * v0 + 3 * v2 - 6 * v1;
24609
- c[1] = 3 * (v1 - v0);
24610
- c[0] = v0;
24807
+ return rv;
24808
+ }
24809
+ function resolvePort(n, other, port) {
24810
+ if (!port.dyna) return port;
24811
+ const compass = closestSide(n, other, port);
24812
+ const rv = makePort();
24813
+ rv.name = port.name;
24814
+ compassPort(n, { bp: port.bp, compass: compass ?? "", sides: port.side }, rv);
24815
+ return rv;
24816
+ }
24817
+ function walkToOrig(e) {
24818
+ let cur = e;
24819
+ while (cur.info.to_orig != null && cur.info.edge_type !== 0) {
24820
+ cur = cur.info.to_orig;
24611
24821
  }
24612
- static addRoot(root, acc) {
24613
- if (root >= 0 && root <= 1) {
24614
- acc.roots[acc.n] = root;
24615
- acc.n++;
24616
- }
24822
+ return cur;
24823
+ }
24824
+ function clearClipForOrig(e, n, isTail) {
24825
+ const orig = walkToOrig(e);
24826
+ if (isTail) {
24827
+ if (n === orig.tail) orig.info.tail_port.clip = false;
24828
+ else orig.info.head_port.clip = false;
24829
+ } else {
24830
+ if (n === orig.head) orig.info.head_port.clip = false;
24831
+ else orig.info.tail_port.clip = false;
24617
24832
  }
24618
- static silBothZero(sps, lc, acc) {
24619
- const sc = [0, 0, 0, 0], xr = [0, 0, 0], yr = [0, 0, 0];
24620
- _RouteHelper.points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, sc);
24621
- sc[0] -= lc.xc0;
24622
- const xn = solve3(sc, xr);
24623
- _RouteHelper.points2coeff(sps[0].y, sps[1].y, sps[2].y, sps[3].y, sc);
24624
- sc[0] -= lc.yc0;
24625
- const yn = solve3(sc, yr);
24626
- if (xn === 4) {
24627
- if (yn === 4) return 4;
24628
- for (let j = 0; j < yn; j++) _RouteHelper.addRoot(yr[j], acc);
24629
- } else if (yn === 4) {
24630
- for (let i = 0; i < xn; i++) _RouteHelper.addRoot(xr[i], acc);
24631
- } else {
24632
- for (let i = 0; i < xn; i++)
24633
- for (let j = 0; j < yn; j++)
24634
- if (xr[i] === yr[j]) _RouteHelper.addRoot(xr[i], acc);
24635
- }
24636
- return acc.n;
24833
+ }
24834
+ function applyDefaultBoxes(P, et, endp) {
24835
+ if (et === FLATEDGE) {
24836
+ if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.start.p.y;
24837
+ else endp.boxes[0].ur.y = P.start.p.y;
24838
+ } else {
24839
+ endp.boxes[0].ur.y = P.start.p.y;
24840
+ endp.sidemask = BOTTOM;
24841
+ P.start.p.y -= 1;
24637
24842
  }
24638
- static silXzeroYnonzero(sps, lc, acc) {
24639
- const sc = [0, 0, 0, 0], xr = [0, 0, 0];
24640
- _RouteHelper.points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, sc);
24641
- sc[0] -= lc.xc0;
24642
- const xn = solve3(sc, xr);
24643
- if (xn === 4) return 4;
24644
- for (let i = 0; i < xn; i++) {
24645
- const tv = xr[i];
24646
- if (tv >= 0 && tv <= 1) {
24647
- _RouteHelper.points2coeff(sps[0].y, sps[1].y, sps[2].y, sps[3].y, sc);
24648
- const sv = (sc[0] + tv * (sc[1] + tv * (sc[2] + tv * sc[3])) - lc.yc0) / lc.yc1;
24649
- if (sv >= 0 && sv <= 1) _RouteHelper.addRoot(tv, acc);
24650
- }
24651
- }
24652
- return acc.n;
24843
+ }
24844
+ function applyEndDefaultBoxes(P, et, endp) {
24845
+ if (et === FLATEDGE) {
24846
+ if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.end.p.y;
24847
+ else endp.boxes[0].ur.y = P.end.p.y;
24848
+ } else {
24849
+ endp.boxes[0].ll.y = P.end.p.y;
24850
+ endp.sidemask = TOP;
24851
+ P.end.p.y += 1;
24653
24852
  }
24654
- static silGeneral(sps, lc, acc) {
24655
- const sc = [0, 0, 0, 0], xr = [0, 0, 0];
24656
- const rat = lc.yc1 / lc.xc1;
24657
- _RouteHelper.points2coeff(
24658
- sps[0].y - rat * sps[0].x,
24659
- sps[1].y - rat * sps[1].x,
24660
- sps[2].y - rat * sps[2].x,
24661
- sps[3].y - rat * sps[3].x,
24662
- sc
24663
- );
24664
- sc[0] += rat * lc.xc0 - lc.yc0;
24665
- const xn = solve3(sc, xr);
24666
- if (xn === 4) return 4;
24667
- for (let i = 0; i < xn; i++) {
24668
- const tv = xr[i];
24669
- if (tv >= 0 && tv <= 1) {
24670
- _RouteHelper.points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, sc);
24671
- const sv = (sc[0] + tv * (sc[1] + tv * (sc[2] + tv * sc[3])) - lc.xc0) / lc.xc1;
24672
- if (sv >= 0 && sv <= 1) _RouteHelper.addRoot(tv, acc);
24673
- }
24674
- }
24675
- return acc.n;
24853
+ }
24854
+ function invokePboxfn(fns, n, port, side, endp) {
24855
+ if (!fns?.pboxfn) return 0;
24856
+ const kptr = [endp.boxn];
24857
+ const mask = fns.pboxfn(n, port, side, endp.boxes, kptr);
24858
+ endp.boxn = kptr[0];
24859
+ return mask;
24860
+ }
24861
+ function setStartTheta(P, e, merge) {
24862
+ if (merge) {
24863
+ P.start.theta = concSlope(e.tail);
24864
+ P.start.constrained = true;
24865
+ } else if (e.info.tail_port.constrained) {
24866
+ P.start.theta = e.info.tail_port.theta;
24867
+ P.start.constrained = true;
24868
+ } else {
24869
+ P.start.constrained = false;
24676
24870
  }
24677
- static splineIntersectsLine(sps, lps, roots) {
24678
- const lc = {
24679
- xc0: lps[0].x,
24680
- xc1: lps[1].x - lps[0].x,
24681
- yc0: lps[0].y,
24682
- yc1: lps[1].y - lps[0].y
24683
- };
24684
- const acc = { roots, n: 0 };
24685
- if (lc.xc1 === 0) {
24686
- if (lc.yc1 === 0) return _RouteHelper.silBothZero(sps, lc, acc);
24687
- return _RouteHelper.silXzeroYnonzero(sps, lc, acc);
24688
- }
24689
- return _RouteHelper.silGeneral(sps, lc, acc);
24871
+ }
24872
+ function setEndTheta(P, e, merge) {
24873
+ if (merge) {
24874
+ P.end.theta = concSlope(e.head) + Math.PI;
24875
+ P.end.constrained = true;
24876
+ } else if (e.info.head_port.constrained) {
24877
+ P.end.theta = e.info.head_port.theta;
24878
+ P.end.constrained = true;
24879
+ } else {
24880
+ P.end.constrained = false;
24690
24881
  }
24691
- static splineIsInside(barriers, sps) {
24692
- const roots = [0, 0, 0, 0];
24693
- for (let ei = 0; ei < barriers.length; ei++) {
24694
- const lps = [barriers[ei].a, barriers[ei].b];
24882
+ }
24883
+
24884
+ // src/pathplan/solvers.ts
24885
+ var EPS2 = 1e-7;
24886
+ var Solver = class _Solver {
24887
+ static aeq0(x) {
24888
+ return x < EPS2 && x > -EPS2;
24889
+ }
24890
+ static solve1(c, r) {
24891
+ if (_Solver.aeq0(c[1])) return _Solver.aeq0(c[0]) ? 4 : 0;
24892
+ r[0] = -c[0] / c[1];
24893
+ return 1;
24894
+ }
24895
+ static solve2(c, r) {
24896
+ if (_Solver.aeq0(c[2])) return _Solver.solve1(c, r);
24897
+ const b2a = c[1] / (2 * c[2]);
24898
+ const disc = b2a * b2a - c[0] / c[2];
24899
+ if (disc < 0) return 0;
24900
+ if (disc > 0) {
24901
+ r[0] = -b2a + Math.sqrt(disc);
24902
+ r[1] = -2 * b2a - r[0];
24903
+ return 2;
24904
+ }
24905
+ r[0] = -b2a;
24906
+ return 1;
24907
+ }
24908
+ static solve3neg(disc, q, b3a, r) {
24909
+ const rr = 0.5 * Math.sqrt(-disc + q * q);
24910
+ const theta = Math.atan2(Math.sqrt(-disc), -q);
24911
+ const temp = 2 * Math.cbrt(rr);
24912
+ r[0] = temp * Math.cos(theta / 3) - b3a;
24913
+ r[1] = temp * Math.cos((theta + 2 * Math.PI) / 3) - b3a;
24914
+ r[2] = temp * Math.cos((theta - 2 * Math.PI) / 3) - b3a;
24915
+ return 3;
24916
+ }
24917
+ static solve3pos(disc, q, b3a, r) {
24918
+ const al = 0.5 * (Math.sqrt(disc) - q);
24919
+ r[0] = Math.cbrt(al) + Math.cbrt(-q - al) - b3a;
24920
+ if (disc > 0) return 1;
24921
+ r[1] = r[2] = -0.5 * (r[0] + b3a) - b3a;
24922
+ return 3;
24923
+ }
24924
+ static solve3(c, r) {
24925
+ const a = c[3], b = c[2], cv = c[1], d = c[0];
24926
+ if (_Solver.aeq0(a)) return _Solver.solve2(c, r);
24927
+ const b3a = b / (3 * a), p0 = b3a * b3a;
24928
+ const q = 2 * b3a * p0 - b3a * cv / a + d / a;
24929
+ const p = cv / (3 * a) - p0;
24930
+ const disc = q * q + 4 * p * p * p;
24931
+ return disc < 0 ? _Solver.solve3neg(disc, q, b3a, r) : _Solver.solve3pos(disc, q, b3a, r);
24932
+ }
24933
+ };
24934
+ function solve3(coeff, roots) {
24935
+ return Solver.solve3(coeff, roots);
24936
+ }
24937
+
24938
+ // src/pathplan/route.ts
24939
+ var EPSILON1 = 1e-3;
24940
+ var EPSILON2 = 1e-6;
24941
+ var RouteHelper = class _RouteHelper {
24942
+ static B0(t) {
24943
+ const u = 1 - t;
24944
+ return u * u * u;
24945
+ }
24946
+ static B1(t) {
24947
+ const u = 1 - t;
24948
+ return 3 * t * u * u;
24949
+ }
24950
+ static B2(t) {
24951
+ const u = 1 - t;
24952
+ return 3 * t * t * u;
24953
+ }
24954
+ static B3(t) {
24955
+ return t * t * t;
24956
+ }
24957
+ static B01(t) {
24958
+ const u = 1 - t;
24959
+ return u * u * (u + 3 * t);
24960
+ }
24961
+ static B23(t) {
24962
+ const u = 1 - t;
24963
+ return t * t * (3 * u + t);
24964
+ }
24965
+ static normv(v) {
24966
+ const d = v.x * v.x + v.y * v.y;
24967
+ if (d > 1e-6) {
24968
+ const s = Math.sqrt(d);
24969
+ return { x: v.x / s, y: v.y / s };
24970
+ }
24971
+ return v;
24972
+ }
24973
+ static ptAdd(a, b) {
24974
+ return { x: a.x + b.x, y: a.y + b.y };
24975
+ }
24976
+ static ptSub(a, b) {
24977
+ return { x: a.x - b.x, y: a.y - b.y };
24978
+ }
24979
+ static ptScale(p, c) {
24980
+ return { x: p.x * c, y: p.y * c };
24981
+ }
24982
+ static ptDot(a, b) {
24983
+ return a.x * b.x + a.y * b.y;
24984
+ }
24985
+ static ptDist(a, b) {
24986
+ return Math.hypot(a.x - b.x, a.y - b.y);
24987
+ }
24988
+ static distN(pts) {
24989
+ let rv = 0;
24990
+ for (let i = 1; i < pts.length; i++) rv += _RouteHelper.ptDist(pts[i], pts[i - 1]);
24991
+ return rv;
24992
+ }
24993
+ static points2coeff(v0, v1, v2, v3, c) {
24994
+ c[3] = v3 + 3 * v1 - (v0 + 3 * v2);
24995
+ c[2] = 3 * v0 + 3 * v2 - 6 * v1;
24996
+ c[1] = 3 * (v1 - v0);
24997
+ c[0] = v0;
24998
+ }
24999
+ static addRoot(root, acc) {
25000
+ if (root >= 0 && root <= 1) {
25001
+ acc.roots[acc.n] = root;
25002
+ acc.n++;
25003
+ }
25004
+ }
25005
+ static silBothZero(sps, lc, acc) {
25006
+ const sc = [0, 0, 0, 0], xr = [0, 0, 0], yr = [0, 0, 0];
25007
+ _RouteHelper.points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, sc);
25008
+ sc[0] -= lc.xc0;
25009
+ const xn = solve3(sc, xr);
25010
+ _RouteHelper.points2coeff(sps[0].y, sps[1].y, sps[2].y, sps[3].y, sc);
25011
+ sc[0] -= lc.yc0;
25012
+ const yn = solve3(sc, yr);
25013
+ if (xn === 4) {
25014
+ if (yn === 4) return 4;
25015
+ for (let j = 0; j < yn; j++) _RouteHelper.addRoot(yr[j], acc);
25016
+ } else if (yn === 4) {
25017
+ for (let i = 0; i < xn; i++) _RouteHelper.addRoot(xr[i], acc);
25018
+ } else {
25019
+ for (let i = 0; i < xn; i++)
25020
+ for (let j = 0; j < yn; j++)
25021
+ if (xr[i] === yr[j]) _RouteHelper.addRoot(xr[i], acc);
25022
+ }
25023
+ return acc.n;
25024
+ }
25025
+ static silXzeroYnonzero(sps, lc, acc) {
25026
+ const sc = [0, 0, 0, 0], xr = [0, 0, 0];
25027
+ _RouteHelper.points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, sc);
25028
+ sc[0] -= lc.xc0;
25029
+ const xn = solve3(sc, xr);
25030
+ if (xn === 4) return 4;
25031
+ for (let i = 0; i < xn; i++) {
25032
+ const tv = xr[i];
25033
+ if (tv >= 0 && tv <= 1) {
25034
+ _RouteHelper.points2coeff(sps[0].y, sps[1].y, sps[2].y, sps[3].y, sc);
25035
+ const sv = (sc[0] + tv * (sc[1] + tv * (sc[2] + tv * sc[3])) - lc.yc0) / lc.yc1;
25036
+ if (sv >= 0 && sv <= 1) _RouteHelper.addRoot(tv, acc);
25037
+ }
25038
+ }
25039
+ return acc.n;
25040
+ }
25041
+ static silGeneral(sps, lc, acc) {
25042
+ const sc = [0, 0, 0, 0], xr = [0, 0, 0];
25043
+ const rat = lc.yc1 / lc.xc1;
25044
+ _RouteHelper.points2coeff(
25045
+ sps[0].y - rat * sps[0].x,
25046
+ sps[1].y - rat * sps[1].x,
25047
+ sps[2].y - rat * sps[2].x,
25048
+ sps[3].y - rat * sps[3].x,
25049
+ sc
25050
+ );
25051
+ sc[0] += rat * lc.xc0 - lc.yc0;
25052
+ const xn = solve3(sc, xr);
25053
+ if (xn === 4) return 4;
25054
+ for (let i = 0; i < xn; i++) {
25055
+ const tv = xr[i];
25056
+ if (tv >= 0 && tv <= 1) {
25057
+ _RouteHelper.points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, sc);
25058
+ const sv = (sc[0] + tv * (sc[1] + tv * (sc[2] + tv * sc[3])) - lc.xc0) / lc.xc1;
25059
+ if (sv >= 0 && sv <= 1) _RouteHelper.addRoot(tv, acc);
25060
+ }
25061
+ }
25062
+ return acc.n;
25063
+ }
25064
+ static splineIntersectsLine(sps, lps, roots) {
25065
+ const lc = {
25066
+ xc0: lps[0].x,
25067
+ xc1: lps[1].x - lps[0].x,
25068
+ yc0: lps[0].y,
25069
+ yc1: lps[1].y - lps[0].y
25070
+ };
25071
+ const acc = { roots, n: 0 };
25072
+ if (lc.xc1 === 0) {
25073
+ if (lc.yc1 === 0) return _RouteHelper.silBothZero(sps, lc, acc);
25074
+ return _RouteHelper.silXzeroYnonzero(sps, lc, acc);
25075
+ }
25076
+ return _RouteHelper.silGeneral(sps, lc, acc);
25077
+ }
25078
+ static splineIsInside(barriers, sps) {
25079
+ const roots = [0, 0, 0, 0];
25080
+ for (let ei = 0; ei < barriers.length; ei++) {
25081
+ const lps = [barriers[ei].a, barriers[ei].b];
24695
25082
  const rootn = _RouteHelper.splineIntersectsLine(sps, lps, roots);
24696
25083
  if (rootn === 4) continue;
24697
25084
  for (let ri = 0; ri < rootn; ri++) {
@@ -24792,1057 +25179,496 @@ var RouteHelper = class _RouteHelper {
24792
25179
  }
24793
25180
  a = a > 0.01 ? a / 2 : 0;
24794
25181
  }
24795
- return 0;
24796
- }
24797
- static reallyRoute(state3, inps, ev0, ev1) {
24798
- const tnas = _RouteHelper.initTnas(inps, ev0, ev1);
24799
- const ends = _RouteHelper.mkspline(inps, tnas, ev0, ev1);
24800
- if (_RouteHelper.splineFits(state3, ends, inps)) return true;
24801
- const maxi = _RouteHelper.findMaxDev(inps, tnas, ends);
24802
- const sv1 = _RouteHelper.normv(_RouteHelper.ptSub(inps[maxi], inps[maxi - 1]));
24803
- const sv2 = _RouteHelper.normv(_RouteHelper.ptSub(inps[maxi + 1], inps[maxi]));
24804
- const splitv = _RouteHelper.normv(_RouteHelper.ptAdd(sv1, sv2));
24805
- if (!_RouteHelper.reallyRoute(state3, inps.slice(0, maxi + 1), ev0, splitv)) return false;
24806
- if (!_RouteHelper.reallyRoute(state3, inps.slice(maxi), splitv, ev1)) return false;
24807
- return true;
24808
- }
24809
- static run(barriers, route, slopes) {
24810
- const state3 = { ops: [], barriers };
24811
- const ev0 = _RouteHelper.normv(slopes[0]);
24812
- const ev1 = _RouteHelper.normv(slopes[1]);
24813
- state3.ops.push(route[0]);
24814
- _RouteHelper.reallyRoute(state3, route, ev0, ev1);
24815
- return state3.ops;
24816
- }
24817
- };
24818
- function routeSpline(barriers, route, slopes) {
24819
- return RouteHelper.run(barriers, route, slopes);
24820
- }
24821
- function polyBarriers(polys) {
24822
- const out = [];
24823
- for (const poly of polys) {
24824
- const n = poly.ps.length;
24825
- for (let j = 0; j < n; j++) out.push({ a: poly.ps[j], b: poly.ps[(j + 1) % n] });
24826
- }
24827
- return out;
24828
- }
24829
- function makePolyline(pts) {
24830
- const out = [];
24831
- out.push(pts[0], pts[0]);
24832
- for (let i = 1; i + 1 < pts.length; i++) out.push(pts[i], pts[i], pts[i]);
24833
- out.push(pts[pts.length - 1], pts[pts.length - 1]);
24834
- return out;
24835
- }
24836
-
24837
- // src/pathplan/triang.ts
24838
- var ISCCW = 1;
24839
- var ISCW = 2;
24840
- var ISON = 3;
24841
- var SPLIT2 = 134217729;
24842
- function twoProductErr(a, b, P) {
24843
- const ac = SPLIT2 * a;
24844
- const ahi = ac - (ac - a);
24845
- const alo = a - ahi;
24846
- const bc = SPLIT2 * b;
24847
- const bhi = bc - (bc - b);
24848
- const blo = b - bhi;
24849
- return ahi * bhi - P + ahi * blo + alo * bhi + alo * blo;
24850
- }
24851
- function twoDiffTail(P, D, s) {
24852
- const bvirt = P - s;
24853
- const avirt = s + bvirt;
24854
- const bround = bvirt - D;
24855
- const around = P - avirt;
24856
- return around + bround;
24857
- }
24858
- var dyadicView = new DataView(new ArrayBuffer(8));
24859
- function dyadic(x) {
24860
- if (x === 0) return { m: 0n, e: 0 };
24861
- dyadicView.setFloat64(0, x);
24862
- const hi = dyadicView.getUint32(0);
24863
- const lo = dyadicView.getUint32(4);
24864
- const sign2 = hi >>> 31 ? -1n : 1n;
24865
- const biasedExp = hi >>> 20 & 2047;
24866
- let mant = BigInt(hi & 1048575) << 32n | BigInt(lo);
24867
- let e;
24868
- if (biasedExp === 0) {
24869
- e = -1074;
24870
- } else {
24871
- mant |= 1n << 52n;
24872
- e = biasedExp - 1075;
24873
- }
24874
- return { m: sign2 * mant, e };
24875
- }
24876
- function signOfSum3(a, b, c) {
24877
- const da = dyadic(a);
24878
- const db = dyadic(b);
24879
- const dc = dyadic(c);
24880
- const e = Math.min(da.e, db.e, dc.e);
24881
- const sum = (da.m << BigInt(da.e - e)) + (db.m << BigInt(db.e - e)) + (dc.m << BigInt(dc.e - e));
24882
- return sum > 0n ? 1 : sum < 0n ? -1 : 0;
24883
- }
24884
- var TriangHelper = class _TriangHelper {
24885
- static ccw(p1, p2, p3) {
24886
- const a = p1.y - p2.y;
24887
- const b = p3.x - p2.x;
24888
- const c = p3.y - p2.y;
24889
- const e = p1.x - p2.x;
24890
- const P = a * b;
24891
- const D = c * e;
24892
- const d = P - D;
24893
- const bound = 23e-17 * (Math.abs(P) + Math.abs(D));
24894
- if (d > bound) return ISCW;
24895
- if (d < -bound) return ISCCW;
24896
- const err = twoProductErr(a, b, P);
24897
- const s = P - D;
24898
- const tail = twoDiffTail(P, D, s);
24899
- const sign2 = signOfSum3(s, tail, err);
24900
- return sign2 > 0 ? ISCW : sign2 < 0 ? ISCCW : ISON;
24901
- }
24902
- /** Is pc on segment [pa, pb]? */
24903
- static between(pa, pb, pc) {
24904
- if (_TriangHelper.ccw(pa, pb, pc) !== ISON) return false;
24905
- const pba = { x: pb.x - pa.x, y: pb.y - pa.y };
24906
- const pca = { x: pc.x - pa.x, y: pc.y - pa.y };
24907
- return pca.x * pba.x + pca.y * pba.y >= 0 && pca.x * pca.x + pca.y * pca.y <= pba.x * pba.x + pba.y * pba.y;
24908
- }
24909
- static anyCollinear(pa, pb, pc, pd) {
24910
- const c = _TriangHelper.ccw;
24911
- return c(pa, pb, pc) === ISON || c(pa, pb, pd) === ISON || c(pc, pd, pa) === ISON || c(pc, pd, pb) === ISON;
24912
- }
24913
- static collinearIntersects(pa, pb, pc, pd) {
24914
- const b = _TriangHelper.between;
24915
- return b(pa, pb, pc) || b(pa, pb, pd) || b(pc, pd, pa) || b(pc, pd, pb);
24916
- }
24917
- static crossIntersects(pa, pb, pc, pd) {
24918
- const c = _TriangHelper.ccw;
24919
- const c1 = c(pa, pb, pc) === ISCCW ? 1 : 0;
24920
- const c2 = c(pa, pb, pd) === ISCCW ? 1 : 0;
24921
- const c3 = c(pc, pd, pa) === ISCCW ? 1 : 0;
24922
- const c4 = c(pc, pd, pb) === ISCCW ? 1 : 0;
24923
- return (c1 ^ c2) !== 0 && (c3 ^ c4) !== 0;
24924
- }
24925
- static intersects(pa, pb, pc, pd) {
24926
- if (_TriangHelper.anyCollinear(pa, pb, pc, pd))
24927
- return _TriangHelper.collinearIntersects(pa, pb, pc, pd);
24928
- return _TriangHelper.crossIntersects(pa, pb, pc, pd);
24929
- }
24930
- static isdiagNeighborhood(i, ip2, get, pointn) {
24931
- const ip1 = (i + 1) % pointn;
24932
- const im1 = (i + pointn - 1) % pointn;
24933
- const c = _TriangHelper.ccw;
24934
- if (c(get(im1), get(i), get(ip1)) === ISCCW) {
24935
- return c(get(i), get(ip2), get(im1)) === ISCCW && c(get(ip2), get(i), get(ip1)) === ISCCW;
24936
- }
24937
- return c(get(i), get(ip2), get(ip1)) === ISCW;
24938
- }
24939
- static isdiagonal(i, ip2, get, pointn) {
24940
- if (!_TriangHelper.isdiagNeighborhood(i, ip2, get, pointn)) return false;
24941
- for (let j = 0; j < pointn; j++) {
24942
- const jp1 = (j + 1) % pointn;
24943
- if (j !== i && jp1 !== i && j !== ip2 && jp1 !== ip2) {
24944
- if (_TriangHelper.intersects(get(i), get(ip2), get(j), get(jp1)))
24945
- return false;
24946
- }
24947
- }
24948
- return true;
24949
- }
24950
- static triangulateInner(pts, fn) {
24951
- let cur = pts;
24952
- while (cur.length > 3) {
24953
- const n = cur.length;
24954
- let cut = -1;
24955
- for (let i = 0; i < n; i++) {
24956
- const ip2 = (i + 2) % n;
24957
- if (_TriangHelper.isdiagonal(i, ip2, (k) => cur[k], n)) {
24958
- fn([cur[i], cur[(i + 1) % n], cur[ip2]]);
24959
- cut = (i + 1) % n;
24960
- break;
24961
- }
24962
- }
24963
- if (cut === -1) return false;
24964
- cur = cur.filter((_, idx) => idx !== cut);
24965
- }
24966
- fn([cur[0], cur[1], cur[2]]);
24967
- return true;
24968
- }
24969
- };
24970
- function ccw(p1, p2, p3) {
24971
- return TriangHelper.ccw(p1, p2, p3);
24972
- }
24973
- function isdiagonal(i, ip2, get, pointn) {
24974
- return TriangHelper.isdiagonal(i, ip2, get, pointn);
24975
- }
24976
-
24977
- // src/pathplan/shortest.ts
24978
- var NO_TRI = -1;
24979
- var DQ_FRONT = 1;
24980
- var DQ_BACK = 2;
24981
- var ShortestHelper = class _ShortestHelper {
24982
- static buildTri(a, b, c) {
24983
- const mk = (p0, p1) => ({ pnl0: p0, pnl1: p1, rightIndex: NO_TRI });
24984
- return { mark: 0, e: [mk(a, b), mk(b, c), mk(c, a)] };
24985
- }
24986
- static triInner(pnlps, tris) {
24987
- let pts = pnlps;
24988
- while (pts.length > 3) {
24989
- const n = pts.length;
24990
- let cut = -1;
24991
- for (let i = 0; i < n; i++) {
24992
- const ip2 = (i + 2) % n;
24993
- if (isdiagonal(i, ip2, (k) => pts[k].pp, n)) {
24994
- tris.push(_ShortestHelper.buildTri(pts[i], pts[(i + 1) % n], pts[ip2]));
24995
- cut = (i + 1) % n;
24996
- break;
24997
- }
24998
- }
24999
- if (cut === -1) return false;
25000
- pts = pts.filter((_, j) => j !== cut);
25001
- }
25002
- tris.push(_ShortestHelper.buildTri(pts[0], pts[1], pts[2]));
25003
- return true;
25004
- }
25005
- static connectTri(i, j, tris) {
25006
- const t1 = tris[i], t2 = tris[j];
25007
- for (let ei = 0; ei < 3; ei++) {
25008
- for (let ej = 0; ej < 3; ej++) {
25009
- const e1 = t1.e[ei], e2 = t2.e[ej];
25010
- if (e1.pnl0 === e2.pnl0 && e1.pnl1 === e2.pnl1 || e1.pnl0 === e2.pnl1 && e1.pnl1 === e2.pnl0)
25011
- e1.rightIndex = j, e2.rightIndex = i;
25012
- }
25013
- }
25014
- }
25015
- static connectAll(tris) {
25016
- for (let i = 0; i < tris.length; i++)
25017
- for (let j = i + 1; j < tris.length; j++)
25018
- _ShortestHelper.connectTri(i, j, tris);
25019
- }
25020
- static pointInTri(tri, p) {
25021
- let sum = 0;
25022
- for (let ei = 0; ei < 3; ei++)
25023
- if (ccw(tri.e[ei].pnl0.pp, tri.e[ei].pnl1.pp, p) !== ISCW) sum++;
25024
- return sum === 3 || sum === 0;
25025
- }
25026
- static markPath(tris, trii, trij) {
25027
- if (tris[trii].mark) return false;
25028
- tris[trii].mark = 1;
25029
- if (trii === trij) return true;
25030
- for (let ei = 0; ei < 3; ei++) {
25031
- const ri = tris[trii].e[ei].rightIndex;
25032
- if (ri !== NO_TRI && _ShortestHelper.markPath(tris, ri, trij)) return true;
25033
- }
25034
- tris[trii].mark = 0;
25035
- return false;
25036
- }
25037
- static findTri(tris, p) {
25038
- for (let i = 0; i < tris.length; i++)
25039
- if (_ShortestHelper.pointInTri(tris[i], p)) return i;
25040
- return NO_TRI;
25041
- }
25042
- static findMinX(ps) {
25043
- let minx = Infinity, minpi = 0;
25044
- for (let i = 0; i < ps.length; i++)
25045
- if (ps[i].x < minx) {
25046
- minx = ps[i].x;
25047
- minpi = i;
25048
- }
25049
- return minpi;
25050
- }
25051
- static isPolyCW(ps, minpi) {
25052
- const pn = ps.length;
25053
- const p2 = ps[minpi];
25054
- const p1 = ps[minpi === 0 ? pn - 1 : minpi - 1];
25055
- const p3 = ps[(minpi + 1) % pn];
25056
- if (p1.x === p2.x && p2.x === p3.x && p3.y > p2.y) return true;
25057
- return ccw(p1, p2, p3) !== ISCCW;
25058
- }
25059
- static loadForward(ps) {
25060
- const r = [];
25061
- for (let i = 0; i < ps.length; i++) {
25062
- if (i > 0 && ps[i].x === ps[i - 1].x && ps[i].y === ps[i - 1].y) continue;
25063
- r.push({ pp: ps[i], link: null });
25064
- }
25065
- return r;
25066
- }
25067
- static loadReverse(ps) {
25068
- const r = [];
25069
- for (let i = ps.length - 1; i >= 0; i--) {
25070
- if (i < ps.length - 1 && ps[i].x === ps[i + 1].x && ps[i].y === ps[i + 1].y) continue;
25071
- r.push({ pp: ps[i], link: null });
25072
- }
25073
- return r;
25074
- }
25075
- static loadPoints(poly) {
25076
- const ps = poly.ps;
25077
- return _ShortestHelper.isPolyCW(ps, _ShortestHelper.findMinX(ps)) ? _ShortestHelper.loadReverse(ps) : _ShortestHelper.loadForward(ps);
25078
- }
25079
- static initDeque(pnll) {
25080
- const pnlpn = pnll * 2;
25081
- const pnlps = new Array(pnlpn).fill(null);
25082
- const fpnlpi = Math.floor(pnlpn / 2);
25083
- return { pnlps, pnlpn, fpnlpi, lpnlpi: fpnlpi - 1, apex: 0 };
25084
- }
25085
- static add2dq(dq, side, pnl) {
25086
- if (side === DQ_FRONT) {
25087
- if (dq.lpnlpi >= dq.fpnlpi) pnl.link = dq.pnlps[dq.fpnlpi];
25088
- dq.fpnlpi--;
25089
- dq.pnlps[dq.fpnlpi] = pnl;
25090
- } else {
25091
- if (dq.lpnlpi >= dq.fpnlpi) pnl.link = dq.pnlps[dq.lpnlpi];
25092
- dq.lpnlpi++;
25093
- dq.pnlps[dq.lpnlpi] = pnl;
25094
- }
25095
- }
25096
- static splitdq(dq, side, index) {
25097
- if (side === DQ_FRONT) dq.lpnlpi = index;
25098
- else dq.fpnlpi = index;
25099
- }
25100
- static finddqsplit(dq, pnl) {
25101
- for (let idx = dq.fpnlpi; idx < dq.apex; idx++)
25102
- if (ccw(dq.pnlps[idx + 1].pp, dq.pnlps[idx].pp, pnl.pp) === ISCCW) return idx;
25103
- for (let idx = dq.lpnlpi; idx > dq.apex; idx--)
25104
- if (ccw(dq.pnlps[idx - 1].pp, dq.pnlps[idx].pp, pnl.pp) === ISCW) return idx;
25105
- return dq.apex;
25106
- }
25107
- static fuFindEi(trip, tris) {
25108
- for (let ei = 0; ei < 3; ei++) {
25109
- const ri = trip.e[ei].rightIndex;
25110
- if (ri !== NO_TRI && tris[ri].mark === 1) return ei;
25111
- }
25112
- return 3;
25113
- }
25114
- static fuNextTri(trip, tris) {
25115
- for (let ei = 0; ei < 3; ei++) {
25116
- const ri = trip.e[ei].rightIndex;
25117
- if (ri !== NO_TRI && tris[ri].mark === 1) return ri;
25118
- }
25119
- return NO_TRI;
25120
- }
25121
- static fuGetPairLast(epnl1, dq) {
25122
- if (ccw(epnl1.pp, dq.pnlps[dq.fpnlpi].pp, dq.pnlps[dq.lpnlpi].pp) === ISCCW)
25123
- return [dq.pnlps[dq.lpnlpi], epnl1];
25124
- return [epnl1, dq.pnlps[dq.lpnlpi]];
25125
- }
25126
- static fuGetPairNormal(trip, ei) {
25127
- const e = trip.e[ei];
25128
- const pnl = trip.e[(ei + 1) % 3].pnl1;
25129
- if (ccw(e.pnl0.pp, pnl.pp, e.pnl1.pp) === ISCCW) return [e.pnl1, e.pnl0];
25130
- return [e.pnl0, e.pnl1];
25131
- }
25132
- static fuUpdateRight(dq, r) {
25133
- const si = _ShortestHelper.finddqsplit(dq, r);
25134
- _ShortestHelper.splitdq(dq, DQ_BACK, si);
25135
- _ShortestHelper.add2dq(dq, DQ_FRONT, r);
25136
- if (si > dq.apex) dq.apex = si;
25137
- }
25138
- static fuUpdateLeft(dq, l) {
25139
- const si = _ShortestHelper.finddqsplit(dq, l);
25140
- _ShortestHelper.splitdq(dq, DQ_FRONT, si);
25141
- _ShortestHelper.add2dq(dq, DQ_BACK, l);
25142
- if (si < dq.apex) dq.apex = si;
25143
- }
25144
- static fuUpdate(dq, l, r, isFirst) {
25145
- if (isFirst) {
25146
- _ShortestHelper.add2dq(dq, DQ_BACK, l);
25147
- _ShortestHelper.add2dq(dq, DQ_FRONT, r);
25148
- return;
25149
- }
25150
- if (dq.pnlps[dq.fpnlpi] !== r && dq.pnlps[dq.lpnlpi] !== r)
25151
- _ShortestHelper.fuUpdateRight(dq, r);
25152
- else
25153
- _ShortestHelper.fuUpdateLeft(dq, l);
25154
- }
25155
- static fuLoop(tris, dq, epnls, ftrii) {
25156
- let trii = ftrii;
25157
- while (trii !== NO_TRI) {
25158
- const trip = tris[trii];
25159
- trip.mark = 2;
25160
- const ei = _ShortestHelper.fuFindEi(trip, tris);
25161
- const [l, r] = ei === 3 ? _ShortestHelper.fuGetPairLast(epnls[1], dq) : _ShortestHelper.fuGetPairNormal(trip, ei);
25162
- _ShortestHelper.fuUpdate(dq, l, r, trii === ftrii);
25163
- trii = _ShortestHelper.fuNextTri(trip, tris);
25164
- }
25165
- }
25166
- static extractPath(epnl1) {
25167
- const pts = [];
25168
- for (let p = epnl1; p !== null; p = p.link) pts.push(p.pp);
25169
- pts.reverse();
25170
- return pts;
25171
- }
25172
- static run(poly, eps) {
25173
- const pnlps = _ShortestHelper.loadPoints(poly);
25174
- const tris = [];
25175
- if (!_ShortestHelper.triInner(pnlps.slice(), tris)) {
25176
- console.warn("triangulation failed");
25177
- }
25178
- _ShortestHelper.connectAll(tris);
25179
- const ftrii = _ShortestHelper.findTri(tris, eps[0]);
25180
- const ltrii = _ShortestHelper.findTri(tris, eps[1]);
25181
- if (ftrii === NO_TRI || ltrii === NO_TRI) return null;
25182
- if (ftrii === ltrii) return [eps[0], eps[1]];
25183
- if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) return [eps[0], eps[1]];
25184
- const epnls = [{ pp: eps[0], link: null }, { pp: eps[1], link: null }];
25185
- const dq = _ShortestHelper.initDeque(pnlps.length);
25186
- _ShortestHelper.add2dq(dq, DQ_FRONT, epnls[0]);
25187
- dq.apex = dq.fpnlpi;
25188
- _ShortestHelper.fuLoop(tris, dq, epnls, ftrii);
25189
- return _ShortestHelper.extractPath(epnls[1]);
25190
- }
25191
- };
25192
- function shortestPath(poly, eps) {
25193
- return ShortestHelper.run(poly, eps);
25194
- }
25195
-
25196
- // src/layout/dot/edge-route-poly.ts
25197
- function fwdDirs(boxes, bi) {
25198
- const n = boxes.length;
25199
- const prev = bi > 0 ? boxes[bi].ll.y > boxes[bi - 1].ll.y ? -1 : 1 : 0;
25200
- const next = bi + 1 < n ? boxes[bi + 1].ll.y > boxes[bi].ll.y ? 1 : -1 : 0;
25201
- return { prev, next };
25202
- }
25203
- function emitFwdBox(b, prev, next, pts) {
25204
- if (prev !== next) {
25205
- if (next === -1 || prev === 1) {
25206
- pts.push({ x: b.ll.x, y: b.ur.y }, { x: b.ll.x, y: b.ll.y });
25207
- } else {
25208
- pts.push({ x: b.ur.x, y: b.ll.y }, { x: b.ur.x, y: b.ur.y });
25209
- }
25210
- } else if (prev === 0) {
25211
- pts.push({ x: b.ll.x, y: b.ur.y }, { x: b.ll.x, y: b.ll.y });
25212
- }
25213
- }
25214
- function addForwardPolyPts(boxes, pts) {
25215
- for (let bi = 0; bi < boxes.length; bi++) {
25216
- const { prev, next } = fwdDirs(boxes, bi);
25217
- emitFwdBox(boxes[bi], prev, next, pts);
25218
- }
25219
- }
25220
- function revDirs(boxes, bi) {
25221
- const n = boxes.length;
25222
- const prev = bi + 1 < n ? boxes[bi].ll.y > boxes[bi + 1].ll.y ? -1 : 1 : 0;
25223
- const next = bi > 0 ? boxes[bi - 1].ll.y > boxes[bi].ll.y ? 1 : -1 : 0;
25224
- return { prev, next };
25225
- }
25226
- function emitAllCorners(b, pts) {
25227
- pts.push(
25228
- { x: b.ur.x, y: b.ll.y },
25229
- { x: b.ur.x, y: b.ur.y },
25230
- { x: b.ll.x, y: b.ur.y },
25231
- { x: b.ll.x, y: b.ll.y }
25232
- );
25233
- }
25234
- function emitRevBox(b, prev, next, pts) {
25235
- if (prev !== next) {
25236
- if (next === -1 || prev === 1) {
25237
- pts.push({ x: b.ll.x, y: b.ur.y }, { x: b.ll.x, y: b.ll.y });
25238
- } else {
25239
- pts.push({ x: b.ur.x, y: b.ll.y }, { x: b.ur.x, y: b.ur.y });
25240
- }
25241
- } else if (prev === 0) {
25242
- pts.push({ x: b.ur.x, y: b.ll.y }, { x: b.ur.x, y: b.ur.y });
25243
- } else {
25244
- emitAllCorners(b, pts);
25245
- }
25246
- }
25247
- function addReversePolyPts(boxes, pts) {
25248
- for (let bi = boxes.length - 1; bi >= 0; bi--) {
25249
- const { prev, next } = revDirs(boxes, bi);
25250
- emitRevBox(boxes[bi], prev, next, pts);
25251
- }
25252
- }
25253
- function boxesToPolygon(boxes) {
25254
- if (boxes.length === 0) return [];
25255
- const pts = [];
25256
- addForwardPolyPts(boxes, pts);
25257
- addReversePolyPts(boxes, pts);
25258
- return pts;
25259
- }
25260
- function polyEdgesFromPts(polygon) {
25261
- const n = polygon.length;
25262
- return polygon.map((a, i) => ({ a, b: polygon[(i + 1) % n] }));
25263
- }
25264
- function linearBezier(p0, p3) {
25265
- return [
25266
- p0,
25267
- { x: p0.x + (p3.x - p0.x) / 3, y: p0.y + (p3.y - p0.y) / 3 },
25268
- { x: p0.x + 2 * (p3.x - p0.x) / 3, y: p0.y + 2 * (p3.y - p0.y) / 3 },
25269
- p3
25270
- ];
25271
- }
25272
- function tryRouteSpline(barriers, wpts) {
25273
- const zero = { x: 0, y: 0 };
25274
- const raw = routeSpline(barriers, wpts, [zero, zero]);
25275
- if (raw.length < 4) return null;
25276
- return [raw[0], raw[1], raw[2], raw[3]];
25277
- }
25278
- function computeSpline(boxes, startPt, endPt) {
25279
- const polygon = boxesToPolygon(boxes);
25280
- if (polygon.length < 3) return linearBezier(startPt, endPt);
25281
- const wpts = shortestPath({ ps: polygon }, [startPt, endPt]);
25282
- if (wpts === null || wpts.length < 2) return linearBezier(startPt, endPt);
25283
- return tryRouteSpline(polyEdgesFromPts(polygon), wpts) ?? linearBezier(startPt, endPt);
25284
- }
25285
-
25286
- // src/layout/dot/edge-route-boxes.ts
25287
- var FUDGE2 = 2;
25288
- function makeTailBox(nb, startPy) {
25289
- return { ll: nb.ll, ur: { x: nb.ur.x, y: startPy } };
25290
- }
25291
- function makeHeadBox(nb, endPy) {
25292
- return { ll: { x: nb.ll.x, y: endPy }, ur: nb.ur };
25293
- }
25294
- function makeRankBox(p) {
25295
- return {
25296
- ll: { x: p.llx, y: p.headCy + p.headHt2 },
25297
- ur: { x: p.urx, y: p.tailCy - p.tailHt1 }
25298
- };
25299
- }
25300
- function makeMaximalBbox(box, ht1, ht22) {
25301
- return {
25302
- ll: { x: box.center.x - box.lw - FUDGE2, y: box.center.y - ht1 },
25303
- ur: { x: box.center.x + box.rw + FUDGE2, y: box.center.y + ht22 }
25304
- };
25305
- }
25306
-
25307
- // src/layout/dot/edge-route-routing.ts
25308
- var ARROW_LENGTH2 = 10;
25309
- var CLIP_BOTH = { tail: true, head: true };
25310
- function buildRankCorridor(tailBox, headBox, rank3, ports) {
25311
- const startPy = tailBox.center.y;
25312
- const endPy = headBox.center.y;
25313
- const tailNb = makeMaximalBbox(tailBox, rank3.tailHt1, rank3.tailHt2);
25314
- const headNb = makeMaximalBbox(headBox, rank3.headHt1, rank3.headHt2);
25315
- const rp = {
25316
- llx: rank3.leftBound,
25317
- urx: rank3.rightBound,
25318
- tailCy: tailBox.center.y,
25319
- tailHt1: rank3.tailHt1,
25320
- headCy: headBox.center.y,
25321
- headHt2: rank3.headHt2
25322
- };
25323
- return {
25324
- startPt: ports?.tailP ?? { x: tailBox.center.x, y: startPy - 1 },
25325
- endPt: ports?.headP ?? { x: headBox.center.x, y: endPy + 1 },
25326
- boxes: [makeTailBox(tailNb, startPy), makeRankBox(rp), makeHeadBox(headNb, endPy)]
25327
- };
25328
- }
25329
- function normalArrowLen(penwidth = DEFAULT_NODEPENWIDTH) {
25330
- const ARROWWIDTH = 0.35;
25331
- return ARROW_LENGTH2 + penwidth * Math.sqrt(1 + ARROWWIDTH * ARROWWIDTH) / (2 * ARROWWIDTH);
25332
- }
25333
- function nodeInsideFn2(box) {
25334
- const halfW = (box.lw + box.rw) / 2;
25335
- const halfH = box.ht / 2;
25336
- const pw = box.penwidth ?? DEFAULT_NODEPENWIDTH;
25337
- return box.isEllipse ? makeEllipseInsideFn(halfW, halfH, pw) : makeBoxInsideFn(halfW, halfH, pw);
25338
- }
25339
- function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
25340
- const step1 = clip.tail ? bezierClipNode(bezier, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier;
25341
- const step2 = clip.head ? bezierClipNode(step1, headBox.center.x, headBox.center.y, nodeInsideFn2(headBox), false) : step1;
25342
- const arrowTip = step2[3];
25343
- const elen = normalArrowLen(penwidth);
25344
- const clipped = arrowEndClip(step2, arrowTip, elen);
25345
- const arrowDir = normalizeVec({
25346
- x: clipped[3].x - arrowTip.x,
25347
- y: clipped[3].y - arrowTip.y
25348
- });
25349
- return { clipped, arrowTip, arrowDir };
25350
- }
25351
- function routeWithRank(tailBox, headBox, rank3, penwidth = DEFAULT_NODEPENWIDTH, port) {
25352
- const corridor = port ? { tailP: port.tailP, headP: port.headP } : void 0;
25353
- const { startPt, endPt, boxes } = buildRankCorridor(tailBox, headBox, rank3, corridor);
25354
- const bezier = computeSpline(boxes, startPt, endPt);
25355
- const clip = port ? { tail: port.clipTail, head: port.clipHead } : CLIP_BOTH;
25356
- const { clipped, arrowTip, arrowDir } = clipToNodes(bezier, tailBox, headBox, penwidth, clip);
25357
- return { bezierPts: clipped, arrowTip, arrowDir };
25358
- }
25359
- function routeSimple(tailBox, headBox, routeBezierFn) {
25360
- const dir = normalizeVec({
25361
- x: headBox.center.x - tailBox.center.x,
25362
- y: headBox.center.y - tailBox.center.y
25363
- });
25364
- const headToTail = negateVec(dir);
25365
- const tailClip = clipToNodeBox(tailBox, dir);
25366
- const arrowTip = clipToNodeBox(headBox, headToTail);
25367
- return {
25368
- bezierPts: routeBezierFn(tailClip, offsetPoint(arrowTip, headToTail, ARROW_LENGTH2)),
25369
- arrowTip,
25370
- arrowDir: headToTail
25371
- };
25372
- }
25373
-
25374
- // src/layout/dot/edge-route-helpers.ts
25375
- function defaultEdgeDir(g) {
25376
- return g.kind === "directed" || g.kind === "strict-directed" ? "forward" : "none";
25377
- }
25378
- var ELLIPSE_SHAPES = /* @__PURE__ */ new Set([
25379
- "ellipse",
25380
- "oval",
25381
- "circle",
25382
- "egg",
25383
- "doublecircle",
25384
- "Mcircle",
25385
- "point"
25386
- ]);
25387
- function recordClipBox(n) {
25388
- const f = n.info.shape_info;
25389
- if (!f?.b) return void 0;
25390
- const halfW = (f.b.ur.x - f.b.ll.x) / 2;
25391
- return { lw: halfW, rw: halfW, ht: f.b.ur.y - f.b.ll.y };
25392
- }
25393
- function nodePenwidthOf(n, g) {
25394
- const v = nodeAttr(n, g, "penwidth");
25395
- const pw = v !== void 0 ? parseFloat(v) : NaN;
25396
- return Number.isFinite(pw) && pw >= 0 ? pw : DEFAULT_NODEPENWIDTH;
25397
- }
25398
- function nodeBoxOf(n, g) {
25399
- const shapeName = nodeAttr(n, g, "shape") ?? "ellipse";
25400
- const penwidth = nodePenwidthOf(n, g);
25401
- if (shapeName === "record" || shapeName === "Mrecord") {
25402
- const rb = recordClipBox(n);
25403
- if (rb) return { center: n.info.coord, ...rb, isEllipse: false, penwidth };
25182
+ return 0;
25404
25183
  }
25405
- const si = n.info.shape_info;
25406
- if (si?.option?.fixedshape === true && si.vertices) {
25407
- const ext = fixedShapeSize(si);
25408
- const flip = g.root.info.flip === true;
25409
- const w = flip ? ext.h : ext.w;
25410
- const h = flip ? ext.w : ext.h;
25411
- return {
25412
- center: n.info.coord,
25413
- lw: w / 2,
25414
- rw: w / 2,
25415
- ht: h,
25416
- isEllipse: ELLIPSE_SHAPES.has(shapeName),
25417
- penwidth: 0
25418
- };
25184
+ static reallyRoute(state3, inps, ev0, ev1) {
25185
+ const tnas = _RouteHelper.initTnas(inps, ev0, ev1);
25186
+ const ends = _RouteHelper.mkspline(inps, tnas, ev0, ev1);
25187
+ if (_RouteHelper.splineFits(state3, ends, inps)) return true;
25188
+ const maxi = _RouteHelper.findMaxDev(inps, tnas, ends);
25189
+ const sv1 = _RouteHelper.normv(_RouteHelper.ptSub(inps[maxi], inps[maxi - 1]));
25190
+ const sv2 = _RouteHelper.normv(_RouteHelper.ptSub(inps[maxi + 1], inps[maxi]));
25191
+ const splitv = _RouteHelper.normv(_RouteHelper.ptAdd(sv1, sv2));
25192
+ if (!_RouteHelper.reallyRoute(state3, inps.slice(0, maxi + 1), ev0, splitv)) return false;
25193
+ if (!_RouteHelper.reallyRoute(state3, inps.slice(maxi), splitv, ev1)) return false;
25194
+ return true;
25419
25195
  }
25420
- return {
25421
- center: n.info.coord,
25422
- lw: n.info.lw > 0 ? n.info.lw : 27,
25423
- rw: n.info.rw > 0 ? n.info.rw : 27,
25424
- ht: n.info.ht > 0 ? n.info.ht : 36,
25425
- isEllipse: ELLIPSE_SHAPES.has(shapeName),
25426
- penwidth
25427
- };
25196
+ static run(barriers, route, slopes) {
25197
+ const state3 = { ops: [], barriers };
25198
+ const ev0 = _RouteHelper.normv(slopes[0]);
25199
+ const ev1 = _RouteHelper.normv(slopes[1]);
25200
+ state3.ops.push(route[0]);
25201
+ _RouteHelper.reallyRoute(state3, route, ev0, ev1);
25202
+ return state3.ops;
25203
+ }
25204
+ };
25205
+ function routeSpline(barriers, route, slopes) {
25206
+ return RouteHelper.run(barriers, route, slopes);
25428
25207
  }
25429
- function edgeRenderPenwidth(e) {
25430
- return resolvePenWidth(parseStyleFlags(e.attrs.get("style")), e.attrs.get("penwidth"));
25208
+ function polyBarriers(polys) {
25209
+ const out = [];
25210
+ for (const poly of polys) {
25211
+ const n = poly.ps.length;
25212
+ for (let j = 0; j < n; j++) out.push({ a: poly.ps[j], b: poly.ps[(j + 1) % n] });
25213
+ }
25214
+ return out;
25431
25215
  }
25432
- function edgePenwidthAttr(e) {
25433
- const pw = parseFloat(e.attrs.get("penwidth") ?? "");
25434
- return isNaN(pw) ? 1 : pw;
25216
+ function makePolyline(pts) {
25217
+ const out = [];
25218
+ out.push(pts[0], pts[0]);
25219
+ for (let i = 1; i + 1 < pts.length; i++) out.push(pts[i], pts[i], pts[i]);
25220
+ out.push(pts[pts.length - 1], pts[pts.length - 1]);
25221
+ return out;
25435
25222
  }
25436
- function makeBezierRecord(bezierPts, arrowTip) {
25437
- return {
25438
- list: bezierPts,
25439
- size: bezierPts.length,
25440
- sflag: 0,
25441
- eflag: 1,
25442
- sp: bezierPts[0] ?? { x: 0, y: 0 },
25443
- ep: arrowTip
25444
- };
25223
+
25224
+ // src/pathplan/triang.ts
25225
+ var ISCCW = 1;
25226
+ var ISCW = 2;
25227
+ var ISON = 3;
25228
+ var SPLIT2 = 134217729;
25229
+ function twoProductErr(a, b, P) {
25230
+ const ac = SPLIT2 * a;
25231
+ const ahi = ac - (ac - a);
25232
+ const alo = a - ahi;
25233
+ const bc = SPLIT2 * b;
25234
+ const bhi = bc - (bc - b);
25235
+ const blo = b - bhi;
25236
+ return ahi * bhi - P + ahi * blo + alo * bhi + alo * blo;
25445
25237
  }
25446
- function makeSplineRecord(bz) {
25447
- return { list: [bz], size: 1, bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } } };
25238
+ function twoDiffTail(P, D, s) {
25239
+ const bvirt = P - s;
25240
+ const avirt = s + bvirt;
25241
+ const bround = bvirt - D;
25242
+ const around = P - avirt;
25243
+ return around + bround;
25448
25244
  }
25449
- function installEdgeSpline(e, bezierPts, arrowTip) {
25450
- e.info.spl = makeSplineRecord(makeBezierRecord(bezierPts, arrowTip));
25451
- const bb = e.tail.root.info.bb;
25452
- if (bb != null) {
25453
- for (let i = 0; i + 3 < bezierPts.length; i += 3) {
25454
- updateBbBz(bb, [bezierPts[i], bezierPts[i + 1], bezierPts[i + 2], bezierPts[i + 3]]);
25455
- }
25245
+ var dyadicView = new DataView(new ArrayBuffer(8));
25246
+ function dyadic(x) {
25247
+ if (x === 0) return { m: 0n, e: 0 };
25248
+ dyadicView.setFloat64(0, x);
25249
+ const hi = dyadicView.getUint32(0);
25250
+ const lo = dyadicView.getUint32(4);
25251
+ const sign2 = hi >>> 31 ? -1n : 1n;
25252
+ const biasedExp = hi >>> 20 & 2047;
25253
+ let mant = BigInt(hi & 1048575) << 32n | BigInt(lo);
25254
+ let e;
25255
+ if (biasedExp === 0) {
25256
+ e = -1074;
25257
+ } else {
25258
+ mant |= 1n << 52n;
25259
+ e = biasedExp - 1075;
25456
25260
  }
25261
+ return { m: sign2 * mant, e };
25457
25262
  }
25458
- function isUsableSpline(raw) {
25459
- if (raw.length < 4) return false;
25460
- const p0 = raw[0];
25461
- const p1 = raw[1];
25462
- return p0.x !== p1.x || p0.y !== p1.y;
25463
- }
25464
- function routeBezier(from, to) {
25465
- const noBarriers = [];
25466
- const zero = { x: 0, y: 0 };
25467
- const raw = routeSpline(noBarriers, [from, to], [zero, zero]);
25468
- if (!isUsableSpline(raw)) return linearBezier(from, to);
25469
- return [raw[0], raw[1], raw[2], raw[3]];
25470
- }
25471
- function straightEdgeSplineWithRank(tailBox, headBox, rankInfo, penwidth = 1, port) {
25472
- return rankInfo !== void 0 ? routeWithRank(tailBox, headBox, rankInfo, penwidth, port) : routeSimple(tailBox, headBox, routeBezier);
25263
+ function signOfSum3(a, b, c) {
25264
+ const da = dyadic(a);
25265
+ const db = dyadic(b);
25266
+ const dc = dyadic(c);
25267
+ const e = Math.min(da.e, db.e, dc.e);
25268
+ const sum = (da.m << BigInt(da.e - e)) + (db.m << BigInt(db.e - e)) + (dc.m << BigInt(dc.e - e));
25269
+ return sum > 0n ? 1 : sum < 0n ? -1 : 0;
25473
25270
  }
25474
-
25475
- // src/layout/dot/sameport.ts
25476
- function sameedge(same, e, id) {
25477
- for (const grp of same) {
25478
- if (grp.id === id) {
25479
- grp.edges.push(e);
25480
- return;
25271
+ var TriangHelper = class _TriangHelper {
25272
+ static ccw(p1, p2, p3) {
25273
+ const a = p1.y - p2.y;
25274
+ const b = p3.x - p2.x;
25275
+ const c = p3.y - p2.y;
25276
+ const e = p1.x - p2.x;
25277
+ const P = a * b;
25278
+ const D = c * e;
25279
+ const d = P - D;
25280
+ const bound = 23e-17 * (Math.abs(P) + Math.abs(D));
25281
+ if (d > bound) return ISCW;
25282
+ if (d < -bound) return ISCCW;
25283
+ const err = twoProductErr(a, b, P);
25284
+ const s = P - D;
25285
+ const tail = twoDiffTail(P, D, s);
25286
+ const sign2 = signOfSum3(s, tail, err);
25287
+ return sign2 > 0 ? ISCW : sign2 < 0 ? ISCCW : ISON;
25288
+ }
25289
+ /** Is pc on segment [pa, pb]? */
25290
+ static between(pa, pb, pc) {
25291
+ if (_TriangHelper.ccw(pa, pb, pc) !== ISON) return false;
25292
+ const pba = { x: pb.x - pa.x, y: pb.y - pa.y };
25293
+ const pca = { x: pc.x - pa.x, y: pc.y - pa.y };
25294
+ return pca.x * pba.x + pca.y * pba.y >= 0 && pca.x * pca.x + pca.y * pca.y <= pba.x * pba.x + pba.y * pba.y;
25295
+ }
25296
+ static anyCollinear(pa, pb, pc, pd) {
25297
+ const c = _TriangHelper.ccw;
25298
+ return c(pa, pb, pc) === ISON || c(pa, pb, pd) === ISON || c(pc, pd, pa) === ISON || c(pc, pd, pb) === ISON;
25299
+ }
25300
+ static collinearIntersects(pa, pb, pc, pd) {
25301
+ const b = _TriangHelper.between;
25302
+ return b(pa, pb, pc) || b(pa, pb, pd) || b(pc, pd, pa) || b(pc, pd, pb);
25303
+ }
25304
+ static crossIntersects(pa, pb, pc, pd) {
25305
+ const c = _TriangHelper.ccw;
25306
+ const c1 = c(pa, pb, pc) === ISCCW ? 1 : 0;
25307
+ const c2 = c(pa, pb, pd) === ISCCW ? 1 : 0;
25308
+ const c3 = c(pc, pd, pa) === ISCCW ? 1 : 0;
25309
+ const c4 = c(pc, pd, pb) === ISCCW ? 1 : 0;
25310
+ return (c1 ^ c2) !== 0 && (c3 ^ c4) !== 0;
25311
+ }
25312
+ static intersects(pa, pb, pc, pd) {
25313
+ if (_TriangHelper.anyCollinear(pa, pb, pc, pd))
25314
+ return _TriangHelper.collinearIntersects(pa, pb, pc, pd);
25315
+ return _TriangHelper.crossIntersects(pa, pb, pc, pd);
25316
+ }
25317
+ static isdiagNeighborhood(i, ip2, get, pointn) {
25318
+ const ip1 = (i + 1) % pointn;
25319
+ const im1 = (i + pointn - 1) % pointn;
25320
+ const c = _TriangHelper.ccw;
25321
+ if (c(get(im1), get(i), get(ip1)) === ISCCW) {
25322
+ return c(get(i), get(ip2), get(im1)) === ISCCW && c(get(ip2), get(i), get(ip1)) === ISCCW;
25481
25323
  }
25324
+ return c(get(i), get(ip2), get(ip1)) === ISCW;
25482
25325
  }
25483
- same.push({ id, edges: [e] });
25484
- }
25485
- function shapeClip(u, curve) {
25486
- const box = nodeBoxOf(u, u.root);
25487
- const clipped = bezierClipNode(curve, box.center.x, box.center.y, nodeInsideFn2(box), true);
25488
- for (let i = 0; i < curve.length && i < clipped.length; i++) curve[i] = clipped[i];
25489
- }
25490
- function computeBoundaryOffset(u, x, y, ranksep) {
25491
- const r = Math.max(
25492
- (u.info.lw ?? 0) + (u.info.rw ?? 0),
25493
- (u.info.ht ?? 0) + ranksep
25494
- );
25495
- const x2 = x * r + u.info.coord.x;
25496
- const y2 = y * r + u.info.coord.y;
25497
- const curve = [
25498
- { x: u.info.coord.x, y: u.info.coord.y },
25499
- { x: (2 * u.info.coord.x + x2) / 3, y: (2 * u.info.coord.y + y2) / 3 },
25500
- { x: (2 * x2 + u.info.coord.x) / 3, y: (2 * y2 + u.info.coord.y) / 3 },
25501
- { x: x2, y: y2 }
25502
- ];
25503
- shapeClip(u, curve);
25504
- return {
25505
- x1: curve[0].x - u.info.coord.x,
25506
- y1: curve[0].y - u.info.coord.y
25507
- };
25508
- }
25509
- function averageDirection(u, edges) {
25510
- let sx = 0, sy = 0;
25511
- for (const e of edges) {
25512
- const v = e.head === u ? e.tail : e.head;
25513
- const dx = v.info.coord.x - u.info.coord.x;
25514
- const dy = v.info.coord.y - u.info.coord.y;
25515
- const r2 = Math.hypot(dx, dy);
25516
- if (r2 !== 0) {
25517
- sx += dx / r2;
25518
- sy += dy / r2;
25326
+ static isdiagonal(i, ip2, get, pointn) {
25327
+ if (!_TriangHelper.isdiagNeighborhood(i, ip2, get, pointn)) return false;
25328
+ for (let j = 0; j < pointn; j++) {
25329
+ const jp1 = (j + 1) % pointn;
25330
+ if (j !== i && jp1 !== i && j !== ip2 && jp1 !== ip2) {
25331
+ if (_TriangHelper.intersects(get(i), get(ip2), get(j), get(jp1)))
25332
+ return false;
25333
+ }
25334
+ }
25335
+ return true;
25336
+ }
25337
+ static triangulateInner(pts, fn) {
25338
+ let cur = pts;
25339
+ while (cur.length > 3) {
25340
+ const n = cur.length;
25341
+ let cut = -1;
25342
+ for (let i = 0; i < n; i++) {
25343
+ const ip2 = (i + 2) % n;
25344
+ if (_TriangHelper.isdiagonal(i, ip2, (k) => cur[k], n)) {
25345
+ fn([cur[i], cur[(i + 1) % n], cur[ip2]]);
25346
+ cut = (i + 1) % n;
25347
+ break;
25348
+ }
25349
+ }
25350
+ if (cut === -1) return false;
25351
+ cur = cur.filter((_, idx) => idx !== cut);
25519
25352
  }
25353
+ fn([cur[0], cur[1], cur[2]]);
25354
+ return true;
25520
25355
  }
25521
- const r = Math.hypot(sx, sy);
25522
- return r !== 0 ? { x: sx / r, y: sy / r } : { x: 0, y: 0 };
25523
- }
25524
- function buildSharedPort(u, edges, ranksep) {
25525
- const dir = averageDirection(u, edges);
25526
- const { x1, y1 } = computeBoundaryOffset(u, dir.x, dir.y, ranksep);
25527
- const px = cround(x1);
25528
- const py = cround(y1);
25529
- const lw = u.info.lw ?? 0;
25530
- const rw = u.info.rw ?? 0;
25531
- const order = lw + rw !== 0 ? MC_SCALE * (lw + px) / (lw + rw) : 0;
25532
- return {
25533
- p: { x: px, y: py },
25534
- theta: 0,
25535
- bp: null,
25536
- defined: true,
25537
- constrained: false,
25538
- clip: false,
25539
- dyna: false,
25540
- order,
25541
- side: 0,
25542
- name: null
25543
- };
25356
+ };
25357
+ function ccw(p1, p2, p3) {
25358
+ return TriangHelper.ccw(p1, p2, p3);
25544
25359
  }
25545
- function assignHeadIfMatch(f, u, prt) {
25546
- if (f.head === u) f.info.head_port = prt;
25547
- if (f.tail === u) f.info.tail_port = prt;
25360
+ function isdiagonal(i, ip2, get, pointn) {
25361
+ return TriangHelper.isdiagonal(i, ip2, get, pointn);
25548
25362
  }
25549
- function assignPortForwardChain(e, u, prt) {
25550
- for (let f = e; f !== void 0; f = (f.info.edge_type ?? 0) === VIRTUAL && (f.head.info.node_type ?? 0) === VIRTUAL && (f.head.info.out?.size ?? 0) === 1 ? f.head.info.out.list[0] : void 0) {
25551
- assignHeadIfMatch(f, u, prt);
25363
+
25364
+ // src/pathplan/shortest.ts
25365
+ var NO_TRI = -1;
25366
+ var DQ_FRONT = 1;
25367
+ var DQ_BACK = 2;
25368
+ var ShortestHelper = class _ShortestHelper {
25369
+ static buildTri(a, b, c) {
25370
+ const mk = (p0, p1) => ({ pnl0: p0, pnl1: p1, rightIndex: NO_TRI });
25371
+ return { mark: 0, e: [mk(a, b), mk(b, c), mk(c, a)] };
25552
25372
  }
25553
- }
25554
- function assignPortBackwardChain(e, u, prt) {
25555
- for (let f = e; f !== void 0; f = (f.info.edge_type ?? 0) === VIRTUAL && (f.tail.info.node_type ?? 0) === VIRTUAL && (f.tail.info.in?.size ?? 0) === 1 ? f.tail.info.in.list[0] : void 0) {
25556
- assignHeadIfMatch(f, u, prt);
25373
+ static triInner(pnlps, tris) {
25374
+ let pts = pnlps;
25375
+ while (pts.length > 3) {
25376
+ const n = pts.length;
25377
+ let cut = -1;
25378
+ for (let i = 0; i < n; i++) {
25379
+ const ip2 = (i + 2) % n;
25380
+ if (isdiagonal(i, ip2, (k) => pts[k].pp, n)) {
25381
+ tris.push(_ShortestHelper.buildTri(pts[i], pts[(i + 1) % n], pts[ip2]));
25382
+ cut = (i + 1) % n;
25383
+ break;
25384
+ }
25385
+ }
25386
+ if (cut === -1) return false;
25387
+ pts = pts.filter((_, j) => j !== cut);
25388
+ }
25389
+ tris.push(_ShortestHelper.buildTri(pts[0], pts[1], pts[2]));
25390
+ return true;
25557
25391
  }
25558
- }
25559
- function assignPortToAllVirts(e, u, prt) {
25560
- for (let virt = e; virt !== void 0; virt = virt.info.to_virt) {
25561
- assignPortForwardChain(virt, u, prt);
25562
- assignPortBackwardChain(virt, u, prt);
25392
+ static connectTri(i, j, tris) {
25393
+ const t1 = tris[i], t2 = tris[j];
25394
+ for (let ei = 0; ei < 3; ei++) {
25395
+ for (let ej = 0; ej < 3; ej++) {
25396
+ const e1 = t1.e[ei], e2 = t2.e[ej];
25397
+ if (e1.pnl0 === e2.pnl0 && e1.pnl1 === e2.pnl1 || e1.pnl0 === e2.pnl1 && e1.pnl1 === e2.pnl0)
25398
+ e1.rightIndex = j, e2.rightIndex = i;
25399
+ }
25400
+ }
25563
25401
  }
25564
- }
25565
- function sameport(u, edges, ranksep) {
25566
- if (edges.length < 2) return;
25567
- const prt = buildSharedPort(u, edges, ranksep);
25568
- for (const e of edges) assignPortToAllVirts(e, u, prt);
25569
- u.info.has_port = true;
25570
- }
25571
- function graphHasSamehead(g) {
25572
- for (const e of g.edges) {
25573
- if (e.info.samehead !== void 0) return true;
25402
+ static connectAll(tris) {
25403
+ for (let i = 0; i < tris.length; i++)
25404
+ for (let j = i + 1; j < tris.length; j++)
25405
+ _ShortestHelper.connectTri(i, j, tris);
25574
25406
  }
25575
- return false;
25576
- }
25577
- function graphHasSametail(g) {
25578
- for (const e of g.edges) {
25579
- if (e.info.sametail !== void 0) return true;
25407
+ static pointInTri(tri, p) {
25408
+ let sum = 0;
25409
+ for (let ei = 0; ei < 3; ei++)
25410
+ if (ccw(tri.e[ei].pnl0.pp, tri.e[ei].pnl1.pp, p) !== ISCW) sum++;
25411
+ return sum === 3 || sum === 0;
25580
25412
  }
25581
- return false;
25582
- }
25583
- function collectSamehead(n) {
25584
- const same = [];
25585
- for (const e of n.root.edges) {
25586
- if (e.head === n && e.tail === n) continue;
25587
- const id = e.head === n ? e.info.samehead : void 0;
25588
- if (id && id !== "") sameedge(same, e, id);
25413
+ static markPath(tris, trii, trij) {
25414
+ if (tris[trii].mark) return false;
25415
+ tris[trii].mark = 1;
25416
+ if (trii === trij) return true;
25417
+ for (let ei = 0; ei < 3; ei++) {
25418
+ const ri = tris[trii].e[ei].rightIndex;
25419
+ if (ri !== NO_TRI && _ShortestHelper.markPath(tris, ri, trij)) return true;
25420
+ }
25421
+ tris[trii].mark = 0;
25422
+ return false;
25589
25423
  }
25590
- return same;
25591
- }
25592
- function collectSametail(n) {
25593
- const same = [];
25594
- for (const e of n.root.edges) {
25595
- if (e.head === n && e.tail === n) continue;
25596
- const id = e.tail === n ? e.info.sametail : void 0;
25597
- if (id && id !== "") sameedge(same, e, id);
25424
+ static findTri(tris, p) {
25425
+ for (let i = 0; i < tris.length; i++)
25426
+ if (_ShortestHelper.pointInTri(tris[i], p)) return i;
25427
+ return NO_TRI;
25598
25428
  }
25599
- return same;
25600
- }
25601
- function applyGroupsToNode(n, groups, ranksep) {
25602
- for (const grp of groups) {
25603
- if (grp.edges.length > 1) sameport(n, grp.edges, ranksep);
25429
+ static findMinX(ps) {
25430
+ let minx = Infinity, minpi = 0;
25431
+ for (let i = 0; i < ps.length; i++)
25432
+ if (ps[i].x < minx) {
25433
+ minx = ps[i].x;
25434
+ minpi = i;
25435
+ }
25436
+ return minpi;
25604
25437
  }
25605
- }
25606
- function dotSameports(g) {
25607
- const hasSH = graphHasSamehead(g);
25608
- const hasST = graphHasSametail(g);
25609
- if (!hasSH && !hasST) return;
25610
- const ranksep = g.info.ranksep ?? 0;
25611
- for (const n of nodesInSeq(g)) {
25612
- if (hasSH) applyGroupsToNode(n, collectSamehead(n), ranksep);
25613
- if (hasST) applyGroupsToNode(n, collectSametail(n), ranksep);
25438
+ static isPolyCW(ps, minpi) {
25439
+ const pn = ps.length;
25440
+ const p2 = ps[minpi];
25441
+ const p1 = ps[minpi === 0 ? pn - 1 : minpi - 1];
25442
+ const p3 = ps[(minpi + 1) % pn];
25443
+ if (p1.x === p2.x && p2.x === p3.x && p3.y > p2.y) return true;
25444
+ return ccw(p1, p2, p3) !== ISCCW;
25614
25445
  }
25615
- }
25616
-
25617
- // src/common/spline-midpoint.ts
25618
- function dist2(p, q) {
25619
- const dx = p.x - q.x;
25620
- const dy = p.y - q.y;
25621
- return dx * dx + dy * dy;
25622
- }
25623
- function distPt(p, q) {
25624
- return Math.sqrt(dist2(p, q));
25625
- }
25626
- function evalBez4(pts, t) {
25627
- const u = 1 - t;
25628
- const [p0, p1, p2, p3] = pts;
25629
- return {
25630
- x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
25631
- y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
25632
- };
25633
- }
25634
- function dotneatoClosest(spl, pt) {
25635
- let besti = 0;
25636
- let bestj = -1;
25637
- let bestd2 = Number.MAX_VALUE;
25638
- for (let i = 0; i < spl.list.length; i++) {
25639
- const bz2 = spl.list[i];
25640
- const n = bz2.size ?? bz2.list.length;
25641
- for (let j2 = 0; j2 < n; j2++) {
25642
- const d2 = dist2(bz2.list[j2], pt);
25643
- if (bestj === -1 || d2 < bestd2) {
25644
- besti = i;
25645
- bestj = j2;
25646
- bestd2 = d2;
25647
- }
25446
+ static loadForward(ps) {
25447
+ const r = [];
25448
+ for (let i = 0; i < ps.length; i++) {
25449
+ if (i > 0 && ps[i].x === ps[i - 1].x && ps[i].y === ps[i - 1].y) continue;
25450
+ r.push({ pp: ps[i], link: null });
25451
+ }
25452
+ return r;
25453
+ }
25454
+ static loadReverse(ps) {
25455
+ const r = [];
25456
+ for (let i = ps.length - 1; i >= 0; i--) {
25457
+ if (i < ps.length - 1 && ps[i].x === ps[i + 1].x && ps[i].y === ps[i + 1].y) continue;
25458
+ r.push({ pp: ps[i], link: null });
25459
+ }
25460
+ return r;
25461
+ }
25462
+ static loadPoints(poly) {
25463
+ const ps = poly.ps;
25464
+ return _ShortestHelper.isPolyCW(ps, _ShortestHelper.findMinX(ps)) ? _ShortestHelper.loadReverse(ps) : _ShortestHelper.loadForward(ps);
25465
+ }
25466
+ static initDeque(pnll) {
25467
+ const pnlpn = pnll * 2;
25468
+ const pnlps = new Array(pnlpn).fill(null);
25469
+ const fpnlpi = Math.floor(pnlpn / 2);
25470
+ return { pnlps, pnlpn, fpnlpi, lpnlpi: fpnlpi - 1, apex: 0 };
25471
+ }
25472
+ static add2dq(dq, side, pnl) {
25473
+ if (side === DQ_FRONT) {
25474
+ if (dq.lpnlpi >= dq.fpnlpi) pnl.link = dq.pnlps[dq.fpnlpi];
25475
+ dq.fpnlpi--;
25476
+ dq.pnlps[dq.fpnlpi] = pnl;
25477
+ } else {
25478
+ if (dq.lpnlpi >= dq.fpnlpi) pnl.link = dq.pnlps[dq.lpnlpi];
25479
+ dq.lpnlpi++;
25480
+ dq.pnlps[dq.lpnlpi] = pnl;
25481
+ }
25482
+ }
25483
+ static splitdq(dq, side, index) {
25484
+ if (side === DQ_FRONT) dq.lpnlpi = index;
25485
+ else dq.fpnlpi = index;
25486
+ }
25487
+ static finddqsplit(dq, pnl) {
25488
+ for (let idx = dq.fpnlpi; idx < dq.apex; idx++)
25489
+ if (ccw(dq.pnlps[idx + 1].pp, dq.pnlps[idx].pp, pnl.pp) === ISCCW) return idx;
25490
+ for (let idx = dq.lpnlpi; idx > dq.apex; idx--)
25491
+ if (ccw(dq.pnlps[idx - 1].pp, dq.pnlps[idx].pp, pnl.pp) === ISCW) return idx;
25492
+ return dq.apex;
25493
+ }
25494
+ static fuFindEi(trip, tris) {
25495
+ for (let ei = 0; ei < 3; ei++) {
25496
+ const ri = trip.e[ei].rightIndex;
25497
+ if (ri !== NO_TRI && tris[ri].mark === 1) return ei;
25498
+ }
25499
+ return 3;
25500
+ }
25501
+ static fuNextTri(trip, tris) {
25502
+ for (let ei = 0; ei < 3; ei++) {
25503
+ const ri = trip.e[ei].rightIndex;
25504
+ if (ri !== NO_TRI && tris[ri].mark === 1) return ri;
25648
25505
  }
25506
+ return NO_TRI;
25649
25507
  }
25650
- const bz = spl.list[besti];
25651
- const bzn = bz.size ?? bz.list.length;
25652
- let j = bestj === bzn - 1 ? bestj - 1 : bestj;
25653
- j = 3 * Math.floor(j / 3);
25654
- const c = [bz.list[j], bz.list[j + 1], bz.list[j + 2], bz.list[j + 3]];
25655
- let lo = 0;
25656
- let hi = 1;
25657
- let dlo2 = dist2(c[0], pt);
25658
- let dhi2 = dist2(c[3], pt);
25659
- let pt2 = c[0];
25660
- for (; ; ) {
25661
- const t = (lo + hi) / 2;
25662
- pt2 = evalBez4(c, t);
25663
- if (Math.abs(dlo2 - dhi2) < 1 || Math.abs(hi - lo) < 1e-5) break;
25664
- if (dlo2 < dhi2) {
25665
- hi = t;
25666
- dhi2 = dist2(pt2, pt);
25667
- } else {
25668
- lo = t;
25669
- dlo2 = dist2(pt2, pt);
25670
- }
25508
+ static fuGetPairLast(epnl1, dq) {
25509
+ if (ccw(epnl1.pp, dq.pnlps[dq.fpnlpi].pp, dq.pnlps[dq.lpnlpi].pp) === ISCCW)
25510
+ return [dq.pnlps[dq.lpnlpi], epnl1];
25511
+ return [epnl1, dq.pnlps[dq.lpnlpi]];
25671
25512
  }
25672
- return pt2;
25673
- }
25674
- function polylineMidpoint(spl) {
25675
- let total = 0;
25676
- for (const bz of spl.list) {
25677
- for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3)
25678
- total += distPt(bz.list[j], bz.list[k]);
25513
+ static fuGetPairNormal(trip, ei) {
25514
+ const e = trip.e[ei];
25515
+ const pnl = trip.e[(ei + 1) % 3].pnl1;
25516
+ if (ccw(e.pnl0.pp, pnl.pp, e.pnl1.pp) === ISCCW) return [e.pnl1, e.pnl0];
25517
+ return [e.pnl0, e.pnl1];
25679
25518
  }
25680
- total /= 2;
25681
- for (const bz of spl.list) {
25682
- for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3) {
25683
- const pf = bz.list[j];
25684
- const qf = bz.list[k];
25685
- const d = distPt(pf, qf);
25686
- if (d >= total) {
25687
- return {
25688
- x: fma(qf.x, total, pf.x * (d - total)) / d,
25689
- y: fma(qf.y, total, pf.y * (d - total)) / d
25690
- };
25691
- }
25692
- total -= d;
25519
+ static fuUpdateRight(dq, r) {
25520
+ const si = _ShortestHelper.finddqsplit(dq, r);
25521
+ _ShortestHelper.splitdq(dq, DQ_BACK, si);
25522
+ _ShortestHelper.add2dq(dq, DQ_FRONT, r);
25523
+ if (si > dq.apex) dq.apex = si;
25524
+ }
25525
+ static fuUpdateLeft(dq, l) {
25526
+ const si = _ShortestHelper.finddqsplit(dq, l);
25527
+ _ShortestHelper.splitdq(dq, DQ_FRONT, si);
25528
+ _ShortestHelper.add2dq(dq, DQ_BACK, l);
25529
+ if (si < dq.apex) dq.apex = si;
25530
+ }
25531
+ static fuUpdate(dq, l, r, isFirst) {
25532
+ if (isFirst) {
25533
+ _ShortestHelper.add2dq(dq, DQ_BACK, l);
25534
+ _ShortestHelper.add2dq(dq, DQ_FRONT, r);
25535
+ return;
25693
25536
  }
25537
+ if (dq.pnlps[dq.fpnlpi] !== r && dq.pnlps[dq.lpnlpi] !== r)
25538
+ _ShortestHelper.fuUpdateRight(dq, r);
25539
+ else
25540
+ _ShortestHelper.fuUpdateLeft(dq, l);
25694
25541
  }
25695
- const first = spl.list[0];
25696
- return first.sflag ? first.sp : first.list[0];
25697
- }
25698
- function splEndPoints(spl) {
25699
- const first = spl.list[0];
25700
- const p = first.sflag ? first.sp : first.list[0];
25701
- const last = spl.list[spl.list.length - 1];
25702
- const q = last.eflag ? last.ep : last.list[last.list.length - 1];
25703
- return { p, q };
25704
- }
25705
-
25706
- // src/common/splines-path-shared.ts
25707
- function addBox(P, b) {
25708
- if (b.ll.x < b.ur.x && b.ll.y < b.ur.y) {
25709
- P.boxes[P.nbox++] = b;
25542
+ static fuLoop(tris, dq, epnls, ftrii) {
25543
+ let trii = ftrii;
25544
+ while (trii !== NO_TRI) {
25545
+ const trip = tris[trii];
25546
+ trip.mark = 2;
25547
+ const ei = _ShortestHelper.fuFindEi(trip, tris);
25548
+ const [l, r] = ei === 3 ? _ShortestHelper.fuGetPairLast(epnls[1], dq) : _ShortestHelper.fuGetPairNormal(trip, ei);
25549
+ _ShortestHelper.fuUpdate(dq, l, r, trii === ftrii);
25550
+ trii = _ShortestHelper.fuNextTri(trip, tris);
25551
+ }
25710
25552
  }
25711
- }
25712
- function concSlope(n) {
25713
- const inE = n.info.in;
25714
- const outE = n.info.out;
25715
- const cntIn = inE?.size ?? 0;
25716
- const cntOut = outE?.size ?? 0;
25717
- let sIn = 0;
25718
- let sOut = 0;
25719
- for (let i = 0; i < cntIn; i++) sIn += inE.list[i].tail.info.coord.x;
25720
- for (let i = 0; i < cntOut; i++) sOut += outE.list[i].head.info.coord.x;
25721
- const x1 = n.info.coord.x - sIn / cntIn;
25722
- const y1 = n.info.coord.y - inE.list[0].tail.info.coord.y;
25723
- const x2 = sOut / cntOut - n.info.coord.x;
25724
- const y2 = outE.list[0].head.info.coord.y - n.info.coord.y;
25725
- return (Math.atan2(y1, x1) + Math.atan2(y2, x2)) / 2;
25726
- }
25727
- var SIDE_PORT = ["s", "e", "n", "w"];
25728
- var ALL_SIDES2 = TOP | BOTTOM | LEFT | RIGHT;
25729
- function cvtPt(p, rankdir) {
25730
- switch (rankdir) {
25731
- case RANKDIR_BT:
25732
- return { x: p.x, y: -p.y };
25733
- case RANKDIR_LR:
25734
- return { x: -p.y, y: p.x };
25735
- case RANKDIR_RL:
25736
- return { x: p.y, y: p.x };
25737
- default:
25738
- return { x: p.x, y: p.y };
25553
+ static extractPath(epnl1) {
25554
+ const pts = [];
25555
+ for (let p = epnl1; p !== null; p = p.link) pts.push(p.pp);
25556
+ pts.reverse();
25557
+ return pts;
25739
25558
  }
25740
- }
25741
- function nodeBoxLocal(n, flip) {
25742
- const lw = n.info.lw, ht22 = n.info.ht / 2;
25743
- return flip ? { ll: { x: -ht22, y: -lw }, ur: { x: ht22, y: lw } } : { ll: { x: -lw, y: -ht22 }, ur: { x: lw, y: ht22 } };
25744
- }
25745
- function faceMidpoint(b, ix) {
25746
- const cx = (b.ll.x + b.ur.x) / 2, cy = (b.ll.y + b.ur.y) / 2;
25747
- if (ix === BOTTOM_IX) return { x: cx, y: b.ll.y };
25748
- if (ix === RIGHT_IX) return { x: b.ur.x, y: cy };
25749
- if (ix === TOP_IX) return { x: cx, y: b.ur.y };
25750
- return { x: b.ll.x, y: cy };
25751
- }
25752
- function closestSide(n, other, port) {
25753
- const sides = port.side;
25754
- if (sides === 0 || sides === ALL_SIDES2) return null;
25755
- const rkd = (n.root.info.rankdir ?? 0) & 3;
25756
- const pt = cvtPt(n.info.coord, rkd);
25757
- const opt = cvtPt(other.info.coord, rkd);
25758
- const b = port.bp ?? nodeBoxLocal(n, rkd === RANKDIR_LR || rkd === RANKDIR_RL);
25759
- let rv = null;
25760
- let mind = 0;
25761
- for (let i = 0; i < 4; i++) {
25762
- if ((sides & 1 << i) === 0) continue;
25763
- const m = faceMidpoint(b, i);
25764
- const d = dist2({ x: m.x + pt.x, y: m.y + pt.y }, opt);
25765
- if (rv === null || d < mind) {
25766
- mind = d;
25767
- rv = SIDE_PORT[i];
25559
+ static run(poly, eps) {
25560
+ const pnlps = _ShortestHelper.loadPoints(poly);
25561
+ const tris = [];
25562
+ if (!_ShortestHelper.triInner(pnlps.slice(), tris)) {
25563
+ console.warn("triangulation failed");
25768
25564
  }
25565
+ _ShortestHelper.connectAll(tris);
25566
+ const ftrii = _ShortestHelper.findTri(tris, eps[0]);
25567
+ const ltrii = _ShortestHelper.findTri(tris, eps[1]);
25568
+ if (ftrii === NO_TRI || ltrii === NO_TRI) return null;
25569
+ if (ftrii === ltrii) return [eps[0], eps[1]];
25570
+ if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) return [eps[0], eps[1]];
25571
+ const epnls = [{ pp: eps[0], link: null }, { pp: eps[1], link: null }];
25572
+ const dq = _ShortestHelper.initDeque(pnlps.length);
25573
+ _ShortestHelper.add2dq(dq, DQ_FRONT, epnls[0]);
25574
+ dq.apex = dq.fpnlpi;
25575
+ _ShortestHelper.fuLoop(tris, dq, epnls, ftrii);
25576
+ return _ShortestHelper.extractPath(epnls[1]);
25769
25577
  }
25770
- return rv;
25771
- }
25772
- function resolvePort(n, other, port) {
25773
- if (!port.dyna) return port;
25774
- const compass = closestSide(n, other, port);
25775
- const rv = makePort();
25776
- rv.name = port.name;
25777
- compassPort(n, { bp: port.bp, compass: compass ?? "", sides: port.side }, rv);
25778
- return rv;
25578
+ };
25579
+ function shortestPath(poly, eps) {
25580
+ return ShortestHelper.run(poly, eps);
25779
25581
  }
25780
- function walkToOrig(e) {
25781
- let cur = e;
25782
- while (cur.info.to_orig != null && cur.info.edge_type !== 0) {
25783
- cur = cur.info.to_orig;
25784
- }
25785
- return cur;
25582
+
25583
+ // src/layout/dot/edge-route-poly.ts
25584
+ function fwdDirs(boxes, bi) {
25585
+ const n = boxes.length;
25586
+ const prev = bi > 0 ? boxes[bi].ll.y > boxes[bi - 1].ll.y ? -1 : 1 : 0;
25587
+ const next = bi + 1 < n ? boxes[bi + 1].ll.y > boxes[bi].ll.y ? 1 : -1 : 0;
25588
+ return { prev, next };
25786
25589
  }
25787
- function clearClipForOrig(e, n, isTail) {
25788
- const orig = walkToOrig(e);
25789
- if (isTail) {
25790
- if (n === orig.tail) orig.info.tail_port.clip = false;
25791
- else orig.info.head_port.clip = false;
25792
- } else {
25793
- if (n === orig.head) orig.info.head_port.clip = false;
25794
- else orig.info.tail_port.clip = false;
25590
+ function emitFwdBox(b, prev, next, pts) {
25591
+ if (prev !== next) {
25592
+ if (next === -1 || prev === 1) {
25593
+ pts.push({ x: b.ll.x, y: b.ur.y }, { x: b.ll.x, y: b.ll.y });
25594
+ } else {
25595
+ pts.push({ x: b.ur.x, y: b.ll.y }, { x: b.ur.x, y: b.ur.y });
25596
+ }
25597
+ } else if (prev === 0) {
25598
+ pts.push({ x: b.ll.x, y: b.ur.y }, { x: b.ll.x, y: b.ll.y });
25795
25599
  }
25796
25600
  }
25797
- function applyDefaultBoxes(P, et, endp) {
25798
- if (et === FLATEDGE) {
25799
- if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.start.p.y;
25800
- else endp.boxes[0].ur.y = P.start.p.y;
25801
- } else {
25802
- endp.boxes[0].ur.y = P.start.p.y;
25803
- endp.sidemask = BOTTOM;
25804
- P.start.p.y -= 1;
25601
+ function addForwardPolyPts(boxes, pts) {
25602
+ for (let bi = 0; bi < boxes.length; bi++) {
25603
+ const { prev, next } = fwdDirs(boxes, bi);
25604
+ emitFwdBox(boxes[bi], prev, next, pts);
25805
25605
  }
25806
25606
  }
25807
- function applyEndDefaultBoxes(P, et, endp) {
25808
- if (et === FLATEDGE) {
25809
- if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.end.p.y;
25810
- else endp.boxes[0].ur.y = P.end.p.y;
25811
- } else {
25812
- endp.boxes[0].ll.y = P.end.p.y;
25813
- endp.sidemask = TOP;
25814
- P.end.p.y += 1;
25815
- }
25607
+ function revDirs(boxes, bi) {
25608
+ const n = boxes.length;
25609
+ const prev = bi + 1 < n ? boxes[bi].ll.y > boxes[bi + 1].ll.y ? -1 : 1 : 0;
25610
+ const next = bi > 0 ? boxes[bi - 1].ll.y > boxes[bi].ll.y ? 1 : -1 : 0;
25611
+ return { prev, next };
25816
25612
  }
25817
- function invokePboxfn(fns, n, port, side, endp) {
25818
- if (!fns?.pboxfn) return 0;
25819
- const kptr = [endp.boxn];
25820
- const mask = fns.pboxfn(n, port, side, endp.boxes, kptr);
25821
- endp.boxn = kptr[0];
25822
- return mask;
25613
+ function emitAllCorners(b, pts) {
25614
+ pts.push(
25615
+ { x: b.ur.x, y: b.ll.y },
25616
+ { x: b.ur.x, y: b.ur.y },
25617
+ { x: b.ll.x, y: b.ur.y },
25618
+ { x: b.ll.x, y: b.ll.y }
25619
+ );
25823
25620
  }
25824
- function setStartTheta(P, e, merge) {
25825
- if (merge) {
25826
- P.start.theta = concSlope(e.tail);
25827
- P.start.constrained = true;
25828
- } else if (e.info.tail_port.constrained) {
25829
- P.start.theta = e.info.tail_port.theta;
25830
- P.start.constrained = true;
25621
+ function emitRevBox(b, prev, next, pts) {
25622
+ if (prev !== next) {
25623
+ if (next === -1 || prev === 1) {
25624
+ pts.push({ x: b.ll.x, y: b.ur.y }, { x: b.ll.x, y: b.ll.y });
25625
+ } else {
25626
+ pts.push({ x: b.ur.x, y: b.ll.y }, { x: b.ur.x, y: b.ur.y });
25627
+ }
25628
+ } else if (prev === 0) {
25629
+ pts.push({ x: b.ur.x, y: b.ll.y }, { x: b.ur.x, y: b.ur.y });
25831
25630
  } else {
25832
- P.start.constrained = false;
25631
+ emitAllCorners(b, pts);
25833
25632
  }
25834
25633
  }
25835
- function setEndTheta(P, e, merge) {
25836
- if (merge) {
25837
- P.end.theta = concSlope(e.head) + Math.PI;
25838
- P.end.constrained = true;
25839
- } else if (e.info.head_port.constrained) {
25840
- P.end.theta = e.info.head_port.theta;
25841
- P.end.constrained = true;
25842
- } else {
25843
- P.end.constrained = false;
25634
+ function addReversePolyPts(boxes, pts) {
25635
+ for (let bi = boxes.length - 1; bi >= 0; bi--) {
25636
+ const { prev, next } = revDirs(boxes, bi);
25637
+ emitRevBox(boxes[bi], prev, next, pts);
25844
25638
  }
25845
25639
  }
25640
+ function boxesToPolygon(boxes) {
25641
+ if (boxes.length === 0) return [];
25642
+ const pts = [];
25643
+ addForwardPolyPts(boxes, pts);
25644
+ addReversePolyPts(boxes, pts);
25645
+ return pts;
25646
+ }
25647
+ function polyEdgesFromPts(polygon) {
25648
+ const n = polygon.length;
25649
+ return polygon.map((a, i) => ({ a, b: polygon[(i + 1) % n] }));
25650
+ }
25651
+ function linearBezier(p0, p3) {
25652
+ return [
25653
+ p0,
25654
+ { x: p0.x + (p3.x - p0.x) / 3, y: p0.y + (p3.y - p0.y) / 3 },
25655
+ { x: p0.x + 2 * (p3.x - p0.x) / 3, y: p0.y + 2 * (p3.y - p0.y) / 3 },
25656
+ p3
25657
+ ];
25658
+ }
25659
+ function tryRouteSpline(barriers, wpts) {
25660
+ const zero = { x: 0, y: 0 };
25661
+ const raw = routeSpline(barriers, wpts, [zero, zero]);
25662
+ if (raw.length < 4) return null;
25663
+ return [raw[0], raw[1], raw[2], raw[3]];
25664
+ }
25665
+ function computeSpline(boxes, startPt, endPt) {
25666
+ const polygon = boxesToPolygon(boxes);
25667
+ if (polygon.length < 3) return linearBezier(startPt, endPt);
25668
+ const wpts = shortestPath({ ps: polygon }, [startPt, endPt]);
25669
+ if (wpts === null || wpts.length < 2) return linearBezier(startPt, endPt);
25670
+ return tryRouteSpline(polyEdgesFromPts(polygon), wpts) ?? linearBezier(startPt, endPt);
25671
+ }
25846
25672
 
25847
25673
  // src/layout/dot/edge-route-rank.ts
25848
25674
  var MINW = 16;
@@ -26898,7 +26724,7 @@ function rankY(g, r) {
26898
26724
  return g.info.rank[r].v[0].info.coord?.y ?? 0;
26899
26725
  }
26900
26726
  function sizeyAtMaxrank(g, n, r) {
26901
- if (r > 0) return rankY(g, r - 1) - (n.info.coord?.y ?? 0);
26727
+ if (r - (g.info.abomShift ?? 0) > 0) return rankY(g, r - 1) - (n.info.coord?.y ?? 0);
26902
26728
  return n.info.ht ?? 0;
26903
26729
  }
26904
26730
  function sizeyAtMinrank(g, n, r) {
@@ -29735,7 +29561,7 @@ function makeFlatLabeledEdge(g, e) {
29735
29561
  const et = edgeType(g);
29736
29562
  if (et === EDGETYPE_LINE) {
29737
29563
  const ps2 = flatLabeledLinePoints(e.tail, e.head, e);
29738
- if (ps2 !== null && ps2.length !== 0) clipAndInstall(e, e.head, ps2, ps2.length, buildDotSinfo());
29564
+ clipAndInstall(e, e.head, ps2, ps2.length, buildDotSinfo());
29739
29565
  return true;
29740
29566
  }
29741
29567
  const tailIsLeft = (e.tail.info.order ?? 0) <= (e.head.info.order ?? 0);
@@ -29919,6 +29745,195 @@ function makeAdjFlatNoPortEdge(g, e) {
29919
29745
  return true;
29920
29746
  }
29921
29747
 
29748
+ // src/layout/dot/edge-route-boxes.ts
29749
+ var FUDGE2 = 2;
29750
+ function makeTailBox(nb, startPy) {
29751
+ return { ll: nb.ll, ur: { x: nb.ur.x, y: startPy } };
29752
+ }
29753
+ function makeHeadBox(nb, endPy) {
29754
+ return { ll: { x: nb.ll.x, y: endPy }, ur: nb.ur };
29755
+ }
29756
+ function makeRankBox(p) {
29757
+ return {
29758
+ ll: { x: p.llx, y: p.headCy + p.headHt2 },
29759
+ ur: { x: p.urx, y: p.tailCy - p.tailHt1 }
29760
+ };
29761
+ }
29762
+ function makeMaximalBbox(box, ht1, ht22) {
29763
+ return {
29764
+ ll: { x: box.center.x - box.lw - FUDGE2, y: box.center.y - ht1 },
29765
+ ur: { x: box.center.x + box.rw + FUDGE2, y: box.center.y + ht22 }
29766
+ };
29767
+ }
29768
+
29769
+ // src/layout/dot/edge-route-routing.ts
29770
+ var ARROW_LENGTH2 = 10;
29771
+ var CLIP_BOTH = { tail: true, head: true };
29772
+ function buildRankCorridor(tailBox, headBox, rank3, ports) {
29773
+ const startPy = tailBox.center.y;
29774
+ const endPy = headBox.center.y;
29775
+ const tailNb = makeMaximalBbox(tailBox, rank3.tailHt1, rank3.tailHt2);
29776
+ const headNb = makeMaximalBbox(headBox, rank3.headHt1, rank3.headHt2);
29777
+ const rp = {
29778
+ llx: rank3.leftBound,
29779
+ urx: rank3.rightBound,
29780
+ tailCy: tailBox.center.y,
29781
+ tailHt1: rank3.tailHt1,
29782
+ headCy: headBox.center.y,
29783
+ headHt2: rank3.headHt2
29784
+ };
29785
+ return {
29786
+ startPt: ports?.tailP ?? { x: tailBox.center.x, y: startPy - 1 },
29787
+ endPt: ports?.headP ?? { x: headBox.center.x, y: endPy + 1 },
29788
+ boxes: [makeTailBox(tailNb, startPy), makeRankBox(rp), makeHeadBox(headNb, endPy)]
29789
+ };
29790
+ }
29791
+ function normalArrowLen(penwidth = DEFAULT_NODEPENWIDTH) {
29792
+ const ARROWWIDTH = 0.35;
29793
+ return ARROW_LENGTH2 + penwidth * Math.sqrt(1 + ARROWWIDTH * ARROWWIDTH) / (2 * ARROWWIDTH);
29794
+ }
29795
+ function nodeInsideFn2(box) {
29796
+ const halfW = (box.lw + box.rw) / 2;
29797
+ const halfH = box.ht / 2;
29798
+ const pw = box.penwidth ?? DEFAULT_NODEPENWIDTH;
29799
+ return box.isEllipse ? makeEllipseInsideFn(halfW, halfH, pw) : makeBoxInsideFn(halfW, halfH, pw);
29800
+ }
29801
+ function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
29802
+ const step1 = clip.tail ? bezierClipNode(bezier, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier;
29803
+ const step2 = clip.head ? bezierClipNode(step1, headBox.center.x, headBox.center.y, nodeInsideFn2(headBox), false) : step1;
29804
+ const arrowTip = step2[3];
29805
+ const elen = normalArrowLen(penwidth);
29806
+ const clipped = arrowEndClip(step2, arrowTip, elen);
29807
+ const arrowDir = normalizeVec({
29808
+ x: clipped[3].x - arrowTip.x,
29809
+ y: clipped[3].y - arrowTip.y
29810
+ });
29811
+ return { clipped, arrowTip, arrowDir };
29812
+ }
29813
+ function routeWithRank(tailBox, headBox, rank3, penwidth = DEFAULT_NODEPENWIDTH, port) {
29814
+ const corridor = port ? { tailP: port.tailP, headP: port.headP } : void 0;
29815
+ const { startPt, endPt, boxes } = buildRankCorridor(tailBox, headBox, rank3, corridor);
29816
+ const bezier = computeSpline(boxes, startPt, endPt);
29817
+ const clip = port ? { tail: port.clipTail, head: port.clipHead } : CLIP_BOTH;
29818
+ const { clipped, arrowTip, arrowDir } = clipToNodes(bezier, tailBox, headBox, penwidth, clip);
29819
+ return { bezierPts: clipped, arrowTip, arrowDir };
29820
+ }
29821
+ function routeSimple(tailBox, headBox, routeBezierFn) {
29822
+ const dir = normalizeVec({
29823
+ x: headBox.center.x - tailBox.center.x,
29824
+ y: headBox.center.y - tailBox.center.y
29825
+ });
29826
+ const headToTail = negateVec(dir);
29827
+ const tailClip = clipToNodeBox(tailBox, dir);
29828
+ const arrowTip = clipToNodeBox(headBox, headToTail);
29829
+ return {
29830
+ bezierPts: routeBezierFn(tailClip, offsetPoint(arrowTip, headToTail, ARROW_LENGTH2)),
29831
+ arrowTip,
29832
+ arrowDir: headToTail
29833
+ };
29834
+ }
29835
+
29836
+ // src/layout/dot/edge-route-helpers.ts
29837
+ function defaultEdgeDir(g) {
29838
+ return g.kind === "directed" || g.kind === "strict-directed" ? "forward" : "none";
29839
+ }
29840
+ var ELLIPSE_SHAPES = /* @__PURE__ */ new Set([
29841
+ "ellipse",
29842
+ "oval",
29843
+ "circle",
29844
+ "egg",
29845
+ "doublecircle",
29846
+ "Mcircle",
29847
+ "point"
29848
+ ]);
29849
+ function recordClipBox(n) {
29850
+ const f = n.info.shape_info;
29851
+ if (!f?.b) return void 0;
29852
+ const halfW = (f.b.ur.x - f.b.ll.x) / 2;
29853
+ return { lw: halfW, rw: halfW, ht: f.b.ur.y - f.b.ll.y };
29854
+ }
29855
+ function nodePenwidthOf(n, g) {
29856
+ const v = nodeAttr(n, g, "penwidth");
29857
+ const pw = v !== void 0 ? parseFloat(v) : NaN;
29858
+ return Number.isFinite(pw) && pw >= 0 ? pw : DEFAULT_NODEPENWIDTH;
29859
+ }
29860
+ function nodeBoxOf(n, g) {
29861
+ const shapeName = nodeAttr(n, g, "shape") ?? "ellipse";
29862
+ const penwidth = nodePenwidthOf(n, g);
29863
+ if (shapeName === "record" || shapeName === "Mrecord") {
29864
+ const rb = recordClipBox(n);
29865
+ if (rb) return { center: n.info.coord, ...rb, isEllipse: false, penwidth };
29866
+ }
29867
+ const si = n.info.shape_info;
29868
+ if (si?.option?.fixedshape === true && si.vertices) {
29869
+ const ext = fixedShapeSize(si);
29870
+ const flip = g.root.info.flip === true;
29871
+ const w = flip ? ext.h : ext.w;
29872
+ const h = flip ? ext.w : ext.h;
29873
+ return {
29874
+ center: n.info.coord,
29875
+ lw: w / 2,
29876
+ rw: w / 2,
29877
+ ht: h,
29878
+ isEllipse: ELLIPSE_SHAPES.has(shapeName),
29879
+ penwidth: 0
29880
+ };
29881
+ }
29882
+ return {
29883
+ center: n.info.coord,
29884
+ lw: n.info.lw > 0 ? n.info.lw : 27,
29885
+ rw: n.info.rw > 0 ? n.info.rw : 27,
29886
+ ht: n.info.ht > 0 ? n.info.ht : 36,
29887
+ isEllipse: ELLIPSE_SHAPES.has(shapeName),
29888
+ penwidth
29889
+ };
29890
+ }
29891
+ function edgeRenderPenwidth(e) {
29892
+ return resolvePenWidth(parseStyleFlags(e.attrs.get("style")), e.attrs.get("penwidth"));
29893
+ }
29894
+ function edgePenwidthAttr(e) {
29895
+ const pw = parseFloat(e.attrs.get("penwidth") ?? "");
29896
+ return isNaN(pw) ? 1 : pw;
29897
+ }
29898
+ function makeBezierRecord(bezierPts, arrowTip) {
29899
+ return {
29900
+ list: bezierPts,
29901
+ size: bezierPts.length,
29902
+ sflag: 0,
29903
+ eflag: 1,
29904
+ sp: bezierPts[0] ?? { x: 0, y: 0 },
29905
+ ep: arrowTip
29906
+ };
29907
+ }
29908
+ function makeSplineRecord(bz) {
29909
+ return { list: [bz], size: 1, bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } } };
29910
+ }
29911
+ function installEdgeSpline(e, bezierPts, arrowTip) {
29912
+ e.info.spl = makeSplineRecord(makeBezierRecord(bezierPts, arrowTip));
29913
+ const bb = e.tail.root.info.bb;
29914
+ if (bb != null) {
29915
+ for (let i = 0; i + 3 < bezierPts.length; i += 3) {
29916
+ updateBbBz(bb, [bezierPts[i], bezierPts[i + 1], bezierPts[i + 2], bezierPts[i + 3]]);
29917
+ }
29918
+ }
29919
+ }
29920
+ function isUsableSpline(raw) {
29921
+ if (raw.length < 4) return false;
29922
+ const p0 = raw[0];
29923
+ const p1 = raw[1];
29924
+ return p0.x !== p1.x || p0.y !== p1.y;
29925
+ }
29926
+ function routeBezier(from, to) {
29927
+ const noBarriers = [];
29928
+ const zero = { x: 0, y: 0 };
29929
+ const raw = routeSpline(noBarriers, [from, to], [zero, zero]);
29930
+ if (!isUsableSpline(raw)) return linearBezier(from, to);
29931
+ return [raw[0], raw[1], raw[2], raw[3]];
29932
+ }
29933
+ function straightEdgeSplineWithRank(tailBox, headBox, rankInfo, penwidth = 1, port) {
29934
+ return rankInfo !== void 0 ? routeWithRank(tailBox, headBox, rankInfo, penwidth, port) : routeSimple(tailBox, headBox, routeBezier);
29935
+ }
29936
+
29922
29937
  // src/layout/dot/edge-route.ts
29923
29938
  function coordsDefined(e) {
29924
29939
  return e.tail.info.coord !== void 0 && e.head.info.coord !== void 0;
@@ -30022,8 +30037,14 @@ function hasSidePort(e) {
30022
30037
  const hp = resolvePort(e.head, e.tail, e.info.head_port);
30023
30038
  return (tp.side ?? 0) !== 0 || (hp.side ?? 0) !== 0;
30024
30039
  }
30040
+ function hasDefinedPort(e) {
30041
+ return e.info.tail_port.defined || e.info.head_port.defined;
30042
+ }
30043
+ function isAdjacentPortFlat(e, g) {
30044
+ return e.tail.info.rank !== void 0 && e.tail.info.rank === e.head.info.rank && isFlatAdjacent(g, e) && hasDefinedPort(e);
30045
+ }
30025
30046
  function isGroupableFlat(x, g) {
30026
- return x.info.spl === void 0 && x.tail.info.rank === x.head.info.rank && isFlatAdjacent(g, x) && hasSidePort(x);
30047
+ return x.info.spl === void 0 && isAdjacentPortFlat(x, g);
30027
30048
  }
30028
30049
  function collectAdjacentFlatGroup(e, g) {
30029
30050
  const u = e.tail, v = e.head;
@@ -30064,7 +30085,7 @@ function routeFlatEdge(e, g) {
30064
30085
  if (isLostFlatAdjRecordEdge(g, e)) return true;
30065
30086
  if (makeFlatLabeledEdge(g, e)) return true;
30066
30087
  if (makeAdjFlatNoPortEdge(g, e)) return true;
30067
- if (hasSidePort(e) && routeFaithfulSidePort(e, g)) return true;
30088
+ if ((hasSidePort(e) || isAdjacentPortFlat(e, g)) && routeFaithfulSidePort(e, g)) return true;
30068
30089
  if (e.tail.info.rank !== void 0 && e.tail.info.rank === e.head.info.rank && e.info.label === void 0 && !isFlatAdjacent(g, e)) {
30069
30090
  const group = collectNonAdjacentFlatGroup(e, g);
30070
30091
  if (group.length > 0 && routeFlatEdgeGroupFaithful(g, group, group.length) && e.info.spl !== void 0) return true;
@@ -30118,8 +30139,8 @@ var M_LEFT = 2;
30118
30139
  var M_BOTTOM = 3;
30119
30140
 
30120
30141
  // src/common/random.ts
30121
- var A = 0x5deece66dn;
30122
- var C = 0xbn;
30142
+ var A = BigInt("0x5deece66d");
30143
+ var C = BigInt("0xb");
30123
30144
  var MASK48 = (1n << 48n) - 1n;
30124
30145
  var TWO48 = 2 ** 48;
30125
30146
  var state = 0x330en;
@@ -32661,8 +32682,11 @@ function dispatchEdgeGroup(g, group, ctx) {
32661
32682
  }
32662
32683
  return;
32663
32684
  }
32664
- if (nodeRankOf(e0.tail) === nodeRankOf(e0.head)) return;
32665
32685
  const uniq = dedupByOrig(group);
32686
+ if (nodeRankOf(e0.tail) === nodeRankOf(e0.head)) {
32687
+ for (const e of uniq) routeLoneEdge(e, g);
32688
+ return;
32689
+ }
32666
32690
  if (uniq.length <= 1) {
32667
32691
  const runOrig = routeEntryRun(g, e0);
32668
32692
  if (runOrig !== null) {
@@ -36974,7 +36998,6 @@ var Cdt = class {
36974
36998
  v1 = r.v1;
36975
36999
  v2 = r.v2;
36976
37000
  v3 = r.v3;
36977
- prev = r.e1;
36978
37001
  e2 = r.e2;
36979
37002
  e3 = r.e3;
36980
37003
  }
@@ -40315,9 +40338,9 @@ var POW_LOG_TABLE_BITS = 7;
40315
40338
  var N_LOG = 1 << POW_LOG_TABLE_BITS;
40316
40339
  var EXP_TABLE_BITS = 7;
40317
40340
  var N_EXP = 1 << EXP_TABLE_BITS;
40318
- var OFF = 0x3fe6955500000000n;
40319
- var INF_BITS = 0x7ff0000000000000n;
40320
- var ONE_BITS = 0x3ff0000000000000n;
40341
+ var OFF = BigInt("0x3fe6955500000000");
40342
+ var INF_BITS = BigInt("0x7ff0000000000000");
40343
+ var ONE_BITS = BigInt("0x3ff0000000000000");
40321
40344
  var SIGN_BIAS = 2048 << EXP_TABLE_BITS;
40322
40345
  var C22 = EC[0];
40323
40346
  var C3 = EC[1];
@@ -45396,7 +45419,7 @@ function circoLayout(g) {
45396
45419
  dg.components = comps;
45397
45420
  let blockCount = 0;
45398
45421
  if (comps.length === 1) {
45399
- blockCount = circularLayout(comps[0], g, blockCount, allEdges3);
45422
+ circularLayout(comps[0], g, blockCount, allEdges3);
45400
45423
  copyPositions(comps[0]);
45401
45424
  adjustNodesFull(g);
45402
45425
  } else {