@kazant/flexgraph 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- /*! FlexGraph v1.2.0 | MIT License | (c) 2026 Alexander Pettersen (kazant) | https://github.com/kazant/flexgraph */
1
+ /*! FlexGraph v1.3.0 | MIT License | (c) 2026 Alexander Pettersen (kazant) | https://github.com/kazant/flexgraph */
2
2
  var kn=["top","right","bottom","left"],vn=["TB","LR","BT","RL"],Sn=["orthogonal","straight","curved"],Ut=Object.freeze({direction:"TB",nodeSpacing:40,rankSpacing:80,edgeSpacing:10,edgeRouting:"orthogonal",cornerRadius:6,lineHops:!0,hopRadius:4,animate:!0,animationDuration:300,nodeMargin:14,groupPadding:20,groupLabelHeight:22,componentSpacing:80,maxIterations:24,bendPenalty:24,crossingPenalty:120,bundleFanOut:5,defaultNodeWidth:160,defaultNodeHeight:60,pinOnDrag:!0,draggable:!0,zoomable:!0,pannable:!0,minZoom:.1,maxZoom:4,controls:!0,showPorts:!0,hoverHighlight:"chain",highlightSelection:!0,multiSelect:!0,history:!0,historyLimit:100,expandText:"Details",collapseText:"Hide",explore:!1,exploreUnfocused:"dim",exploreLimit:25,exploreBreadcrumb:!0,exploreStartText:"Start",exploreMoreText:void 0,edgeTypes:{default:{markerEnd:"arrow"},"has-many":{markerStart:"one",markerEnd:"many"},"has-one":{markerStart:"one",markerEnd:"onlyOne"},"belongs-to":{markerStart:"many",markerEnd:"one"},"many-to-many":{markerStart:"many",markerEnd:"many"},association:{markerEnd:"open"},composition:{markerStart:"diamond"},dependency:{markerEnd:"open"}}}),ut=class extends Error{constructor(s,c=[]){super(s),this.name="GraphValidationError",this.details=c}},Z=t=>typeof t=="number"&&Number.isFinite(t);function In(...t){let s={...Ut,edgeTypes:{...Ut.edgeTypes}};for(let c of t)if(c)for(let[i,o]of Object.entries(c))o!==void 0&&(i==="edgeTypes"?Object.assign(s.edgeTypes,o):s[i]=o);return vn.includes(s.direction)||(s.direction="TB"),Sn.includes(s.edgeRouting)||(s.edgeRouting="orthogonal"),s}function Gt(t,s,c){if(!t||typeof t!="object")throw new ut("Graph must be an object");let i=In(t.options,s),o=[],n=[],r=[],h=new Map;for(let f of t.groups||[]){if(!f||f.id==null){o.push("Group without id");continue}let e=String(f.id);if(h.has(e)){o.push(`Duplicate group id "${e}"`);continue}let l={id:e,label:f.label??e,className:f.className||"",data:f};r.push(l),h.set(e,l)}let a=[],u=new Map;(t.nodes||[]).forEach((f,e)=>{if(!f||f.id==null){o.push(`Node at index ${e} has no id`);return}let l=String(f.id);if(u.has(l)){o.push(`Duplicate node id "${l}"`);return}let w=c&&c.get(l),x=Z(f.width)?f.width:w?w.width:i.defaultNodeWidth,M=Z(f.height)?f.height:w?w.height:i.defaultNodeHeight,m=f.group!=null?String(f.group):null;if(m&&!h.has(m)){let I={id:m,label:m,className:"",data:{id:m}};r.push(I),h.set(m,I)}let y=[],b=new Set;for(let I of f.ports||[]){if(!I||I.id==null){n.push(`Node "${l}": port without id ignored`);continue}let R=String(I.id);if(b.has(R)){n.push(`Node "${l}": duplicate port "${R}" ignored`);continue}let B=kn.includes(I.side)?I.side:null;B||n.push(`Node "${l}": port "${R}" has invalid side "${I.side}", using auto side`),b.add(R),y.push({id:R,side:B,offset:Z(I.offset)?Math.min(1,Math.max(0,I.offset)):null,label:I.label,data:I})}let k=f.constraints||{},O={pinned:!!k.pinned,x:Z(k.x)?k.x:Z(f.x)&&k.pinned?f.x:null,y:Z(k.y)?k.y:Z(f.y)&&k.pinned?f.y:null,rank:Number.isInteger(k.rank)&&k.rank>=0?k.rank:null};O.pinned&&(O.x==null||O.y==null)&&(n.push(`Node "${l}" is pinned but has no x/y; pin ignored until it has a position`),O.pinned=!1);let N={id:l,index:e,label:f.label??l,width:x,height:M,group:m,ports:y,portById:new Map(y.map(I=>[I.id,I])),constraints:O,className:f.className||"",type:f.type||null,data:f};a.push(N),u.set(l,N)});let g=[],p=new Map;(t.edges||[]).forEach((f,e)=>{if(!f)return;let l=f.id!=null?String(f.id):`e${e}`;if(p.has(l)){o.push(`Duplicate edge id "${l}"`);return}let w=String(f.source),x=String(f.target);if(!u.has(w)){o.push(`Edge "${l}": unknown source node "${w}"`);return}if(!u.has(x)){o.push(`Edge "${l}": unknown target node "${x}"`);return}let M=f.sourcePort!=null?String(f.sourcePort):null,m=f.targetPort!=null?String(f.targetPort):null;M&&!u.get(w).portById.has(M)&&(n.push(`Edge "${l}": node "${w}" has no port "${M}"`),M=null),m&&!u.get(x).portById.has(m)&&(n.push(`Edge "${l}": node "${x}" has no port "${m}"`),m=null);let y=i.edgeTypes[f.type]||i.edgeTypes.default||{},b={id:l,index:e,source:w,target:x,sourcePort:M,targetPort:m,type:f.type||"default",label:f.label??null,minLen:Number.isInteger(f.minLen)&&f.minLen>=0?f.minLen:1,weight:Z(f.weight)?f.weight:1,markerStart:f.markerStart!==void 0?f.markerStart:y.markerStart||null,markerEnd:f.markerEnd!==void 0?f.markerEnd:y.markerEnd||null,className:f.className||"",data:f};g.push(b),p.set(l,b)});let d=[];for(let f of t.constraints||[])if(!(!f||!f.type))if(f.type==="leftOf"||f.type==="before"){let e=String(f.a),l=String(f.b);if(!u.has(e)||!u.has(l)){n.push("leftOf constraint references unknown node");continue}d.push({type:"leftOf",a:e,b:l})}else if(f.type==="sameRank"){let e=(f.nodes||[]).map(String).filter(l=>u.has(l));e.length>=2?d.push({type:"sameRank",nodes:e}):n.push("sameRank constraint needs at least two known nodes")}else if(f.type==="rank"){let e=String(f.node);u.has(e)&&Number.isInteger(f.rank)&&(u.get(e).constraints.rank=f.rank)}else n.push(`Unknown constraint type "${f.type}"`);if(o.length)throw new ut(o[0],o);return{options:i,nodes:a,nodeById:u,edges:g,edgeById:p,groups:r,groupById:h,constraints:d,warnings:n}}function Pn(t,s){let c=new Map(t.map(h=>[h,[]])),i=new Map(t.map(h=>[h,0]));for(let h of s)c.get(h.v).push(h),i.set(h.w,i.get(h.w)+1);let o=new Map,n=new Set,r=[...t.filter(h=>i.get(h)===0),...t];for(let h of r){if(o.has(h))continue;let a=[[h,0]];for(o.set(h,1);a.length;){let u=a[a.length-1],[g,p]=u,d=c.get(g);if(p>=d.length){o.set(g,2),a.pop();continue}u[1]++;let f=d[p],e=o.get(f.w);e===1?n.add(f.id):e||(o.set(f.w,1),a.push([f.w,0]))}}return n}var kt=class{constructor(s){this.p=new Map(s.map(c=>[c,c]))}find(s){let c=s;for(;this.p.get(c)!==c;)c=this.p.get(c);for(;this.p.get(s)!==c;){let i=this.p.get(s);this.p.set(s,c),s=i}return c}union(s,c){let i=this.find(s),o=this.find(c);i!==o&&this.p.set(o,i)}};function Yt(t,s,c={}){let i=[],o=new kt(t);for(let y of c.sameRank||[])for(let b=1;b<y.length;b++)o.union(y[0],y[b]);let n=[...new Set(t.map(y=>o.find(y)))],r=new Set,h=new Set,a=[];for(let y of s){if(y.source===y.target){r.add(y.id);continue}let b=o.find(y.source),k=o.find(y.target);if(b===k){h.add(y.id);continue}a.push({id:y.id,v:b,w:k,minLen:y.minLen??1})}let u=Pn(n,a),g=a.map(y=>u.has(y.id)?{...y,v:y.w,w:y.v}:y),p=new Map;if(c.fixedRank)for(let[y,b]of c.fixedRank){if(!o.p.has(y))continue;let k=o.find(y);p.has(k)&&p.get(k)!==b&&i.push(`Conflicting fixed ranks in sameRank set of "${y}"`),p.set(k,Math.max(p.get(k)??0,b))}let d=new Map(n.map(y=>[y,[]])),f=new Map(n.map(y=>[y,[]]));for(let y of g)d.get(y.v).push(y),f.get(y.w).push(y);let e=new Map(n.map(y=>[y,f.get(y).length])),l=n.filter(y=>e.get(y)===0),w=[];for(;l.length;){let y=l.shift();w.push(y);for(let b of d.get(y))e.set(b.w,e.get(b.w)-1),e.get(b.w)===0&&l.push(b.w)}let x=new Map;for(let y of w){let b=p.get(y)??0;for(let k of f.get(y))b=Math.max(b,x.get(k.v)+k.minLen);p.has(y)&&b!==p.get(y)&&i.push(`Fixed rank ${p.get(y)} for "${y}" cannot be satisfied; using ${b}`),x.set(y,b)}for(let y=w.length-1;y>=0;y--){let b=w[y];if(p.has(b))continue;let k=d.get(b);if(!k.length||f.get(b).length>=k.length)continue;let N=1/0;for(let I of k)N=Math.min(N,x.get(I.w)-I.minLen);N>x.get(b)&&x.set(b,N)}let M=1/0;for(let y of x.values())M=Math.min(M,y);if(!p.size&&M>0)for(let[y,b]of x)x.set(y,b-M);let m=new Map;for(let y of t)m.set(y,x.get(o.find(y))??0);return{rank:m,reversed:u,flat:h,selfLoops:r,warnings:i}}function Vt(t,s,c=0){let i=[],o=new Map;for(let u of t)o.set(u.id,i.length),i.push({id:u.id,real:!0,rank:u.rank,width:u.width,height:u.height??0,group:u.group??null,pinX:u.pinX??null,inputIndex:i.length,up:[],down:[]});let n=new Map,r=[],h=(u,g,p,d,f,e)=>{let l={a:u,b:g,aOff:p,bOff:d,w:f,edgeId:e};r.push(l),i[u].down.push({n:g,off:d,own:p,w:f}),i[g].up.push({n:u,off:p,own:d,w:f})};for(let u of s){let g=o.get(u.v),p=o.get(u.w),d=i[g].rank,f=i[p].rank;if(f<=d)continue;let e=i[g].group&&i[g].group===i[p].group?i[g].group:null,l=[],w=g,x=u.vOff||0;for(let M=d+1;M<f;M++){let m=i.length;i.push({id:`__d_${u.id}_${M}`,real:!1,rank:M,width:c,height:0,group:e,pinX:null,edgeId:u.id,inputIndex:m,up:[],down:[]}),h(w,m,x,0,u.weight??1,u.id),l.push(m),w=m,x=0}h(w,p,x,u.wOff||0,u.weight??1,u.id),n.set(u.id,l)}let a=0;for(let u of i)a=Math.max(a,u.rank);return{v:i,index:o,segs:r,chains:n,maxRank:a}}function St(t){let s=Array.from({length:t.maxRank+1},()=>[]),c=new Uint8Array(t.v.length),i=t.v.map((o,n)=>n).filter(o=>t.v[o].real).sort((o,n)=>t.v[o].rank-t.v[n].rank||t.v[o].inputIndex-t.v[n].inputIndex);for(let o of i){if(c[o])continue;let n=[o];for(;n.length;){let r=n.pop();if(c[r])continue;c[r]=1,s[t.v[r].rank].push(r);let h=t.v[r].down.map(a=>a.n);for(let a=h.length-1;a>=0;a--)c[h[a]]||n.push(h[a])}}for(let o=0;o<t.v.length;o++)c[o]||s[t.v[o].rank].push(o);return s}function On(t){let s=t.length;if(s<2)return 0;let c=t.slice(),i=new Array(s),o=0;for(let n=1;n<s;n*=2){for(let r=0;r<s;r+=2*n){let h=Math.min(r+n,s),a=Math.min(r+2*n,s),u=r,g=h,p=r;for(;u<h&&g<a;)c[u]<=c[g]?i[p++]=c[u++]:(o+=h-u,i[p++]=c[g++]);for(;u<h;)i[p++]=c[u++];for(;g<a;)i[p++]=c[g++]}[c,i]=[i,c]}return o}function Rn(t,s,c,i){let o=[];for(let n of s)for(let r of t.v[n].down)o.push([i[n]+r.own*.999,i[r.n]+r.off*.999]);return o.sort((n,r)=>n[0]-r[0]||n[1]-r[1]),On(o.map(n=>n[1]))}function vt(t,s){let c=lt(t,s),i=0;for(let o=0;o<s.length-1;o++)i+=Rn(t,s[o],s[o+1],c);return i}function lt(t,s){let c=new Float64Array(t.v.length);for(let i of s)i.forEach((o,n)=>{c[o]=n});return c}function Wt(t,s={}){let c=s.maxIterations??24,i=(s.leftOf||[]).map(([u,g])=>[t.index.get(u),t.index.get(g)]).filter(([u,g])=>u!=null&&g!=null&&t.v[u].rank===t.v[g].rank),o=new Set(i.map(([u,g])=>u+":"+g)),n=[];s.initial&&n.push(s.initial.map(u=>u.slice()));let r=St(t);n.push(r),!s.initial&&c>4&&(n.push(r.map(u=>u.slice().reverse())),n.push(r.map(u=>u.slice().sort((g,p)=>t.v[g].inputIndex-t.v[p].inputIndex))));let h=null,a=1/0;for(let u of n){let g=En(t,u,i,o,c);if(g.crossings<a&&(h=g.layers,a=g.crossings),a===0)break}return{layers:h,crossings:a}}function En(t,s,c,i,o){let n=s.map(u=>Kt(t,u,c)),r=n.map(u=>u.slice()),h=vt(t,n),a=0;for(let u=0;u<o&&h>0;u++){let g=u%2===0;Ln(t,n,g,c,u%4>=2),An(t,n,i);let p=vt(t,n);if(p<h)h=p,r=n.map(d=>d.slice()),a=0;else if(++a>=6)break}if(h>0&&o>4){n=r.map(u=>u.slice());for(let u=0;u<2;u++){Nn(t,n,i);let g=vt(t,n);if(g<h)h=g,r=n.map(p=>p.slice());else break}}return{layers:r,crossings:h}}function Nn(t,s,c){let i=lt(t,s);for(let o of s)if(!(o.length<3||o.length>120))for(let n of o.slice()){let r=t.v[n];if(r.pinX!=null||r.group)continue;let h=o.indexOf(n),a=0,u=h,g=0;for(let p=h-1;p>=0;p--){let d=o[p];if(c.has(d+":"+n)||t.v[d].group)break;g+=et(t,n,d,i)-et(t,d,n,i),g<a&&(a=g,u=p)}g=0;for(let p=h+1;p<o.length;p++){let d=o[p];if(c.has(n+":"+d)||t.v[d].group)break;g+=et(t,d,n,i)-et(t,n,d,i),g<a&&(a=g,u=p)}u!==h&&(o.splice(h,1),o.splice(u,0,n),o.forEach((p,d)=>{i[p]=d}))}}function Ln(t,s,c,i,o){let n=lt(t,s),r=s.length,h=c?_t(1,r):_t(r-2,-1,-1);for(let a of h){let u=s[a],g=new Map;u.forEach((e,l)=>{let w=c?t.v[e].up:t.v[e].down;if(!w.length){g.set(e,l);return}if(o){let m=w.map(b=>n[b.n]+b.off-b.own).sort((b,k)=>b-k),y=m.length>>1;g.set(e,m.length%2?m[y]:(m[y-1]+m[y])/2);return}let x=0,M=0;for(let m of w)x+=(n[m.n]+m.off-m.own)*m.w,M+=m.w;g.set(e,x/M)});let p=new Map;for(let e of u){let l=t.v[e].group;if(!l)continue;let w=p.get(l)||[0,0];w[0]+=g.get(e),w[1]++,p.set(l,w)}let d=e=>{let l=t.v[e].group;return l?p.get(l)[0]/p.get(l)[1]:g.get(e)},f=new Map(u.map((e,l)=>[e,l]));u.sort((e,l)=>d(e)-d(l)||(t.v[e].group||"").localeCompare(t.v[l].group||"")||g.get(e)-g.get(l)||f.get(e)-f.get(l)),s[a]=Kt(t,u,i),s[a].forEach((e,l)=>{n[e]=l})}}function Kt(t,s,c){let i=s.slice(),o=[],n=[];if(i.forEach((r,h)=>{t.v[r].pinX!=null&&(o.push(h),n.push(r))}),n.length>1&&(n.sort((r,h)=>t.v[r].pinX-t.v[h].pinX),o.forEach((r,h)=>{i[r]=n[h]})),c.length)for(let r=0;r<c.length+1;r++){let h=!1;for(let[a,u]of c){let g=i.indexOf(a),p=i.indexOf(u);g<0||p<0||g<p||(i.splice(g,1),i.splice(i.indexOf(u),0,a),h=!0)}if(!h)break}return i}function et(t,s,c,i){let o=0;for(let n of["up","down"]){let r=t.v[s][n],h=t.v[c][n];for(let a of r)for(let u of h)i[a.n]+a.off>i[u.n]+u.off&&(o+=a.w*u.w)}return o}function An(t,s,c){let i=lt(t,s),o=!0,n=0;for(;o&&n++<8;){o=!1;for(let r of s)for(let h=0;h<r.length-1;h++){let a=r[h],u=r[h+1],g=t.v[a],p=t.v[u];if((g.group||null)!==(p.group||null)||g.pinX!=null&&p.pinX!=null||c.has(a+":"+u))continue;let d=et(t,a,u,i);et(t,u,a,i)<d&&(r[h]=u,r[h+1]=a,i[u]=h,i[a]=h+1,o=!0)}}}function _t(t,s,c=1){let i=[];if(c>0)for(let o=t;o<s;o+=c)i.push(o);else for(let o=t;o>s;o+=c)i.push(o);return i}function Bn(t,s,c){let i=t.length,o=new Float64Array(i);for(let g=1;g<i;g++)o[g]=o[g-1]+c[g];let n=[],r=[],h=[],a=[];for(let g=0;g<i;g++)for(n.push(t[g]-o[g]),r.push(s[g]),h.push(g),a.push(g);n.length>1&&n[n.length-2]>n[n.length-1];){let p=n.length-1,d=p-1,f=r[d]+r[p];n[d]=(n[d]*r[d]+n[p]*r[p])/f,r[d]=f,a[d]=a[p],n.pop(),r.pop(),h.pop(),a.pop()}let u=new Float64Array(i);for(let g=0;g<n.length;g++)for(let p=h[g];p<=a[g];p++)u[p]=n[g]+o[p];return u}function Zt(t,s,c){let i=t.v.length,o=new Float64Array(i),n=new Float64Array(i),r=c.groupPadding??20,h=c.groupLabelHeight??0,a=(m,y)=>{let b=t.v[m],k=t.v[y],O;return!b.real&&!k.real?O=c.edgeSpacing:!b.real||!k.real?O=(c.nodeSpacing+c.edgeSpacing)/2:O=c.nodeSpacing,b.group!==k.group&&(b.group&&(O+=r),k.group&&(O+=r+(c.labelOnOrderAxis?h:0))),(b.width+k.width)/2+O},u=s.map(m=>m.map((y,b)=>b===0?0:a(m[b-1],y)));s.forEach((m,y)=>{let b=0;m.forEach((O,N)=>{b+=u[y][N],o[O]=b});let k=b/2;m.forEach(O=>{o[O]-=k})});let g=m=>t.v[m].real?1:4,p=(m,y,b)=>{let k=s[m];if(!k.length)return;let O=new Array(k.length),N=new Array(k.length);k.forEach((R,B)=>{let T=t.v[R];if(T.pinX!=null){O[B]=T.pinX,N[B]=1e6;return}let v=0,E=0,X=U=>{for(let V of U){let Q=V.w*(t.v[V.n].real?1:2)*g(R);v+=(o[V.n]+V.off*t.v[V.n].width-V.own*T.width)*Q,E+=Q}};y&&X(T.up),b&&X(T.down),E===0?(O[B]=o[R],N[B]=.05):(O[B]=v/E,N[B]=E)});let I=Bn(O,N,u[m]);k.forEach((R,B)=>{o[R]=I[B]})},d=s.length,f=c.iterations??8;for(let m=0;m<f;m++){for(let y=1;y<d;y++)p(y,!0,!1);for(let y=d-2;y>=0;y--)p(y,!1,!0)}for(let m=0;m<2;m++){for(let y=0;y<d;y++)p(y,!0,!0);for(let y=d-1;y>=0;y--)p(y,!0,!0)}let e=s.map(m=>m.reduce((y,b)=>Math.max(y,t.v[b].height||0),0)),l=new Map;for(let m of t.v){if(!m.real||!m.group)continue;let y=l.get(m.group)||[1/0,-1/0];y[0]=Math.min(y[0],m.rank),y[1]=Math.max(y[1],m.rank),l.set(m.group,y)}let w=new Float64Array(d);for(let[m,y]of l.values()){let b=c.labelOnOrderAxis||c.labelAtRankEnd?0:h,k=c.labelAtRankEnd&&!c.labelOnOrderAxis?h:0;m>0&&(w[m]=Math.max(w[m],r+b)),y<d-1&&(w[y+1]=Math.max(w[y+1],r+k))}let x=[],M=0;for(let m=0;m<d;m++)m>0?M+=e[m-1]/2+c.rankSpacing+w[m]+e[m]/2:M=e[0]/2,x.push(M);for(let m=0;m<i;m++)n[m]=x[t.v[m].rank];return{x:o,y:n,rankY:x,rankHeight:e}}var ot={top:{x:0,y:-1},bottom:{x:0,y:1},left:{x:-1,y:0},right:{x:1,y:0}};function Jt(t){switch(t){case"LR":return{x:1,y:0,exit:"right",entry:"left"};case"RL":return{x:-1,y:0,exit:"left",entry:"right"};case"BT":return{x:0,y:-1,exit:"top",entry:"bottom"};default:return{x:0,y:1,exit:"bottom",entry:"top"}}}function ht(t){let s=new Map,c={top:[],right:[],bottom:[],left:[]};for(let i of t.ports)i.side&&c[i.side].push(i);for(let i of Object.keys(c)){let o=c[i];o.forEach((n,r)=>{let h=n.offset!=null?n.offset:(r+1)/(o.length+1);s.set(n.id,{side:i,t:h,...$n(i,h)})})}return s}function $n(t,s){switch(t){case"top":return{px:s,py:0};case"bottom":return{px:s,py:1};case"left":return{px:0,py:s};default:return{px:1,py:s}}}var J=t=>({x:t.x+t.width/2,y:t.y+t.height/2});function Qt(t,s){return{x:t.x-s,y:t.y-s,width:t.width+2*s,height:t.height+2*s}}function qt(t,s,c,i=.5){return s>t.x+i&&s<t.x+t.width-i&&c>t.y+i&&c<t.y+t.height-i}function tn(t,s,c,i=.5){let o=c.x+i,n=c.x+c.width-i,r=c.y+i,h=c.y+c.height-i;if(n<=o||h<=r)return!1;let a=0,u=1,g=s.x-t.x,p=s.y-t.y,d=(f,e)=>{if(f===0)return e>0;let l=e/f;if(f<0){if(l>u)return!1;l>a&&(a=l)}else{if(l<a)return!1;l<u&&(u=l)}return!0};return!d(-g,t.x-o)||!d(g,n-t.x)||!d(-p,t.y-r)||!d(p,h-t.y)?!1:u-a>1e-9}function It(t,s){let c=J(t),i=s.x-c.x,o=s.y-c.y;if(i===0&&o===0)return{...c,side:"bottom"};let n=i!==0?t.width/2/Math.abs(i):1/0,r=o!==0?t.height/2/Math.abs(o):1/0,h=Math.min(n,r),a=n<r?i>0?"right":"left":o>0?"bottom":"top";return{x:c.x+i*h,y:c.y+o*h,side:a}}function dt(t){let s=[];for(let c of t){let i=s[s.length-1];i&&Math.abs(i.x-c.x)<.01&&Math.abs(i.y-c.y)<.01||s.push({x:c.x,y:c.y})}for(let c=s.length-2;c>=1;c--){let i=s[c-1],o=s[c],n=s[c+1],r=(o.x-i.x)*(n.y-o.y)-(o.y-i.y)*(n.x-o.x),h=(o.x-i.x)*(n.x-o.x)+(o.y-i.y)*(n.y-o.y);Math.abs(r)<.01&&h>=0&&s.splice(c,1)}return s}var nn=(t,s,c)=>{switch(t){case"BT":return{x:s,y:-c};case"LR":return{x:c,y:s};case"RL":return{x:-c,y:s};default:return{x:s,y:c}}},Pt=(t,s,c)=>{switch(t){case"BT":return{x:s,y:-c};case"LR":return{x:c,y:s};case"RL":return{x:c,y:-s};default:return{x:s,y:c}}};function sn(t,s={}){return t.nodes.some(c=>c.group)?Tn(t,s):Ot(t,s)}function en(t,s,c){let i=new Set(s.map(n=>n.id)),o=s.map(n=>({...n,group:null,constraints:c?n.constraints:{...n.constraints,pinned:!1}}));return{options:t.options,nodes:o,nodeById:new Map(o.map(n=>[n.id,n])),edges:t.edges.filter(n=>i.has(n.source)&&i.has(n.target)),groups:[],groupById:new Map,constraints:t.constraints.filter(n=>n.type==="sameRank"?n.nodes.every(r=>i.has(r)):i.has(n.a)&&i.has(n.b)),warnings:[]}}function Tn(t,s){let c=on(t,s.hints||null,s.hints||null),i=new Map,o=r=>({x:r.x+r.width/2,y:r.y+r.height/2});for(let r of t.nodes){let h=c.nodes.get(r.id);if(!r.group){i.set(r.id,o(h));continue}let a=0,u=0,g=0;for(let p of t.edges){let d=p.source===r.id?p.target:p.target===r.id?p.source:null;if(d==null||t.nodeById.get(d).group===r.group)continue;let f=o(c.nodes.get(d));a+=f.x,u+=f.y,g++}i.set(r.id,g?{x:a/g,y:u/g}:o(h))}let n=on(t,i,s.hints||i);return n.crossingsRouted<=c.crossingsRouted?n:c}function on(t,s,c){let i=t.options,o=i.groupPadding,n=i.groupLabelHeight,r=new Map;for(let e of t.nodes)e.group&&(r.has(e.group)||r.set(e.group,[]),r.get(e.group).push(e));let h=new Map,a=[];for(let[e,l]of r){let w=Ot(en(t,l,!1),{hints:s}),x=w.bounds,M="\0group:"+e;h.set(e,{sub:w,b:x,blockId:M}),a.push({id:M,index:-1,label:e,width:x.width+2*o,height:x.height+2*o+n,group:null,ports:[],portById:new Map,constraints:{pinned:!1,x:null,y:null,rank:null}})}let u=e=>{let l=t.nodeById.get(e);return l.group?h.get(l.group).blockId:e},g=en(t,t.nodes.filter(e=>!e.group),!0);g.nodes.push(...a);for(let e of a)g.nodeById.set(e.id,e);g.edges=[];for(let e of t.edges){let l=u(e.source),w=u(e.target);l===w&&(e.source!==e.target||l!==e.source)||g.edges.push({...e,source:l,target:w,sourcePort:l===e.source?e.sourcePort:null,targetPort:w===e.target?e.targetPort:null})}let p=null;if(c){p=new Map(c);for(let[e,l]of r){let w=0,x=0,M=0;for(let m of l){let y=c.get(m.id);y&&(w+=y.x,x+=y.y,M++)}M&&p.set(h.get(e).blockId,{x:w/M,y:x/M})}}let d=Ot(g,{hints:p}),f={nodes:new Map,edges:new Map,groups:new Map,crossings:d.crossings,warnings:d.warnings};for(let e of t.nodes)e.group||f.nodes.set(e.id,d.nodes.get(e.id));for(let[,{sub:e,b:l,blockId:w}]of h){let x=d.nodes.get(w),M=x.x+o-l.x,m=x.y+o+n-l.y;for(let[y,b]of e.nodes)f.nodes.set(y,{...b,x:b.x+M,y:b.y+m});for(let[y,b]of e.edges)f.edges.set(y,{...b,waypoints:b.waypoints.map(k=>({x:k.x+M,y:k.y+m}))});f.crossings+=e.crossings}for(let e of t.edges){if(f.edges.has(e.id))continue;let l=d.edges.get(e.id),w=u(e.source)!==e.source||u(e.target)!==e.target;f.edges.set(e.id,l?{...l,waypoints:w?[]:l.waypoints}:{waypoints:[],reversed:!1,selfLoop:e.source===e.target,flat:!1})}for(let e of t.nodes)if(e.group&&e.constraints.pinned){let l=f.nodes.get(e.id);l.x=e.constraints.x,l.y=e.constraints.y}if(cn(t,f),!t.nodes.some(e=>e.constraints.pinned)){let e=Rt(f);rn(f,-e.x,-e.y);for(let l of f.groups.values())l.x-=e.x,l.y-=e.y}return f.bounds=Rt(f),f.crossingsRouted=Xn(t,f),f}function Xn(t,s){let c=[];for(let n of t.edges){if(n.source===n.target)continue;let r=s.nodes.get(n.source),h=s.nodes.get(n.target);c.push([n.source,n.target,r.x+r.width/2,r.y+r.height/2,h.x+h.width/2,h.y+h.height/2])}let i=0,o=(n,r,h,a,u,g)=>(h-n)*(g-r)-(a-r)*(u-n);for(let n=0;n<c.length;n++)for(let r=n+1;r<c.length;r++){let h=c[n],a=c[r];if(h[0]===a[0]||h[0]===a[1]||h[1]===a[0]||h[1]===a[1])continue;let u=o(a[2],a[3],a[4],a[5],h[2],h[3]),g=o(a[2],a[3],a[4],a[5],h[4],h[5]),p=o(h[2],h[3],h[4],h[5],a[2],a[3]),d=o(h[2],h[3],h[4],h[5],a[4],a[5]);u*g<0&&p*d<0&&i++}return i}function Ot(t,s={}){let c=t.options,i=c.direction,o=i==="LR"||i==="RL",n=[],r=new Map;for(let d of t.nodes){let f=ht(d),e=new Map;for(let[x,M]of f)e.set(x,(o?M.py:M.px)-.5);let l=null,w=null;if(d.constraints.pinned){let x=Pt(i,d.constraints.x+d.width/2,d.constraints.y+d.height/2);l=x.x,w=x.y}r.set(d.id,{iw:o?d.height:d.width,ih:o?d.width:d.height,frac:e,pinX:l,pinY:w})}let h=Fn(t),a={nodes:new Map,edges:new Map,groups:new Map,crossings:0,warnings:n},u=[];for(let d of h){let f=d.nodes,e=new Set(f),l=t.edges.filter(v=>e.has(v.source)),w=new Map;for(let v of f){let E=t.nodeById.get(v).constraints.rank;E!=null&&w.set(v,E)}let x=t.constraints.filter(v=>v.type==="sameRank").map(v=>v.nodes.filter(E=>e.has(E))).filter(v=>v.length>1),M=Yt(f,l,{fixedRank:w,sameRank:x});n.push(...M.warnings);let m=f.map(v=>{let E=t.nodeById.get(v),X=r.get(v);return{id:v,rank:M.rank.get(v),width:X.iw,height:X.ih,group:E.group,pinX:X.pinX}}),y=[];for(let v of l){if(M.selfLoops.has(v.id)||M.flat.has(v.id))continue;let E=M.reversed.has(v.id),X=v.sourcePort?r.get(v.source).frac.get(v.sourcePort)??0:0,U=v.targetPort?r.get(v.target).frac.get(v.targetPort)??0:0;y.push(E?{id:v.id,v:v.target,w:v.source,vOff:U,wOff:X,weight:v.weight}:{id:v.id,v:v.source,w:v.target,vOff:X,wOff:U,weight:v.weight})}let b=Vt(m,y,0),k;s.hints&&s.hints.size&&(k=Hn(b,s.hints,i));let O=t.constraints.filter(v=>v.type==="leftOf"&&e.has(v.a)&&e.has(v.b)).map(v=>[v.a,v.b]),N=Wt(b,{maxIterations:c.maxIterations,leftOf:O,initial:k});a.crossings+=N.crossings;let I=Zt(b,N.layers,{nodeSpacing:c.nodeSpacing,edgeSpacing:c.edgeSpacing,rankSpacing:c.rankSpacing,groupPadding:c.groupPadding,groupLabelHeight:c.groupLabelHeight,labelOnOrderAxis:o,labelAtRankEnd:i==="BT"||i==="RL"?!o:!1}),R=f.filter(v=>r.get(v).pinY!=null);if(R.length){let v=0;for(let E of R)v+=r.get(E).pinY-I.y[b.index.get(E)];v/=R.length;for(let E=0;E<I.y.length;E++)I.y[E]+=v;for(let E of R){let X=b.index.get(E);I.x[X]=r.get(E).pinX,I.y[X]=r.get(E).pinY}}let B=1/0,T=-1/0;for(let v=0;v<b.v.length;v++){let E=b.v[v].width/2;B=Math.min(B,I.x[v]-E),T=Math.max(T,I.x[v]+E)}u.push({comp:d,g:b,co:I,rk:M,layers:N.layers,minX:B,maxX:T,pinned:R.length>0})}if(s.hints&&s.hints.size){let d=e=>{let l=0,w=0;for(let x of e.comp.nodes){let M=s.hints.get(x);M&&(l+=Pt(i,M.x,M.y).x,w++)}return w?l/w:1/0},f=new Map(u.map(e=>[e,d(e)]));u.sort((e,l)=>f.get(e)-f.get(l))}let g=-1/0;for(let d of u)d.pinned&&(g=Math.max(g,d.maxX+c.componentSpacing));g===-1/0&&(g=0);for(let d of u){if(d.pinned)continue;let f=g-d.minX;for(let e=0;e<d.co.x.length;e++)d.co.x[e]+=f;g+=d.maxX-d.minX+c.componentSpacing}for(let d of u){let{g:f,co:e,rk:l}=d;d.layers.forEach((w,x)=>w.forEach((M,m)=>{let y=f.v[M];if(!y.real)return;let b=t.nodeById.get(y.id),k=nn(i,e.x[M],e.y[M]);a.nodes.set(y.id,{x:k.x-b.width/2,y:k.y-b.height/2,width:b.width,height:b.height,rank:x,order:m})}));for(let w of t.edges){if(!d.comp.set.has(w.source))continue;let M=(f.chains.get(w.id)||[]).map(y=>nn(i,e.x[y],e.y[y])),m=l.reversed.has(w.id);m&&(M=M.reverse()),a.edges.set(w.id,{waypoints:M,reversed:m,selfLoop:l.selfLoops.has(w.id),flat:l.flat.has(w.id)})}}if(!u.some(d=>d.pinned)){let d=1/0,f=1/0;for(let l of a.nodes.values())d=Math.min(d,l.x),f=Math.min(f,l.y);let e=t.groups.length?c.groupPadding+c.groupLabelHeight:0;Number.isFinite(d)&&rn(a,-d+e,-f+e)}return cn(t,a),a.bounds=Rt(a),a}function rn(t,s,c){for(let i of t.nodes.values())i.x+=s,i.y+=c;for(let i of t.edges.values())for(let o of i.waypoints)o.x+=s,o.y+=c}function cn(t,s){let c=t.options;s.groups=new Map;for(let i of t.groups){let o=1/0,n=1/0,r=-1/0,h=-1/0;for(let u of t.nodes){if(u.group!==i.id)continue;let g=s.nodes.get(u.id);g&&(o=Math.min(o,g.x),n=Math.min(n,g.y),r=Math.max(r,g.x+g.width),h=Math.max(h,g.y+g.height))}if(!Number.isFinite(o))continue;let a=c.groupPadding;s.groups.set(i.id,{x:o-a,y:n-a-c.groupLabelHeight,width:r-o+2*a,height:h-n+2*a+c.groupLabelHeight})}return s.groups}function Rt(t){let s=1/0,c=1/0,i=-1/0,o=-1/0,n=r=>{s=Math.min(s,r.x),c=Math.min(c,r.y),i=Math.max(i,r.x+r.width),o=Math.max(o,r.y+r.height)};for(let r of t.nodes.values())n(r);for(let r of t.groups.values())n(r);return Number.isFinite(s)?{x:s,y:c,width:i-s,height:o-c}:{x:0,y:0,width:0,height:0}}function Hn(t,s,c){let i=St(t),o=new Float64Array(t.v.length).fill(NaN);t.v.forEach((n,r)=>{if(n.real&&s.has(n.id)){let h=s.get(n.id);o[r]=Pt(c,h.x,h.y).x}});for(let[,n]of t.chains){if(!n.length)continue;let r=t.v[n[0]].up[0]?.n,h=t.v[n[n.length-1]].down[0]?.n;r==null||h==null||Number.isNaN(o[r])||Number.isNaN(o[h])||n.forEach((a,u)=>{o[a]=o[r]+(o[h]-o[r])*(u+1)/(n.length+1)})}return i.map(n=>{let r=new Map(n.map((h,a)=>[h,a]));return n.slice().sort((h,a)=>{let u=o[h],g=o[a];return Number.isNaN(u)&&Number.isNaN(g)?r.get(h)-r.get(a):Number.isNaN(u)?1:Number.isNaN(g)?-1:u-g})})}function Fn(t){let s=new Map(t.nodes.map(r=>[r.id,r.id])),c=r=>{for(;s.get(r)!==r;)s.set(r,s.get(s.get(r))),r=s.get(r);return r},i=(r,h)=>{let a=c(r),u=c(h);a!==u&&s.set(u,a)};for(let r of t.edges)i(r.source,r.target);let o=new Map;for(let r of t.nodes)r.group&&(o.has(r.group)?i(o.get(r.group),r.id):o.set(r.group,r.id));for(let r of t.constraints)if(r.type==="leftOf"&&i(r.a,r.b),r.type==="sameRank")for(let h of r.nodes)i(r.nodes[0],h);let n=new Map;for(let r of t.nodes){let h=c(r.id);n.has(h)||n.set(h,[]),n.get(h).push(r.id)}return[...n.values()].map(r=>({nodes:r,set:new Set(r)}))}function zn(t,s,c){let i=Jt(c),o=J(t),n=J(s),r=n.x-o.x,h=n.y-o.y,a=Math.abs(r)-(t.width+s.width)/2,u=Math.abs(h)-(t.height+s.height)/2,g=i.x!==0?r*i.x:h*i.y,p=i.x!==0?a:u;return g>0&&p>4?[i.exit,i.entry]:a>=u?r>=0?["right","left"]:["left","right"]:h>=0?["bottom","top"]:["top","bottom"]}function fn(t,s){let c=t.options.direction,i=new Map,o=new Map;for(let a of t.nodes){let u=s.get(a.id);if(!u)continue;let g=ht(a),p=new Map;for(let[d,f]of g)p.set(d,{x:u.x+f.px*u.width,y:u.y+f.py*u.height,side:f.side,t:f.t});o.set(a.id,p)}let n=new Map,r=new Map;for(let a of t.edges){let u=s.get(a.source),g=s.get(a.target);if(!u||!g)continue;if(a.source===a.target){let l=o.get(a.source),w=a.sourcePort&&l.get(a.sourcePort),x=a.targetPort&&l.get(a.targetPort),M=Math.min(20,u.width/4,u.height/4);i.set(a.id,{source:w&&w.side?{x:w.x,y:w.y,side:w.side}:{x:u.x+u.width-M,y:u.y,side:"top"},target:x&&x.side?{x:x.x,y:x.y,side:x.side}:{x:u.x+u.width,y:u.y+M,side:"right"}});continue}let[p,d]=zn(u,g,c),f=a.sourcePort&&o.get(a.source).get(a.sourcePort),e=a.targetPort&&o.get(a.target).get(a.targetPort);f&&f.side&&(p=f.side),e&&e.side&&(d=e.side),r.set(a.id,[p,d]),f||an(n,a.source+"|"+p,{edgeId:a.id,end:"source",other:a.target}),e||an(n,a.target+"|"+d,{edgeId:a.id,end:"target",other:a.source}),i.set(a.id,{source:f?{x:f.x,y:f.y,side:f.side}:null,target:e?{x:e.x,y:e.y,side:e.side}:null})}for(let a=0;a<2;a++)for(let[u,g]of n){let[p,d]=u.split("|"),f=s.get(p),e=d==="top"||d==="bottom",l=x=>{let M=i.get(x.edgeId),m=x.end==="source"?M.target:M.source;if(a>0&&m)return e?m.x:m.y;let y=J(s.get(x.other));return e?y.x:y.y};g.sort((x,M)=>l(x)-l(M)||(x.edgeId<M.edgeId?-1:1));let w=[...o.get(p)?.values()||[]].filter(x=>x.side===d).map(x=>x.t);g.forEach((x,M)=>{let m=(M+1)/(g.length+1);for(let b of w)Math.abs(b-m)<.06&&(m=Math.min(.95,m+.1));let y=e?{x:f.x+m*f.width,y:d==="top"?f.y:f.y+f.height,side:d}:{x:d==="left"?f.x:f.x+f.width,y:f.y+m*f.height,side:d};i.get(x.edgeId)[x.end]=y})}let h=a=>{a&&(a.x=Math.round(a.x*100)/100,a.y=Math.round(a.y*100)/100)};for(let a of i.values())h(a.source),h(a.target);return{ends:i,ports:o,sides:r}}function an(t,s,c){t.has(s)||t.set(s,[]),t.get(s).push(c)}function dn(t,s,c){let i=c.spacing,o={h:new Map,v:new Map},n=new Map;for(let[p,d]of t)n.set(p,d.points.map(f=>({x:f.x,y:f.y})));for(let[p,d]of t){if(d.kind!=="orthogonal"||d.bundle)continue;let f=d.points;for(let e=0;e<f.length-1;e++){if(c.fixedEnds!==!1&&(e===0||e===f.length-2))continue;let l=f[e],w=f[e+1],x=Math.abs(l.x-w.x)<.01,M=Math.abs(l.y-w.y)<.01;if(!x&&!M)continue;let m=x?l.x:l.y,y=x?Math.min(l.y,w.y):Math.min(l.x,w.x),b=x?Math.max(l.y,w.y):Math.max(l.x,w.x);if(b-y<.5)continue;let k=x?l.y<=w.y:l.x<=w.x,O=f[e-1],N=f[e+2],I=(X,U)=>X?Math.sign(x?X.x-U.x:X.y-U.y):0,R=I(O,l),B=I(N,w),T={id:p,i:e,c:m,lo:y,hi:b,vertical:x,loIsA:k,pts:n.get(p),dirLo:k?R:B,dirHi:k?B:R},v=Math.round(m*100),E=x?o.v:o.h;E.has(v)||E.set(v,[]),E.get(v).push(T)}}let r=[...o.v.values()].flat(),h=[...o.h.values()].flat(),a=[];for(let p of[!0,!1])for(let d of(p?o.v:o.h).values()){if(d.length<2)continue;d.sort((w,x)=>w.lo-x.lo);let f=[d[0]],e=d[0].hi,l=()=>{f.length>1&&u(f,p)};for(let w=1;w<d.length;w++){let x=d[w];x.lo<e-.5?(f.push(x),e=Math.max(e,x.hi)):(l(),f=[x],e=x.hi)}l()}function u(p,d){if(new Set(p.map(k=>k.id)).size<2)return;let f=[];for(let k of p){let O=f.length;for(;O>0&&Dn(k,f[O-1])<0;)O--;f.splice(O,0,k)}let e=p[0].c,l=Math.min(...p.map(k=>k.lo)),w=Math.max(...p.map(k=>k.hi)),x=1/0,M=1/0;for(let k of s.values()){let O=d?k.y:k.x;if((d?k.y+k.height:k.x+k.width)<=l||O>=w)continue;let I=d?k.x:k.y,R=d?k.x+k.width:k.y+k.height;R<=e+.01?x=Math.min(x,e-R):I>=e-.01&&(M=Math.min(M,I-e))}for(let k of d?r:h){if(k.c===e||k.hi<=l||k.lo>=w)continue;let O=Math.abs(k.c-e)/2;k.c<e?x=Math.min(x,O+4):M=Math.min(M,O+4)}let m=f.length,y=Math.max(0,Math.min(x,M)-4),b=Math.min(i,m>1?2*y/(m-1):i);f.forEach((k,O)=>{let N=(O-(m-1)/2)*b;Math.abs(N)>.01&&a.push([k.id,k.i,d,N])})}let g=new Map;for(let[p,d,f,e]of a)g.has(p)||g.set(p,new Map),g.get(p).set(d,f?{x:e,y:0}:{x:0,y:e});for(let[p,d]of g){let f=t.get(p),e=f.points,l=[],w={x:0,y:0};for(let x=0;x<e.length;x++){let M=x>0&&d.get(x-1)||w,m=x<e.length-1&&d.get(x)||w,y=x>0&&x<e.length-1&&Cn(e[x-1],e[x],e[x+1]);y&&(M.x!==m.x||M.y!==m.y)?(l.push({x:e[x].x+M.x,y:e[x].y+M.y}),l.push({x:e[x].x+m.x,y:e[x].y+m.y})):l.push({x:e[x].x+M.x+(y?0:m.x),y:e[x].y+M.y+(y?0:m.y)})}f.points=jn(l)}}function Cn(t,s,c){return Math.abs(t.x-s.x)<.01&&Math.abs(s.x-c.x)<.01||Math.abs(t.y-s.y)<.01&&Math.abs(s.y-c.y)<.01}function jn(t){let s=[];for(let c of t){let i=s[s.length-1];i&&Math.abs(i.x-c.x)<.01&&Math.abs(i.y-c.y)<.01||s.push(c)}return s}function Dn(t,s){return hn(t,s,!0)||hn(t,s,!1)||Gn(t,s)}function un(t,s){let{pts:c,i,loIsA:o}=t,n=[];if(s?!o:o)for(let h=i+1;h<c.length;h++)n.push(c[h]);else for(let h=i;h>=0;h--)n.push(c[h]);return n}var Un=(t,s)=>t.x*s.y-t.y*s.x,ln=(t,s)=>{let c=s.x-t.x,i=s.y-t.y,o=Math.hypot(c,i)||1;return{x:c/o,y:i/o}};function hn(t,s,c){let i=t.vertical,o=un(t,c),n=un(s,c),r=c?Math.max(t.lo,s.lo):Math.min(t.hi,s.hi),h=i?{x:t.c,y:r}:{x:r,y:t.c},a=i?{x:0,y:c?-1:1}:{x:c?-1:1,y:0},u=a,g=0,p=0,d=(e,l)=>{if(e===l)return 0;let w={x:a.y,y:-a.x},x=i?w.x:w.y;return(e<l?x<0:x>0)?-1:1},f=(e,l)=>l+1<e.length?Math.sign(Math.round(Un(u,ln(e[l],e[l+1]))*1e3)):null;for(let e=0;e<64;e++){let l=g<o.length?(o[g].x-h.x)*u.x+(o[g].y-h.y)*u.y:1/0,w=p<n.length?(n[p].x-h.x)*u.x+(n[p].y-h.y)*u.y:1/0;if(l===1/0&&w===1/0)return 0;if(Math.abs(l-w)<.5){let M=f(o,g),m=f(n,p);if(M===null||m===null)return M===null&&m===null?0:d(M??0,m??0);if(M!==m)return d(M,m);h=o[g],u=ln(o[g],o[g+1]),g++,p++;continue}if(l<w){let M=f(o,g);return M?d(M,0):0}let x=f(n,p);return x?d(0,x):0}return 0}var Gn=(t,s)=>t.id<s.id?-1:t.id>s.id?1:t.i-s.i;function gn(t,s,c=4){let i=[],o=new Map(s.map((a,u)=>[a,u]));for(let a of s){let u=t.get(a).points;for(let g=0;g<u.length-1;g++){let p=u[g],d=u[g+1];Math.abs(p.x-d.x)<.01&&Math.abs(p.y-d.y)>.01&&i.push([o.get(a),p.x,Math.min(p.y,d.y),Math.max(p.y,d.y)])}}let n=64,r=new Map;for(let a of i){let u=Math.floor(a[1]/n);r.has(u)||r.set(u,[]),r.get(u).push(a)}let h=new Map;for(let a of s){let u=o.get(a),g=t.get(a).points,p=[];for(let d=0;d<g.length-1;d++){let f=g[d],e=g[d+1];if(Math.abs(f.y-e.y)>.01)continue;let l=Math.min(f.x,e.x)+c*2,w=Math.max(f.x,e.x)-c*2;if(!(w<=l))for(let x=Math.floor(l/n);x<=Math.floor(w/n);x++)for(let M of r.get(x)||[])M[0]>=u||M[1]<=l||M[1]>=w||f.y<=M[2]+c||f.y>=M[3]-c||p.push({seg:d,x:M[1],y:f.y})}p.length&&h.set(a,p)}return h}var Et=1,Yn=[1,-1,0,0],_n=[0,0,1,-1],Nt=[1,0,3,2],pn=t=>t.x>0?0:t.x<0?1:t.y>0?2:3;function yn(t,s,c={}){let i=t.options,o=s.nodes,n=i.edgeRouting,{ends:r,ports:h}=fn(t,o),a=new Map,u=new Map,g=c.only||null,p=c.prev||null;if(n==="orthogonal")Vn(t,o,r,i,g,p,a,u),dn(a,o,{spacing:i.edgeSpacing,fixedEnds:!0});else for(let e of t.edges){let l=o.get(e.source),w=o.get(e.target);if(!l||!w)continue;if(e.source===e.target){a.set(e.id,xn(l,i,r.get(e.id)));continue}let x=s.edges.get(e.id),M=x&&x.waypoints||[],m=r.get(e.id),y,b;n==="curved"||e.sourcePort?y=m.source:y=It(l,M[0]||J(w)),n==="curved"||e.targetPort?b=m.target:b=It(w,M[M.length-1]||J(l)),a.set(e.id,{points:[y,...M.map(k=>({...k})),b],kind:n,sourceSide:y.side,targetSide:b.side})}let d=t.edges.map(e=>e.id).filter(e=>a.has(e)),f=i.lineHops&&n==="orthogonal"?gn(a,d,i.hopRadius):new Map;for(let[e,l]of a)l.hops=f.get(e)||[];return{paths:a,raw:u,ports:h,mode:n}}function xn(t,s,c){let i=Math.max(s.nodeMargin*1.5,16),o=c.source,n=c.target,r=ot[o.side],h=ot[n.side],a={x:o.x+r.x*i,y:o.y+r.y*i},u={x:n.x+h.x*i,y:n.y+h.y*i},g;if(o.side===n.side)g=o.side==="left"||o.side==="right"?[a,{x:a.x,y:u.y}]:[a,{x:u.x,y:a.y}];else if((o.side==="top"||o.side==="bottom")!=(n.side==="top"||n.side==="bottom"))g=o.side==="top"||o.side==="bottom"?[a,{x:u.x,y:a.y}]:[a,{x:a.x,y:u.y}];else{let p=o.side==="top"||o.side==="bottom",d=p?t.x+t.width+i:t.y+t.height+i;g=p?[a,{x:d,y:a.y},{x:d,y:u.y}]:[a,{x:a.x,y:d},{x:u.x,y:d}]}return{points:dt([o,...g,u,n]),kind:"orthogonal",sourceSide:o.side,targetSide:n.side,selfLoop:!0}}function Vn(t,s,c,i,o,n,r,h){let a=i.nodeMargin,u=[];for(let[e,l]of s)u.push({id:e,...Qt(l,a)});let g=new Map;for(let e of t.edges){let l=c.get(e.id);if(!l||!l.source||!l.target)continue;let w=ot[l.source.side],x=ot[l.target.side];g.set(e.id,{s:{x:l.source.x+w.x*a,y:l.source.y+w.y*a},t:{x:l.target.x+x.x*a,y:l.target.y+x.y*a},sDir:pn(w),tNeed:Nt[pn(x)]})}let p=Jn(u,g,a);for(let e of t.edges)if(o&&!o.has(e.id)&&n&&n.raw.has(e.id)){let l=n.raw.get(e.id);h.set(e.id,l),p.markPath(l);let w=n.paths.get(e.id);w&&r.set(e.id,{...w,points:l.map(x=>({...x}))})}let d=t.edges.filter(e=>!r.has(e.id)&&s.has(e.source)&&s.has(e.target)),f=e=>{let l=g.get(e.id);return l?Math.abs(l.s.x-l.t.x)+Math.abs(l.s.y-l.t.y):0};d.sort((e,l)=>f(e)-f(l)),Kn(t,s,c,i,u,d,p,r,h);for(let e of d){if(r.has(e.id))continue;if(e.source===e.target){let m=xn(s.get(e.source),i,c.get(e.id));r.set(e.id,m),h.set(e.id,m.points);continue}let l=c.get(e.id),w=g.get(e.id),x=p.astar(w.s,w.sDir,w.t,w.tNeed,i);x||(x=Zn(w.s,w.t,l.source.side,l.target.side));let M=[l.source,...dt(x),l.target];p.markPath(M),h.set(e.id,M),r.set(e.id,{points:M.map(m=>({...m})),kind:"orthogonal",sourceSide:l.source.side,targetSide:l.target.side})}}var Wn={top:"bottom",bottom:"top",left:"right",right:"left"};function Kn(t,s,c,i,o,n,r,h,a){let u=i.bundleFanOut;if(!u)return;let g=new Map;for(let p of n){if(p.source===p.target||p.sourcePort||p.markerStart)continue;let d=c.get(p.id);if(!d||!d.source||!d.target||d.target.side!==Wn[d.source.side])continue;let f=p.source+"|"+d.source.side;g.has(f)||g.set(f,[]),g.get(f).push(p)}for(let[p,d]of g){if(d.length<u)continue;let f=d[0].source,e=c.get(d[0].id).source.side,l=s.get(f),w=ot[e],x=w.y!==0,M=x?w.y:w.x,m=e==="bottom"?l.y+l.height:e==="top"?l.y:e==="right"?l.x+l.width:l.x,y=1/0;for(let I of d){let R=c.get(I.id).target;y=Math.min(y,((x?R.y:R.x)-m)*M)}if(y<2*i.nodeMargin+4)continue;let b=m+M*y/2,k=x?{x:l.x+l.width/2,y:m}:{x:m,y:l.y+l.height/2},O=[],N=!0;for(let I of d){let R=c.get(I.id).target,B=dt(x?[k,{x:k.x,y:b},{x:R.x,y:b},R]:[k,{x:b,y:k.y},{x:b,y:R.y},R]);for(let T=0;N&&T<B.length-1;T++)for(let v of o)if(v.id!==f&&v.id!==I.target&&tn(B[T],B[T+1],v,1)){N=!1;break}if(!N)break;O.push([I,B,R.side])}if(N)for(let[I,R,B]of O)r.markPath(R),a.set(I.id,R),h.set(I.id,{points:R.map(T=>({...T})),kind:"orthogonal",sourceSide:e,targetSide:B,bundle:p})}}function Zn(t,s,c,i){let o=c==="top"||c==="bottom",n=i==="top"||i==="bottom";if(o&&n){let r=(t.y+s.y)/2;return[t,{x:t.x,y:r},{x:s.x,y:r},s]}if(!o&&!n){let r=(t.x+s.x)/2;return[t,{x:r,y:t.y},{x:r,y:s.y},s]}return o?[t,{x:t.x,y:s.y},s]:[t,{x:s.x,y:t.y},s]}function Jn(t,s,c){let i=new Set,o=new Set,n=S=>Math.round(S*100)/100,r=1/0,h=1/0,a=-1/0,u=-1/0;for(let S of t)i.add(n(S.x)),i.add(n(S.x+S.width)),o.add(n(S.y)),o.add(n(S.y+S.height)),r=Math.min(r,S.x),h=Math.min(h,S.y),a=Math.max(a,S.x+S.width),u=Math.max(u,S.y+S.height);for(let S of s.values())for(let P of[S.s,S.t])i.add(n(P.x)),o.add(n(P.y)),r=Math.min(r,P.x),h=Math.min(h,P.y),a=Math.max(a,P.x),u=Math.max(u,P.y);i.add(n(r-2*c)),i.add(n(a+2*c)),o.add(n(h-2*c)),o.add(n(u+2*c));let g=S=>{let P=[...S].sort((A,G)=>A-G),L=[];for(let A=0;A<P.length;A++)L.push(P[A]),A+1<P.length&&P[A+1]-P[A]>4&&L.push(n((P[A]+P[A+1])/2));return L},p=g(i),d=g(o),f=p.length,e=d.length,l=f*e,w=new Map(p.map((S,P)=>[S,P])),x=new Map(d.map((S,P)=>[S,P])),M=120,m=new Map,y=(S,P)=>S*73856093^P*19349663;for(let S of t)for(let P=Math.floor(S.x/M);P<=Math.floor((S.x+S.width)/M);P++)for(let L=Math.floor(S.y/M);L<=Math.floor((S.y+S.height)/M);L++){let A=y(P,L);m.has(A)||m.set(A,[]),m.get(A).push(S)}let b=(S,P)=>{let L=m.get(y(Math.floor(S/M),Math.floor(P/M)));if(!L)return!1;for(let A of L)if(qt(A,S,P,.01))return!0;return!1},k=l<=4e6?new Uint8Array(l):null,O=k?null:new Map,N=S=>{if(k){let L=k[S];return L||(L=b(p[S%f],d[S/f|0])?2:1,k[S]=L),L===2}let P=O.get(S);return P===void 0&&(P=b(p[S%f],d[S/f|0]),O.set(S,P)),P},I=new Uint8Array(l),R=new Uint8Array(l),B=(S,P)=>{let L=P?I:R,A=L[S];if(!A){let G=S%f,Y=S/f|0,H=P?(p[G]+p[G+1])/2:p[G],C=P?d[Y]:(d[Y]+d[Y+1])/2;A=b(H,C)?2:1,L[S]=A}return A===2},T=new Uint16Array(l),v=new Uint16Array(l),E=new Uint16Array(l),X=new Uint16Array(l),U=S=>{let P=w.get(n(S.x)),L=x.get(n(S.y));return P==null||L==null?-1:L*f+P};function V(S){for(let P=0;P<S.length-1;P++){let L=S[P],A=S[P+1],G=Math.abs(L.y-A.y)<.01,Y=Math.abs(L.x-A.x)<.01;if(G){let H=x.get(n(L.y)),C=w.get(n(L.x)),D=w.get(n(A.x));if(H==null||C==null||D==null)continue;let q=Math.min(C,D),tt=Math.max(C,D);for(let z=q;z<=tt;z++)T[H*f+z]++,z<tt&&E[H*f+z]++}else if(Y){let H=w.get(n(L.x)),C=x.get(n(L.y)),D=x.get(n(A.y));if(H==null||C==null||D==null)continue;let q=Math.min(C,D),tt=Math.max(C,D);for(let z=q;z<=tt;z++)v[z*f+H]++,z<tt&&X[z*f+H]++}}}let Q=l*4,W=Q<=16e6,At=W?new Float64Array(Q):null,Bt=W?new Int32Array(Q):null,$t=W?new Uint32Array(Q):null,gt=0;function mn(S,P,L,A,G){let Y=U(S),H=U(L);if(Y<0||H<0)return null;let C=p[H%f],D=d[H/f|0],q=G.bendPenalty,tt=G.crossingPenalty;gt++;let z=W?null:new Map,Tt=W?null:new Map,Xt=W?$=>$t[$]===gt?At[$]:void 0:$=>z.get($),Ht=W?($,j,K)=>{$t[$]=gt,At[$]=j,Bt[$]=K}:($,j,K)=>{z.set($,j),Tt.set($,K)},wn=W?$=>Bt[$]:$=>Tt.get($),st=new Lt,Ft=Y*4+P;Ht(Ft,0,-1),st.push(Ft,Et*(Math.abs(p[Y%f]-C)+Math.abs(d[Y/f|0]-D)));let pt=1/0,yt=-1,Mn=0,bn=4e5;for(;st.size;){let $=st.peekKey(),j=st.pop();if($>=pt)break;let K=Xt(j),nt=j>>2,at=j&3,mt=nt%f,wt=nt/f|0;if(!($>K+Et*(Math.abs(p[mt]-C)+Math.abs(d[wt]-D))+1e-6)){if(++Mn>bn)break;if(nt===H){let F=at===A?0:at===Nt[A]?2*q:q;K+F<pt&&(pt=K+F,yt=j);continue}for(let F=0;F<4;F++){if(F===Nt[at])continue;let it=mt+Yn[F],rt=wt+_n[F];if(it<0||rt<0||it>=f||rt>=e)continue;let _=rt*f+it;if(_!==H&&N(_))continue;let ct=F<2;if(B(ct?F===0?nt:_:F===2?nt:_,ct))continue;let zt=Math.abs(ct?p[it]-p[mt]:d[rt]-d[wt]),ft=zt;F!==at&&(ft+=q);let Ct=F===0||F===2?nt:_,jt=ct?E[Ct]:X[Ct];jt&&(ft+=zt*.25*jt),_!==H&&(ct?v[_]:T[_])&&(ft+=tt);let Mt=_*4+F,bt=K+ft,Dt=Xt(Mt);(Dt===void 0||bt<Dt-1e-9)&&(Ht(Mt,bt,j),st.push(Mt,bt+Et*(Math.abs(p[it]-C)+Math.abs(d[rt]-D))))}}}if(yt<0)return null;let xt=[];for(let $=yt;$!==-1&&$!==void 0;$=wn($)){let j=$>>2;xt.push({x:p[j%f],y:d[j/f|0]})}return xt.reverse(),xt}return{astar:mn,markPath:V,xs:p,ys:d,N:l}}var Lt=class{constructor(){this.k=[],this.v=[]}get size(){return this.k.length}push(s,c){let i=this.k,o=this.v,n=i.length;for(i.push(c),o.push(s);n>0;){let r=n-1>>1;if(i[r]<=c)break;i[n]=i[r],o[n]=o[r],n=r}i[n]=c,o[n]=s}peekKey(){return this.k[0]}pop(){let s=this.k,c=this.v,i=c[0],o=s.pop(),n=c.pop();if(s.length){let r=0,h=s.length;for(;;){let a=2*r+1;if(a>=h||(a+1<h&&s[a+1]<s[a]&&a++,s[a]>=o))break;s[r]=s[a],c[r]=c[a],r=a}s[r]=o,c[r]=n}return i}};self.onmessage=t=>{let{seq:s,data:c,options:i,hints:o}=t.data;try{let n=Gt(c,i),r=sn(n,{hints:o||null}),h=yn(n,r);self.postMessage({seq:s,nodes:r.nodes,edges:r.edges,crossings:r.crossings,routing:{paths:h.paths,raw:h.raw,ports:h.ports,mode:h.mode}})}catch(n){self.postMessage({seq:s,error:String(n&&n.message||n)})}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kazant/flexgraph",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Flexible relationship graph for the browser: layered layout with strong crossing minimization, ports, orthogonal edge routing with obstacle avoidance, groups and constraints. Plain HTML, CSS & JS.",
5
5
  "license": "MIT",
6
6
  "author": "Alexander Pettersen (https://github.com/kazant)",
package/types/index.d.ts CHANGED
@@ -46,6 +46,10 @@ export interface NodeSpec {
46
46
  expandable?: boolean;
47
47
  /** Start with the details open. */
48
48
  expanded?: boolean;
49
+ /** Show a +/− toggle that stacks the nodes before ("upstream") or after ("downstream", also `true`) this node under it. */
50
+ stackable?: boolean | 'upstream' | 'downstream';
51
+ /** Start with the stack folded (direction from `stackable`). */
52
+ stacked?: boolean;
49
53
  [key: string]: unknown;
50
54
  }
51
55
 
@@ -172,6 +176,7 @@ export interface ViewState {
172
176
  nodes: Record<string, { x: number; y: number; pinned: boolean }>;
173
177
  /** Exact edge lines as [x, y] points. Reused on import while the nodes still match, so lines come back as the user left them. */
174
178
  edges?: Record<string, [number, number][]>;
179
+ stacked?: [string, 'upstream' | 'downstream'][];
175
180
  transform: { x: number; y: number; k: number };
176
181
  }
177
182
 
@@ -200,6 +205,8 @@ export interface GraphEvents {
200
205
  viewport: { x: number; y: number; k: number };
201
206
  animationend: void;
202
207
  expand: { id: string; expanded: boolean; node: NodeSpec };
208
+ stack: { id: string; direction: 'upstream' | 'downstream'; stacked: string[] };
209
+ unstack: { id: string };
203
210
  /** Explore mode: the open path changed. */
204
211
  explore: { path: string[] };
205
212
  /** Right-click anywhere in the graph. Registering a listener suppresses the browser menu inside the graph. */
@@ -232,7 +239,7 @@ export interface GraphView {
232
239
  readonly ready: Promise<GraphView>;
233
240
  readonly options: GraphOptions;
234
241
  relayout(opts?: { animate?: boolean; fresh?: boolean }): Promise<GraphView>;
235
- updateGraph(data: GraphData, opts?: { mode?: 'stable' | 'relayout'; animate?: boolean }): Promise<GraphView>;
242
+ updateGraph(data: GraphData, opts?: { mode?: 'stable' | 'relayout'; animate?: boolean; from?: string | Map<string, string>; to?: string | Map<string, string> }): Promise<GraphView>;
236
243
  setOptions(options: Partial<GraphOptions>, opts?: { relayout?: boolean }): Promise<GraphView>;
237
244
  exportState(): ViewState;
238
245
  importState(state: ViewState, opts?: { animate?: boolean; transform?: boolean }): Promise<GraphView>;
@@ -278,6 +285,19 @@ export interface GraphView {
278
285
  collapse(id: string): Promise<boolean>;
279
286
  toggleExpand(id: string): Promise<boolean>;
280
287
  isExpanded(id: string): boolean;
288
+ /** Fold nodes under `id` with an animation: they slide in underneath it; the node shows a card stack and "+N". */
289
+ stack(id: string, opts?: { direction?: 'upstream' | 'downstream'; animate?: boolean }): Promise<boolean>;
290
+ /** Unfold a stack: the nodes come back out from underneath `id`. */
291
+ unstack(id: string, opts?: { animate?: boolean }): Promise<boolean>;
292
+ toggleStack(id: string, opts?: { direction?: 'upstream' | 'downstream'; animate?: boolean }): Promise<boolean>;
293
+ unstackAll(opts?: { animate?: boolean }): Promise<GraphView>;
294
+ isStacked(id: string): boolean;
295
+ /** Ids folded under `id`, or every folded-away node when `id` is omitted. */
296
+ getStacked(id?: string): string[];
297
+ /** Add nodes/edges; the new nodes come out from underneath `from` (default: their first existing predecessor). */
298
+ addNodes(add: { nodes?: NodeSpec[]; edges?: EdgeSpec[]; groups?: GroupSpec[] }, opts?: { from?: string; animate?: boolean; mode?: 'stable' | 'relayout' }): Promise<GraphView>;
299
+ /** Remove nodes and their edges; they slide into `to` (default: their first remaining neighbour) and disappear. */
300
+ removeNodes(ids: string | string[], opts?: { to?: string; animate?: boolean; mode?: 'stable' | 'relayout' }): Promise<GraphView>;
281
301
  /** Explore mode: the open nodes, from a start node down. */
282
302
  getExplorePath(): string[];
283
303
  /** Explore mode: open a path (each id a child of the one before). [] shows only the start nodes. */