@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/index.js CHANGED
@@ -19894,7 +19894,7 @@ function leaveEdge(ctx) {
19894
19894
  }
19895
19895
  if (j > 0) {
19896
19896
  ctx.sI = 0;
19897
- [rv, cnt] = leaveEdgeScan(ctx, j, cnt, rv);
19897
+ [rv] = leaveEdgeScan(ctx, j, cnt, rv);
19898
19898
  }
19899
19899
  return rv;
19900
19900
  }
@@ -28461,6 +28461,393 @@ function dotPosition(g) {
28461
28461
  return 0;
28462
28462
  }
28463
28463
 
28464
+ // src/common/shape-clip.ts
28465
+ function shapeClip0(ctx, insidefn, n, curve, leftInside) {
28466
+ const saveRealSize = n.info.rw;
28467
+ const c = curve.map((p) => ({ x: p.x - n.info.coord.x, y: p.y - n.info.coord.y }));
28468
+ bezierClip(ctx, insidefn, c, leftInside);
28469
+ for (let i = 0; i < 4; i++) {
28470
+ curve[i] = { x: c[i].x + n.info.coord.x, y: c[i].y + n.info.coord.y };
28471
+ }
28472
+ n.info.rw = saveRealSize;
28473
+ }
28474
+ function shapeClip(n, curve) {
28475
+ const insidefn = n.info.shape?.fns?.insidefn ?? null;
28476
+ if (insidefn === null) return;
28477
+ const ctx = { nodeCoord: n.info.coord, rw: n.info.rw, bp: null, node: n };
28478
+ const saveRealSize = n.info.rw;
28479
+ const leftInside = insidefn(ctx, { x: curve[0].x - n.info.coord.x, y: curve[0].y - n.info.coord.y });
28480
+ n.info.rw = saveRealSize;
28481
+ shapeClip0(ctx, insidefn, n, curve, leftInside);
28482
+ }
28483
+
28484
+ // src/layout/dot/sameport.ts
28485
+ function sameedge(same, e, id) {
28486
+ for (const grp of same) {
28487
+ if (grp.id === id) {
28488
+ grp.edges.push(e);
28489
+ return;
28490
+ }
28491
+ }
28492
+ same.push({ id, edges: [e] });
28493
+ }
28494
+ function computeBoundaryOffset(u, x, y, ranksep) {
28495
+ const r = Math.max(
28496
+ (u.info.lw ?? 0) + (u.info.rw ?? 0),
28497
+ (u.info.ht ?? 0) + ranksep
28498
+ );
28499
+ const x2 = x * r + u.info.coord.x;
28500
+ const y2 = y * r + u.info.coord.y;
28501
+ const curve = [
28502
+ { x: u.info.coord.x, y: u.info.coord.y },
28503
+ { x: (2 * u.info.coord.x + x2) / 3, y: (2 * u.info.coord.y + y2) / 3 },
28504
+ { x: (2 * x2 + u.info.coord.x) / 3, y: (2 * y2 + u.info.coord.y) / 3 },
28505
+ { x: x2, y: y2 }
28506
+ ];
28507
+ shapeClip(u, curve);
28508
+ return {
28509
+ x1: curve[0].x - u.info.coord.x,
28510
+ y1: curve[0].y - u.info.coord.y
28511
+ };
28512
+ }
28513
+ function averageDirection(u, edges) {
28514
+ let sx = 0, sy = 0;
28515
+ for (const e of edges) {
28516
+ const v = e.head === u ? e.tail : e.head;
28517
+ const dx = v.info.coord.x - u.info.coord.x;
28518
+ const dy = v.info.coord.y - u.info.coord.y;
28519
+ const r2 = Math.hypot(dx, dy);
28520
+ if (r2 !== 0) {
28521
+ sx += dx / r2;
28522
+ sy += dy / r2;
28523
+ }
28524
+ }
28525
+ const r = Math.hypot(sx, sy);
28526
+ return r !== 0 ? { x: sx / r, y: sy / r } : { x: 0, y: 0 };
28527
+ }
28528
+ function buildSharedPort(u, edges, ranksep) {
28529
+ const dir = averageDirection(u, edges);
28530
+ const { x1, y1 } = computeBoundaryOffset(u, dir.x, dir.y, ranksep);
28531
+ const px = cround(x1);
28532
+ const py = cround(y1);
28533
+ const lw = u.info.lw ?? 0;
28534
+ const rw = u.info.rw ?? 0;
28535
+ const order = lw + rw !== 0 ? MC_SCALE * (lw + px) / (lw + rw) : 0;
28536
+ return {
28537
+ p: { x: px, y: py },
28538
+ theta: 0,
28539
+ bp: null,
28540
+ defined: true,
28541
+ constrained: false,
28542
+ clip: false,
28543
+ dyna: false,
28544
+ order,
28545
+ side: 0,
28546
+ name: null
28547
+ };
28548
+ }
28549
+ function assignHeadIfMatch(f, u, prt) {
28550
+ if (f.head === u) f.info.head_port = prt;
28551
+ if (f.tail === u) f.info.tail_port = prt;
28552
+ }
28553
+ function assignPortForwardChain(e, u, prt) {
28554
+ 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) {
28555
+ assignHeadIfMatch(f, u, prt);
28556
+ }
28557
+ }
28558
+ function assignPortBackwardChain(e, u, prt) {
28559
+ 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) {
28560
+ assignHeadIfMatch(f, u, prt);
28561
+ }
28562
+ }
28563
+ function assignPortToAllVirts(e, u, prt) {
28564
+ for (let virt = e; virt !== void 0; virt = virt.info.to_virt) {
28565
+ assignPortForwardChain(virt, u, prt);
28566
+ assignPortBackwardChain(virt, u, prt);
28567
+ }
28568
+ }
28569
+ function sameport(u, edges, ranksep) {
28570
+ if (edges.length < 2) return;
28571
+ const prt = buildSharedPort(u, edges, ranksep);
28572
+ for (const e of edges) assignPortToAllVirts(e, u, prt);
28573
+ u.info.has_port = true;
28574
+ }
28575
+ function graphHasSamehead(g) {
28576
+ for (const e of g.edges) {
28577
+ if (e.info.samehead !== void 0) return true;
28578
+ }
28579
+ return false;
28580
+ }
28581
+ function graphHasSametail(g) {
28582
+ for (const e of g.edges) {
28583
+ if (e.info.sametail !== void 0) return true;
28584
+ }
28585
+ return false;
28586
+ }
28587
+ function collectSamehead(n) {
28588
+ const same = [];
28589
+ for (const e of n.root.edges) {
28590
+ if (e.head === n && e.tail === n) continue;
28591
+ const id = e.head === n ? e.info.samehead : void 0;
28592
+ if (id && id !== "") sameedge(same, e, id);
28593
+ }
28594
+ return same;
28595
+ }
28596
+ function collectSametail(n) {
28597
+ const same = [];
28598
+ for (const e of n.root.edges) {
28599
+ if (e.head === n && e.tail === n) continue;
28600
+ const id = e.tail === n ? e.info.sametail : void 0;
28601
+ if (id && id !== "") sameedge(same, e, id);
28602
+ }
28603
+ return same;
28604
+ }
28605
+ function applyGroupsToNode(n, groups, ranksep) {
28606
+ for (const grp of groups) {
28607
+ if (grp.edges.length > 1) sameport(n, grp.edges, ranksep);
28608
+ }
28609
+ }
28610
+ function dotSameports(g) {
28611
+ const hasSH = graphHasSamehead(g);
28612
+ const hasST = graphHasSametail(g);
28613
+ if (!hasSH && !hasST) return;
28614
+ const ranksep = g.info.ranksep ?? 0;
28615
+ for (const n of nodesInSeq(g)) {
28616
+ if (hasSH) applyGroupsToNode(n, collectSamehead(n), ranksep);
28617
+ if (hasST) applyGroupsToNode(n, collectSametail(n), ranksep);
28618
+ }
28619
+ }
28620
+
28621
+ // src/common/spline-midpoint.ts
28622
+ function dist2(p, q) {
28623
+ const dx = p.x - q.x;
28624
+ const dy = p.y - q.y;
28625
+ return dx * dx + dy * dy;
28626
+ }
28627
+ function distPt(p, q) {
28628
+ return Math.sqrt(dist2(p, q));
28629
+ }
28630
+ function evalBez4(pts, t) {
28631
+ const u = 1 - t;
28632
+ const [p0, p1, p2, p3] = pts;
28633
+ return {
28634
+ x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
28635
+ y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
28636
+ };
28637
+ }
28638
+ function dotneatoClosest(spl, pt) {
28639
+ let besti = 0;
28640
+ let bestj = -1;
28641
+ let bestd2 = Number.MAX_VALUE;
28642
+ for (let i = 0; i < spl.list.length; i++) {
28643
+ const bz2 = spl.list[i];
28644
+ const n = bz2.size ?? bz2.list.length;
28645
+ for (let j2 = 0; j2 < n; j2++) {
28646
+ const d2 = dist2(bz2.list[j2], pt);
28647
+ if (bestj === -1 || d2 < bestd2) {
28648
+ besti = i;
28649
+ bestj = j2;
28650
+ bestd2 = d2;
28651
+ }
28652
+ }
28653
+ }
28654
+ const bz = spl.list[besti];
28655
+ const bzn = bz.size ?? bz.list.length;
28656
+ let j = bestj === bzn - 1 ? bestj - 1 : bestj;
28657
+ j = 3 * Math.floor(j / 3);
28658
+ const c = [bz.list[j], bz.list[j + 1], bz.list[j + 2], bz.list[j + 3]];
28659
+ let lo = 0;
28660
+ let hi = 1;
28661
+ let dlo2 = dist2(c[0], pt);
28662
+ let dhi2 = dist2(c[3], pt);
28663
+ let pt2;
28664
+ for (; ; ) {
28665
+ const t = (lo + hi) / 2;
28666
+ pt2 = evalBez4(c, t);
28667
+ if (Math.abs(dlo2 - dhi2) < 1 || Math.abs(hi - lo) < 1e-5) break;
28668
+ if (dlo2 < dhi2) {
28669
+ hi = t;
28670
+ dhi2 = dist2(pt2, pt);
28671
+ } else {
28672
+ lo = t;
28673
+ dlo2 = dist2(pt2, pt);
28674
+ }
28675
+ }
28676
+ return pt2;
28677
+ }
28678
+ function polylineMidpoint(spl) {
28679
+ let total = 0;
28680
+ for (const bz of spl.list) {
28681
+ for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3)
28682
+ total += distPt(bz.list[j], bz.list[k]);
28683
+ }
28684
+ total /= 2;
28685
+ for (const bz of spl.list) {
28686
+ for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3) {
28687
+ const pf = bz.list[j];
28688
+ const qf = bz.list[k];
28689
+ const d = distPt(pf, qf);
28690
+ if (d >= total) {
28691
+ return {
28692
+ x: fma(qf.x, total, pf.x * (d - total)) / d,
28693
+ y: fma(qf.y, total, pf.y * (d - total)) / d
28694
+ };
28695
+ }
28696
+ total -= d;
28697
+ }
28698
+ }
28699
+ const first = spl.list[0];
28700
+ return first.sflag ? first.sp : first.list[0];
28701
+ }
28702
+ function splEndPoints(spl) {
28703
+ const first = spl.list[0];
28704
+ const p = first.sflag ? first.sp : first.list[0];
28705
+ const last = spl.list[spl.list.length - 1];
28706
+ const q = last.eflag ? last.ep : last.list[last.list.length - 1];
28707
+ return { p, q };
28708
+ }
28709
+
28710
+ // src/common/splines-path-shared.ts
28711
+ function addBox(P, b) {
28712
+ if (b.ll.x < b.ur.x && b.ll.y < b.ur.y) {
28713
+ P.boxes[P.nbox++] = b;
28714
+ }
28715
+ }
28716
+ function concSlope(n) {
28717
+ const inE = n.info.in;
28718
+ const outE = n.info.out;
28719
+ const cntIn = inE?.size ?? 0;
28720
+ const cntOut = outE?.size ?? 0;
28721
+ let sIn = 0;
28722
+ let sOut = 0;
28723
+ for (let i = 0; i < cntIn; i++) sIn += inE.list[i].tail.info.coord.x;
28724
+ for (let i = 0; i < cntOut; i++) sOut += outE.list[i].head.info.coord.x;
28725
+ const x1 = n.info.coord.x - sIn / cntIn;
28726
+ const y1 = n.info.coord.y - inE.list[0].tail.info.coord.y;
28727
+ const x2 = sOut / cntOut - n.info.coord.x;
28728
+ const y2 = outE.list[0].head.info.coord.y - n.info.coord.y;
28729
+ return (Math.atan2(y1, x1) + Math.atan2(y2, x2)) / 2;
28730
+ }
28731
+ var SIDE_PORT = ["s", "e", "n", "w"];
28732
+ var ALL_SIDES2 = TOP | BOTTOM | LEFT | RIGHT;
28733
+ function cvtPt(p, rankdir) {
28734
+ switch (rankdir) {
28735
+ case RANKDIR_BT:
28736
+ return { x: p.x, y: -p.y };
28737
+ case RANKDIR_LR:
28738
+ return { x: -p.y, y: p.x };
28739
+ case RANKDIR_RL:
28740
+ return { x: p.y, y: p.x };
28741
+ default:
28742
+ return { x: p.x, y: p.y };
28743
+ }
28744
+ }
28745
+ function nodeBoxLocal(n, flip) {
28746
+ const lw = n.info.lw, ht22 = n.info.ht / 2;
28747
+ return flip ? { ll: { x: -ht22, y: -lw }, ur: { x: ht22, y: lw } } : { ll: { x: -lw, y: -ht22 }, ur: { x: lw, y: ht22 } };
28748
+ }
28749
+ function faceMidpoint(b, ix) {
28750
+ const cx = (b.ll.x + b.ur.x) / 2, cy = (b.ll.y + b.ur.y) / 2;
28751
+ if (ix === BOTTOM_IX) return { x: cx, y: b.ll.y };
28752
+ if (ix === RIGHT_IX) return { x: b.ur.x, y: cy };
28753
+ if (ix === TOP_IX) return { x: cx, y: b.ur.y };
28754
+ return { x: b.ll.x, y: cy };
28755
+ }
28756
+ function closestSide(n, other, port) {
28757
+ const sides = port.side;
28758
+ if (sides === 0 || sides === ALL_SIDES2) return null;
28759
+ const rkd = (n.root.info.rankdir ?? 0) & 3;
28760
+ const pt = cvtPt(n.info.coord, rkd);
28761
+ const opt = cvtPt(other.info.coord, rkd);
28762
+ const b = port.bp ?? nodeBoxLocal(n, rkd === RANKDIR_LR || rkd === RANKDIR_RL);
28763
+ let rv = null;
28764
+ let mind = 0;
28765
+ for (let i = 0; i < 4; i++) {
28766
+ if ((sides & 1 << i) === 0) continue;
28767
+ const m = faceMidpoint(b, i);
28768
+ const d = dist2({ x: m.x + pt.x, y: m.y + pt.y }, opt);
28769
+ if (rv === null || d < mind) {
28770
+ mind = d;
28771
+ rv = SIDE_PORT[i];
28772
+ }
28773
+ }
28774
+ return rv;
28775
+ }
28776
+ function resolvePort(n, other, port) {
28777
+ if (!port.dyna) return port;
28778
+ const compass = closestSide(n, other, port);
28779
+ const rv = makePort();
28780
+ rv.name = port.name;
28781
+ compassPort(n, { bp: port.bp, compass: compass ?? "", sides: port.side }, rv);
28782
+ return rv;
28783
+ }
28784
+ function walkToOrig(e) {
28785
+ let cur = e;
28786
+ while (cur.info.to_orig != null && cur.info.edge_type !== 0) {
28787
+ cur = cur.info.to_orig;
28788
+ }
28789
+ return cur;
28790
+ }
28791
+ function clearClipForOrig(e, n, isTail) {
28792
+ const orig = walkToOrig(e);
28793
+ if (isTail) {
28794
+ if (n === orig.tail) orig.info.tail_port.clip = false;
28795
+ else orig.info.head_port.clip = false;
28796
+ } else {
28797
+ if (n === orig.head) orig.info.head_port.clip = false;
28798
+ else orig.info.tail_port.clip = false;
28799
+ }
28800
+ }
28801
+ function applyDefaultBoxes(P, et, endp) {
28802
+ if (et === FLATEDGE) {
28803
+ if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.start.p.y;
28804
+ else endp.boxes[0].ur.y = P.start.p.y;
28805
+ } else {
28806
+ endp.boxes[0].ur.y = P.start.p.y;
28807
+ endp.sidemask = BOTTOM;
28808
+ P.start.p.y -= 1;
28809
+ }
28810
+ }
28811
+ function applyEndDefaultBoxes(P, et, endp) {
28812
+ if (et === FLATEDGE) {
28813
+ if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.end.p.y;
28814
+ else endp.boxes[0].ur.y = P.end.p.y;
28815
+ } else {
28816
+ endp.boxes[0].ll.y = P.end.p.y;
28817
+ endp.sidemask = TOP;
28818
+ P.end.p.y += 1;
28819
+ }
28820
+ }
28821
+ function invokePboxfn(fns, n, port, side, endp) {
28822
+ if (!fns?.pboxfn) return 0;
28823
+ const kptr = [endp.boxn];
28824
+ const mask = fns.pboxfn(n, port, side, endp.boxes, kptr);
28825
+ endp.boxn = kptr[0];
28826
+ return mask;
28827
+ }
28828
+ function setStartTheta(P, e, merge) {
28829
+ if (merge) {
28830
+ P.start.theta = concSlope(e.tail);
28831
+ P.start.constrained = true;
28832
+ } else if (e.info.tail_port.constrained) {
28833
+ P.start.theta = e.info.tail_port.theta;
28834
+ P.start.constrained = true;
28835
+ } else {
28836
+ P.start.constrained = false;
28837
+ }
28838
+ }
28839
+ function setEndTheta(P, e, merge) {
28840
+ if (merge) {
28841
+ P.end.theta = concSlope(e.head) + Math.PI;
28842
+ P.end.constrained = true;
28843
+ } else if (e.info.head_port.constrained) {
28844
+ P.end.theta = e.info.head_port.theta;
28845
+ P.end.constrained = true;
28846
+ } else {
28847
+ P.end.constrained = false;
28848
+ }
28849
+ }
28850
+
28464
28851
  // src/pathplan/solvers.ts
28465
28852
  var EPS2 = 1e-7;
28466
28853
  var Solver = class _Solver {
@@ -29250,567 +29637,6 @@ function computeSpline(boxes, startPt, endPt) {
29250
29637
  return tryRouteSpline(polyEdgesFromPts(polygon), wpts) ?? linearBezier(startPt, endPt);
29251
29638
  }
29252
29639
 
29253
- // src/layout/dot/edge-route-boxes.ts
29254
- var FUDGE2 = 2;
29255
- function makeTailBox(nb, startPy) {
29256
- return { ll: nb.ll, ur: { x: nb.ur.x, y: startPy } };
29257
- }
29258
- function makeHeadBox(nb, endPy) {
29259
- return { ll: { x: nb.ll.x, y: endPy }, ur: nb.ur };
29260
- }
29261
- function makeRankBox(p) {
29262
- return {
29263
- ll: { x: p.llx, y: p.headCy + p.headHt2 },
29264
- ur: { x: p.urx, y: p.tailCy - p.tailHt1 }
29265
- };
29266
- }
29267
- function makeMaximalBbox(box, ht1, ht22) {
29268
- return {
29269
- ll: { x: box.center.x - box.lw - FUDGE2, y: box.center.y - ht1 },
29270
- ur: { x: box.center.x + box.rw + FUDGE2, y: box.center.y + ht22 }
29271
- };
29272
- }
29273
-
29274
- // src/layout/dot/edge-route-routing.ts
29275
- var ARROW_LENGTH2 = 10;
29276
- var CLIP_BOTH = { tail: true, head: true };
29277
- function buildRankCorridor(tailBox, headBox, rank3, ports) {
29278
- const startPy = tailBox.center.y;
29279
- const endPy = headBox.center.y;
29280
- const tailNb = makeMaximalBbox(tailBox, rank3.tailHt1, rank3.tailHt2);
29281
- const headNb = makeMaximalBbox(headBox, rank3.headHt1, rank3.headHt2);
29282
- const rp = {
29283
- llx: rank3.leftBound,
29284
- urx: rank3.rightBound,
29285
- tailCy: tailBox.center.y,
29286
- tailHt1: rank3.tailHt1,
29287
- headCy: headBox.center.y,
29288
- headHt2: rank3.headHt2
29289
- };
29290
- return {
29291
- startPt: ports?.tailP ?? { x: tailBox.center.x, y: startPy - 1 },
29292
- endPt: ports?.headP ?? { x: headBox.center.x, y: endPy + 1 },
29293
- boxes: [makeTailBox(tailNb, startPy), makeRankBox(rp), makeHeadBox(headNb, endPy)]
29294
- };
29295
- }
29296
- function normalArrowLen(penwidth = DEFAULT_NODEPENWIDTH) {
29297
- const ARROWWIDTH = 0.35;
29298
- return ARROW_LENGTH2 + penwidth * Math.sqrt(1 + ARROWWIDTH * ARROWWIDTH) / (2 * ARROWWIDTH);
29299
- }
29300
- function nodeInsideFn2(box) {
29301
- const halfW = (box.lw + box.rw) / 2;
29302
- const halfH = box.ht / 2;
29303
- const pw = box.penwidth ?? DEFAULT_NODEPENWIDTH;
29304
- return box.isEllipse ? makeEllipseInsideFn(halfW, halfH, pw) : makeBoxInsideFn(halfW, halfH, pw);
29305
- }
29306
- function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
29307
- const step1 = clip.tail ? bezierClipNode(bezier, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier;
29308
- const step2 = clip.head ? bezierClipNode(step1, headBox.center.x, headBox.center.y, nodeInsideFn2(headBox), false) : step1;
29309
- const arrowTip = step2[3];
29310
- const elen = normalArrowLen(penwidth);
29311
- const clipped = arrowEndClip(step2, arrowTip, elen);
29312
- const arrowDir = normalizeVec({
29313
- x: clipped[3].x - arrowTip.x,
29314
- y: clipped[3].y - arrowTip.y
29315
- });
29316
- return { clipped, arrowTip, arrowDir };
29317
- }
29318
- function routeWithRank(tailBox, headBox, rank3, penwidth = DEFAULT_NODEPENWIDTH, port) {
29319
- const corridor = port ? { tailP: port.tailP, headP: port.headP } : void 0;
29320
- const { startPt, endPt, boxes } = buildRankCorridor(tailBox, headBox, rank3, corridor);
29321
- const bezier = computeSpline(boxes, startPt, endPt);
29322
- const clip = port ? { tail: port.clipTail, head: port.clipHead } : CLIP_BOTH;
29323
- const { clipped, arrowTip, arrowDir } = clipToNodes(bezier, tailBox, headBox, penwidth, clip);
29324
- return { bezierPts: clipped, arrowTip, arrowDir };
29325
- }
29326
- function routeSimple(tailBox, headBox, routeBezierFn) {
29327
- const dir = normalizeVec({
29328
- x: headBox.center.x - tailBox.center.x,
29329
- y: headBox.center.y - tailBox.center.y
29330
- });
29331
- const headToTail = negateVec(dir);
29332
- const tailClip = clipToNodeBox(tailBox, dir);
29333
- const arrowTip = clipToNodeBox(headBox, headToTail);
29334
- return {
29335
- bezierPts: routeBezierFn(tailClip, offsetPoint(arrowTip, headToTail, ARROW_LENGTH2)),
29336
- arrowTip,
29337
- arrowDir: headToTail
29338
- };
29339
- }
29340
-
29341
- // src/layout/dot/edge-route-helpers.ts
29342
- function defaultEdgeDir(g) {
29343
- return g.kind === "directed" || g.kind === "strict-directed" ? "forward" : "none";
29344
- }
29345
- var ELLIPSE_SHAPES = /* @__PURE__ */ new Set([
29346
- "ellipse",
29347
- "oval",
29348
- "circle",
29349
- "egg",
29350
- "doublecircle",
29351
- "Mcircle",
29352
- "point"
29353
- ]);
29354
- function recordClipBox(n) {
29355
- const f = n.info.shape_info;
29356
- if (!f?.b) return void 0;
29357
- const halfW = (f.b.ur.x - f.b.ll.x) / 2;
29358
- return { lw: halfW, rw: halfW, ht: f.b.ur.y - f.b.ll.y };
29359
- }
29360
- function nodePenwidthOf(n, g) {
29361
- const v = nodeAttr(n, g, "penwidth");
29362
- const pw = v !== void 0 ? parseFloat(v) : NaN;
29363
- return Number.isFinite(pw) && pw >= 0 ? pw : DEFAULT_NODEPENWIDTH;
29364
- }
29365
- function nodeBoxOf(n, g) {
29366
- const shapeName = nodeAttr(n, g, "shape") ?? "ellipse";
29367
- const penwidth = nodePenwidthOf(n, g);
29368
- if (shapeName === "record" || shapeName === "Mrecord") {
29369
- const rb = recordClipBox(n);
29370
- if (rb) return { center: n.info.coord, ...rb, isEllipse: false, penwidth };
29371
- }
29372
- const si = n.info.shape_info;
29373
- if (si?.option?.fixedshape === true && si.vertices) {
29374
- const ext = fixedShapeSize(si);
29375
- const flip = g.root.info.flip === true;
29376
- const w = flip ? ext.h : ext.w;
29377
- const h = flip ? ext.w : ext.h;
29378
- return {
29379
- center: n.info.coord,
29380
- lw: w / 2,
29381
- rw: w / 2,
29382
- ht: h,
29383
- isEllipse: ELLIPSE_SHAPES.has(shapeName),
29384
- penwidth: 0
29385
- };
29386
- }
29387
- return {
29388
- center: n.info.coord,
29389
- lw: n.info.lw > 0 ? n.info.lw : 27,
29390
- rw: n.info.rw > 0 ? n.info.rw : 27,
29391
- ht: n.info.ht > 0 ? n.info.ht : 36,
29392
- isEllipse: ELLIPSE_SHAPES.has(shapeName),
29393
- penwidth
29394
- };
29395
- }
29396
- function edgeRenderPenwidth(e) {
29397
- return resolvePenWidth(parseStyleFlags(e.attrs.get("style")), e.attrs.get("penwidth"));
29398
- }
29399
- function edgePenwidthAttr(e) {
29400
- const pw = parseFloat(e.attrs.get("penwidth") ?? "");
29401
- return isNaN(pw) ? 1 : pw;
29402
- }
29403
- function makeBezierRecord(bezierPts, arrowTip) {
29404
- return {
29405
- list: bezierPts,
29406
- size: bezierPts.length,
29407
- sflag: 0,
29408
- eflag: 1,
29409
- sp: bezierPts[0] ?? { x: 0, y: 0 },
29410
- ep: arrowTip
29411
- };
29412
- }
29413
- function makeSplineRecord(bz) {
29414
- return { list: [bz], size: 1, bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } } };
29415
- }
29416
- function installEdgeSpline(e, bezierPts, arrowTip) {
29417
- e.info.spl = makeSplineRecord(makeBezierRecord(bezierPts, arrowTip));
29418
- const bb = e.tail.root.info.bb;
29419
- if (bb != null) {
29420
- for (let i = 0; i + 3 < bezierPts.length; i += 3) {
29421
- updateBbBz(bb, [bezierPts[i], bezierPts[i + 1], bezierPts[i + 2], bezierPts[i + 3]]);
29422
- }
29423
- }
29424
- }
29425
- function isUsableSpline(raw) {
29426
- if (raw.length < 4) return false;
29427
- const p0 = raw[0];
29428
- const p1 = raw[1];
29429
- return p0.x !== p1.x || p0.y !== p1.y;
29430
- }
29431
- function routeBezier(from, to) {
29432
- const noBarriers = [];
29433
- const zero = { x: 0, y: 0 };
29434
- const raw = routeSpline(noBarriers, [from, to], [zero, zero]);
29435
- if (!isUsableSpline(raw)) return linearBezier(from, to);
29436
- return [raw[0], raw[1], raw[2], raw[3]];
29437
- }
29438
- function straightEdgeSplineWithRank(tailBox, headBox, rankInfo, penwidth = 1, port) {
29439
- return rankInfo !== void 0 ? routeWithRank(tailBox, headBox, rankInfo, penwidth, port) : routeSimple(tailBox, headBox, routeBezier);
29440
- }
29441
-
29442
- // src/layout/dot/sameport.ts
29443
- function sameedge(same, e, id) {
29444
- for (const grp of same) {
29445
- if (grp.id === id) {
29446
- grp.edges.push(e);
29447
- return;
29448
- }
29449
- }
29450
- same.push({ id, edges: [e] });
29451
- }
29452
- function shapeClip(u, curve) {
29453
- const box = nodeBoxOf(u, u.root);
29454
- const clipped = bezierClipNode(curve, box.center.x, box.center.y, nodeInsideFn2(box), true);
29455
- for (let i = 0; i < curve.length && i < clipped.length; i++) curve[i] = clipped[i];
29456
- }
29457
- function computeBoundaryOffset(u, x, y, ranksep) {
29458
- const r = Math.max(
29459
- (u.info.lw ?? 0) + (u.info.rw ?? 0),
29460
- (u.info.ht ?? 0) + ranksep
29461
- );
29462
- const x2 = x * r + u.info.coord.x;
29463
- const y2 = y * r + u.info.coord.y;
29464
- const curve = [
29465
- { x: u.info.coord.x, y: u.info.coord.y },
29466
- { x: (2 * u.info.coord.x + x2) / 3, y: (2 * u.info.coord.y + y2) / 3 },
29467
- { x: (2 * x2 + u.info.coord.x) / 3, y: (2 * y2 + u.info.coord.y) / 3 },
29468
- { x: x2, y: y2 }
29469
- ];
29470
- shapeClip(u, curve);
29471
- return {
29472
- x1: curve[0].x - u.info.coord.x,
29473
- y1: curve[0].y - u.info.coord.y
29474
- };
29475
- }
29476
- function averageDirection(u, edges) {
29477
- let sx = 0, sy = 0;
29478
- for (const e of edges) {
29479
- const v = e.head === u ? e.tail : e.head;
29480
- const dx = v.info.coord.x - u.info.coord.x;
29481
- const dy = v.info.coord.y - u.info.coord.y;
29482
- const r2 = Math.hypot(dx, dy);
29483
- if (r2 !== 0) {
29484
- sx += dx / r2;
29485
- sy += dy / r2;
29486
- }
29487
- }
29488
- const r = Math.hypot(sx, sy);
29489
- return r !== 0 ? { x: sx / r, y: sy / r } : { x: 0, y: 0 };
29490
- }
29491
- function buildSharedPort(u, edges, ranksep) {
29492
- const dir = averageDirection(u, edges);
29493
- const { x1, y1 } = computeBoundaryOffset(u, dir.x, dir.y, ranksep);
29494
- const px = cround(x1);
29495
- const py = cround(y1);
29496
- const lw = u.info.lw ?? 0;
29497
- const rw = u.info.rw ?? 0;
29498
- const order = lw + rw !== 0 ? MC_SCALE * (lw + px) / (lw + rw) : 0;
29499
- return {
29500
- p: { x: px, y: py },
29501
- theta: 0,
29502
- bp: null,
29503
- defined: true,
29504
- constrained: false,
29505
- clip: false,
29506
- dyna: false,
29507
- order,
29508
- side: 0,
29509
- name: null
29510
- };
29511
- }
29512
- function assignHeadIfMatch(f, u, prt) {
29513
- if (f.head === u) f.info.head_port = prt;
29514
- if (f.tail === u) f.info.tail_port = prt;
29515
- }
29516
- function assignPortForwardChain(e, u, prt) {
29517
- 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) {
29518
- assignHeadIfMatch(f, u, prt);
29519
- }
29520
- }
29521
- function assignPortBackwardChain(e, u, prt) {
29522
- 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) {
29523
- assignHeadIfMatch(f, u, prt);
29524
- }
29525
- }
29526
- function assignPortToAllVirts(e, u, prt) {
29527
- for (let virt = e; virt !== void 0; virt = virt.info.to_virt) {
29528
- assignPortForwardChain(virt, u, prt);
29529
- assignPortBackwardChain(virt, u, prt);
29530
- }
29531
- }
29532
- function sameport(u, edges, ranksep) {
29533
- if (edges.length < 2) return;
29534
- const prt = buildSharedPort(u, edges, ranksep);
29535
- for (const e of edges) assignPortToAllVirts(e, u, prt);
29536
- u.info.has_port = true;
29537
- }
29538
- function graphHasSamehead(g) {
29539
- for (const e of g.edges) {
29540
- if (e.info.samehead !== void 0) return true;
29541
- }
29542
- return false;
29543
- }
29544
- function graphHasSametail(g) {
29545
- for (const e of g.edges) {
29546
- if (e.info.sametail !== void 0) return true;
29547
- }
29548
- return false;
29549
- }
29550
- function collectSamehead(n) {
29551
- const same = [];
29552
- for (const e of n.root.edges) {
29553
- if (e.head === n && e.tail === n) continue;
29554
- const id = e.head === n ? e.info.samehead : void 0;
29555
- if (id && id !== "") sameedge(same, e, id);
29556
- }
29557
- return same;
29558
- }
29559
- function collectSametail(n) {
29560
- const same = [];
29561
- for (const e of n.root.edges) {
29562
- if (e.head === n && e.tail === n) continue;
29563
- const id = e.tail === n ? e.info.sametail : void 0;
29564
- if (id && id !== "") sameedge(same, e, id);
29565
- }
29566
- return same;
29567
- }
29568
- function applyGroupsToNode(n, groups, ranksep) {
29569
- for (const grp of groups) {
29570
- if (grp.edges.length > 1) sameport(n, grp.edges, ranksep);
29571
- }
29572
- }
29573
- function dotSameports(g) {
29574
- const hasSH = graphHasSamehead(g);
29575
- const hasST = graphHasSametail(g);
29576
- if (!hasSH && !hasST) return;
29577
- const ranksep = g.info.ranksep ?? 0;
29578
- for (const n of nodesInSeq(g)) {
29579
- if (hasSH) applyGroupsToNode(n, collectSamehead(n), ranksep);
29580
- if (hasST) applyGroupsToNode(n, collectSametail(n), ranksep);
29581
- }
29582
- }
29583
-
29584
- // src/common/spline-midpoint.ts
29585
- function dist2(p, q) {
29586
- const dx = p.x - q.x;
29587
- const dy = p.y - q.y;
29588
- return dx * dx + dy * dy;
29589
- }
29590
- function distPt(p, q) {
29591
- return Math.sqrt(dist2(p, q));
29592
- }
29593
- function evalBez4(pts, t) {
29594
- const u = 1 - t;
29595
- const [p0, p1, p2, p3] = pts;
29596
- return {
29597
- x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
29598
- y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
29599
- };
29600
- }
29601
- function dotneatoClosest(spl, pt) {
29602
- let besti = 0;
29603
- let bestj = -1;
29604
- let bestd2 = Number.MAX_VALUE;
29605
- for (let i = 0; i < spl.list.length; i++) {
29606
- const bz2 = spl.list[i];
29607
- const n = bz2.size ?? bz2.list.length;
29608
- for (let j2 = 0; j2 < n; j2++) {
29609
- const d2 = dist2(bz2.list[j2], pt);
29610
- if (bestj === -1 || d2 < bestd2) {
29611
- besti = i;
29612
- bestj = j2;
29613
- bestd2 = d2;
29614
- }
29615
- }
29616
- }
29617
- const bz = spl.list[besti];
29618
- const bzn = bz.size ?? bz.list.length;
29619
- let j = bestj === bzn - 1 ? bestj - 1 : bestj;
29620
- j = 3 * Math.floor(j / 3);
29621
- const c = [bz.list[j], bz.list[j + 1], bz.list[j + 2], bz.list[j + 3]];
29622
- let lo = 0;
29623
- let hi = 1;
29624
- let dlo2 = dist2(c[0], pt);
29625
- let dhi2 = dist2(c[3], pt);
29626
- let pt2 = c[0];
29627
- for (; ; ) {
29628
- const t = (lo + hi) / 2;
29629
- pt2 = evalBez4(c, t);
29630
- if (Math.abs(dlo2 - dhi2) < 1 || Math.abs(hi - lo) < 1e-5) break;
29631
- if (dlo2 < dhi2) {
29632
- hi = t;
29633
- dhi2 = dist2(pt2, pt);
29634
- } else {
29635
- lo = t;
29636
- dlo2 = dist2(pt2, pt);
29637
- }
29638
- }
29639
- return pt2;
29640
- }
29641
- function polylineMidpoint(spl) {
29642
- let total = 0;
29643
- for (const bz of spl.list) {
29644
- for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3)
29645
- total += distPt(bz.list[j], bz.list[k]);
29646
- }
29647
- total /= 2;
29648
- for (const bz of spl.list) {
29649
- for (let j = 0, k = 3; k < bz.list.length; j += 3, k += 3) {
29650
- const pf = bz.list[j];
29651
- const qf = bz.list[k];
29652
- const d = distPt(pf, qf);
29653
- if (d >= total) {
29654
- return {
29655
- x: fma(qf.x, total, pf.x * (d - total)) / d,
29656
- y: fma(qf.y, total, pf.y * (d - total)) / d
29657
- };
29658
- }
29659
- total -= d;
29660
- }
29661
- }
29662
- const first = spl.list[0];
29663
- return first.sflag ? first.sp : first.list[0];
29664
- }
29665
- function splEndPoints(spl) {
29666
- const first = spl.list[0];
29667
- const p = first.sflag ? first.sp : first.list[0];
29668
- const last = spl.list[spl.list.length - 1];
29669
- const q = last.eflag ? last.ep : last.list[last.list.length - 1];
29670
- return { p, q };
29671
- }
29672
-
29673
- // src/common/splines-path-shared.ts
29674
- function addBox(P, b) {
29675
- if (b.ll.x < b.ur.x && b.ll.y < b.ur.y) {
29676
- P.boxes[P.nbox++] = b;
29677
- }
29678
- }
29679
- function concSlope(n) {
29680
- const inE = n.info.in;
29681
- const outE = n.info.out;
29682
- const cntIn = inE?.size ?? 0;
29683
- const cntOut = outE?.size ?? 0;
29684
- let sIn = 0;
29685
- let sOut = 0;
29686
- for (let i = 0; i < cntIn; i++) sIn += inE.list[i].tail.info.coord.x;
29687
- for (let i = 0; i < cntOut; i++) sOut += outE.list[i].head.info.coord.x;
29688
- const x1 = n.info.coord.x - sIn / cntIn;
29689
- const y1 = n.info.coord.y - inE.list[0].tail.info.coord.y;
29690
- const x2 = sOut / cntOut - n.info.coord.x;
29691
- const y2 = outE.list[0].head.info.coord.y - n.info.coord.y;
29692
- return (Math.atan2(y1, x1) + Math.atan2(y2, x2)) / 2;
29693
- }
29694
- var SIDE_PORT = ["s", "e", "n", "w"];
29695
- var ALL_SIDES2 = TOP | BOTTOM | LEFT | RIGHT;
29696
- function cvtPt(p, rankdir) {
29697
- switch (rankdir) {
29698
- case RANKDIR_BT:
29699
- return { x: p.x, y: -p.y };
29700
- case RANKDIR_LR:
29701
- return { x: -p.y, y: p.x };
29702
- case RANKDIR_RL:
29703
- return { x: p.y, y: p.x };
29704
- default:
29705
- return { x: p.x, y: p.y };
29706
- }
29707
- }
29708
- function nodeBoxLocal(n, flip) {
29709
- const lw = n.info.lw, ht22 = n.info.ht / 2;
29710
- return flip ? { ll: { x: -ht22, y: -lw }, ur: { x: ht22, y: lw } } : { ll: { x: -lw, y: -ht22 }, ur: { x: lw, y: ht22 } };
29711
- }
29712
- function faceMidpoint(b, ix) {
29713
- const cx = (b.ll.x + b.ur.x) / 2, cy = (b.ll.y + b.ur.y) / 2;
29714
- if (ix === BOTTOM_IX) return { x: cx, y: b.ll.y };
29715
- if (ix === RIGHT_IX) return { x: b.ur.x, y: cy };
29716
- if (ix === TOP_IX) return { x: cx, y: b.ur.y };
29717
- return { x: b.ll.x, y: cy };
29718
- }
29719
- function closestSide(n, other, port) {
29720
- const sides = port.side;
29721
- if (sides === 0 || sides === ALL_SIDES2) return null;
29722
- const rkd = (n.root.info.rankdir ?? 0) & 3;
29723
- const pt = cvtPt(n.info.coord, rkd);
29724
- const opt = cvtPt(other.info.coord, rkd);
29725
- const b = port.bp ?? nodeBoxLocal(n, rkd === RANKDIR_LR || rkd === RANKDIR_RL);
29726
- let rv = null;
29727
- let mind = 0;
29728
- for (let i = 0; i < 4; i++) {
29729
- if ((sides & 1 << i) === 0) continue;
29730
- const m = faceMidpoint(b, i);
29731
- const d = dist2({ x: m.x + pt.x, y: m.y + pt.y }, opt);
29732
- if (rv === null || d < mind) {
29733
- mind = d;
29734
- rv = SIDE_PORT[i];
29735
- }
29736
- }
29737
- return rv;
29738
- }
29739
- function resolvePort(n, other, port) {
29740
- if (!port.dyna) return port;
29741
- const compass = closestSide(n, other, port);
29742
- const rv = makePort();
29743
- rv.name = port.name;
29744
- compassPort(n, { bp: port.bp, compass: compass ?? "", sides: port.side }, rv);
29745
- return rv;
29746
- }
29747
- function walkToOrig(e) {
29748
- let cur = e;
29749
- while (cur.info.to_orig != null && cur.info.edge_type !== 0) {
29750
- cur = cur.info.to_orig;
29751
- }
29752
- return cur;
29753
- }
29754
- function clearClipForOrig(e, n, isTail) {
29755
- const orig = walkToOrig(e);
29756
- if (isTail) {
29757
- if (n === orig.tail) orig.info.tail_port.clip = false;
29758
- else orig.info.head_port.clip = false;
29759
- } else {
29760
- if (n === orig.head) orig.info.head_port.clip = false;
29761
- else orig.info.tail_port.clip = false;
29762
- }
29763
- }
29764
- function applyDefaultBoxes(P, et, endp) {
29765
- if (et === FLATEDGE) {
29766
- if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.start.p.y;
29767
- else endp.boxes[0].ur.y = P.start.p.y;
29768
- } else {
29769
- endp.boxes[0].ur.y = P.start.p.y;
29770
- endp.sidemask = BOTTOM;
29771
- P.start.p.y -= 1;
29772
- }
29773
- }
29774
- function applyEndDefaultBoxes(P, et, endp) {
29775
- if (et === FLATEDGE) {
29776
- if (endp.sidemask === TOP) endp.boxes[0].ll.y = P.end.p.y;
29777
- else endp.boxes[0].ur.y = P.end.p.y;
29778
- } else {
29779
- endp.boxes[0].ll.y = P.end.p.y;
29780
- endp.sidemask = TOP;
29781
- P.end.p.y += 1;
29782
- }
29783
- }
29784
- function invokePboxfn(fns, n, port, side, endp) {
29785
- if (!fns?.pboxfn) return 0;
29786
- const kptr = [endp.boxn];
29787
- const mask = fns.pboxfn(n, port, side, endp.boxes, kptr);
29788
- endp.boxn = kptr[0];
29789
- return mask;
29790
- }
29791
- function setStartTheta(P, e, merge) {
29792
- if (merge) {
29793
- P.start.theta = concSlope(e.tail);
29794
- P.start.constrained = true;
29795
- } else if (e.info.tail_port.constrained) {
29796
- P.start.theta = e.info.tail_port.theta;
29797
- P.start.constrained = true;
29798
- } else {
29799
- P.start.constrained = false;
29800
- }
29801
- }
29802
- function setEndTheta(P, e, merge) {
29803
- if (merge) {
29804
- P.end.theta = concSlope(e.head) + Math.PI;
29805
- P.end.constrained = true;
29806
- } else if (e.info.head_port.constrained) {
29807
- P.end.theta = e.info.head_port.theta;
29808
- P.end.constrained = true;
29809
- } else {
29810
- P.end.constrained = false;
29811
- }
29812
- }
29813
-
29814
29640
  // src/layout/dot/edge-route-rank.ts
29815
29641
  var MINW = 16;
29816
29642
  function computeLeftBound(g) {
@@ -30865,7 +30691,7 @@ function rankY(g, r) {
30865
30691
  return g.info.rank[r].v[0].info.coord?.y ?? 0;
30866
30692
  }
30867
30693
  function sizeyAtMaxrank(g, n, r) {
30868
- if (r > 0) return rankY(g, r - 1) - (n.info.coord?.y ?? 0);
30694
+ if (r - (g.info.abomShift ?? 0) > 0) return rankY(g, r - 1) - (n.info.coord?.y ?? 0);
30869
30695
  return n.info.ht ?? 0;
30870
30696
  }
30871
30697
  function sizeyAtMinrank(g, n, r) {
@@ -33702,7 +33528,7 @@ function makeFlatLabeledEdge(g, e) {
33702
33528
  const et = edgeType(g);
33703
33529
  if (et === EDGETYPE_LINE) {
33704
33530
  const ps2 = flatLabeledLinePoints(e.tail, e.head, e);
33705
- if (ps2 !== null && ps2.length !== 0) clipAndInstall(e, e.head, ps2, ps2.length, buildDotSinfo());
33531
+ clipAndInstall(e, e.head, ps2, ps2.length, buildDotSinfo());
33706
33532
  return true;
33707
33533
  }
33708
33534
  const tailIsLeft = (e.tail.info.order ?? 0) <= (e.head.info.order ?? 0);
@@ -33886,6 +33712,195 @@ function makeAdjFlatNoPortEdge(g, e) {
33886
33712
  return true;
33887
33713
  }
33888
33714
 
33715
+ // src/layout/dot/edge-route-boxes.ts
33716
+ var FUDGE2 = 2;
33717
+ function makeTailBox(nb, startPy) {
33718
+ return { ll: nb.ll, ur: { x: nb.ur.x, y: startPy } };
33719
+ }
33720
+ function makeHeadBox(nb, endPy) {
33721
+ return { ll: { x: nb.ll.x, y: endPy }, ur: nb.ur };
33722
+ }
33723
+ function makeRankBox(p) {
33724
+ return {
33725
+ ll: { x: p.llx, y: p.headCy + p.headHt2 },
33726
+ ur: { x: p.urx, y: p.tailCy - p.tailHt1 }
33727
+ };
33728
+ }
33729
+ function makeMaximalBbox(box, ht1, ht22) {
33730
+ return {
33731
+ ll: { x: box.center.x - box.lw - FUDGE2, y: box.center.y - ht1 },
33732
+ ur: { x: box.center.x + box.rw + FUDGE2, y: box.center.y + ht22 }
33733
+ };
33734
+ }
33735
+
33736
+ // src/layout/dot/edge-route-routing.ts
33737
+ var ARROW_LENGTH2 = 10;
33738
+ var CLIP_BOTH = { tail: true, head: true };
33739
+ function buildRankCorridor(tailBox, headBox, rank3, ports) {
33740
+ const startPy = tailBox.center.y;
33741
+ const endPy = headBox.center.y;
33742
+ const tailNb = makeMaximalBbox(tailBox, rank3.tailHt1, rank3.tailHt2);
33743
+ const headNb = makeMaximalBbox(headBox, rank3.headHt1, rank3.headHt2);
33744
+ const rp = {
33745
+ llx: rank3.leftBound,
33746
+ urx: rank3.rightBound,
33747
+ tailCy: tailBox.center.y,
33748
+ tailHt1: rank3.tailHt1,
33749
+ headCy: headBox.center.y,
33750
+ headHt2: rank3.headHt2
33751
+ };
33752
+ return {
33753
+ startPt: ports?.tailP ?? { x: tailBox.center.x, y: startPy - 1 },
33754
+ endPt: ports?.headP ?? { x: headBox.center.x, y: endPy + 1 },
33755
+ boxes: [makeTailBox(tailNb, startPy), makeRankBox(rp), makeHeadBox(headNb, endPy)]
33756
+ };
33757
+ }
33758
+ function normalArrowLen(penwidth = DEFAULT_NODEPENWIDTH) {
33759
+ const ARROWWIDTH = 0.35;
33760
+ return ARROW_LENGTH2 + penwidth * Math.sqrt(1 + ARROWWIDTH * ARROWWIDTH) / (2 * ARROWWIDTH);
33761
+ }
33762
+ function nodeInsideFn2(box) {
33763
+ const halfW = (box.lw + box.rw) / 2;
33764
+ const halfH = box.ht / 2;
33765
+ const pw = box.penwidth ?? DEFAULT_NODEPENWIDTH;
33766
+ return box.isEllipse ? makeEllipseInsideFn(halfW, halfH, pw) : makeBoxInsideFn(halfW, halfH, pw);
33767
+ }
33768
+ function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
33769
+ const step1 = clip.tail ? bezierClipNode(bezier, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier;
33770
+ const step2 = clip.head ? bezierClipNode(step1, headBox.center.x, headBox.center.y, nodeInsideFn2(headBox), false) : step1;
33771
+ const arrowTip = step2[3];
33772
+ const elen = normalArrowLen(penwidth);
33773
+ const clipped = arrowEndClip(step2, arrowTip, elen);
33774
+ const arrowDir = normalizeVec({
33775
+ x: clipped[3].x - arrowTip.x,
33776
+ y: clipped[3].y - arrowTip.y
33777
+ });
33778
+ return { clipped, arrowTip, arrowDir };
33779
+ }
33780
+ function routeWithRank(tailBox, headBox, rank3, penwidth = DEFAULT_NODEPENWIDTH, port) {
33781
+ const corridor = port ? { tailP: port.tailP, headP: port.headP } : void 0;
33782
+ const { startPt, endPt, boxes } = buildRankCorridor(tailBox, headBox, rank3, corridor);
33783
+ const bezier = computeSpline(boxes, startPt, endPt);
33784
+ const clip = port ? { tail: port.clipTail, head: port.clipHead } : CLIP_BOTH;
33785
+ const { clipped, arrowTip, arrowDir } = clipToNodes(bezier, tailBox, headBox, penwidth, clip);
33786
+ return { bezierPts: clipped, arrowTip, arrowDir };
33787
+ }
33788
+ function routeSimple(tailBox, headBox, routeBezierFn) {
33789
+ const dir = normalizeVec({
33790
+ x: headBox.center.x - tailBox.center.x,
33791
+ y: headBox.center.y - tailBox.center.y
33792
+ });
33793
+ const headToTail = negateVec(dir);
33794
+ const tailClip = clipToNodeBox(tailBox, dir);
33795
+ const arrowTip = clipToNodeBox(headBox, headToTail);
33796
+ return {
33797
+ bezierPts: routeBezierFn(tailClip, offsetPoint(arrowTip, headToTail, ARROW_LENGTH2)),
33798
+ arrowTip,
33799
+ arrowDir: headToTail
33800
+ };
33801
+ }
33802
+
33803
+ // src/layout/dot/edge-route-helpers.ts
33804
+ function defaultEdgeDir(g) {
33805
+ return g.kind === "directed" || g.kind === "strict-directed" ? "forward" : "none";
33806
+ }
33807
+ var ELLIPSE_SHAPES = /* @__PURE__ */ new Set([
33808
+ "ellipse",
33809
+ "oval",
33810
+ "circle",
33811
+ "egg",
33812
+ "doublecircle",
33813
+ "Mcircle",
33814
+ "point"
33815
+ ]);
33816
+ function recordClipBox(n) {
33817
+ const f = n.info.shape_info;
33818
+ if (!f?.b) return void 0;
33819
+ const halfW = (f.b.ur.x - f.b.ll.x) / 2;
33820
+ return { lw: halfW, rw: halfW, ht: f.b.ur.y - f.b.ll.y };
33821
+ }
33822
+ function nodePenwidthOf(n, g) {
33823
+ const v = nodeAttr(n, g, "penwidth");
33824
+ const pw = v !== void 0 ? parseFloat(v) : NaN;
33825
+ return Number.isFinite(pw) && pw >= 0 ? pw : DEFAULT_NODEPENWIDTH;
33826
+ }
33827
+ function nodeBoxOf(n, g) {
33828
+ const shapeName = nodeAttr(n, g, "shape") ?? "ellipse";
33829
+ const penwidth = nodePenwidthOf(n, g);
33830
+ if (shapeName === "record" || shapeName === "Mrecord") {
33831
+ const rb = recordClipBox(n);
33832
+ if (rb) return { center: n.info.coord, ...rb, isEllipse: false, penwidth };
33833
+ }
33834
+ const si = n.info.shape_info;
33835
+ if (si?.option?.fixedshape === true && si.vertices) {
33836
+ const ext = fixedShapeSize(si);
33837
+ const flip = g.root.info.flip === true;
33838
+ const w = flip ? ext.h : ext.w;
33839
+ const h = flip ? ext.w : ext.h;
33840
+ return {
33841
+ center: n.info.coord,
33842
+ lw: w / 2,
33843
+ rw: w / 2,
33844
+ ht: h,
33845
+ isEllipse: ELLIPSE_SHAPES.has(shapeName),
33846
+ penwidth: 0
33847
+ };
33848
+ }
33849
+ return {
33850
+ center: n.info.coord,
33851
+ lw: n.info.lw > 0 ? n.info.lw : 27,
33852
+ rw: n.info.rw > 0 ? n.info.rw : 27,
33853
+ ht: n.info.ht > 0 ? n.info.ht : 36,
33854
+ isEllipse: ELLIPSE_SHAPES.has(shapeName),
33855
+ penwidth
33856
+ };
33857
+ }
33858
+ function edgeRenderPenwidth(e) {
33859
+ return resolvePenWidth(parseStyleFlags(e.attrs.get("style")), e.attrs.get("penwidth"));
33860
+ }
33861
+ function edgePenwidthAttr(e) {
33862
+ const pw = parseFloat(e.attrs.get("penwidth") ?? "");
33863
+ return isNaN(pw) ? 1 : pw;
33864
+ }
33865
+ function makeBezierRecord(bezierPts, arrowTip) {
33866
+ return {
33867
+ list: bezierPts,
33868
+ size: bezierPts.length,
33869
+ sflag: 0,
33870
+ eflag: 1,
33871
+ sp: bezierPts[0] ?? { x: 0, y: 0 },
33872
+ ep: arrowTip
33873
+ };
33874
+ }
33875
+ function makeSplineRecord(bz) {
33876
+ return { list: [bz], size: 1, bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } } };
33877
+ }
33878
+ function installEdgeSpline(e, bezierPts, arrowTip) {
33879
+ e.info.spl = makeSplineRecord(makeBezierRecord(bezierPts, arrowTip));
33880
+ const bb = e.tail.root.info.bb;
33881
+ if (bb != null) {
33882
+ for (let i = 0; i + 3 < bezierPts.length; i += 3) {
33883
+ updateBbBz(bb, [bezierPts[i], bezierPts[i + 1], bezierPts[i + 2], bezierPts[i + 3]]);
33884
+ }
33885
+ }
33886
+ }
33887
+ function isUsableSpline(raw) {
33888
+ if (raw.length < 4) return false;
33889
+ const p0 = raw[0];
33890
+ const p1 = raw[1];
33891
+ return p0.x !== p1.x || p0.y !== p1.y;
33892
+ }
33893
+ function routeBezier(from, to) {
33894
+ const noBarriers = [];
33895
+ const zero = { x: 0, y: 0 };
33896
+ const raw = routeSpline(noBarriers, [from, to], [zero, zero]);
33897
+ if (!isUsableSpline(raw)) return linearBezier(from, to);
33898
+ return [raw[0], raw[1], raw[2], raw[3]];
33899
+ }
33900
+ function straightEdgeSplineWithRank(tailBox, headBox, rankInfo, penwidth = 1, port) {
33901
+ return rankInfo !== void 0 ? routeWithRank(tailBox, headBox, rankInfo, penwidth, port) : routeSimple(tailBox, headBox, routeBezier);
33902
+ }
33903
+
33889
33904
  // src/layout/dot/edge-route.ts
33890
33905
  function coordsDefined(e) {
33891
33906
  return e.tail.info.coord !== void 0 && e.head.info.coord !== void 0;
@@ -33989,8 +34004,14 @@ function hasSidePort(e) {
33989
34004
  const hp = resolvePort(e.head, e.tail, e.info.head_port);
33990
34005
  return (tp.side ?? 0) !== 0 || (hp.side ?? 0) !== 0;
33991
34006
  }
34007
+ function hasDefinedPort(e) {
34008
+ return e.info.tail_port.defined || e.info.head_port.defined;
34009
+ }
34010
+ function isAdjacentPortFlat(e, g) {
34011
+ return e.tail.info.rank !== void 0 && e.tail.info.rank === e.head.info.rank && isFlatAdjacent(g, e) && hasDefinedPort(e);
34012
+ }
33992
34013
  function isGroupableFlat(x, g) {
33993
- return x.info.spl === void 0 && x.tail.info.rank === x.head.info.rank && isFlatAdjacent(g, x) && hasSidePort(x);
34014
+ return x.info.spl === void 0 && isAdjacentPortFlat(x, g);
33994
34015
  }
33995
34016
  function collectAdjacentFlatGroup(e, g) {
33996
34017
  const u = e.tail, v = e.head;
@@ -34031,7 +34052,7 @@ function routeFlatEdge(e, g) {
34031
34052
  if (isLostFlatAdjRecordEdge(g, e)) return true;
34032
34053
  if (makeFlatLabeledEdge(g, e)) return true;
34033
34054
  if (makeAdjFlatNoPortEdge(g, e)) return true;
34034
- if (hasSidePort(e) && routeFaithfulSidePort(e, g)) return true;
34055
+ if ((hasSidePort(e) || isAdjacentPortFlat(e, g)) && routeFaithfulSidePort(e, g)) return true;
34035
34056
  if (e.tail.info.rank !== void 0 && e.tail.info.rank === e.head.info.rank && e.info.label === void 0 && !isFlatAdjacent(g, e)) {
34036
34057
  const group = collectNonAdjacentFlatGroup(e, g);
34037
34058
  if (group.length > 0 && routeFlatEdgeGroupFaithful(g, group, group.length) && e.info.spl !== void 0) return true;
@@ -34085,8 +34106,8 @@ var M_LEFT = 2;
34085
34106
  var M_BOTTOM = 3;
34086
34107
 
34087
34108
  // src/common/random.ts
34088
- var A = 0x5deece66dn;
34089
- var C = 0xbn;
34109
+ var A = BigInt("0x5deece66d");
34110
+ var C = BigInt("0xb");
34090
34111
  var MASK48 = (1n << 48n) - 1n;
34091
34112
  var TWO48 = 2 ** 48;
34092
34113
  var state = 0x330en;
@@ -36628,8 +36649,11 @@ function dispatchEdgeGroup(g, group, ctx) {
36628
36649
  }
36629
36650
  return;
36630
36651
  }
36631
- if (nodeRankOf(e0.tail) === nodeRankOf(e0.head)) return;
36632
36652
  const uniq = dedupByOrig(group);
36653
+ if (nodeRankOf(e0.tail) === nodeRankOf(e0.head)) {
36654
+ for (const e of uniq) routeLoneEdge(e, g);
36655
+ return;
36656
+ }
36633
36657
  if (uniq.length <= 1) {
36634
36658
  const runOrig = routeEntryRun(g, e0);
36635
36659
  if (runOrig !== null) {
@@ -40941,7 +40965,6 @@ var Cdt = class {
40941
40965
  v1 = r.v1;
40942
40966
  v2 = r.v2;
40943
40967
  v3 = r.v3;
40944
- prev = r.e1;
40945
40968
  e2 = r.e2;
40946
40969
  e3 = r.e3;
40947
40970
  }
@@ -44282,9 +44305,9 @@ var POW_LOG_TABLE_BITS = 7;
44282
44305
  var N_LOG = 1 << POW_LOG_TABLE_BITS;
44283
44306
  var EXP_TABLE_BITS = 7;
44284
44307
  var N_EXP = 1 << EXP_TABLE_BITS;
44285
- var OFF = 0x3fe6955500000000n;
44286
- var INF_BITS = 0x7ff0000000000000n;
44287
- var ONE_BITS = 0x3ff0000000000000n;
44308
+ var OFF = BigInt("0x3fe6955500000000");
44309
+ var INF_BITS = BigInt("0x7ff0000000000000");
44310
+ var ONE_BITS = BigInt("0x3ff0000000000000");
44288
44311
  var SIGN_BIAS = 2048 << EXP_TABLE_BITS;
44289
44312
  var C22 = EC[0];
44290
44313
  var C3 = EC[1];
@@ -49363,7 +49386,7 @@ function circoLayout(g) {
49363
49386
  dg.components = comps;
49364
49387
  let blockCount = 0;
49365
49388
  if (comps.length === 1) {
49366
- blockCount = circularLayout(comps[0], g, blockCount, allEdges3);
49389
+ circularLayout(comps[0], g, blockCount, allEdges3);
49367
49390
  copyPositions(comps[0]);
49368
49391
  adjustNodesFull(g);
49369
49392
  } else {
@@ -53433,10 +53456,8 @@ function snapshotEdge(edge, flipY) {
53433
53456
  head: edge.head.name,
53434
53457
  points: collectEdgePoints(edge, flipY)
53435
53458
  };
53436
- const lbl = edge.info.label;
53437
- if (lbl !== void 0) {
53438
- geom.label = { x: lbl.pos.x, y: flipY(lbl.pos.y) };
53439
- }
53459
+ const label = placedLabelPos(edge.info.label, flipY);
53460
+ if (label !== void 0) geom.label = label;
53440
53461
  const tailLabel = placedLabelPos(edge.info.tail_label, flipY);
53441
53462
  if (tailLabel !== void 0) geom.tailLabel = tailLabel;
53442
53463
  const headLabel = placedLabelPos(edge.info.head_label, flipY);