@euphrasiologist/lwphylo 1.2.19 → 1.2.21

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
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("d3")):"function"==typeof define&&define.amd?define(["d3"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).lwPhyloPlot=e(t.d3)}(this,function(t){"use strict";function e(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(n){if("default"!==n){var a=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,a.get?a:{enumerable:!0,get:function(){return t[n]}})}}),e.default=t,Object.freeze(e)}var n=e(t);function a(t,e,n,a){return{x:t+n*Math.cos(a),y:e-n*Math.sin(a)}}function r(t,e,n,r,i){const s=2*Math.PI,o=t=>(t%s+s)%s;let l=o(r),c=o(i);if((l-c+s)%s<(c-l+s)%s){const t=l;l=c,c=t}const h=(c-l+s)%s;if(h<1e-9){const r=a(t,e,n,l);return`M ${r.x} ${r.y}`}const d=h>Math.PI?1:0,p=a(t,e,n,l),f=a(t,e,n,c);return`M ${p.x} ${p.y} A ${n} ${n} 0 ${d} 0 ${f.x} ${f.y}`}const i=2*Math.PI,s=t=>(t%i+i)%i;function o(t,e,n,a,r,i){const o=s(0===i?r-a:a-r);if(!(n>0)||o<1e-9)return"";const l=o>Math.PI?1:0;return`M ${t+n*Math.cos(a)} ${e-n*Math.sin(a)} A ${n} ${n} 0 ${l} ${i} ${t+n*Math.cos(r)} ${e-n*Math.sin(r)}`}function l(t,e=[]){e.push(t);for(let n=0;n<(t.children?.length||0);n++)e=l(t.children[n],e);return e}function c(t,e=!0){var n=[];for(const e of l(t))null===e.parent?n.push({parentId:null,parentLabel:null,thisId:e.id,thisLabel:e.label,children:e.children.map(t=>t.id),branchLength:0,isTip:!1,x:e.x,y:e.y,angle:e.angle}):n.push({parentId:e.parent.id,parentLabel:e.parent.label,thisId:e.id,thisLabel:e.label,children:e.children.map(t=>t.id),branchLength:e.branchLength,isTip:0==e.children.length,x:e.x,y:e.y,angle:e.angle});return e&&(n=n.sort(function(t,e){return t.thisId-e.thisId})),n}function h(t){const e=2*Math.PI,n=c(t,!0),a=new Map(n.map(t=>[t.thisId,t])),r=new Map(n.map(t=>[t.thisId,t.children||[]]));let i=null;for(const t of n)if(null==t.parentId){i=t.thisId;break}const s=[];!function t(e){const n=r.get(e)||[];if(0!==n.length)for(const e of n)t(e);else s.push(e)}(i);const o=Math.max(1,s.length),l=new Map;s.forEach((t,n)=>{l.set(t,n/o*e)}),function t(n){const a=r.get(n)||[];for(const e of a)t(e);if(a.length>0){let t=0,r=0;for(const e of a){const n=l.get(e);t+=Math.cos(n),r+=Math.sin(n)}l.set(n,(Math.atan2(r,t)%e+e)%e)}}(i);const h=new Map;h.set(i,0),function t(e){const n=r.get(e)||[],i=h.get(e)||0;for(const e of n){const n=a.get(e),r=n?.branchLength??0;h.set(e,i+r),t(e)}}(i);for(const t of n){const e=l.get(t.thisId)??0,n=h.get(t.thisId)??0;t.angle=e,t.r=n,t.x=n*Math.cos(e),t.y=n*Math.sin(e)}return n}const d=2*Math.PI,p=t=>(t%d+d)%d;function f(t,e={}){const{openAngleDeg:n=0,rotateDeg:a=0}=e,r=n/360*d,i=a/360*d;let s=null;const o=new Map(t.map(t=>[t.thisId,t.children||[]]));for(const e of t)if(null==e.parentId){s=e.thisId;break}const l=[];!function t(e){const n=o.get(e)||[];if(n.length)for(const e of n)t(e);else l.push(e)}(s);const c=Math.max(1,l.length),h=c>1?(d*(1-1/c)-r)/(c-1):0,f=new Map;return l.forEach((t,e)=>f.set(t,p(e*h+i))),function t(e){const n=o.get(e)||[];for(const e of n)t(e);if(n.length){const t=f.get(n[0]),a=n.map(e=>function(t,e){let n=e;for(;n<t-Math.PI;)n+=d;for(;n>t+Math.PI;)n-=d;return n}(t,f.get(e)));f.set(e,p(a.reduce((t,e)=>t+e,0)/a.length))}}(s),f}function u(t,e={}){const{angleStrategy:n="fan",arcsStyle:a="fan",openAngleDeg:r=0,rotateDeg:i=0}=e,s=h(t);if("fan"===n){const t=f(s,{openAngleDeg:r,rotateDeg:i});for(const e of s){const n=t.get(e.thisId);null!=n&&(e.angle=n,e.x=e.r*Math.cos(n),e.y=e.r*Math.sin(n))}}const o="fan"===n?function(t){const e=new Map(t.map(t=>[t.thisId,t])),n=t.find(t=>null==t.parentId)?.thisId,a=[];for(const r of t){if(r.thisId===n)continue;const t=e.get(r.parentId);if(!t)continue;const i=r.angle,s=t.r,o=r.r;a.push({parentId:t.thisId,childId:r.thisId,x0:s*Math.cos(i),y0:s*Math.sin(i),x1:o*Math.cos(i),y1:o*Math.sin(i),isTip:!!r.isTip})}return a}(s):function(t){const e=h(t),n=new Map(e.map(t=>[t.thisId,t])),a=e.find(t=>null==t.parentId)?.thisId,r=[];for(const t of e){if(t.thisId===a)continue;const e=n.get(t.parentId);if(!e)continue;const i=t.angle,s=e.r,o=t.r;r.push({parentId:e.thisId,childId:t.thisId,x0:s*Math.cos(i),y0:s*Math.sin(i),x1:o*Math.cos(i),y1:o*Math.sin(i),isTip:!!t.isTip})}return r}(t),l="fan"===a?function(t){const e=new Map(t.map(t=>[t.thisId,t])),n=[];for(const a of t){const t=a.children||[];if(t.length<2||!(a.r>0))continue;const r=e.get(t[0])?.angle,i=e.get(t[t.length-1])?.angle;if(null==r||null==i)continue;const s=r,o=i,l=o>=s?0:1;n.push({parentId:a.parentId,thisId:a.thisId,radius:a.r,start:s,end:o,sweep:l})}return n}(s):function(t){const e=2*Math.PI;new Map(t.map(t=>[t.thisId,t]));const n=new Map;let a=null;for(const e of t)null==e.parentId&&(a=e.thisId),n.has(e.parentId)||n.set(e.parentId,[]),n.get(e.parentId).push(e);const r=[];for(const i of t){const t=i.thisId;if(t===a)continue;const s=n.get(t)||[];if(s.length<2)continue;const o=s.map(t=>(t.angle%e+e)%e).sort((t,e)=>t-e),l=o[0],c=o[o.length-1],h=c-l,d=e-h;let p,f,u;h<=d?(p=l,f=c,u=h):(p=c,f=l,u=d),u<1e-6||!isFinite(i.r)||i.r<=0||r.push({start:p,end:f,radius:i.r,thisId:t,parentId:i.parentId})}return r}(s);let c=[];return c="fan"===e.arcsStyle?function(t){const e=2*Math.PI,n=t=>(t%e+e)%e;function a(t,a){return n(t+(a-t+e)%e/2)}const r=t=>"string"==typeof t?+t:t,i=new Map(t.map(t=>[r(t.thisId),t])),s=new Map(t.map(t=>[r(t.thisId),(t.children||[]).map(t=>"object"==typeof t?t.thisId:t).map(r).filter(t=>i.has(t))])),o=[];for(const e of t){const t=r(e.thisId),l=s.get(t)||[];if(l.length<2)continue;const c=l.map(t=>{const e=i.get(t);return e?{id:t,a:n(e.angle)}:null}).filter(Boolean).sort((t,e)=>t.a-e.a),h=c.length;if(h<2)continue;const d=i.get(t),p=d?.r;if(p>0)for(let e=0;e<h;e++){const n=c[(e-1+h)%h],r=c[e],i=c[(e+1)%h],s=a(n.a,r.a),l=a(r.a,i.a);o.push({parentId:t,childId:r.id,radius:p,start:s,end:l,sweep:0})}}return o}(s):function(t){const e=new Map(t.map(t=>[t.thisId,t])),n=[];for(const a of t){if(null==a.parentId)continue;const t=e.get(a.parentId);t&&n.push({parentId:t.thisId,childId:a.thisId,radius:t.r,start:t.angle,end:a.angle})}return n}(s),{data:s,radii:o,arcs:l,child_arcs:c}}function g(t){const e=c(t),n=new Map(e.map((t,e)=>[t.thisId,e])),a=[];!function t(e){e.children&&0!==e.children.length?e.children.forEach(t):a.push(e.id)}(t);const r=new Map(a.map((t,e)=>[t,e+1]));return function t(a){const i=n.get(a.id);if(!a.children||0===a.children.length){const t=r.get(a.id);return e[i].y0=t,e[i].y1=t,t}const s=function(t){let e=0,n=0;for(let a of t)null!=a&&(a=+a)>=a&&(++e,n+=a);if(e)return n/e}(a.children.map(t));return e[i].y0=s,e[i].y1=s,s}(t),function t(a,r){const i=n.get(a.id),s=r??0,o=s+(e[i].branchLength??0);e[i].x0=s,e[i].x1=o,a.children&&a.children.length&&a.children.forEach(e=>t(e,o))}(t,0),e.map(t=>{const{y:e,x:n,angle:a,...r}=t;return r})}function I(t){const e=g(t),n=function(t){const e=g(t),n=new Map;for(const t of e){if(null==t.parentId)continue;const e=n.get(t.parentId);e?e.push(t):n.set(t.parentId,[t])}const a=[];for(const[t,e]of n.entries()){if(!e.length)continue;const n=e.map(t=>t.y0),r=Math.min(...n),i=Math.max(...n),s=e[0].x0;a.push({parentId:t,x0:s,x1:s,y0:r,y1:i,heights:i-r})}return a}(t),a=function(t){const e=g(t),n=new Map(e.map(t=>[t.thisId,t])),a=[];for(const t of e){if(null==t.parentId)continue;const e=n.get(t.parentId);if(!e)continue;const r=t.x0,i=t.y0,s=e.y0,o=Math.min(i,s),l=Math.max(i,s);a.push({parentId:t.parentId,childId:t.thisId,x:r,y0:o,y1:l})}return a}(t);new Map(e.map(t=>[t.thisId,t]));const r=e.filter(t=>null!=t.parentId).map(t=>({parentId:t.parentId,childId:t.thisId,thisId:t.thisId,thisLabel:t.thisLabel,isTip:t.isTip,x0:t.x0,x1:t.x1,y0:t.y0,y1:t.y0}));return{data:e,vertical_lines:n,child_vertical_lines:a,horizontal_lines:r}}function y(t){null===t.parent&&(function t(e){if(!e.children||0===e.children.length)return e.ntips=1,1;let n=0;for(const a of e.children)n+=t(a);return e.ntips=n,n}(t),t.start=0,t.end=2,t.angle=0,t.ntips=function(t){var e=0;for(const n of function(t){const e=[t],n=[];for(;e.length;){const t=e.shift();n.push(t);for(const n of t.children)e.push(n)}return n}(t))0==n.children.length&&e++;return e}(t),t.x=0,t.y=0);let e=t.start;for(let n=0;n<t.children.length;n++){const a=t.children[n],r=(t.end-t.start)*(a.ntips/t.ntips);a.start=e,a.end=e+r,a.angle=a.start+(a.end-a.start)/2,e=a.end;const i=a.angle*Math.PI,s=a.branchLength??0;a.x=t.x+s*Math.sin(i),a.y=t.y+s*Math.cos(i),y(a)}return t}function x(t){var e={},n=c(y(t));return e.data=n,e.edges=function(t,e=!1){const n=[...t].sort((t,e)=>t.thisId-e.thisId),a=new Map(n.map(t=>[t.thisId,t])),r=[];for(const t of n){if(null==t.parentId)continue;const n=a.get(t.parentId);n&&(e?(r.push({x1:t.x,y1:t.y,id1:t.thisId,x2:n.x,y2:t.y,id2:void 0}),r.push({x1:n.x,y1:t.y,id1:void 0,x2:n.x,y2:n.y,id2:t.parentId})):r.push({x1:t.x,y1:t.y,id1:t.thisId,x2:n.x,y2:n.y,id2:t.parentId}))}return r}(n),e}function m(t){const e=(t=String(t).replace(/\s+/g,"")).split(/(;|\(|\)|,)/),n={parent:null,children:[]};let a=n,r=0;for(const t of e)if(t&&";"!==t)if("("===t){const t={parent:a,children:[]};a.children.push(t),a=t}else if(","===t){a=a.parent;const t={parent:a,children:[]};a.children.push(t),a=t}else if(")"===t){if(a=a.parent,null===a)break}else{const e=t.split(":");1===e.length?t.startsWith(":")?(a.label="",a.branchLength=parseFloat(e[0])):(a.label=e[0],a.branchLength=null):2===e.length?(a.label=e[0],a.branchLength=parseFloat(e[1])):(console.warn(t,"Unhandled token with multiple ':' characters"),a.label=e[0]||"",a.branchLength=parseFloat(e[e.length-1])),a.id=r++}return null==n.id&&(n.id=r),n}return function(t,{layout:e="rect",width:a=800,height:i=800,margin:s={top:20,right:300,bottom:20,left:50},radialMargin:l=80,strokeWidth:c=1,radialMode:h="outer",tipLabels:d=!0,showTooltips:p=!0,tooltipFormatter:f=(t,e)=>`${t.thisLabel??"(unnamed)"}\nroot→tip: ${(+e).toFixed(4)}`,hoverStroke:g="#1f77b4",hoverWidth:y=3,highlightTips:M=[],highlightStroke:w="#e63946",highlightWidth:b=2.5}={}){const k=t=>"number"==typeof t&&Number.isFinite(t);function v(t,{prefer:e="auto"}={}){return function(n){let a=t.get(n);if(!a)return 0;if("r"===e||"auto"===e&&"r"in a)return Number(a.r??0);if("x1"===e||"auto"===e&&"x1"in a)return Number(a.x1??0);let r=0;for(;a&&null!=a.parentId;)r+=Number(a.branchLength||0),a=t.get(a.parentId);return r}}if("rect"===e){const _=I(m(t)),L=_.horizontal_lines,A=_.vertical_lines,$=L.filter(t=>t.isTip),T=new Map(L.map(t=>[t.thisId,t])),P=new Map($.map(t=>[t.thisId,t])),j=new Map($.map(t=>[t.thisLabel,t])),E=v(T,{prefer:"x1"}),S=n.max(L,t=>t.y1),z=n.min(L,t=>t.y1),D=n.max(L,t=>t.x1),F=n.scaleLinear().domain([z-1,S+1]).range([s.top,i-s.bottom]),O=n.scaleLinear().domain([0,D]).range([s.left,a-s.right]),X=n.create("svg").attr("width",a).attr("height",i).attr("font-family","sans-serif").attr("font-size",10),Y=X.append("g"),B=X.append("g").attr("class","phylo_static_highlight"),N=X.append("g").attr("class","phylo_hover_highlight");Y.selectAll(".hline").data(L).join("line").attr("x1",t=>O(t.x0)).attr("y1",t=>F(t.y0)).attr("x2",t=>O(t.x1)).attr("y2",t=>F(t.y1)).attr("stroke","#555").attr("stroke-width",c),Y.selectAll(".vline").data(A).join("line").attr("x1",t=>O(t.x0)).attr("y1",t=>F(t.y0)).attr("x2",t=>O(t.x1)).attr("y2",t=>F(t.y1)).attr("stroke","#555").attr("stroke-width",c);const W=Y.selectAll(".tip-dot").data($).join("circle").attr("cx",t=>O(t.x1)).attr("cy",t=>F(t.y1)).attr("r",2).attr("fill","black");if(p&&W.append("title").text(t=>f(t,E(t.thisId))),W.on("mouseenter",function(t,e){U(e.thisId,N,g,y),n.select(this).attr("r",4)}).on("mouseleave",function(){N.selectAll("*").remove(),n.select(this).attr("r",2)}),d){const q=X.append("g").attr("class","phylo_labels").selectAll("text").data($).join("text").attr("x",t=>O(t.x1)+4).attr("y",t=>F(t.y1)).attr("dy","0.32em").attr("font-size",10).text(t=>t.thisLabel?.replace(/_/g," ")??"");p&&q.append("title").text(t=>f(t,E(t.thisId))),q.on("mouseenter",function(t,e){U(e.thisId,N,g,y),n.select(this).attr("font-weight",600)}).on("mouseleave",function(){N.selectAll("*").remove(),n.select(this).attr("font-weight",null)})}if(M&&M.length){new Set([...M.filter(k).map(t=>P.get(t)),...M.filter(t=>!k(t)).map(t=>j.get(t))].filter(Boolean)).forEach(t=>{U(t.thisId,B,w,b)})}function U(t,e,n,a){e.selectAll("*").remove();let r=T.get(t);for(;r&&null!=r.parentId;){const t=T.get(r.parentId);if(!t)break;e.append("line").attr("x1",O(r.x0)).attr("x2",O(r.x0)).attr("y1",F(t.y0)).attr("y2",F(r.y0)).attr("stroke",n).attr("stroke-width",a).attr("stroke-linecap","round"),e.append("line").attr("x1",O(r.x0)).attr("x2",O(r.x1)).attr("y1",F(r.y0)).attr("y2",F(r.y1)).attr("stroke",n).attr("stroke-width",a).attr("stroke-linecap","round"),r=t}}return X.node()}if("radial"===e){if(a!==i)throw new Error("width and height must be the same for radial layout");const C=u(m(t),{angleStrategy:"fan",arcsStyle:"fan"}),G="outer"===h;if("phylo"!==h&&"outer"!==h)throw new Error("radialMode must be either 'phylo' or 'outer'");const H=3,J=0,K=(n.max(C.data,t=>t.r)??0)+2*l,Q=a,R=i,V=Q/2,Z=R/2,tt=n.scaleLinear().domain([-K,K]).range([0,Q]),et=n.scaleLinear().domain([-K,K]).range([R,0]),nt=t=>t*(Q/(2*K)),at=new Map(C.data.map(t=>[t.thisId,t])),rt=C.data.filter(t=>t.isTip),it=rt.length?n.max(rt,t=>t.r):0,st=v(at,{prefer:"r"}),ot=new Map(rt.map(t=>[t.thisId,t])),lt=new Map(rt.map(t=>[t.thisLabel,t]));function ct(t,e,n,a){const r=tt(t),i=et(e),s=tt(n)-r,o=et(a)-i,l=Math.hypot(s,o)||1,c=Math.max(0,(l-J)/l);return{X0:r,Y0:i,X1s:r+s*c,Y1s:i+o*c,len:l}}const ht=n.create("svg").attr("width",Q).attr("height",R).attr("font-family","sans-serif").attr("font-size",10),dt=ht.append("g"),pt=ht.append("g").attr("class","phylo_static_lines"),ft=ht.append("g").attr("class","phylo_static_arcs"),ut=ht.append("g").attr("class","phylo_hover_lines"),gt=ht.append("g").attr("class","phylo_hover_arcs");dt.append("g").attr("class","phylo_arcs").selectAll("path").data(C.arcs).join("path").attr("d",t=>null==t.sweep?r(V,Z,nt(t.radius),t.start,t.end):o(V,Z,nt(t.radius),t.start,t.end,t.sweep)).attr("fill","none").attr("stroke","#777").attr("stroke-width",c),dt.append("g").attr("class","phylo_radii").selectAll("line").data(C.radii).join("line").each(function(t,e){const a=t.x0,r=t.y0,i=(s=t).childId??s.thisId??s.id1??null;var s;const o=null!=i?at.get(i):void 0,l=!(!o||!o.isTip);let h=t.x1,d=t.y1;G&&l&&(h=it*Math.cos(o.angle),d=it*Math.sin(o.angle));const{X0:p,Y0:f,X1s:u,Y1s:g}=ct(a,r,h,d);n.select(this).attr("x1",p).attr("y1",f).attr("x2",u).attr("y2",g).attr("stroke","#777").attr("stroke-width",c)});const It=dt.append("g").attr("class","phylo_tip_dots").selectAll("circle").data(rt).join("circle").each(function(t,e){const a=G?it*Math.cos(t.angle):t.x,r=G?it*Math.sin(t.angle):t.y;n.select(this).attr("cx",tt(a)).attr("cy",et(r)).attr("r",H).attr("fill","black").attr("stroke","black").attr("stroke-width",1.5)});p&&It.append("title").text(t=>f(t,st(t.thisId)));const yt=t=>"string"==typeof t?+t:t,xt=new Map(C.radii.map(t=>[yt(t.childId??t.thisId??t.id1),t])),mt=new Map(C.child_arcs.map(t=>[yt(t.childId),t]));if(d){const wt=dt.append("g").attr("class","phylo_labels").selectAll("g.label").data(rt).join("g").attr("class","label").attr("transform",t=>{const e=G?it:t.r,n=e*Math.cos(t.angle),a=e*Math.sin(t.angle);return`translate(${tt(n)},${et(a)})`}).each(function(t){let e=180*-t.angle/Math.PI,a=10,r="start";t.angle>Math.PI/2&&t.angle<3*Math.PI/2&&(e+=180,a*=-1,r="end"),n.select(this).append("g").attr("transform",`rotate(${e})`).append("text").attr("x",a).attr("alignment-baseline","middle").attr("text-anchor",r).attr("font-size",10).attr("fill","black").text(t=>t.thisLabel?.replace(/_/g," ")??"")});p&&wt.append("title").text(t=>f(t,st(t.thisId))),wt.on("mouseenter",function(t,e){Mt(e,ut,gt,g,y),n.select(this).select("text").attr("font-weight",600)}).on("mouseleave",function(){ut.selectAll("*").remove(),gt.selectAll("*").remove(),n.select(this).select("text").attr("font-weight",null)})}function Mt(t,e,n,a="#1f77b4",i=3){e.selectAll("*").remove(),n.selectAll("*").remove();let s="number"==typeof t?at.get(t):t;if(!s)return;let l=!0;for(;s&&null!=s.parentId;){const c=xt.get(yt(s.thisId));if(c){const p=c.x0,f=c.y0;let u=c.x1,g=c.y1;if(G&&l&&s.isTip){const M=it;u=M*Math.cos(s.angle),g=M*Math.sin(s.angle)}const{X0:I,Y0:y,X1s:x,Y1s:m}=ct(p,f,u,g);e.append("line").attr("x1",I).attr("y1",y).attr("x2",x).attr("y2",m).attr("stroke",a).attr("stroke-width",i).attr("stroke-linecap","round")}const h=mt.get(yt(s.thisId));function d(t){const e=nt(t.radius);return null==t.sweep?r(V,Z,e,t.start,t.end):o(V,Z,e,t.start,t.end,t.sweep)}h&&n.append("path").attr("d",d(h)).attr("fill","none").attr("stroke",a).attr("stroke-width",i),l=!1,s=at.get(s.parentId)}}if(It.on("mouseenter",function(t,e){Mt(e,ut,gt,g,y),n.select(this).attr("r",H+2)}).on("mouseleave",function(){ut.selectAll("*").remove(),gt.selectAll("*").remove(),n.select(this).attr("r",H)}),M&&M.length){new Set([...M.filter(k).map(t=>ot.get(t)),...M.filter(t=>!k(t)).map(t=>lt.get(t))].filter(Boolean)).forEach(t=>{Mt(t.thisId,pt,ft,w,b)})}return ht.node()}if("unrooted"===e){const bt=x(m(t)),kt=a,vt=i,_t=n.extent(bt.data,t=>t.x),Lt=n.extent(bt.data,t=>t.y),At=Math.max(Math.abs(_t[0]),Math.abs(_t[1])),$t=Math.max(Math.abs(Lt[0]),Math.abs(Lt[1])),Tt=Math.max(At,$t)+2*l,Pt=n.scaleLinear().domain([-Tt,Tt]).range([0,kt]),jt=n.scaleLinear().domain([-Tt,Tt]).range([vt,0]),Et=n.create("svg").attr("width",kt).attr("height",vt).attr("font-family","sans-serif").attr("font-size",10),St=Et.append("g"),zt=Et.append("g").attr("class","phylo_static_highlight"),Dt=Et.append("g").attr("class","phylo_hover_highlight");St.append("g").attr("class","phylo_lines").selectAll("line").data(bt.edges).join("line").attr("x1",t=>Pt(t.x1)).attr("y1",t=>jt(t.y1)).attr("x2",t=>Pt(t.x2)).attr("y2",t=>jt(t.y2)).attr("stroke-width",c).attr("stroke","#777");const Ft=St.append("g").attr("class","phylo_points").selectAll("circle").data(bt.data).join("circle").attr("class","dot").attr("r",t=>t.isTip?4:0).attr("cx",t=>Pt(t.x)).attr("cy",t=>jt(t.y)).attr("stroke","black").attr("stroke-width",2).attr("fill",t=>t.isTip?"black":"white"),Ot=new Map(bt.data.map(t=>[t.thisId,t])),Xt=new Map(bt.data.filter(t=>t.isTip).map(t=>[t.thisId,t])),Yt=new Map(bt.data.filter(t=>t.isTip).map(t=>[t.thisLabel,t])),Bt=v(Ot);p&&Ft.filter(t=>t.isTip).append("title").text(t=>f(t,Bt(t.thisId)));const Nt=new Map,Wt=new Map(bt.data.map(t=>[t.thisId,t]));if(bt.edges.forEach(t=>{const e=Wt.get(t.id1);e?.isTip&&Nt.set(t.id1,t)}),d){const qt=St.append("g").attr("class","phylo_labels").selectAll("g").data(bt.data.filter(t=>t.isTip)).join("g").attr("transform",t=>`translate(${Pt(t.x)},${jt(t.y)})`).each(function(t){const e=Nt.get(t.thisId);if(!e)return;const a=Pt(e.x1),r=jt(e.y1),i=Pt(e.x2)-a,s=jt(e.y2)-r;let o=180*Math.atan2(s,i)/Math.PI,l=-10,c="end";(o>90||o<-90)&&(o+=180,c="start",l=10),n.select(this).append("g").attr("transform",`rotate(${o})`).append("text").attr("x",l).attr("alignment-baseline","middle").attr("text-anchor",c).attr("font-size",10).attr("fill","black").text(t.thisLabel?.replace(/_/g," ")??"")});p&&qt.append("title").text(t=>f(t,Bt(t.thisId))),qt.on("mouseenter",function(t,e){Ut(e.thisId,Dt,g,y),n.select(this).select("text").attr("font-weight",600)}).on("mouseleave",function(){Dt.selectAll("*").remove(),n.select(this).select("text").attr("font-weight",null)})}if(Ft.filter(t=>t.isTip).on("mouseenter",function(t,e){Ut(e.thisId,Dt,g,y),n.select(this).attr("r",6)}).on("mouseleave",function(){Dt.selectAll("*").remove(),n.select(this).attr("r",4)}),M&&M.length){new Set([...M.filter(k).map(t=>Xt.get(t)),...M.filter(t=>!k(t)).map(t=>Yt.get(t))].filter(Boolean)).forEach(t=>{Ut(t.thisId,zt,w,b)})}function Ut(t,e,n,a){const r=new Map(bt.edges.map(t=>[t.id1,t]));e.selectAll("*").remove();let i=Ot.get(t);for(;i&&null!=i.parentId;){const t=r.get(i.thisId);t&&e.append("line").attr("x1",Pt(t.x1)).attr("y1",jt(t.y1)).attr("x2",Pt(t.x2)).attr("y2",jt(t.y2)).attr("stroke",n).attr("stroke-width",a).attr("stroke-linecap","round"),i=Ot.get(i.parentId)}}return Et.node()}throw new Error("Unsupported layout type. Use 'rect', 'radial', or 'unrooted'.")}});
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("d3")):"function"==typeof define&&define.amd?define(["d3"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).lwPhyloPlot=e(t.d3)}(this,function(t){"use strict";function e(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(n){if("default"!==n){var a=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,a.get?a:{enumerable:!0,get:function(){return t[n]}})}}),e.default=t,Object.freeze(e)}var n=e(t);function a(t,e,n,a){return{x:t+n*Math.cos(a),y:e-n*Math.sin(a)}}function r(t,e,n,r,i){const s=2*Math.PI,o=t=>(t%s+s)%s;let l=o(r),c=o(i);if((l-c+s)%s<(c-l+s)%s){const t=l;l=c,c=t}const h=(c-l+s)%s;if(h<1e-9){const r=a(t,e,n,l);return`M ${r.x} ${r.y}`}const d=h>Math.PI?1:0,p=a(t,e,n,l),f=a(t,e,n,c);return`M ${p.x} ${p.y} A ${n} ${n} 0 ${d} 0 ${f.x} ${f.y}`}const i=2*Math.PI,s=t=>(t%i+i)%i;function o(t,e,n,a,r,i){const o=s(0===i?r-a:a-r);if(!(n>0)||o<1e-9)return"";const l=o>Math.PI?1:0;return`M ${t+n*Math.cos(a)} ${e-n*Math.sin(a)} A ${n} ${n} 0 ${l} ${i} ${t+n*Math.cos(r)} ${e-n*Math.sin(r)}`}function l(t,e=[]){e.push(t);for(let n=0;n<(t.children?.length||0);n++)e=l(t.children[n],e);return e}function c(t,e=!0){var n=[];for(const e of l(t))null===e.parent?n.push({parentId:null,parentLabel:null,thisId:e.id,thisLabel:e.label,children:e.children.map(t=>t.id),branchLength:0,isTip:!1,x:e.x,y:e.y,angle:e.angle}):n.push({parentId:e.parent.id,parentLabel:e.parent.label,thisId:e.id,thisLabel:e.label,children:e.children.map(t=>t.id),branchLength:e.branchLength,isTip:0==e.children.length,x:e.x,y:e.y,angle:e.angle});return e&&(n=n.sort(function(t,e){return t.thisId-e.thisId})),n}function h(t){const e=2*Math.PI,n=c(t,!0),a=new Map(n.map(t=>[t.thisId,t])),r=new Map(n.map(t=>[t.thisId,t.children||[]]));let i=null;for(const t of n)if(null==t.parentId){i=t.thisId;break}const s=[];!function t(e){const n=r.get(e)||[];if(0!==n.length)for(const e of n)t(e);else s.push(e)}(i);const o=Math.max(1,s.length),l=new Map;s.forEach((t,n)=>{l.set(t,n/o*e)}),function t(n){const a=r.get(n)||[];for(const e of a)t(e);if(a.length>0){let t=0,r=0;for(const e of a){const n=l.get(e);t+=Math.cos(n),r+=Math.sin(n)}l.set(n,(Math.atan2(r,t)%e+e)%e)}}(i);const h=new Map;h.set(i,0),function t(e){const n=r.get(e)||[],i=h.get(e)||0;for(const e of n){const n=a.get(e),r=n?.branchLength??0;h.set(e,i+r),t(e)}}(i);for(const t of n){const e=l.get(t.thisId)??0,n=h.get(t.thisId)??0;t.angle=e,t.r=n,t.x=n*Math.cos(e),t.y=n*Math.sin(e)}return n}const d=2*Math.PI,p=t=>(t%d+d)%d;function f(t,e={}){const{openAngleDeg:n=0,rotateDeg:a=0}=e,r=n/360*d,i=a/360*d;let s=null;const o=new Map(t.map(t=>[t.thisId,t.children||[]]));for(const e of t)if(null==e.parentId){s=e.thisId;break}const l=[];!function t(e){const n=o.get(e)||[];if(n.length)for(const e of n)t(e);else l.push(e)}(s);const c=Math.max(1,l.length),h=c>1?(d*(1-1/c)-r)/(c-1):0,f=new Map;return l.forEach((t,e)=>f.set(t,p(e*h+i))),function t(e){const n=o.get(e)||[];for(const e of n)t(e);if(n.length){const t=f.get(n[0]),a=n.map(e=>function(t,e){let n=e;for(;n<t-Math.PI;)n+=d;for(;n>t+Math.PI;)n-=d;return n}(t,f.get(e)));f.set(e,p(a.reduce((t,e)=>t+e,0)/a.length))}}(s),f}function u(t,e={}){const{angleStrategy:n="fan",arcsStyle:a="fan",openAngleDeg:r=0,rotateDeg:i=0}=e,s=h(t);if("fan"===n){const t=f(s,{openAngleDeg:r,rotateDeg:i});for(const e of s){const n=t.get(e.thisId);null!=n&&(e.angle=n,e.x=e.r*Math.cos(n),e.y=e.r*Math.sin(n))}}const o="fan"===n?function(t){const e=new Map(t.map(t=>[t.thisId,t])),n=t.find(t=>null==t.parentId)?.thisId,a=[];for(const r of t){if(r.thisId===n)continue;const t=e.get(r.parentId);if(!t)continue;const i=r.angle,s=t.r,o=r.r;a.push({parentId:t.thisId,childId:r.thisId,x0:s*Math.cos(i),y0:s*Math.sin(i),x1:o*Math.cos(i),y1:o*Math.sin(i),isTip:!!r.isTip})}return a}(s):function(t){const e=h(t),n=new Map(e.map(t=>[t.thisId,t])),a=e.find(t=>null==t.parentId)?.thisId,r=[];for(const t of e){if(t.thisId===a)continue;const e=n.get(t.parentId);if(!e)continue;const i=t.angle,s=e.r,o=t.r;r.push({parentId:e.thisId,childId:t.thisId,x0:s*Math.cos(i),y0:s*Math.sin(i),x1:o*Math.cos(i),y1:o*Math.sin(i),isTip:!!t.isTip})}return r}(t),l="fan"===a?function(t){const e=new Map(t.map(t=>[t.thisId,t])),n=[];for(const a of t){const t=a.children||[];if(t.length<2||!(a.r>0))continue;const r=e.get(t[0])?.angle,i=e.get(t[t.length-1])?.angle;if(null==r||null==i)continue;const s=r,o=i,l=o>=s?0:1;n.push({parentId:a.parentId,thisId:a.thisId,radius:a.r,start:s,end:o,sweep:l})}return n}(s):function(t){const e=2*Math.PI;new Map(t.map(t=>[t.thisId,t]));const n=new Map;let a=null;for(const e of t)null==e.parentId&&(a=e.thisId),n.has(e.parentId)||n.set(e.parentId,[]),n.get(e.parentId).push(e);const r=[];for(const i of t){const t=i.thisId;if(t===a)continue;const s=n.get(t)||[];if(s.length<2)continue;const o=s.map(t=>(t.angle%e+e)%e).sort((t,e)=>t-e),l=o[0],c=o[o.length-1],h=c-l,d=e-h;let p,f,u;h<=d?(p=l,f=c,u=h):(p=c,f=l,u=d),u<1e-6||!isFinite(i.r)||i.r<=0||r.push({start:p,end:f,radius:i.r,thisId:t,parentId:i.parentId})}return r}(s);let c=[];return c="fan"===a?function(t){const e=2*Math.PI,n=t=>(t%e+e)%e,a=(t,n)=>(n-t+e)%e,r=(t,n)=>(t-n+e)%e;function i(t,a){return n(t+(a-t+e)%e/2)}const s=t=>"string"==typeof t?+t:t,o=new Map(t.map(t=>[s(t.thisId),t])),l=new Map(t.map(t=>[s(t.thisId),(t.children||[]).map(t=>"object"==typeof t?t.thisId:t).map(s).filter(t=>o.has(t))])),c=[];for(const e of t){const t=s(e.thisId),h=l.get(t)||[];if(h.length<2)continue;const d=h.map(t=>{const e=o.get(t);return e?{id:t,a:n(e.angle)}:null}).filter(Boolean).sort((t,e)=>t.a-e.a),p=d.length;if(p<2)continue;const f=o.get(t),u=f?.r;if(u>0)for(let e=0;e<p;e++){const n=d[(e-1+p)%p],s=d[e],o=d[(e+1)%p],l=i(n.a,s.a),h=i(s.a,o.a),f=a(l,h)<=r(l,h)?0:1;c.push({parentId:t,childId:s.id,radius:u,start:l,end:h,sweep:f})}}return c}(s):function(t){const e=new Map(t.map(t=>[t.thisId,t])),n=[];for(const a of t){if(null==a.parentId)continue;const t=e.get(a.parentId);t&&n.push({parentId:t.thisId,childId:a.thisId,radius:t.r,start:t.angle,end:a.angle})}return n}(s),{data:s,radii:o,arcs:l,child_arcs:c}}function g(t){const e=c(t),n=new Map(e.map((t,e)=>[t.thisId,e])),a=[];!function t(e){e.children&&0!==e.children.length?e.children.forEach(t):a.push(e.id)}(t);const r=new Map(a.map((t,e)=>[t,e+1]));return function t(a){const i=n.get(a.id);if(!a.children||0===a.children.length){const t=r.get(a.id);return e[i].y0=t,e[i].y1=t,t}const s=function(t){let e=0,n=0;for(let a of t)null!=a&&(a=+a)>=a&&(++e,n+=a);if(e)return n/e}(a.children.map(t));return e[i].y0=s,e[i].y1=s,s}(t),function t(a,r){const i=n.get(a.id),s=r??0,o=s+(e[i].branchLength??0);e[i].x0=s,e[i].x1=o,a.children&&a.children.length&&a.children.forEach(e=>t(e,o))}(t,0),e.map(t=>{const{y:e,x:n,angle:a,...r}=t;return r})}function I(t){const e=g(t),n=function(t){const e=g(t),n=new Map;for(const t of e){if(null==t.parentId)continue;const e=n.get(t.parentId);e?e.push(t):n.set(t.parentId,[t])}const a=[];for(const[t,e]of n.entries()){if(!e.length)continue;const n=e.map(t=>t.y0),r=Math.min(...n),i=Math.max(...n),s=e[0].x0;a.push({parentId:t,x0:s,x1:s,y0:r,y1:i,heights:i-r})}return a}(t),a=function(t){const e=g(t),n=new Map(e.map(t=>[t.thisId,t])),a=[];for(const t of e){if(null==t.parentId)continue;const e=n.get(t.parentId);if(!e)continue;const r=t.x0,i=t.y0,s=e.y0,o=Math.min(i,s),l=Math.max(i,s);a.push({parentId:t.parentId,childId:t.thisId,x:r,y0:o,y1:l})}return a}(t);new Map(e.map(t=>[t.thisId,t]));const r=e.filter(t=>null!=t.parentId).map(t=>({parentId:t.parentId,childId:t.thisId,thisId:t.thisId,thisLabel:t.thisLabel,isTip:t.isTip,x0:t.x0,x1:t.x1,y0:t.y0,y1:t.y0}));return{data:e,vertical_lines:n,child_vertical_lines:a,horizontal_lines:r}}function y(t){null===t.parent&&(function t(e){if(!e.children||0===e.children.length)return e.ntips=1,1;let n=0;for(const a of e.children)n+=t(a);return e.ntips=n,n}(t),t.start=0,t.end=2,t.angle=0,t.ntips=function(t){var e=0;for(const n of function(t){const e=[t],n=[];for(;e.length;){const t=e.shift();n.push(t);for(const n of t.children)e.push(n)}return n}(t))0==n.children.length&&e++;return e}(t),t.x=0,t.y=0);let e=t.start;for(let n=0;n<t.children.length;n++){const a=t.children[n],r=(t.end-t.start)*(a.ntips/t.ntips);a.start=e,a.end=e+r,a.angle=a.start+(a.end-a.start)/2,e=a.end;const i=a.angle*Math.PI,s=a.branchLength??0;a.x=t.x+s*Math.sin(i),a.y=t.y+s*Math.cos(i),y(a)}return t}function x(t){var e={},n=c(y(t));return e.data=n,e.edges=function(t,e=!1){const n=[...t].sort((t,e)=>t.thisId-e.thisId),a=new Map(n.map(t=>[t.thisId,t])),r=[];for(const t of n){if(null==t.parentId)continue;const n=a.get(t.parentId);n&&(e?(r.push({x1:t.x,y1:t.y,id1:t.thisId,x2:n.x,y2:t.y,id2:void 0}),r.push({x1:n.x,y1:t.y,id1:void 0,x2:n.x,y2:n.y,id2:t.parentId})):r.push({x1:t.x,y1:t.y,id1:t.thisId,x2:n.x,y2:n.y,id2:t.parentId}))}return r}(n),e}function m(t){const e=(t=String(t).replace(/\s+/g,"")).split(/(;|\(|\)|,)/),n={parent:null,children:[]};let a=n,r=0;for(const t of e)if(t&&";"!==t)if("("===t){const t={parent:a,children:[]};a.children.push(t),a=t}else if(","===t){a=a.parent;const t={parent:a,children:[]};a.children.push(t),a=t}else if(")"===t){if(a=a.parent,null===a)break}else{const e=t.split(":");1===e.length?t.startsWith(":")?(a.label="",a.branchLength=parseFloat(e[0])):(a.label=e[0],a.branchLength=null):2===e.length?(a.label=e[0],a.branchLength=parseFloat(e[1])):(console.warn(t,"Unhandled token with multiple ':' characters"),a.label=e[0]||"",a.branchLength=parseFloat(e[e.length-1])),a.id=r++}return null==n.id&&(n.id=r),n}return function(t,{layout:e="rect",width:a=800,height:i=800,margin:s={top:20,right:300,bottom:20,left:50},radialMargin:l=80,strokeWidth:c=1,radialMode:h="outer",tipLabels:d=!0,showTooltips:p=!0,tooltipFormatter:f=(t,e)=>`${t.thisLabel??"(unnamed)"}\nroot→tip: ${(+e).toFixed(4)}`,hoverStroke:g="#1f77b4",hoverWidth:y=3,highlightTips:M=[],highlightStroke:w="#e63946",highlightWidth:b=2.5}={}){const k=t=>"number"==typeof t&&Number.isFinite(t);function v(t,{prefer:e="auto"}={}){return function(n){let a=t.get(n);if(!a)return 0;if("r"===e||"auto"===e&&"r"in a)return Number(a.r??0);if("x1"===e||"auto"===e&&"x1"in a)return Number(a.x1??0);let r=0;for(;a&&null!=a.parentId;)r+=Number(a.branchLength||0),a=t.get(a.parentId);return r}}if("rect"===e){const _=I(m(t)),L=_.horizontal_lines,A=_.vertical_lines,$=L.filter(t=>t.isTip),T=new Map(L.map(t=>[t.thisId,t])),P=new Map($.map(t=>[t.thisId,t])),j=new Map($.map(t=>[t.thisLabel,t])),E=v(T,{prefer:"x1"}),S=n.max(L,t=>t.y1),z=n.min(L,t=>t.y1),D=n.max(L,t=>t.x1),F=n.scaleLinear().domain([z-1,S+1]).range([s.top,i-s.bottom]),O=n.scaleLinear().domain([0,D]).range([s.left,a-s.right]),X=n.create("svg").attr("width",a).attr("height",i).attr("font-family","sans-serif").attr("font-size",10),Y=X.append("g"),B=X.append("g").attr("class","phylo_static_highlight"),N=X.append("g").attr("class","phylo_hover_highlight");Y.selectAll(".hline").data(L).join("line").attr("x1",t=>O(t.x0)).attr("y1",t=>F(t.y0)).attr("x2",t=>O(t.x1)).attr("y2",t=>F(t.y1)).attr("stroke","#555").attr("stroke-width",c),Y.selectAll(".vline").data(A).join("line").attr("x1",t=>O(t.x0)).attr("y1",t=>F(t.y0)).attr("x2",t=>O(t.x1)).attr("y2",t=>F(t.y1)).attr("stroke","#555").attr("stroke-width",c);const W=Y.selectAll(".tip-dot").data($).join("circle").attr("cx",t=>O(t.x1)).attr("cy",t=>F(t.y1)).attr("r",2).attr("fill","black");if(p&&W.append("title").text(t=>f(t,E(t.thisId))),W.on("mouseenter",function(t,e){U(e.thisId,N,g,y),n.select(this).attr("r",4)}).on("mouseleave",function(){N.selectAll("*").remove(),n.select(this).attr("r",2)}),d){const q=X.append("g").attr("class","phylo_labels").selectAll("text").data($).join("text").attr("x",t=>O(t.x1)+4).attr("y",t=>F(t.y1)).attr("dy","0.32em").attr("font-size",10).text(t=>t.thisLabel?.replace(/_/g," ")??"");p&&q.append("title").text(t=>f(t,E(t.thisId))),q.on("mouseenter",function(t,e){U(e.thisId,N,g,y),n.select(this).attr("font-weight",600)}).on("mouseleave",function(){N.selectAll("*").remove(),n.select(this).attr("font-weight",null)})}if(M&&M.length){new Set([...M.filter(k).map(t=>P.get(t)),...M.filter(t=>!k(t)).map(t=>j.get(t))].filter(Boolean)).forEach(t=>{U(t.thisId,B,w,b)})}function U(t,e,n,a){e.selectAll("*").remove();let r=T.get(t);for(;r&&null!=r.parentId;){const t=T.get(r.parentId);if(!t)break;e.append("line").attr("x1",O(r.x0)).attr("x2",O(r.x0)).attr("y1",F(t.y0)).attr("y2",F(r.y0)).attr("stroke",n).attr("stroke-width",a).attr("stroke-linecap","round"),e.append("line").attr("x1",O(r.x0)).attr("x2",O(r.x1)).attr("y1",F(r.y0)).attr("y2",F(r.y1)).attr("stroke",n).attr("stroke-width",a).attr("stroke-linecap","round"),r=t}}return X.node()}if("radial"===e){if(a!==i)throw new Error("width and height must be the same for radial layout");const C=u(m(t),{angleStrategy:"fan",arcsStyle:"fan"}),G="outer"===h;if("phylo"!==h&&"outer"!==h)throw new Error("radialMode must be either 'phylo' or 'outer'");const H=3,J=0,K=(n.max(C.data,t=>t.r)??0)+2*l,Q=a,R=i,V=Q/2,Z=R/2,tt=n.scaleLinear().domain([-K,K]).range([0,Q]),et=n.scaleLinear().domain([-K,K]).range([R,0]),nt=t=>t*(Q/(2*K)),at=new Map(C.data.map(t=>[t.thisId,t])),rt=C.data.filter(t=>t.isTip),it=rt.length?n.max(rt,t=>t.r):0,st=v(at,{prefer:"r"}),ot=new Map(rt.map(t=>[t.thisId,t])),lt=new Map(rt.map(t=>[t.thisLabel,t]));function ct(t,e,n,a){const r=tt(t),i=et(e),s=tt(n)-r,o=et(a)-i,l=Math.hypot(s,o)||1,c=Math.max(0,(l-J)/l);return{X0:r,Y0:i,X1s:r+s*c,Y1s:i+o*c,len:l}}const ht=n.create("svg").attr("width",Q).attr("height",R).attr("font-family","sans-serif").attr("font-size",10),dt=ht.append("g"),pt=ht.append("g").attr("class","phylo_static_lines"),ft=ht.append("g").attr("class","phylo_static_arcs"),ut=ht.append("g").attr("class","phylo_hover_lines"),gt=ht.append("g").attr("class","phylo_hover_arcs");dt.append("g").attr("class","phylo_arcs").selectAll("path").data(C.arcs).join("path").attr("d",t=>null==t.sweep?r(V,Z,nt(t.radius),t.start,t.end):o(V,Z,nt(t.radius),t.start,t.end,t.sweep)).attr("fill","none").attr("stroke","#777").attr("stroke-width",c),dt.append("g").attr("class","phylo_radii").selectAll("line").data(C.radii).join("line").each(function(t,e){const a=t.x0,r=t.y0,i=(s=t).childId??s.thisId??s.id1??null;var s;const o=null!=i?at.get(i):void 0,l=!(!o||!o.isTip);let h=t.x1,d=t.y1;G&&l&&(h=it*Math.cos(o.angle),d=it*Math.sin(o.angle));const{X0:p,Y0:f,X1s:u,Y1s:g}=ct(a,r,h,d);n.select(this).attr("x1",p).attr("y1",f).attr("x2",u).attr("y2",g).attr("stroke","#777").attr("stroke-width",c)});const It=dt.append("g").attr("class","phylo_tip_dots").selectAll("circle").data(rt).join("circle").each(function(t,e){const a=G?it*Math.cos(t.angle):t.x,r=G?it*Math.sin(t.angle):t.y;n.select(this).attr("cx",tt(a)).attr("cy",et(r)).attr("r",H).attr("fill","black").attr("stroke","black").attr("stroke-width",1.5)});p&&It.append("title").text(t=>f(t,st(t.thisId)));const yt=t=>"string"==typeof t?+t:t,xt=new Map(C.radii.map(t=>[yt(t.childId??t.thisId??t.id1),t])),mt=new Map(C.child_arcs.map(t=>[yt(t.childId),t]));if(d){const wt=dt.append("g").attr("class","phylo_labels").selectAll("g.label").data(rt).join("g").attr("class","label").attr("transform",t=>{const e=G?it:t.r,n=e*Math.cos(t.angle),a=e*Math.sin(t.angle);return`translate(${tt(n)},${et(a)})`}).each(function(t){let e=180*-t.angle/Math.PI,a=10,r="start";t.angle>Math.PI/2&&t.angle<3*Math.PI/2&&(e+=180,a*=-1,r="end"),n.select(this).append("g").attr("transform",`rotate(${e})`).append("text").attr("x",a).attr("alignment-baseline","middle").attr("text-anchor",r).attr("font-size",10).attr("fill","black").text(t=>t.thisLabel?.replace(/_/g," ")??"")});p&&wt.append("title").text(t=>f(t,st(t.thisId))),wt.on("mouseenter",function(t,e){Mt(e,ut,gt,g,y),n.select(this).select("text").attr("font-weight",600)}).on("mouseleave",function(){ut.selectAll("*").remove(),gt.selectAll("*").remove(),n.select(this).select("text").attr("font-weight",null)})}function Mt(t,e,n,a="#1f77b4",i=3){e.selectAll("*").remove(),n.selectAll("*").remove();let s="number"==typeof t||"string"==typeof t?at.get(t):t;if(!s)return;let l=!0;for(;s&&null!=s.parentId;){const c=xt.get(yt(s.thisId));if(c){const p=c.x0,f=c.y0;let u=c.x1,g=c.y1;if(G&&l&&s.isTip){const M=it;u=M*Math.cos(s.angle),g=M*Math.sin(s.angle)}const{X0:I,Y0:y,X1s:x,Y1s:m}=ct(p,f,u,g);e.append("line").attr("x1",I).attr("y1",y).attr("x2",x).attr("y2",m).attr("stroke",a).attr("stroke-width",i).attr("stroke-linecap","round")}const h=mt.get(yt(s.thisId));function d(t){const e=nt(t.radius);return null==t.sweep?r(V,Z,e,t.start,t.end):o(V,Z,e,t.start,t.end,t.sweep)}h&&n.append("path").attr("d",d(h)).attr("fill","none").attr("stroke",a).attr("stroke-width",i),l=!1,s=at.get(s.parentId)}}if(It.on("mouseenter",function(t,e){Mt(e,ut,gt,g,y),n.select(this).attr("r",H+2)}).on("mouseleave",function(){ut.selectAll("*").remove(),gt.selectAll("*").remove(),n.select(this).attr("r",H)}),M&&M.length){new Set([...M.filter(k).map(t=>ot.get(t)),...M.filter(t=>!k(t)).map(t=>lt.get(t))].filter(Boolean)).forEach(t=>{Mt(t.thisId,pt,ft,w,b)})}return ht.node()}if("unrooted"===e){const bt=x(m(t)),kt=a,vt=i,_t=n.extent(bt.data,t=>t.x),Lt=n.extent(bt.data,t=>t.y),At=Math.max(Math.abs(_t[0]),Math.abs(_t[1])),$t=Math.max(Math.abs(Lt[0]),Math.abs(Lt[1])),Tt=Math.max(At,$t)+2*l,Pt=n.scaleLinear().domain([-Tt,Tt]).range([0,kt]),jt=n.scaleLinear().domain([-Tt,Tt]).range([vt,0]),Et=n.create("svg").attr("width",kt).attr("height",vt).attr("font-family","sans-serif").attr("font-size",10),St=Et.append("g"),zt=Et.append("g").attr("class","phylo_static_highlight"),Dt=Et.append("g").attr("class","phylo_hover_highlight");St.append("g").attr("class","phylo_lines").selectAll("line").data(bt.edges).join("line").attr("x1",t=>Pt(t.x1)).attr("y1",t=>jt(t.y1)).attr("x2",t=>Pt(t.x2)).attr("y2",t=>jt(t.y2)).attr("stroke-width",c).attr("stroke","#777");const Ft=St.append("g").attr("class","phylo_points").selectAll("circle").data(bt.data).join("circle").attr("class","dot").attr("r",t=>t.isTip?4:0).attr("cx",t=>Pt(t.x)).attr("cy",t=>jt(t.y)).attr("stroke","black").attr("stroke-width",2).attr("fill",t=>t.isTip?"black":"white"),Ot=new Map(bt.data.map(t=>[t.thisId,t])),Xt=new Map(bt.data.filter(t=>t.isTip).map(t=>[t.thisId,t])),Yt=new Map(bt.data.filter(t=>t.isTip).map(t=>[t.thisLabel,t])),Bt=v(Ot);p&&Ft.filter(t=>t.isTip).append("title").text(t=>f(t,Bt(t.thisId)));const Nt=new Map,Wt=new Map(bt.data.map(t=>[t.thisId,t]));if(bt.edges.forEach(t=>{const e=Wt.get(t.id1);e?.isTip&&Nt.set(t.id1,t)}),d){const qt=St.append("g").attr("class","phylo_labels").selectAll("g").data(bt.data.filter(t=>t.isTip)).join("g").attr("transform",t=>`translate(${Pt(t.x)},${jt(t.y)})`).each(function(t){const e=Nt.get(t.thisId);if(!e)return;const a=Pt(e.x1),r=jt(e.y1),i=Pt(e.x2)-a,s=jt(e.y2)-r;let o=180*Math.atan2(s,i)/Math.PI,l=-10,c="end";(o>90||o<-90)&&(o+=180,c="start",l=10),n.select(this).append("g").attr("transform",`rotate(${o})`).append("text").attr("x",l).attr("alignment-baseline","middle").attr("text-anchor",c).attr("font-size",10).attr("fill","black").text(t.thisLabel?.replace(/_/g," ")??"")});p&&qt.append("title").text(t=>f(t,Bt(t.thisId))),qt.on("mouseenter",function(t,e){Ut(e.thisId,Dt,g,y),n.select(this).select("text").attr("font-weight",600)}).on("mouseleave",function(){Dt.selectAll("*").remove(),n.select(this).select("text").attr("font-weight",null)})}if(Ft.filter(t=>t.isTip).on("mouseenter",function(t,e){Ut(e.thisId,Dt,g,y),n.select(this).attr("r",6)}).on("mouseleave",function(){Dt.selectAll("*").remove(),n.select(this).attr("r",4)}),M&&M.length){new Set([...M.filter(k).map(t=>Xt.get(t)),...M.filter(t=>!k(t)).map(t=>Yt.get(t))].filter(Boolean)).forEach(t=>{Ut(t.thisId,zt,w,b)})}function Ut(t,e,n,a){const r=new Map(bt.edges.map(t=>[t.id1,t]));e.selectAll("*").remove();let i=Ot.get(t);for(;i&&null!=i.parentId;){const t=r.get(i.thisId);t&&e.append("line").attr("x1",Pt(t.x1)).attr("y1",jt(t.y1)).attr("x2",Pt(t.x2)).attr("y2",jt(t.y2)).attr("stroke",n).attr("stroke-width",a).attr("stroke-linecap","round"),i=Ot.get(i.parentId)}}return Et.node()}throw new Error("Unsupported layout type. Use 'rect', 'radial', or 'unrooted'.")}});
2
2
  //# sourceMappingURL=drawPhylogeny.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"drawPhylogeny.umd.js","sources":["../src/radial/polarToCartesian.js","../src/radial/describeArc.js","../src/radial/describeArcSweep.js","../src/utils/preorder.js","../src/utils/fortify.js","../src/radial/radialData.js","../src/radial/fanAngles.js","../src/radial/radialLayout.js","../src/radial/getRadiiFromPd.js","../src/radial/getRadii.js","../src/radial/getArcsFan.js","../src/radial/getArcs.js","../src/radial/getChildArcsFan.js","../src/radial/getChildArcs.js","../src/rectangle/getHorizontal.js","../src/utils/mean.js","../src/rectangle/rectangleLayout.js","../src/rectangle/getVertical.js","../src/rectangle/getChildVerticals.js","../src/unrooted/equalAngleLayout.js","../src/utils/numTips.js","../src/unrooted/unrooted.js","../src/utils/edges.js","../src/utils/readTree.js","../src/plot/drawPhylogeny.js"],"sourcesContent":["// src/radial/polarToCartesian.js\nexport default function (cx, cy, r, t) {\n return { x: cx + r * Math.cos(t), y: cy - r * Math.sin(t) };\n}\n","import polarToCartesian from \"./polarToCartesian.js\";\n\n/**\n * Draw the shortest arc between startAngle and endAngle (radians), CCW.\n * If the CW path is shorter, swap start/end so the CCW path is still shortest.\n * Works with Y-inverted screen coords (polarToCartesian already inverts Y).\n */\nexport default function describeArc(cx, cy, radius, startAngle, endAngle) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n let a0 = norm(startAngle);\n let a1 = norm(endAngle);\n\n // CCW and CW spans\n const ccw = (a1 - a0 + TAU) % TAU;\n const cw = (a0 - a1 + TAU) % TAU;\n\n // Ensure we always take the shorter span *in CCW* by swapping if needed\n if (cw < ccw) {\n const tmp = a0; a0 = a1; a1 = tmp;\n }\n\n const delta = (a1 - a0 + TAU) % TAU; // now the shorter CCW span\n if (delta < 1e-9) {\n const p = polarToCartesian(cx, cy, radius, a0);\n return `M ${p.x} ${p.y}`; // degenerate span → no arc\n }\n\n const largeArcFlag = delta > Math.PI ? 1 : 0; // should be 0 for “shortest”, but keep for safety\n const sweepFlag = 0; // CCW\n\n const p0 = polarToCartesian(cx, cy, radius, a0);\n const p1 = polarToCartesian(cx, cy, radius, a1);\n\n return `M ${p0.x} ${p0.y} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${p1.x} ${p1.y}`;\n}\n\n","// src/radial/describeArcSweep.js\nconst TAU = Math.PI * 2;\nconst norm = (t) => ((t % TAU) + TAU) % TAU;\n\nexport default function describeArcSweep(cx, cy, r, a0, a1, sweep /*0=CCW,1=CW*/) {\n const delta = sweep === 0 ? norm(a1 - a0) : norm(a0 - a1);\n if (!(r > 0) || delta < 1e-9) return \"\";\n const largeArcFlag = delta > Math.PI ? 1 : 0;\n\n const x0 = cx + r * Math.cos(a0), y0 = cy - r * Math.sin(a0);\n const x1 = cx + r * Math.cos(a1), y1 = cy - r * Math.sin(a1);\n\n return `M ${x0} ${y0} A ${r} ${r} 0 ${largeArcFlag} ${sweep} ${x1} ${y1}`;\n}\n\n","/**\n * Recursive function for pre-order traversal of tree (returns array)\n */\nexport function preorder(node, list = []) {\n list.push(node);\n for (let i = 0; i < (node.children?.length || 0); i++) {\n list = preorder(node.children[i], list);\n }\n return list;\n}\n\n/**\n * Iterative generator traversals (avoid recursion limits on large trees)\n */\nexport function* preorderIter(root) {\n const stack = [root];\n while (stack.length) {\n const n = stack.pop();\n yield n;\n if (n.children) for (let i = n.children.length - 1; i >= 0; --i) stack.push(n.children[i]);\n }\n}\n\nexport function* postorderIter(root) {\n const stack = [[root, 0]];\n while (stack.length) {\n const top = stack[stack.length - 1];\n const [n, i] = top;\n if (!n.children || i >= n.children.length) {\n stack.pop();\n yield n;\n } else {\n top[1] = i + 1;\n stack.push([n.children[i], 0]);\n }\n }\n}\n\n","import { preorder } from \"./preorder.js\"\n\n/**\n * Convert parsed Newick tree from readTree() into data\n * frame.\n * this is akin to a \"phylo\" object in R.\n */\n\nexport default function (tree, sort = true) {\n var df = [];\n\n for (const node of preorder(tree)) {\n if (node.parent === null) {\n df.push({\n 'parentId': null,\n 'parentLabel': null,\n 'thisId': node.id,\n 'thisLabel': node.label,\n 'children': node.children.map(x => x.id),\n 'branchLength': 0.,\n 'isTip': false,\n 'x': node.x,\n 'y': node.y,\n 'angle': node.angle\n })\n }\n else {\n df.push({\n 'parentId': node.parent.id,\n 'parentLabel': node.parent.label,\n 'thisId': node.id,\n 'thisLabel': node.label,\n 'children': node.children.map(x => x.id),\n 'branchLength': node.branchLength,\n 'isTip': (node.children.length == 0),\n 'x': node.x,\n 'y': node.y,\n 'angle': node.angle\n })\n }\n }\n\n if (sort) {\n df = df.sort(function (a, b) {\n return a.thisId - b.thisId;\n })\n }\n return (df);\n}\n","import fortify from \"../utils/fortify.js\"\n\n/**\n * Compute per-node polar coordinates for radial layout:\n * - Tip angles: evenly spaced 0..2π in tip DFS order\n * - Internal angles: circular mean of child angles\n * - Radii: cumulative branch length from root\n * - x,y: cartesian projection\n *\n * Returns the fortified array with added {angle, r, x, y}.\n */\nexport default function radialData(node) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n\n const pd = fortify(node, /*sort*/ true);\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const kids = new Map(pd.map(d => [d.thisId, d.children || []]));\n\n // Find root id\n let root = null;\n for (const d of pd) {\n if (d.parentId == null) { root = d.thisId; break; }\n }\n\n // Collect tip ids in DFS left->right order to preserve input ordering\n const tipIds = [];\n (function dfs(id) {\n const c = kids.get(id) || [];\n if (c.length === 0) {\n tipIds.push(id);\n return;\n }\n for (const ch of c) dfs(ch);\n })(root);\n\n // Assign tip angles evenly spaced 0..2π\n const N = Math.max(1, tipIds.length);\n const angle = new Map();\n tipIds.forEach((id, i) => {\n angle.set(id, (i / N) * TAU);\n });\n\n // Internal node angles: circular mean of child angles (post-order)\n (function setInternalAngles(id) {\n const c = kids.get(id) || [];\n for (const ch of c) setInternalAngles(ch);\n if (c.length > 0) {\n let sx = 0, sy = 0;\n for (const ch of c) {\n const th = angle.get(ch);\n sx += Math.cos(th);\n sy += Math.sin(th);\n }\n angle.set(id, norm(Math.atan2(sy, sx)));\n }\n })(root);\n\n // Radii: cumulative branch lengths from root (root r=0)\n const radius = new Map();\n radius.set(root, 0);\n (function setR(id) {\n const c = kids.get(id) || [];\n const r0 = radius.get(id) || 0;\n for (const ch of c) {\n const child = byId.get(ch);\n const bl = child?.branchLength ?? 0;\n radius.set(ch, r0 + bl);\n setR(ch);\n }\n })(root);\n\n // Enrich pd rows with angle, r, x, y\n for (const d of pd) {\n const th = angle.get(d.thisId) ?? 0;\n const r = radius.get(d.thisId) ?? 0;\n d.angle = th;\n d.r = r;\n d.x = r * Math.cos(th);\n d.y = r * Math.sin(th);\n }\n\n return pd;\n}\n","// APE-like \"fan\" angles: tips evenly spaced with open-angle gap & rotation,\n// internal nodes = arithmetic mean of unwrapped child angles.\nconst TAU = Math.PI * 2;\nconst norm = (t) => ((t % TAU) + TAU) % TAU;\n\nfunction unwrapAround(ref, a) {\n let x = a;\n while (x < ref - Math.PI) x += TAU;\n while (x > ref + Math.PI) x -= TAU;\n return x;\n}\n\nexport default function fanAngles(pd, opts = {}) {\n const { openAngleDeg = 0, rotateDeg = 0 } = opts;\n const gap = (openAngleDeg / 360) * TAU;\n const rot = (rotateDeg / 360) * TAU;\n\n // root + children index\n let root = null;\n const kids = new Map(pd.map((d) => [d.thisId, d.children || []]));\n for (const d of pd) if (d.parentId == null) { root = d.thisId; break; }\n\n // tip order (DFS left→right like your fortify)\n const tipIds = [];\n (function dfs(id) {\n const c = kids.get(id) || [];\n if (!c.length) { tipIds.push(id); return; }\n for (const ch of c) dfs(ch);\n })(root);\n\n const N = Math.max(1, tipIds.length);\n const maxA = TAU * (1 - 1 / N) - gap; // note: no last-step overlap\n const step = N > 1 ? maxA / (N - 1) : 0;\n\n const angle = new Map();\n tipIds.forEach((id, i) => angle.set(id, norm(i * step + rot)));\n\n // internal nodes: arithmetic mean of child angles (unwrapped)\n (function setInternal(id) {\n const c = kids.get(id) || [];\n for (const ch of c) setInternal(ch);\n if (c.length) {\n const a0 = angle.get(c[0]);\n const arr = c.map((ch) => unwrapAround(a0, angle.get(ch)));\n angle.set(id, norm(arr.reduce((s, v) => s + v, 0) / arr.length));\n }\n })(root);\n\n return angle;\n}\n\n","// src/radial/radialLayout.js\nimport radialData from \"./radialData.js\";\nimport getRadii from \"./getRadii.js\";\nimport getRadiiFromPd from \"./getRadiiFromPd.js\";\nimport getArcs from \"./getArcs.js\"; // your existing \"shortest-arc\"\nimport getArcsFan from \"./getArcsFan.js\"; // new\nimport fanAngles from \"./fanAngles.js\";\nimport getChildArcs from \"./getChildArcs.js\"; // optional existing helper\nimport getChildArcsFan from \"./getChildArcsFan.js\"; // optional existing helper\n\n/**\n * radialLayout(node, opts?)\n * opts:\n * - angleStrategy: \"cmean\" (default, your current) | \"fan\" (APE-like)\n * - arcsStyle: \"shortest\" (default) | \"fan\" (block arcs, supports wrap)\n * - openAngleDeg: number (gap wedge for \"fan\" angles)\n * - rotateDeg: number (rotation for \"fan\" angles)\n */\nexport default function radialLayout(node, opts = {}) {\n const {\n // default fan\n angleStrategy = \"fan\",\n arcsStyle = \"fan\",\n openAngleDeg = 0,\n rotateDeg = 0\n } = opts;\n\n // Start with your current enriched nodes (angle + r from radialData)\n const pd = radialData(node);\n\n if (angleStrategy === \"fan\") {\n // overwrite angles with APE-like fan angles; keep radii r as-is\n const angleMap = fanAngles(pd, { openAngleDeg, rotateDeg });\n for (const d of pd) {\n const a = angleMap.get(d.thisId);\n if (a != null) {\n d.angle = a;\n d.x = d.r * Math.cos(a);\n d.y = d.r * Math.sin(a);\n }\n }\n }\n\n // Build spokes using the angles currently on pd\n const radii = (angleStrategy === \"fan\")\n ? getRadiiFromPd(pd)\n : getRadii(node);\n\n // Choose arc builder\n const arcs = (arcsStyle === \"fan\")\n ? getArcsFan(pd)\n : getArcs(pd);\n\n // per-child arcs for half-arc highlighting if you already use them\n let child_arcs = [];\n if (opts.arcsStyle === \"fan\") {\n child_arcs = getChildArcsFan(pd);\n } else {\n child_arcs = getChildArcs(pd);\n }\n\n return { data: pd, radii, arcs, child_arcs };\n}\n\n","// src/radial/getRadiiFromPd.js\n/**\n * Build per-edge spokes using the *current* pd angles/r.\n * Output: [{ parentId, childId, x0,y0,x1,y1, isTip }]\n */\nexport default function getRadiiFromPd(pd) {\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const root = pd.find(d => d.parentId == null)?.thisId;\n\n const segments = [];\n for (const d of pd) {\n if (d.thisId === root) continue;\n const parent = byId.get(d.parentId);\n if (!parent) continue;\n\n const theta = d.angle;\n const r0 = parent.r, r1 = d.r;\n\n segments.push({\n parentId: parent.thisId,\n childId: d.thisId,\n x0: r0 * Math.cos(theta),\n y0: r0 * Math.sin(theta),\n x1: r1 * Math.cos(theta),\n y1: r1 * Math.sin(theta),\n isTip: !!d.isTip\n });\n }\n return segments;\n}\n\n","import radialData from \"./radialData.js\";\n\n/**\n * Per-edge radial segments (for highlighting and drawing).\n * For each non-root node, draw a radial line from the parent radius to the child radius\n * at the CHILD'S angle.\n *\n * Output: [{ parentId, childId, x0, y0, x1, y1, isTip }]\n */\nexport default function getRadii(node) {\n const data = radialData(node);\n const byId = new Map(data.map(d => [d.thisId, d]));\n const root = data.find(d => d.parentId == null)?.thisId;\n\n const segments = [];\n for (const d of data) {\n if (d.thisId === root) continue;\n const parent = byId.get(d.parentId);\n if (!parent) continue;\n\n const theta = d.angle;\n const r0 = parent.r;\n const r1 = d.r;\n\n segments.push({\n parentId: parent.thisId,\n childId: d.thisId,\n x0: r0 * Math.cos(theta),\n y0: r0 * Math.sin(theta),\n x1: r1 * Math.cos(theta),\n y1: r1 * Math.sin(theta),\n isTip: !!d.isTip\n });\n }\n return segments;\n}\n\n","/**\n * Build APE-like block arcs per internal parent:\n * radius = parent.r\n * start = first child's angle\n * end = last child's angle\n * sweep = 0 (CCW) if end>=start; 1 (CW) if wrapped across 2π\n *\n * @param {Array} pd nodes with {thisId,parentId,children,angle,r}\n * @returns {Array} [{parentId,thisId,radius,start,end,sweep}]\n */\nexport default function getArcsFan(pd) {\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const arcs = [];\n\n for (const p of pd) {\n const c = p.children || [];\n if (c.length < 2 || !(p.r > 0)) continue;\n\n const first = byId.get(c[0])?.angle;\n const last = byId.get(c[c.length - 1])?.angle;\n if (first == null || last == null) continue;\n\n const start = first;\n const end = last;\n const sweep = end >= start ? 0 : 1; // CW if wrapped\n\n arcs.push({\n parentId: p.parentId,\n thisId: p.thisId,\n radius: p.r,\n start,\n end,\n sweep\n });\n }\n return arcs;\n}\n\n","/**\n * Build arc descriptors for each internal parent:\n * - One arc per internal node at radius = parent.r\n * - Start/end angles choose the *shortest* wrap-aware span covering the children\n * - Skips degenerate spans (delta ~ 0)\n */\nexport default function getArcs(pd) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n const EPS = 1e-6;\n\n // Quick lookups\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const childrenByParent = new Map();\n let root = null;\n\n for (const d of pd) {\n if (d.parentId == null) root = d.thisId;\n if (!childrenByParent.has(d.parentId)) childrenByParent.set(d.parentId, []);\n childrenByParent.get(d.parentId).push(d);\n }\n\n const arcs = [];\n\n for (const parent of pd) {\n const pid = parent.thisId;\n if (pid === root) continue; // no arc above root\n const kids = childrenByParent.get(pid) || [];\n if (kids.length < 2) continue; // need at least two children\n\n // Collect & sort child angles\n const A = kids.map(k => norm(k.angle)).sort((a, b) => a - b);\n const aMin = A[0], aMax = A[A.length - 1];\n\n // Two candidate spans: direct (aMin -> aMax) and wrapped (aMax -> aMin across 2π)\n const direct = aMax - aMin;\n const wrapped = TAU - direct;\n\n // Choose the shorter span. We'll draw **CCW** (sweepFlag = 0) in describeArc.\n let start, end, span;\n if (direct <= wrapped) {\n start = aMin;\n end = aMax;\n span = direct;\n } else {\n // wrapped is shorter: go CCW from aMax up through 2π to aMin\n start = aMax;\n end = aMin;\n span = wrapped;\n }\n\n if (span < EPS || !isFinite(parent.r) || parent.r <= 0) continue;\n\n arcs.push({\n start,\n end,\n radius: parent.r,\n thisId: pid,\n parentId: parent.parentId\n });\n }\n\n return arcs;\n}\n\n","export default function getChildArcsFan(pd) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n\n // Circular midpoint that travels CCW from a -> b by half the CCW span\n function midCCW(a, b) {\n const d = (b - a + TAU) % TAU; // CCW delta in [0, 2π)\n return norm(a + d / 2);\n }\n\n const key = (x) => (typeof x === \"string\" ? +x : x);\n\n const byId = new Map(pd.map(d => [key(d.thisId), d]));\n const childrenByParent = new Map(\n pd.map(d => [\n key(d.thisId),\n // normalize children to numeric IDs; drop anything we can't resolve\n (d.children || [])\n .map(ch => (typeof ch === \"object\" ? ch.thisId : ch))\n .map(key)\n .filter(id => byId.has(id))\n ])\n );\n\n const child_arcs = [];\n\n for (const parentRaw of pd) {\n const pid = key(parentRaw.thisId);\n const kids = childrenByParent.get(pid) || [];\n if (kids.length < 2) continue;\n\n // Sort children by angle (normalized) around the circle\n const A = kids\n .map(id => {\n const node = byId.get(id);\n return node ? { id, a: norm(node.angle) } : null;\n })\n .filter(Boolean)\n .sort((u, v) => u.a - v.a);\n\n const N = A.length;\n if (N < 2) continue;\n\n const parent = byId.get(pid);\n const radius = parent?.r;\n if (!(radius > 0)) continue;\n\n for (let i = 0; i < N; i++) {\n const prev = A[(i - 1 + N) % N];\n const cur = A[i];\n const next = A[(i + 1) % N];\n\n // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next), CCW\n const start = midCCW(prev.a, cur.a);\n const end = midCCW(cur.a, next.a);\n\n child_arcs.push({\n parentId: pid,\n childId: cur.id,\n radius,\n start,\n end,\n sweep: 0 // CCW (consistent with describeArcSweep and your y-flip)\n });\n }\n }\n\n return child_arcs;\n}\n\n","/**\n * Per-child \"half\" arcs for radial trees.\n *\n * For each non-root node (child), emit an arc at the PARENT's radius that\n * spans between the parent's angle and the child's angle. This is the arc\n * segment that meets the child's spoke and is ideal for root→tip highlighting.\n *\n * Input: pd — the array returned by radialData(node) (each row has .thisId, .parentId, .angle, .r)\n * Output: [{ parentId, childId, radius, start, end }]\n */\nexport default function getChildArcs(pd) {\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const arcs = [];\n\n for (const child of pd) {\n if (child.parentId == null) continue; // skip root\n const parent = byId.get(child.parentId);\n if (!parent) continue;\n\n arcs.push({\n parentId: parent.thisId,\n childId: child.thisId,\n radius: parent.r, // draw on the parent's circle\n start: parent.angle, // start at parent's angle\n end: child.angle // end at child's angle (describeArc will choose the shortest CCW span)\n });\n }\n\n return arcs;\n}\n\n","import mean from \"../utils/mean.js\"\nimport fortify from \"../utils/fortify.js\"\n\n/**\n * Rectangle layout: compute per-node x0,x1 and y0=y1\n * - Tip y is assigned by input order (preserves ladderize/order)\n * - Internal node y is mean of child y's\n * - x1 accumulates branch lengths from root\n */\n\nexport default function getHorizontal(node) {\n const pd = fortify(node);\n\n // Fast lookup from id -> pd index\n const idIndex = new Map(pd.map((d, i) => [d.thisId, i]));\n\n // 1) Leaf order from the INPUT TREE (respects your child order / ladderize)\n const leafIds = [];\n (function dfs(n) {\n if (!n.children || n.children.length === 0) { leafIds.push(n.id); return; }\n n.children.forEach(dfs);\n })(node);\n\n // Map each leaf id to a vertical slot (1..N)\n const tipSlot = new Map(leafIds.map((id, i) => [id, i + 1]));\n\n // 2) Set Y for tips directly from that order; internal node Y via children mean\n (function setY(n) {\n const i = idIndex.get(n.id);\n if (!n.children || n.children.length === 0) {\n const y = tipSlot.get(n.id);\n pd[i].y0 = y; pd[i].y1 = y;\n return y;\n }\n const ys = n.children.map(setY);\n const y = mean(ys);\n pd[i].y0 = y; pd[i].y1 = y;\n return y;\n })(node);\n\n // 3) Set X by accumulating branch lengths down the tree\n (function setX(n, xParent) {\n const i = idIndex.get(n.id);\n const bl = pd[i].branchLength ?? 0;\n const x0 = xParent ?? 0;\n const x1 = x0 + bl;\n pd[i].x0 = x0; pd[i].x1 = x1;\n if (n.children && n.children.length) n.children.forEach(c => setX(c, x1));\n })(node, 0);\n\n // Clean up: remove fields not needed downstream without triggering no-unused-vars\n return pd.map((row) => {\n const { y: _y, x: _x, angle: _angle, ...item } = row;\n return item;\n });\n}\n\n","/**\n * Iterable mean\n * Poached from https://github.com/d3/d3-array/blob/master/src/mean.js\n * (Other array means buggered up the tree)\n */\n\nexport default function (values, valueof) {\n let count = 0;\n let sum = 0;\n if (valueof === undefined) {\n for (let value of values) {\n if (value != null && (value = +value) >= value) {\n ++count, sum += value;\n }\n }\n } else {\n let index = -1;\n for (let value of values) {\n if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {\n ++count, sum += value;\n }\n }\n }\n if (count) return sum / count;\n}\n","import getHorizontal from \"./getHorizontal.js\"\nimport getVertical from \"./getVertical.js\"\nimport getChildVerticals from \"./getChildVerticals.js\"\n\n/**\n * Rectangle layout wrapper.\n * Returns:\n * - data: per-node rows (x0,x1,y0=y1,...)\n * - vertical_lines: single spanning vertical per parent (baseline draw)\n * - child_vertical_lines: one vertical per edge (for highlighting)\n * - horizontal_lines: per-edge child horizontals (x0->x1 at y), with labels & tip flags\n */\nexport default function rectangleLayout(node) {\n const data = getHorizontal(node); // per-node\n const vertical_lines = getVertical(node); // parent spans\n const child_vertical_lines = getChildVerticals(node); // per-edge verticals\n\n // IMPORTANT: include y0 & y1, and carry isTip/labels for the renderer\n const byId = new Map(data.map(d => [d.thisId, d]));\n const horizontal_lines = data\n .filter(d => d.parentId != null)\n .map(d => ({\n parentId: d.parentId,\n childId: d.thisId,\n thisId: d.thisId,\n thisLabel: d.thisLabel,\n isTip: d.isTip,\n x0: d.x0,\n x1: d.x1,\n y0: d.y0,\n y1: d.y0\n }));\n\n return { data, vertical_lines, child_vertical_lines, horizontal_lines };\n}\n\n","import getHorizontal from \"./getHorizontal.js\"\n\nexport default function getVertical(node) {\n const data = getHorizontal(node);\n\n // Group rows by parentId (children that share a parent)\n const byParent = new Map();\n for (const row of data) {\n if (row.parentId == null) continue;\n const a = byParent.get(row.parentId);\n if (a) a.push(row); else byParent.set(row.parentId, [row]);\n }\n\n const verticals = [];\n for (const [parentId, kids] of byParent.entries()) {\n if (!kids.length) continue;\n // Works for binary and multifurcations:\n const yvals = kids.map(d => d.y0);\n const y0 = Math.min(...yvals);\n const y1 = Math.max(...yvals);\n // All children share the same junction x (their x0)\n const x = kids[0].x0;\n\n verticals.push({\n parentId,\n x0: x,\n x1: x,\n y0,\n y1,\n heights: y1 - y0\n });\n }\n\n return verticals;\n}\n\n","import getHorizontal from \"./getHorizontal.js\";\n\n/**\n * Build per-child vertical segments for a rectangular tree:\n * For each non-root node (child), draw a vertical from (parent.x, child.y) to (parent.x, parent.y).\n * This yields exactly one vertical per edge (child->parent), making highlighting trivial.\n *\n * Returns an array of:\n * {\n * parentId: number,\n * childId: number,\n * x: number, // x of the parent junction\n * y0: number, // min(child.y, parent.y)\n * y1: number, // max(child.y, parent.y)\n * }\n */\nexport default function getChildVerticals(node) {\n const data = getHorizontal(node); // has parentId, thisId, x0,x1,y0=y1\n\n // Build a quick index to access parent's y by id\n const byId = new Map(data.map(d => [d.thisId, d]));\n\n const childVerticals = [];\n\n for (const d of data) {\n if (d.parentId == null) continue;\n const parent = byId.get(d.parentId);\n if (!parent) continue;\n\n const x = d.x0; // child’s vertical sits at parent.x == child.x0\n const yc = d.y0; // child y\n const yp = parent.y0; // parent y\n const y0 = Math.min(yc, yp);\n const y1 = Math.max(yc, yp);\n\n childVerticals.push({\n parentId: d.parentId,\n childId: d.thisId,\n x,\n y0,\n y1\n });\n }\n\n return childVerticals;\n}\n\n","import numTips from \"../utils/numTips.js\"\n\n/**\n * Equal-angle layout for unrooted trees.\n * - Precomputes ntips in O(n) to avoid repeated subtree counts\n * - Uses angles in \"π units\" (0..2) to match existing API\n * - Populates x,y positions from branchLength and angle\n */\n\nfunction annotateTipCounts(root) {\n (function post(n) {\n if (!n.children || n.children.length === 0) {\n n.ntips = 1; return 1;\n }\n let sum = 0;\n for (const c of n.children) sum += post(c);\n n.ntips = sum;\n return sum;\n })(root);\n return root;\n}\n\nfunction equalAngleLayout(node) {\n if (node.parent === null) {\n annotateTipCounts(node);\n node.start = 0.; // guarantees no arcs overlap 0\n node.end = 2.; // *π\n node.angle = 0.; // irrelevant at root\n node.ntips = numTips(node); // safe (already computed), left for compatibility\n node.x = 0;\n node.y = 0;\n }\n\n let lastStart = node.start;\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const arc = (node.end - node.start) * (child.ntips / node.ntips);\n\n child.start = lastStart;\n child.end = lastStart + arc;\n\n // bisect the arc in π-units\n child.angle = child.start + (child.end - child.start) / 2.;\n lastStart = child.end;\n\n // map to coordinates (convert π-units to radians by multiplying by Math.PI)\n const theta = child.angle * Math.PI;\n const bl = (child.branchLength ?? 0);\n child.x = node.x + bl * Math.sin(theta);\n child.y = node.y + bl * Math.cos(theta);\n\n equalAngleLayout(child);\n }\n\n return node;\n}\n\nexport default equalAngleLayout\n\n","/**\n * Recursive function for breadth-first search of a tree\n * the root node is visited first.\n */\n\nfunction levelorder(root) {\n const queue = [root], result = [];\n while (queue.length) {\n const curnode = queue.shift(); // <- FIFO\n result.push(curnode);\n for (const child of curnode.children) queue.push(child);\n }\n return result;\n}\n\n\n/**\n * Count the number of tips that descend from this node\n */\n\nexport default function(thisnode) {\n var result = 0;\n for (const node of levelorder(thisnode)) {\n if (node.children.length == 0) result++;\n }\n return (result);\n}\n","import edges from \"../utils/edges.js\";\nimport fortify from \"../utils/fortify.js\";\nimport equalAngleLayout from \"./equalAngleLayout.js\";\n\n/**\n * Simple wrapper function for equalAngleLayout()\n */\n\nexport default function (node) {\n var data = {};\n // use the Felsenstein equal angle layout algorithm\n var eq = fortify(equalAngleLayout(node));\n data.data = eq;\n // make the edges dataset\n data.edges = edges(eq);\n\n return data;\n}\n","/**\n * Convert parsed Newick tree from fortify() into data frame of edges\n * this is akin to a \"phylo\" object in R, where thisID and parentId\n * are the $edge slot. I think.\n */\n\nexport default function edges(df, rectangular = false) {\n const rows = [...df].sort((a, b) => a.thisId - b.thisId);\n const byId = new Map(rows.map((r) => [r.thisId, r]));\n const result = [];\n\n for (const row of rows) {\n if (row.parentId == null) continue;\n const parent = byId.get(row.parentId);\n if (!parent) continue;\n\n if (rectangular) {\n result.push({ x1: row.x, y1: row.y, id1: row.thisId, x2: parent.x, y2: row.y, id2: undefined });\n result.push({ x1: parent.x, y1: row.y, id1: undefined, x2: parent.x, y2: parent.y, id2: row.parentId });\n } else {\n result.push({ x1: row.x, y1: row.y, id1: row.thisId, x2: parent.x, y2: parent.y, id2: row.parentId });\n }\n }\n return result;\n}\n\n","/**\n * Parse a Newick tree string into a doubly-linked list of JS Objects.\n * Assigns labels, branch lengths, and node IDs (tips before internals if input emits them that way).\n *\n * Notes / limitations:\n * - Quoted labels and NHX annotations are not fully supported.\n * - Branch lengths in scientific notation are supported (parseFloat).\n */\n\nexport default function readTree(text) {\n // Remove all whitespace (space, tabs, newlines)\n text = String(text).replace(/\\s+/g, '');\n\n const tokens = text.split(/(;|\\(|\\)|,)/);\n const root = { parent: null, children: [] };\n let curnode = root;\n let nodeId = 0;\n\n for (const token of tokens) {\n if (!token || token === ';') continue;\n\n if (token === '(') {\n const child = { parent: curnode, children: [] };\n curnode.children.push(child);\n curnode = child; // descend\n } else if (token === ',') {\n // back to parent, then create sibling\n curnode = curnode.parent;\n const child = { parent: curnode, children: [] };\n curnode.children.push(child);\n curnode = child;\n } else if (token === ')') {\n // ascend one level\n curnode = curnode.parent;\n if (curnode === null) break;\n } else {\n // label/branch-length chunk (e.g., \"A:0.01\" or \"A\")\n const nodeinfo = token.split(':');\n if (nodeinfo.length === 1) {\n if (token.startsWith(':')) {\n curnode.label = '';\n curnode.branchLength = parseFloat(nodeinfo[0]);\n } else {\n curnode.label = nodeinfo[0];\n curnode.branchLength = null;\n }\n } else if (nodeinfo.length === 2) {\n curnode.label = nodeinfo[0];\n curnode.branchLength = parseFloat(nodeinfo[1]);\n } else {\n console.warn(token, \"Unhandled token with multiple ':' characters\");\n curnode.label = nodeinfo[0] || '';\n curnode.branchLength = parseFloat(nodeinfo[nodeinfo.length - 1]);\n }\n curnode.id = nodeId++; // assign then increment\n }\n }\n\n // Ensure root has an id if not assigned during parsing\n if (root.id == null) root.id = nodeId;\n\n return root;\n}\n\n","import * as d3 from \"d3\";\nimport * as lw from \"../index.js\";\n\nexport default function drawPhylogeny(\n treeText,\n {\n layout = \"rect\", // rect/radial/unrooted\n width = 800,\n height = 800,\n margin = { top: 20, right: 300, bottom: 20, left: 50 },\n radialMargin = 80,\n strokeWidth = 1, // for the phylogeny branches\n radialMode = \"outer\", // \"outer\" (co-circular tips) or \"phylo\" (true terminals)\n tipLabels = true,\n showTooltips = true,\n tooltipFormatter = (d, rtt) =>\n `${d.thisLabel ?? \"(unnamed)\"}\\nroot→tip: ${(+rtt).toFixed(4)}`,\n hoverStroke = \"#1f77b4\",\n hoverWidth = 3,\n highlightTips = [], // array of tip labels or ids for static highlight (optional)\n highlightStroke = \"#e63946\",\n highlightWidth = 2.5\n } = {}\n) {\n\n // shared helpers\n const isNumber = (x) => typeof x === \"number\" && Number.isFinite(x);\n // Works for both radial (uses `r`) and rect (uses `x1`).\n // Falls back to summing branchLength up to the root if neither is present.\n function makeRootToTipGetter(byId, { prefer = \"auto\" } = {}) {\n return function rootToTip(tipId) {\n let n = byId.get(tipId);\n if (!n) return 0;\n\n // Prefer explicit cumulative fields if present\n if (prefer === \"r\" || (prefer === \"auto\" && \"r\" in n)) {\n return Number(n.r ?? 0);\n }\n if (prefer === \"x1\" || (prefer === \"auto\" && \"x1\" in n)) {\n return Number(n.x1 ?? 0);\n }\n\n // Fallback: sum branchLength up the ancestry\n let sum = 0;\n while (n && n.parentId != null) {\n sum += Number(n.branchLength || 0); // null/undefined → 0\n n = byId.get(n.parentId);\n }\n return sum;\n };\n }\n\n\n if (layout === \"rect\") {\n // RECTANGULAR LAYOUT\n const tree_df = lw.rectangleLayout(lw.readTree(treeText));\n const horizontal = tree_df.horizontal_lines;\n const vertical = tree_df.vertical_lines;\n const tips = horizontal.filter((d) => d.isTip);\n\n // indices & root→tip getter\n const byId = new Map(horizontal.map((d) => [d.thisId, d]));\n const tipById = new Map(tips.map((d) => [d.thisId, d]));\n const tipByLabel = new Map(tips.map((d) => [d.thisLabel, d]));\n const rootToTip = makeRootToTipGetter(byId, { prefer: \"x1\" });\n\n const maxY = d3.max(horizontal, (d) => d.y1);\n const minY = d3.min(horizontal, (d) => d.y1);\n const maxX = d3.max(horizontal, (d) => d.x1);\n\n const yScale = d3\n .scaleLinear()\n .domain([minY - 1, maxY + 1])\n .range([margin.top, height - margin.bottom]);\n\n const xScale = d3\n .scaleLinear()\n .domain([0, maxX])\n .range([margin.left, width - margin.right]);\n\n const svg = d3\n .create(\"svg\")\n .attr(\"width\", width)\n .attr(\"height\", height)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"font-size\", 10);\n\n const group = svg.append(\"g\");\n\n // layers for highlight/hover\n const staticLayer = svg.append(\"g\").attr(\"class\", \"phylo_static_highlight\");\n const hoverLayer = svg.append(\"g\").attr(\"class\", \"phylo_hover_highlight\");\n\n group\n .selectAll(\".hline\")\n .data(horizontal)\n .join(\"line\")\n .attr(\"x1\", (d) => xScale(d.x0))\n .attr(\"y1\", (d) => yScale(d.y0))\n .attr(\"x2\", (d) => xScale(d.x1))\n .attr(\"y2\", (d) => yScale(d.y1))\n .attr(\"stroke\", \"#555\")\n .attr(\"stroke-width\", strokeWidth);\n\n group\n .selectAll(\".vline\")\n .data(vertical)\n .join(\"line\")\n .attr(\"x1\", (d) => xScale(d.x0))\n .attr(\"y1\", (d) => yScale(d.y0))\n .attr(\"x2\", (d) => xScale(d.x1))\n .attr(\"y2\", (d) => yScale(d.y1))\n .attr(\"stroke\", \"#555\")\n .attr(\"stroke-width\", strokeWidth);\n\n // tip dots\n const tipDots = group\n .selectAll(\".tip-dot\")\n .data(tips)\n .join(\"circle\")\n .attr(\"cx\", (d) => xScale(d.x1))\n .attr(\"cy\", (d) => yScale(d.y1))\n .attr(\"r\", 2)\n .attr(\"fill\", \"black\");\n\n // tooltips for rect dots\n if (showTooltips) {\n tipDots\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n // interactive root→tip highlight (rect) on dot hover\n tipDots\n .on(\"mouseenter\", function(_event, d) {\n drawRectPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).attr(\"r\", 4);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).attr(\"r\", 2);\n });\n\n // labels\n if (tipLabels) {\n const labels = svg\n .append(\"g\")\n .attr(\"class\", \"phylo_labels\")\n .selectAll(\"text\")\n .data(tips)\n .join(\"text\")\n .attr(\"x\", (d) => xScale(d.x1) + 4)\n .attr(\"y\", (d) => yScale(d.y1))\n .attr(\"dy\", \"0.32em\")\n .attr(\"font-size\", 10)\n .text((d) => d.thisLabel?.replace(/_/g, \" \") ?? \"\");\n\n if (showTooltips) {\n labels\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n labels\n .on(\"mouseenter\", function(_event, d) {\n drawRectPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).attr(\"font-weight\", 600);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).attr(\"font-weight\", null);\n });\n }\n\n // static highlight by ids/labels\n if (highlightTips && highlightTips.length) {\n const chosen = new Set(\n [\n ...highlightTips.filter(isNumber).map((id) => tipById.get(id)),\n ...highlightTips\n .filter((x) => !isNumber(x))\n .map((lb) => tipByLabel.get(lb))\n ].filter(Boolean)\n );\n chosen.forEach((tip) => {\n drawRectPath(tip.thisId, staticLayer, highlightStroke, highlightWidth);\n });\n }\n\n // helper to draw root→tip for rect (both vertical+horizontal)\n function drawRectPath(tipId, layer, stroke, width) {\n layer.selectAll(\"*\").remove();\n let cur = byId.get(tipId);\n while (cur && cur.parentId != null) {\n const parent = byId.get(cur.parentId);\n if (!parent) break;\n\n // vertical at junction x0 from parent.y to child.y\n layer\n .append(\"line\")\n .attr(\"x1\", xScale(cur.x0))\n .attr(\"x2\", xScale(cur.x0))\n .attr(\"y1\", yScale(parent.y0))\n .attr(\"y2\", yScale(cur.y0))\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n\n // horizontal along child's y, from junction x0 to x1\n layer\n .append(\"line\")\n .attr(\"x1\", xScale(cur.x0))\n .attr(\"x2\", xScale(cur.x1))\n .attr(\"y1\", yScale(cur.y0))\n .attr(\"y2\", yScale(cur.y1))\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n\n cur = parent;\n }\n }\n\n return svg.node();\n } else if (layout === \"radial\") {\n // RADIAL LAYOUT\n if (width !== height) {\n throw new Error(\"width and height must be the same for radial layout\");\n }\n const parsedTree = lw.readTree(treeText);\n const rad = lw.radialLayout(parsedTree, {\n angleStrategy: \"fan\",\n arcsStyle: \"fan\"\n });\n\n // ===== MODE =====\n const TIP_MODE = radialMode; // \"phylo\" (shorten to original tips) or \"outer\" (project to one circle)\n const isOuter = TIP_MODE === \"outer\";\n if (TIP_MODE !== \"phylo\" && TIP_MODE !== \"outer\") {\n throw new Error(\"radialMode must be either 'phylo' or 'outer'\");\n }\n\n\n // visuals (0 = let spokes reach the dots)\n const DOT_R = 3;\n const END_CAP = 0;\n\n // ===== SCALES / BOUNDS =====\n const maxRadius = d3.max(rad.data, (d) => d.r) ?? 0;\n const scaleRadial = maxRadius + 2 * radialMargin;\n const w = width,\n h = height;\n const centerX = w / 2,\n centerY = h / 2;\n\n const xScaleRadial = d3\n .scaleLinear()\n .domain([-scaleRadial, scaleRadial])\n .range([0, w]);\n const yScaleRadial = d3\n .scaleLinear()\n .domain([-scaleRadial, scaleRadial])\n .range([h, 0]);\n\n const radiusPx = (r) => r * (w / (2 * scaleRadial));\n\n // ===== INDEXES / HELPERS =====\n const byId = new Map(rad.data.map((d) => [d.thisId, d]));\n const tips = rad.data.filter((d) => d.isTip);\n const tipMaxR = tips.length ? d3.max(tips, (d) => d.r) : 0;\n const rootToTip = makeRootToTipGetter(byId, { prefer: \"r\" });\n const tipById = new Map(tips.map((d) => [d.thisId, d])); // HILITE:\n const tipByLabel = new Map(tips.map((d) => [d.thisLabel, d])); // HILITE:\n\n // Robust child-id extractor (handles multiple shapes)\n function childIdOf(spoke) {\n // prefer explicit child id fields; fall back to thisId; last-ditch id1 (seen in some edge shapes)\n return spoke.childId ?? spoke.thisId ?? spoke.id1 ?? null;\n }\n\n // Shorten the *screen-space* end of a spoke by END_CAP px\n function shortenSpokePx(x0, y0, x1, y1) {\n const X0 = xScaleRadial(x0),\n Y0 = yScaleRadial(y0);\n const X1 = xScaleRadial(x1),\n Y1 = yScaleRadial(y1);\n const dx = X1 - X0,\n dy = Y1 - Y0;\n const len = Math.hypot(dx, dy) || 1;\n const t = Math.max(0, (len - END_CAP) / len);\n return { X0, Y0, X1s: X0 + dx * t, Y1s: Y0 + dy * t, len };\n }\n\n // ===== SVG ROOT =====\n const svg = d3\n .create(\"svg\")\n .attr(\"width\", w)\n .attr(\"height\", h)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"font-size\", 10);\n\n const group = svg.append(\"g\");\n\n // overlay groups (drawn on top)\n const staticLines = svg.append(\"g\").attr(\"class\", \"phylo_static_lines\"); // HILITE:\n const staticArcs = svg.append(\"g\").attr(\"class\", \"phylo_static_arcs\"); // HILITE:\n const hoverLines = svg.append(\"g\").attr(\"class\", \"phylo_hover_lines\"); // HILITE:\n const hoverArcs = svg.append(\"g\").attr(\"class\", \"phylo_hover_arcs\"); // HILITE:\n\n // ===== ARCS (parent circles) =====\n group\n .append(\"g\")\n .attr(\"class\", \"phylo_arcs\")\n .selectAll(\"path\")\n .data(rad.arcs)\n .join(\"path\")\n .attr(\"d\", (d) =>\n d.sweep == null\n ? lw.describeArc(\n centerX,\n centerY,\n radiusPx(d.radius),\n d.start,\n d.end\n )\n : lw.describeArcSweep(\n centerX,\n centerY,\n radiusPx(d.radius),\n d.start,\n d.end,\n d.sweep\n )\n )\n .attr(\"fill\", \"none\")\n .attr(\"stroke\", \"#777\")\n .attr(\"stroke-width\", strokeWidth);\n\n // ===== RADII (spokes) =====\n group\n .append(\"g\")\n .attr(\"class\", \"phylo_radii\")\n .selectAll(\"line\")\n .data(rad.radii)\n .join(\"line\")\n .each(function(s, _i) {\n // parent end (data space)\n const x0 = s.x0,\n y0 = s.y0;\n\n // child end (data space), shape-agnostic\n const cid = childIdOf(s);\n const node = cid != null ? byId.get(cid) : undefined;\n const isTipSpoke = !!(node && node.isTip);\n\n // default to the original child endpoint from the spoke record\n let x1 = s.x1,\n y1 = s.y1;\n\n // In \"outer\" mode, project only *tip* spokes to the common circle\n if (isOuter && isTipSpoke) {\n x1 = tipMaxR * Math.cos(node.angle);\n y1 = tipMaxR * Math.sin(node.angle);\n }\n\n // Shorten in screen space so the spoke doesn’t pierce the dot (END_CAP can be 0)\n const { X0, Y0, X1s, Y1s, _len } = shortenSpokePx(x0, y0, x1, y1);\n\n d3.select(this)\n .attr(\"x1\", X0)\n .attr(\"y1\", Y0)\n .attr(\"x2\", X1s)\n .attr(\"y2\", Y1s)\n .attr(\"stroke\", \"#777\")\n .attr(\"stroke-width\", strokeWidth);\n });\n\n // ===== TIP DOTS =====\n const tipDots = group\n .append(\"g\")\n .attr(\"class\", \"phylo_tip_dots\")\n .selectAll(\"circle\")\n .data(tips)\n .join(\"circle\")\n .each(function(d, _i) {\n // dot at original tip (align) or projected circle (outer)\n const x = isOuter ? tipMaxR * Math.cos(d.angle) : d.x;\n const y = isOuter ? tipMaxR * Math.sin(d.angle) : d.y;\n\n d3.select(this)\n .attr(\"cx\", xScaleRadial(x))\n .attr(\"cy\", yScaleRadial(y))\n .attr(\"r\", DOT_R)\n .attr(\"fill\", \"black\")\n .attr(\"stroke\", \"black\")\n .attr(\"stroke-width\", 1.5);\n });\n\n if (showTooltips) {\n tipDots\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n // maps for fast lookup on hover (childId → spoke / arc)\n const key = (x) => (typeof x === \"string\" ? +x : x);\n const spokeByChild = new Map(rad.radii.map(s => [key(s.childId ?? s.thisId ?? s.id1), s]));\n const arcByChild = new Map(rad.child_arcs.map(a => [key(a.childId), a]));\n\n\n // ===== LABELS =====\n // Labels — make them follow the tip position used by the current mode\n if (tipLabels) {\n const labels = group\n .append(\"g\")\n .attr(\"class\", \"phylo_labels\")\n .selectAll(\"g.label\")\n .data(tips)\n .join(\"g\")\n .attr(\"class\", \"label\")\n .attr(\"transform\", (d) => {\n // same tip position rule as dots/spokes:\n // - \"outer\": snap to common ring (tipMaxR)\n // - otherwise (e.g. \"align\"/\"phylo\"): true tip radius\n const r = isOuter ? tipMaxR : d.r;\n const x = r * Math.cos(d.angle);\n const y = r * Math.sin(d.angle);\n return `translate(${xScaleRadial(x)},${yScaleRadial(y)})`;\n })\n .each(function(d) {\n // rotate so text reads outward; flip when on the left side\n let angle = (-d.angle * 180) / Math.PI;\n let xoff = 10; // radial padding for text (px)\n let anchor = \"start\";\n if (d.angle > Math.PI / 2 && d.angle < (3 * Math.PI) / 2) {\n angle += 180;\n xoff *= -1;\n anchor = \"end\";\n }\n d3.select(this)\n .append(\"g\")\n .attr(\"transform\", `rotate(${angle})`)\n .append(\"text\")\n .attr(\"x\", xoff)\n .attr(\"alignment-baseline\", \"middle\")\n .attr(\"text-anchor\", anchor)\n .attr(\"font-size\", 10)\n .attr(\"fill\", \"black\")\n .text((d) => d.thisLabel?.replace(/_/g, \" \") ?? \"\");\n });\n\n if (showTooltips) {\n labels\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n // label hover\n labels\n .on(\"mouseenter\", function(_event, d) {\n drawRadialPath(d, hoverLines, hoverArcs, hoverStroke, hoverWidth);\n d3.select(this).select(\"text\").attr(\"font-weight\", 600);\n })\n .on(\"mouseleave\", function() {\n hoverLines.selectAll(\"*\").remove();\n hoverArcs.selectAll(\"*\").remove();\n d3.select(this).select(\"text\").attr(\"font-weight\", null);\n });\n }\n\n // draw (overlay) the root→tip path: spokes + arcs (half-arc per child)\n function drawRadialPath(\n target,\n lineLayer,\n arcLayer,\n stroke = \"#1f77b4\",\n width = 3\n ) {\n // target may be a tip node *or* a numeric tip id\n lineLayer.selectAll(\"*\").remove();\n arcLayer.selectAll(\"*\").remove();\n\n let cur = typeof target === \"number\" ? byId.get(target) : target;\n if (!cur) return;\n\n let first = true;\n while (cur && cur.parentId != null) {\n // ----- spoke (parent → child) -----\n const s = spokeByChild.get(key(cur.thisId));\n if (s) {\n const px = s.x0,\n py = s.y0;\n let cx = s.x1,\n cy = s.y1;\n if (isOuter && first && cur.isTip) {\n const r = tipMaxR;\n cx = r * Math.cos(cur.angle);\n cy = r * Math.sin(cur.angle);\n }\n const { X0, Y0, X1s, Y1s } = shortenSpokePx(px, py, cx, cy);\n lineLayer\n .append(\"line\")\n .attr(\"x1\", X0)\n .attr(\"y1\", Y0)\n .attr(\"x2\", X1s)\n .attr(\"y2\", Y1s)\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n }\n\n // ----- half-arc at parent radius (parent.angle → child.angle) -----\n const a = arcByChild.get(key(cur.thisId));\n function pathFromArcRecord(rec) {\n const R = radiusPx(rec.radius);\n return rec.sweep == null\n ? lw.describeArc(centerX, centerY, R, rec.start, rec.end)\n : lw.describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);\n }\n\n if (a) {\n arcLayer\n .append(\"path\")\n .attr(\"d\", pathFromArcRecord(a))\n .attr(\"fill\", \"none\")\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width);\n }\n\n first = false;\n cur = byId.get(cur.parentId);\n }\n }\n\n // tip dot hover\n tipDots\n .on(\"mouseenter\", function(_event, d) {\n drawRadialPath(d, hoverLines, hoverArcs, hoverStroke, hoverWidth);\n d3.select(this).attr(\"r\", DOT_R + 2);\n })\n .on(\"mouseleave\", function() {\n hoverLines.selectAll(\"*\").remove();\n hoverArcs.selectAll(\"*\").remove();\n d3.select(this).attr(\"r\", DOT_R);\n });\n\n if (highlightTips && highlightTips.length) {\n const chosen = new Set(\n [\n ...highlightTips.filter(isNumber).map((id) => tipById.get(id)),\n ...highlightTips\n .filter((x) => !isNumber(x))\n .map((lb) => tipByLabel.get(lb))\n ].filter(Boolean)\n );\n\n chosen.forEach((tip) => {\n drawRadialPath(\n tip.thisId,\n staticLines,\n staticArcs,\n highlightStroke,\n highlightWidth\n );\n });\n }\n\n return svg.node();\n } else if (layout === \"unrooted\") {\n // UNROOTED LAYOUT\n const parsedTree = lw.readTree(treeText);\n const unrootedPhylo = lw.unrooted(parsedTree);\n\n const w = width;\n const h = height;\n\n const xExtent = d3.extent(unrootedPhylo.data, (d) => d.x);\n const yExtent = d3.extent(unrootedPhylo.data, (d) => d.y);\n const maxX = Math.max(Math.abs(xExtent[0]), Math.abs(xExtent[1]));\n const maxY = Math.max(Math.abs(yExtent[0]), Math.abs(yExtent[1]));\n const maxRadius = Math.max(maxX, maxY);\n const scaleUnroot = maxRadius + 2 * radialMargin;\n\n const xScaleUnroot = d3\n .scaleLinear()\n .domain([-scaleUnroot, scaleUnroot])\n .range([0, w]);\n const yScaleUnroot = d3\n .scaleLinear()\n .domain([-scaleUnroot, scaleUnroot])\n .range([h, 0]);\n\n const svg = d3\n .create(\"svg\")\n .attr(\"width\", w)\n .attr(\"height\", h)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"font-size\", 10);\n\n const group = svg.append(\"g\");\n const staticLayer = svg.append(\"g\").attr(\"class\", \"phylo_static_highlight\");\n const hoverLayer = svg.append(\"g\").attr(\"class\", \"phylo_hover_highlight\");\n\n group\n .append(\"g\")\n .attr(\"class\", \"phylo_lines\")\n .selectAll(\"line\")\n .data(unrootedPhylo.edges)\n .join(\"line\")\n .attr(\"x1\", (d) => xScaleUnroot(d.x1))\n .attr(\"y1\", (d) => yScaleUnroot(d.y1))\n .attr(\"x2\", (d) => xScaleUnroot(d.x2))\n .attr(\"y2\", (d) => yScaleUnroot(d.y2))\n .attr(\"stroke-width\", strokeWidth)\n .attr(\"stroke\", \"#777\");\n\n const nodes = group\n .append(\"g\")\n .attr(\"class\", \"phylo_points\")\n .selectAll(\"circle\")\n .data(unrootedPhylo.data)\n .join(\"circle\")\n .attr(\"class\", \"dot\")\n .attr(\"r\", (d) => (d.isTip ? 4 : 0))\n .attr(\"cx\", (d) => xScaleUnroot(d.x))\n .attr(\"cy\", (d) => yScaleUnroot(d.y))\n .attr(\"stroke\", \"black\")\n .attr(\"stroke-width\", 2)\n .attr(\"fill\", (d) => (d.isTip ? \"black\" : \"white\"));\n\n const byId = new Map(unrootedPhylo.data.map((d) => [d.thisId, d]));\n const tipById = new Map(\n unrootedPhylo.data.filter((d) => d.isTip).map((d) => [d.thisId, d])\n );\n const tipByLabel = new Map(\n unrootedPhylo.data.filter((d) => d.isTip).map((d) => [d.thisLabel, d])\n );\n const rootToTip = makeRootToTipGetter(byId);\n\n if (showTooltips) {\n nodes\n .filter((d) => d.isTip)\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n const tipEdges = new Map();\n const nodesById = new Map(unrootedPhylo.data.map((d) => [d.thisId, d]));\n unrootedPhylo.edges.forEach((edge) => {\n const tipNode = nodesById.get(edge.id1);\n if (tipNode?.isTip) tipEdges.set(edge.id1, edge);\n });\n\n if (tipLabels) {\n const tipLabelsSel = group\n .append(\"g\")\n .attr(\"class\", \"phylo_labels\")\n .selectAll(\"g\")\n .data(unrootedPhylo.data.filter((d) => d.isTip))\n .join(\"g\")\n .attr(\"transform\", (d) => {\n const x = xScaleUnroot(d.x);\n const y = yScaleUnroot(d.y);\n return `translate(${x},${y})`;\n })\n .each(function(d) {\n const edge = tipEdges.get(d.thisId);\n if (!edge) return;\n\n const x1 = xScaleUnroot(edge.x1);\n const y1 = yScaleUnroot(edge.y1);\n const x2 = xScaleUnroot(edge.x2);\n const y2 = yScaleUnroot(edge.y2);\n\n const dx = x2 - x1;\n const dy = y2 - y1;\n let angle = (Math.atan2(dy, dx) * 180) / Math.PI;\n\n let xOffset = -10;\n let anchor = \"end\";\n if (angle > 90 || angle < -90) {\n angle += 180;\n anchor = \"start\";\n xOffset = 10;\n }\n\n d3.select(this)\n .append(\"g\")\n .attr(\"transform\", `rotate(${angle})`)\n .append(\"text\")\n .attr(\"x\", xOffset)\n .attr(\"alignment-baseline\", \"middle\")\n .attr(\"text-anchor\", anchor)\n .attr(\"font-size\", 10)\n .attr(\"fill\", \"black\")\n .text(d.thisLabel?.replace(/_/g, \" \") ?? \"\");\n });\n\n if (showTooltips) {\n tipLabelsSel\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n tipLabelsSel\n .on(\"mouseenter\", function(_event, d) {\n drawUnrootedPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).select(\"text\").attr(\"font-weight\", 600);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).select(\"text\").attr(\"font-weight\", null);\n });\n }\n\n nodes\n .filter((d) => d.isTip)\n .on(\"mouseenter\", function(_event, d) {\n drawUnrootedPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).attr(\"r\", 6);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).attr(\"r\", 4);\n });\n\n if (highlightTips && highlightTips.length) {\n const chosen = new Set(\n [\n ...highlightTips.filter(isNumber).map((id) => tipById.get(id)),\n ...highlightTips\n .filter((x) => !isNumber(x))\n .map((lb) => tipByLabel.get(lb))\n ].filter(Boolean)\n );\n chosen.forEach((tip) => {\n drawUnrootedPath(\n tip.thisId,\n staticLayer,\n highlightStroke,\n highlightWidth\n );\n });\n }\n\n function drawUnrootedPath(tipId, layer, stroke, width) {\n const edgeFromChild = new Map(unrootedPhylo.edges.map((e) => [e.id1, e]));\n layer.selectAll(\"*\").remove();\n let cur = byId.get(tipId);\n while (cur && cur.parentId != null) {\n const e = edgeFromChild.get(cur.thisId);\n if (e) {\n layer\n .append(\"line\")\n .attr(\"x1\", xScaleUnroot(e.x1))\n .attr(\"y1\", yScaleUnroot(e.y1))\n .attr(\"x2\", xScaleUnroot(e.x2))\n .attr(\"y2\", yScaleUnroot(e.y2))\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n }\n cur = byId.get(cur.parentId);\n }\n }\n\n return svg.node();\n } else {\n throw new Error(\n \"Unsupported layout type. Use 'rect', 'radial', or 'unrooted'.\"\n );\n }\n}\n"],"names":["polarToCartesian","cx","cy","r","t","x","Math","cos","y","sin","describeArc","radius","startAngle","endAngle","TAU","PI","norm","a0","a1","tmp","delta","p","largeArcFlag","p0","p1","describeArcSweep","sweep","preorder","node","list","push","i","children","length","fortify","tree","sort","df","parent","parentId","parentLabel","thisId","id","thisLabel","label","map","branchLength","isTip","angle","a","b","radialData","pd","byId","Map","d","kids","root","tipIds","dfs","c","get","ch","N","max","forEach","set","setInternalAngles","sx","sy","th","atan2","setR","r0","child","bl","fanAngles","opts","openAngleDeg","rotateDeg","gap","rot","step","setInternal","arr","ref","unwrapAround","reduce","s","v","radialLayout","angleStrategy","arcsStyle","angleMap","radii","find","segments","theta","r1","childId","x0","y0","x1","y1","getRadiiFromPd","data","getRadii","arcs","first","last","start","end","getArcsFan","childrenByParent","has","pid","A","k","aMin","aMax","direct","wrapped","span","isFinite","getArcs","child_arcs","midCCW","key","filter","parentRaw","Boolean","u","prev","cur","next","getChildArcsFan","getChildArcs","getHorizontal","idIndex","leafIds","n","tipSlot","setY","values","count","sum","value","mean","setX","xParent","row","_y","_x","_angle","item","rectangleLayout","vertical_lines","byParent","verticals","entries","yvals","min","heights","getVertical","child_vertical_lines","childVerticals","yc","yp","getChildVerticals","horizontal_lines","equalAngleLayout","post","ntips","thisnode","result","queue","curnode","shift","levelorder","numTips","lastStart","arc","unrooted","eq","edges","rectangular","rows","id1","x2","y2","id2","undefined","readTree","text","tokens","String","replace","split","nodeId","token","nodeinfo","startsWith","parseFloat","console","warn","treeText","layout","width","height","margin","top","right","bottom","left","radialMargin","strokeWidth","radialMode","tipLabels","showTooltips","tooltipFormatter","rtt","toFixed","hoverStroke","hoverWidth","highlightTips","highlightStroke","highlightWidth","isNumber","Number","makeRootToTipGetter","prefer","tipId","tree_df","lw.rectangleLayout","lw.readTree","horizontal","vertical","tips","tipById","tipByLabel","rootToTip","maxY","d3","minY","maxX","yScale","scaleLinear","domain","range","xScale","svg","create","attr","group","append","staticLayer","hoverLayer","selectAll","join","tipDots","on","_event","drawRectPath","select","this","remove","labels","Set","lb","tip","layer","stroke","Error","rad","lw.radialLayout","isOuter","DOT_R","END_CAP","scaleRadial","w","h","centerX","centerY","xScaleRadial","yScaleRadial","radiusPx","tipMaxR","shortenSpokePx","X0","Y0","dx","dy","len","hypot","X1s","Y1s","staticLines","staticArcs","hoverLines","hoverArcs","lw.describeArc","lw.describeArcSweep","each","_i","cid","spoke","isTipSpoke","spokeByChild","arcByChild","xoff","anchor","drawRadialPath","target","lineLayer","arcLayer","px","py","pathFromArcRecord","rec","R","unrootedPhylo","lw.unrooted","xExtent","extent","yExtent","abs","scaleUnroot","xScaleUnroot","yScaleUnroot","nodes","tipEdges","nodesById","edge","tipNode","tipLabelsSel","xOffset","drawUnrootedPath","edgeFromChild","e"],"mappings":"ghBACe,SAAAA,EAAUC,EAAIC,EAAIC,EAAGC,GAClC,MAAO,CAAEC,EAAGJ,EAAKE,EAAIG,KAAKC,IAAIH,GAAII,EAAGN,EAAKC,EAAIG,KAAKG,IAAIL,GACzD,CCIe,SAASM,EAAYT,EAAIC,EAAIS,EAAQC,EAAYC,GAC9D,MAAMC,EAAgB,EAAVR,KAAKS,GACXC,EAAQZ,IAAQA,EAAIU,EAAOA,GAAOA,EACxC,IAAIG,EAAKD,EAAKJ,GACVM,EAAKF,EAAKH,GAOd,IAHYI,EAAKC,EAAKJ,GAAOA,GADhBI,EAAKD,EAAKH,GAAOA,EAIhB,CACZ,MAAMK,EAAMF,EAAIA,EAAKC,EAAIA,EAAKC,CAChC,CAEA,MAAMC,GAASF,EAAKD,EAAKH,GAAOA,EAChC,GAAIM,EAAQ,KAAM,CAChB,MAAMC,EAAIrB,EAAiBC,EAAIC,EAAIS,EAAQM,GAC3C,MAAO,KAAKI,EAAEhB,KAAKgB,EAAEb,GACvB,CAEA,MAAMc,EAAeF,EAAQd,KAAKS,GAAK,EAAI,EAGrCQ,EAAKvB,EAAiBC,EAAIC,EAAIS,EAAQM,GACtCO,EAAKxB,EAAiBC,EAAIC,EAAIS,EAAQO,GAE5C,MAAO,KAAKK,EAAGlB,KAAKkB,EAAGf,OAAOG,KAAUA,OAAYW,OAA6BE,EAAGnB,KAAKmB,EAAGhB,GAC9F,CClCA,MAAMM,EAAgB,EAAVR,KAAKS,GACXC,EAAQZ,IAAQA,EAAIU,EAAOA,GAAOA,EAEzB,SAASW,EAAiBxB,EAAIC,EAAIC,EAAGc,EAAIC,EAAIQ,GAC1D,MAAMN,EAAsBJ,EAAJ,IAAVU,EAAmBR,EAAKD,EAAWA,EAAKC,GACtD,KAAMf,EAAI,IAAMiB,EAAQ,KAAM,MAAO,GACrC,MAAME,EAAeF,EAAQd,KAAKS,GAAK,EAAI,EAK3C,MAAO,KAHId,EAAKE,EAAIG,KAAKC,IAAIU,MAAUf,EAAKC,EAAIG,KAAKG,IAAIQ,QAG/Bd,KAAKA,OAAOmB,KAAgBI,KAF3CzB,EAAKE,EAAIG,KAAKC,IAAIW,MAAUhB,EAAKC,EAAIG,KAAKG,IAAIS,IAG3D,CCVO,SAASS,EAASC,EAAMC,EAAO,IACpCA,EAAKC,KAAKF,GACV,IAAK,IAAIG,EAAI,EAAGA,GAAKH,EAAKI,UAAUC,QAAU,GAAIF,IAChDF,EAAOF,EAASC,EAAKI,SAASD,GAAIF,GAEpC,OAAOA,CACT,CCDe,SAAAK,EAAUC,EAAMC,GAAO,GAClC,IAAIC,EAAK,GAET,IAAK,MAAMT,KAAQD,EAASQ,GACJ,OAAhBP,EAAKU,OACLD,EAAGP,KAAK,CACJS,SAAY,KACZC,YAAe,KACfC,OAAUb,EAAKc,GACfC,UAAaf,EAAKgB,MAClBZ,SAAYJ,EAAKI,SAASa,IAAIxC,GAAKA,EAAEqC,IACrCI,aAAgB,EAChBC,OAAS,EACT1C,EAAKuB,EAAKvB,EACVG,EAAKoB,EAAKpB,EACVwC,MAASpB,EAAKoB,QAIlBX,EAAGP,KAAK,CACJS,SAAYX,EAAKU,OAAOI,GACxBF,YAAeZ,EAAKU,OAAOM,MAC3BH,OAAUb,EAAKc,GACfC,UAAaf,EAAKgB,MAClBZ,SAAYJ,EAAKI,SAASa,IAAIxC,GAAKA,EAAEqC,IACrCI,aAAgBlB,EAAKkB,aACrBC,MAAkC,GAAxBnB,EAAKI,SAASC,OACxB5B,EAAKuB,EAAKvB,EACVG,EAAKoB,EAAKpB,EACVwC,MAASpB,EAAKoB,QAU1B,OALIZ,IACAC,EAAKA,EAAGD,KAAK,SAAUa,EAAGC,GACtB,OAAOD,EAAER,OAASS,EAAET,MACxB,KAGR,CCrCe,SAASU,EAAWvB,GACjC,MAAMd,EAAgB,EAAVR,KAAKS,GAGXqC,EAAKlB,EAAQN,GAAe,GAC5ByB,EAAO,IAAIC,IAAIF,EAAGP,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KACtCC,EAAO,IAAIF,IAAIF,EAAGP,IAAIU,GAAK,CAACA,EAAEd,OAAQc,EAAEvB,UAAY,MAG1D,IAAIyB,EAAO,KACX,IAAK,MAAMF,KAAKH,EACd,GAAkB,MAAdG,EAAEhB,SAAkB,CAAEkB,EAAOF,EAAEd,OAAQ,KAAO,CAIpD,MAAMiB,EAAS,IACf,SAAUC,EAAIjB,GACZ,MAAMkB,EAAIJ,EAAKK,IAAInB,IAAO,GAC1B,GAAiB,IAAbkB,EAAE3B,OAIN,IAAK,MAAM6B,KAAMF,EAAGD,EAAIG,QAHtBJ,EAAO5B,KAAKY,EAIf,CAPD,CAOGe,GAGH,MAAMM,EAAIzD,KAAK0D,IAAI,EAAGN,EAAOzB,QACvBe,EAAQ,IAAIM,IAClBI,EAAOO,QAAQ,CAACvB,EAAIX,KAClBiB,EAAMkB,IAAIxB,EAAKX,EAAIgC,EAAKjD,KAI1B,SAAUqD,EAAkBzB,GAC1B,MAAMkB,EAAIJ,EAAKK,IAAInB,IAAO,GAC1B,IAAK,MAAMoB,KAAMF,EAAGO,EAAkBL,GACtC,GAAIF,EAAE3B,OAAS,EAAG,CAChB,IAAImC,EAAK,EAAGC,EAAK,EACjB,IAAK,MAAMP,KAAMF,EAAG,CAClB,MAAMU,EAAKtB,EAAMa,IAAIC,GACrBM,GAAM9D,KAAKC,IAAI+D,GACfD,GAAM/D,KAAKG,IAAI6D,EACjB,CACAtB,EAAMkB,IAAIxB,GAASpC,KAAKiE,MAAMF,EAAID,GAzCZtD,EAAOA,GAAOA,EA0CtC,CACD,CAZD,CAYG2C,GAGH,MAAM9C,EAAS,IAAI2C,IACnB3C,EAAOuD,IAAIT,EAAM,GACjB,SAAUe,EAAK9B,GACb,MAAMkB,EAAIJ,EAAKK,IAAInB,IAAO,GACpB+B,EAAK9D,EAAOkD,IAAInB,IAAO,EAC7B,IAAK,MAAMoB,KAAMF,EAAG,CAClB,MAAMc,EAAQrB,EAAKQ,IAAIC,GACjBa,EAAKD,GAAO5B,cAAgB,EAClCnC,EAAOuD,IAAIJ,EAAIW,EAAKE,GACpBH,EAAKV,EACP,CACD,CATD,CASGL,GAGH,IAAK,MAAMF,KAAKH,EAAI,CAClB,MAAMkB,EAAKtB,EAAMa,IAAIN,EAAEd,SAAW,EAC5BtC,EAAKQ,EAAOkD,IAAIN,EAAEd,SAAW,EACnCc,EAAEP,MAAQsB,EACVf,EAAEpD,EAAIA,EACNoD,EAAElD,EAAIF,EAAIG,KAAKC,IAAI+D,GACnBf,EAAE/C,EAAIL,EAAIG,KAAKG,IAAI6D,EACrB,CAEA,OAAOlB,CACT,CCjFA,MAAMtC,EAAgB,EAAVR,KAAKS,GACXC,EAAQZ,IAAQA,EAAIU,EAAOA,GAAOA,EASzB,SAAS8D,EAAUxB,EAAIyB,EAAO,IAC3C,MAAMC,aAAEA,EAAe,EAACC,UAAEA,EAAY,GAAMF,EACtCG,EAAOF,EAAe,IAAOhE,EAC7BmE,EAAOF,EAAY,IAAOjE,EAGhC,IAAI2C,EAAO,KACX,MAAMD,EAAO,IAAIF,IAAIF,EAAGP,IAAKU,GAAM,CAACA,EAAEd,OAAQc,EAAEvB,UAAY,MAC5D,IAAK,MAAMuB,KAAKH,EAAI,GAAkB,MAAdG,EAAEhB,SAAkB,CAAEkB,EAAOF,EAAEd,OAAQ,KAAO,CAGtE,MAAMiB,EAAS,IACf,SAAUC,EAAIjB,GACZ,MAAMkB,EAAIJ,EAAKK,IAAInB,IAAO,GAC1B,GAAKkB,EAAE3B,OACP,IAAK,MAAM6B,KAAMF,EAAGD,EAAIG,QADPJ,EAAO5B,KAAKY,EAE9B,CAJD,CAIGe,GAEH,MAAMM,EAAIzD,KAAK0D,IAAI,EAAGN,EAAOzB,QAEvBiD,EAAOnB,EAAI,GADJjD,GAAO,EAAI,EAAIiD,GAAKiB,IACJjB,EAAI,GAAK,EAEhCf,EAAQ,IAAIM,IAclB,OAbAI,EAAOO,QAAQ,CAACvB,EAAIX,IAAMiB,EAAMkB,IAAIxB,EAAI1B,EAAKe,EAAImD,EAAOD,KAGxD,SAAUE,EAAYzC,GACpB,MAAMkB,EAAIJ,EAAKK,IAAInB,IAAO,GAC1B,IAAK,MAAMoB,KAAMF,EAAGuB,EAAYrB,GAChC,GAAIF,EAAE3B,OAAQ,CACZ,MAAMhB,EAAK+B,EAAMa,IAAID,EAAE,IACjBwB,EAAMxB,EAAEf,IAAKiB,GAtCzB,SAAsBuB,EAAKpC,GACzB,IAAI5C,EAAI4C,EACR,KAAO5C,EAAIgF,EAAM/E,KAAKS,IAAIV,GAAKS,EAC/B,KAAOT,EAAIgF,EAAM/E,KAAKS,IAAIV,GAAKS,EAC/B,OAAOT,CACT,CAiCgCiF,CAAarE,EAAI+B,EAAMa,IAAIC,KACrDd,EAAMkB,IAAIxB,EAAI1B,EAAKoE,EAAIG,OAAO,CAACC,EAAGC,IAAMD,EAAIC,EAAG,GAAKL,EAAInD,QAC1D,CACD,CARD,CAQGwB,GAEIT,CACT,CC/Be,SAAS0C,EAAa9D,EAAMiD,EAAO,IAChD,MAAMc,cAEJA,EAAgB,MAAKC,UACrBA,EAAY,MAAKd,aACjBA,EAAe,EAACC,UAChBA,EAAY,GACVF,EAGEzB,EAAKD,EAAWvB,GAEtB,GAAsB,QAAlB+D,EAAyB,CAE3B,MAAME,EAAWjB,EAAUxB,EAAI,CAAE0B,eAAcC,cAC/C,IAAK,MAAMxB,KAAKH,EAAI,CAClB,MAAMH,EAAI4C,EAAShC,IAAIN,EAAEd,QAChB,MAALQ,IACFM,EAAEP,MAAQC,EACVM,EAAElD,EAAIkD,EAAEpD,EAAIG,KAAKC,IAAI0C,GACrBM,EAAE/C,EAAI+C,EAAEpD,EAAIG,KAAKG,IAAIwC,GAEzB,CACF,CAGA,MAAM6C,EAA2B,QAAlBH,ECvCF,SAAwBvC,GACrC,MAAMC,EAAO,IAAIC,IAAIF,EAAGP,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KACtCE,EAAOL,EAAG2C,KAAKxC,GAAmB,MAAdA,EAAEhB,WAAmBE,OAEzCuD,EAAW,GACjB,IAAK,MAAMzC,KAAKH,EAAI,CAClB,GAAIG,EAAEd,SAAWgB,EAAM,SACvB,MAAMnB,EAASe,EAAKQ,IAAIN,EAAEhB,UAC1B,IAAKD,EAAQ,SAEb,MAAM2D,EAAQ1C,EAAEP,MACVyB,EAAKnC,EAAOnC,EAAG+F,EAAK3C,EAAEpD,EAE5B6F,EAASlE,KAAK,CACZS,SAAUD,EAAOG,OACjB0D,QAAS5C,EAAEd,OACX2D,GAAI3B,EAAKnE,KAAKC,IAAI0F,GAClBI,GAAI5B,EAAKnE,KAAKG,IAAIwF,GAClBK,GAAIJ,EAAK5F,KAAKC,IAAI0F,GAClBM,GAAIL,EAAK5F,KAAKG,IAAIwF,GAClBlD,QAASQ,EAAER,OAEf,CACA,OAAOiD,CACT,CDgBMQ,CAAepD,GEpCN,SAAkBxB,GAC/B,MAAM6E,EAAOtD,EAAWvB,GAClByB,EAAO,IAAIC,IAAImD,EAAK5D,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KACxCE,EAAOgD,EAAKV,KAAKxC,GAAmB,MAAdA,EAAEhB,WAAmBE,OAE3CuD,EAAW,GACjB,IAAK,MAAMzC,KAAKkD,EAAM,CACpB,GAAIlD,EAAEd,SAAWgB,EAAM,SACvB,MAAMnB,EAASe,EAAKQ,IAAIN,EAAEhB,UAC1B,IAAKD,EAAQ,SAEb,MAAM2D,EAAQ1C,EAAEP,MACVyB,EAAKnC,EAAOnC,EACZ+F,EAAK3C,EAAEpD,EAEb6F,EAASlE,KAAK,CACZS,SAAUD,EAAOG,OACjB0D,QAAS5C,EAAEd,OACX2D,GAAI3B,EAAKnE,KAAKC,IAAI0F,GAClBI,GAAI5B,EAAKnE,KAAKG,IAAIwF,GAClBK,GAAIJ,EAAK5F,KAAKC,IAAI0F,GAClBM,GAAIL,EAAK5F,KAAKG,IAAIwF,GAClBlD,QAASQ,EAAER,OAEf,CACA,OAAOiD,CACT,CFWMU,CAAS9E,GAGP+E,EAAsB,QAAdf,EGvCD,SAAoBxC,GACjC,MAAMC,EAAO,IAAIC,IAAIF,EAAGP,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KACtCoD,EAAO,GAEb,IAAK,MAAMtF,KAAK+B,EAAI,CAClB,MAAMQ,EAAIvC,EAAEW,UAAY,GACxB,GAAI4B,EAAE3B,OAAS,KAAOZ,EAAElB,EAAI,GAAI,SAEhC,MAAMyG,EAAQvD,EAAKQ,IAAID,EAAE,KAAKZ,MACxB6D,EAAQxD,EAAKQ,IAAID,EAAEA,EAAE3B,OAAS,KAAKe,MACzC,GAAa,MAAT4D,GAAyB,MAARC,EAAc,SAEnC,MAAMC,EAAQF,EACRG,EAAMF,EACNnF,EAAQqF,GAAOD,EAAQ,EAAI,EAEjCH,EAAK7E,KAAK,CACRS,SAAUlB,EAAEkB,SACZE,OAAQpB,EAAEoB,OACV9B,OAAQU,EAAElB,EACV2G,QACAC,MACArF,SAEJ,CACA,OAAOiF,CACT,CHcMK,CAAW5D,GI5CF,SAAiBA,GAC9B,MAAMtC,EAAgB,EAAVR,KAAKS,GAKJ,IAAIuC,IAAIF,EAAGP,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KAC5C,MAAM0D,EAAmB,IAAI3D,IAC7B,IAAIG,EAAO,KAEX,IAAK,MAAMF,KAAKH,EACI,MAAdG,EAAEhB,WAAkBkB,EAAOF,EAAEd,QAC5BwE,EAAiBC,IAAI3D,EAAEhB,WAAW0E,EAAiB/C,IAAIX,EAAEhB,SAAU,IACxE0E,EAAiBpD,IAAIN,EAAEhB,UAAUT,KAAKyB,GAGxC,MAAMoD,EAAO,GAEb,IAAK,MAAMrE,KAAUc,EAAI,CACvB,MAAM+D,EAAM7E,EAAOG,OACnB,GAAI0E,IAAQ1D,EAAM,SAClB,MAAMD,EAAOyD,EAAiBpD,IAAIsD,IAAQ,GAC1C,GAAI3D,EAAKvB,OAAS,EAAG,SAGrB,MAAMmF,EAAI5D,EAAKX,IAAIwE,IAAUA,EAAErE,MAvBPlC,EAAOA,GAAOA,GAuBCsB,KAAK,CAACa,EAAGC,IAAMD,EAAIC,GACpDoE,EAAOF,EAAE,GAAIG,EAAOH,EAAEA,EAAEnF,OAAS,GAGjCuF,EAASD,EAAOD,EAChBG,EAAU3G,EAAM0G,EAGtB,IAAIV,EAAOC,EAAKW,EACZF,GAAUC,GACZX,EAAQQ,EACRP,EAAMQ,EACNG,EAAOF,IAGPV,EAAQS,EACRR,EAAMO,EACNI,EAAOD,GAGLC,EA1CM,OA0CSC,SAASrF,EAAOnC,IAAMmC,EAAOnC,GAAK,GAErDwG,EAAK7E,KAAK,CACRgF,QACAC,MACApG,OAAQ2B,EAAOnC,EACfsC,OAAQ0E,EACR5E,SAAUD,EAAOC,UAErB,CAEA,OAAOoE,CACT,CJZMiB,CAAQxE,GAGZ,IAAIyE,EAAa,GAOjB,OALEA,EADqB,QAAnBhD,EAAKe,UKvDI,SAAyBxC,GACtC,MAAMtC,EAAgB,EAAVR,KAAKS,GACXC,EAAQZ,IAAQA,EAAIU,EAAOA,GAAOA,EAGxC,SAASgH,EAAO7E,EAAGC,GAEjB,OAAOlC,EAAKiC,GADDC,EAAID,EAAInC,GAAOA,EACN,EACtB,CAEA,MAAMiH,EAAO1H,GAAoB,iBAANA,GAAkBA,EAAIA,EAE3CgD,EAAO,IAAIC,IAAIF,EAAGP,IAAIU,GAAK,CAACwE,EAAIxE,EAAEd,QAASc,KAC3C0D,EAAmB,IAAI3D,IAC3BF,EAAGP,IAAIU,GAAK,CACVwE,EAAIxE,EAAEd,SAELc,EAAEvB,UAAY,IACZa,IAAIiB,GAAqB,iBAAPA,EAAkBA,EAAGrB,OAASqB,GAChDjB,IAAIkF,GACJC,OAAOtF,GAAMW,EAAK6D,IAAIxE,OAIvBmF,EAAa,GAEnB,IAAK,MAAMI,KAAa7E,EAAI,CAC1B,MAAM+D,EAAMY,EAAIE,EAAUxF,QACpBe,EAAOyD,EAAiBpD,IAAIsD,IAAQ,GAC1C,GAAI3D,EAAKvB,OAAS,EAAG,SAGrB,MAAMmF,EAAI5D,EACPX,IAAIH,IACH,MAAMd,EAAOyB,EAAKQ,IAAInB,GACtB,OAAOd,EAAO,CAAEc,KAAIO,EAAGjC,EAAKY,EAAKoB,QAAW,OAE7CgF,OAAOE,SACP9F,KAAK,CAAC+F,EAAG1C,IAAM0C,EAAElF,EAAIwC,EAAExC,GAEpBc,EAAIqD,EAAEnF,OACZ,GAAI8B,EAAI,EAAG,SAEX,MAAMzB,EAASe,EAAKQ,IAAIsD,GAClBxG,EAAS2B,GAAQnC,EACvB,GAAMQ,EAAS,EAEf,IAAK,IAAIoB,EAAI,EAAGA,EAAIgC,EAAGhC,IAAK,CAC1B,MAAMqG,EAAOhB,GAAGrF,EAAI,EAAIgC,GAAKA,GACvBsE,EAAOjB,EAAErF,GACTuG,EAAOlB,GAAGrF,EAAI,GAAKgC,GAGnB+C,EAAQgB,EAAOM,EAAKnF,EAAGoF,EAAIpF,GAC3B8D,EAAQe,EAAOO,EAAIpF,EAAGqF,EAAKrF,GAEjC4E,EAAW/F,KAAK,CACdS,SAAU4E,EACVhB,QAAUkC,EAAI3F,GACd/B,SACAmG,QACAC,MACArF,MAAO,GAEX,CACF,CAEA,OAAOmG,CACT,CLZiBU,CAAgBnF,GM9ClB,SAAsBA,GACnC,MAAMC,EAAO,IAAIC,IAAIF,EAAGP,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KACtCoD,EAAO,GAEb,IAAK,MAAMjC,KAAStB,EAAI,CACtB,GAAsB,MAAlBsB,EAAMnC,SAAkB,SAC5B,MAAMD,EAASe,EAAKQ,IAAIa,EAAMnC,UACzBD,GAELqE,EAAK7E,KAAK,CACRS,SAAUD,EAAOG,OACjB0D,QAASzB,EAAMjC,OACf9B,OAAQ2B,EAAOnC,EACf2G,MAAOxE,EAAOU,MACd+D,IAAKrC,EAAM1B,OAEf,CAEA,OAAO2D,CACT,CN6BiB6B,CAAapF,GAGrB,CAAEqD,KAAMrD,EAAI0C,QAAOa,OAAMkB,aAClC,COpDe,SAASY,EAAc7G,GACpC,MAAMwB,EAAKlB,EAAQN,GAGb8G,EAAU,IAAIpF,IAAIF,EAAGP,IAAI,CAACU,EAAGxB,IAAM,CAACwB,EAAEd,OAAQV,KAG9C4G,EAAU,IAChB,SAAUhF,EAAIiF,GACPA,EAAE5G,UAAkC,IAAtB4G,EAAE5G,SAASC,OAC9B2G,EAAE5G,SAASiC,QAAQN,GAD2BgF,EAAQ7G,KAAK8G,EAAElG,GAE9D,CAHD,CAGGd,GAGH,MAAMiH,EAAU,IAAIvF,IAAIqF,EAAQ9F,IAAI,CAACH,EAAIX,IAAM,CAACW,EAAIX,EAAI,KA2BxD,OAxBA,SAAU+G,EAAKF,GACb,MAAM7G,EAAI2G,EAAQ7E,IAAI+E,EAAElG,IACxB,IAAKkG,EAAE5G,UAAkC,IAAtB4G,EAAE5G,SAASC,OAAc,CAC1C,MAAMzB,EAAIqI,EAAQhF,IAAI+E,EAAElG,IAExB,OADAU,EAAGrB,GAAGsE,GAAK7F,EAAG4C,EAAGrB,GAAGwE,GAAK/F,EAClBA,CACT,CACA,MACMA,EC7BK,SAAUuI,GACrB,IAAIC,EAAQ,EACRC,EAAM,EAEN,IAAK,IAAIC,KAASH,EACD,MAATG,IAAkBA,GAASA,IAAUA,MACnCF,EAAOC,GAAOC,GAW5B,GAAIF,EAAO,OAAOC,EAAMD,CAC5B,CDWcG,CADCP,EAAE5G,SAASa,IAAIiG,IAG1B,OADA1F,EAAGrB,GAAGsE,GAAK7F,EAAG4C,EAAGrB,GAAGwE,GAAK/F,EAClBA,CACR,CAXD,CAWGoB,GAGH,SAAUwH,EAAKR,EAAGS,GAChB,MAAMtH,EAAI2G,EAAQ7E,IAAI+E,EAAElG,IAElB0D,EAAKiD,GAAW,EAChB/C,EAAKF,GAFAhD,EAAGrB,GAAGe,cAAgB,GAGjCM,EAAGrB,GAAGqE,GAAKA,EAAIhD,EAAGrB,GAAGuE,GAAKA,EACtBsC,EAAE5G,UAAY4G,EAAE5G,SAASC,QAAQ2G,EAAE5G,SAASiC,QAAQL,GAAKwF,EAAKxF,EAAG0C,GACtE,CAPD,CAOG1E,EAAM,GAGFwB,EAAGP,IAAKyG,IACb,MAAQ9I,EAAG+I,EAAIlJ,EAAGmJ,EAAIxG,MAAOyG,KAAWC,GAASJ,EACjD,OAAOI,GAEX,CE3Ce,SAASC,EAAgB/H,GACtC,MAAM6E,EAAOgC,EAAc7G,GACrBgI,ECZO,SAAqBhI,GAClC,MAAM6E,EAAOgC,EAAc7G,GAGrBiI,EAAW,IAAIvG,IACrB,IAAK,MAAMgG,KAAO7C,EAAM,CACtB,GAAoB,MAAhB6C,EAAI/G,SAAkB,SAC1B,MAAMU,EAAI4G,EAAShG,IAAIyF,EAAI/G,UACvBU,EAAGA,EAAEnB,KAAKwH,GAAWO,EAAS3F,IAAIoF,EAAI/G,SAAU,CAAC+G,GACvD,CAEA,MAAMQ,EAAY,GAClB,IAAK,MAAOvH,EAAUiB,KAASqG,EAASE,UAAW,CACjD,IAAKvG,EAAKvB,OAAQ,SAElB,MAAM+H,EAAQxG,EAAKX,IAAIU,GAAKA,EAAE8C,IACxBA,EAAK/F,KAAK2J,OAAOD,GACjBzD,EAAKjG,KAAK0D,OAAOgG,GAEjB3J,EAAImD,EAAK,GAAG4C,GAElB0D,EAAUhI,KAAK,CACbS,WACA6D,GAAI/F,EACJiG,GAAIjG,EACJgG,KACAE,KACA2D,QAAS3D,EAAKF,GAElB,CAEA,OAAOyD,CACT,CDpByBK,CAAYvI,GAC7BwI,EECO,SAA2BxI,GACxC,MAAM6E,EAAOgC,EAAc7G,GAGrByB,EAAO,IAAIC,IAAImD,EAAK5D,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KAExC8G,EAAiB,GAEvB,IAAK,MAAM9G,KAAKkD,EAAM,CACpB,GAAkB,MAAdlD,EAAEhB,SAAkB,SACxB,MAAMD,EAASe,EAAKQ,IAAIN,EAAEhB,UAC1B,IAAKD,EAAQ,SAEb,MAAMjC,EAAIkD,EAAE6C,GACNkE,EAAK/G,EAAE8C,GACPkE,EAAKjI,EAAO+D,GACZA,EAAK/F,KAAK2J,IAAIK,EAAIC,GAClBhE,EAAKjG,KAAK0D,IAAIsG,EAAIC,GAExBF,EAAevI,KAAK,CAClBS,SAAUgB,EAAEhB,SACZ4D,QAAS5C,EAAEd,OACXpC,IACAgG,KACAE,MAEJ,CAEA,OAAO8D,CACT,CF9B+BG,CAAkB5I,GAGlC,IAAI0B,IAAImD,EAAK5D,IAAIU,GAAK,CAACA,EAAEd,OAAQc,KAC9C,MAAMkH,EAAmBhE,EACtBuB,OAAOzE,GAAmB,MAAdA,EAAEhB,UACdM,IAAIU,IAAC,CACJhB,SAAUgB,EAAEhB,SACZ4D,QAAS5C,EAAEd,OACXA,OAAQc,EAAEd,OACVE,UAAWY,EAAEZ,UACbI,MAAOQ,EAAER,MACTqD,GAAI7C,EAAE6C,GACNE,GAAI/C,EAAE+C,GACND,GAAI9C,EAAE8C,GACNE,GAAIhD,EAAE8C,MAGV,MAAO,CAAEI,OAAMmD,iBAAgBQ,uBAAsBK,mBACvD,CGZA,SAASC,EAAiB9I,GACJ,OAAhBA,EAAKU,SAbT,SAAUqI,EAAK/B,GACb,IAAKA,EAAE5G,UAAkC,IAAtB4G,EAAE5G,SAASC,OACf,OAAb2G,EAAEgC,MAAQ,EAAU,EAEtB,IAAI3B,EAAM,EACV,IAAK,MAAMrF,KAAKgF,EAAE5G,SAAUiH,GAAO0B,EAAK/G,GAExC,OADAgF,EAAEgC,MAAQ3B,EACHA,CACR,CARD,CAcoBrH,GAClBA,EAAKkF,MAAQ,EACblF,EAAKmF,IAAM,EACXnF,EAAKoB,MAAQ,EACbpB,EAAKgJ,MCRM,SAASC,GACtB,IAAIC,EAAS,EACb,IAAK,MAAMlJ,KAjBb,SAAoB6B,GAClB,MAAMsH,EAAQ,CAACtH,GAAOqH,EAAS,GAC/B,KAAOC,EAAM9I,QAAQ,CACnB,MAAM+I,EAAUD,EAAME,QACtBH,EAAOhJ,KAAKkJ,GACZ,IAAK,MAAMtG,KAASsG,EAAQhJ,SAAU+I,EAAMjJ,KAAK4C,EACnD,CACA,OAAOoG,CACT,CASqBI,CAAWL,GACA,GAAxBjJ,EAAKI,SAASC,QAAa6I,IAEjC,QACF,CDEiBK,CAAQvJ,GACrBA,EAAKvB,EAAI,EACTuB,EAAKpB,EAAI,GAGX,IAAI4K,EAAYxJ,EAAKkF,MAErB,IAAK,IAAI/E,EAAI,EAAGA,EAAIH,EAAKI,SAASC,OAAQF,IAAK,CAC7C,MAAM2C,EAAQ9C,EAAKI,SAASD,GACtBsJ,GAAOzJ,EAAKmF,IAAMnF,EAAKkF,QAAUpC,EAAMkG,MAAQhJ,EAAKgJ,OAE1DlG,EAAMoC,MAAQsE,EACd1G,EAAMqC,IAAMqE,EAAYC,EAGxB3G,EAAM1B,MAAQ0B,EAAMoC,OAASpC,EAAMqC,IAAMrC,EAAMoC,OAAS,EACxDsE,EAAY1G,EAAMqC,IAGlB,MAAMd,EAAQvB,EAAM1B,MAAQ1C,KAAKS,GAC3B4D,EAAMD,EAAM5B,cAAgB,EAClC4B,EAAMrE,EAAIuB,EAAKvB,EAAIsE,EAAKrE,KAAKG,IAAIwF,GACjCvB,EAAMlE,EAAIoB,EAAKpB,EAAImE,EAAKrE,KAAKC,IAAI0F,GAEjCyE,EAAiBhG,EACnB,CAEA,OAAO9C,CACT,CEhDe,SAAA0J,EAAU1J,GACvB,IAAI6E,EAAO,CAAA,EAEP8E,EAAKrJ,EAAQwI,EAAiB9I,IAKlC,OAJA6E,EAAKA,KAAO8E,EAEZ9E,EAAK+E,MCRQ,SAAenJ,EAAIoJ,GAAc,GAC9C,MAAMC,EAAO,IAAIrJ,GAAID,KAAK,CAACa,EAAGC,IAAMD,EAAER,OAASS,EAAET,QAC3CY,EAAO,IAAIC,IAAIoI,EAAK7I,IAAK1C,GAAM,CAACA,EAAEsC,OAAQtC,KAC1C2K,EAAS,GAEf,IAAK,MAAMxB,KAAOoC,EAAM,CACtB,GAAoB,MAAhBpC,EAAI/G,SAAkB,SAC1B,MAAMD,EAASe,EAAKQ,IAAIyF,EAAI/G,UACvBD,IAEDmJ,GACFX,EAAOhJ,KAAK,CAAEwE,GAAIgD,EAAIjJ,EAAGkG,GAAI+C,EAAI9I,EAAGmL,IAAKrC,EAAI7G,OAAQmJ,GAAItJ,EAAOjC,EAAGwL,GAAIvC,EAAI9I,EAAGsL,SAAKC,IACnFjB,EAAOhJ,KAAK,CAAEwE,GAAIhE,EAAOjC,EAAGkG,GAAI+C,EAAI9I,EAAGmL,SAAKI,EAAWH,GAAItJ,EAAOjC,EAAGwL,GAAIvJ,EAAO9B,EAAGsL,IAAKxC,EAAI/G,YAE5FuI,EAAOhJ,KAAK,CAAEwE,GAAIgD,EAAIjJ,EAAGkG,GAAI+C,EAAI9I,EAAGmL,IAAKrC,EAAI7G,OAAQmJ,GAAItJ,EAAOjC,EAAGwL,GAAIvJ,EAAO9B,EAAGsL,IAAKxC,EAAI/G,WAE9F,CACA,OAAOuI,CACT,CDVeU,CAAMD,GAEZ9E,CACT,CERe,SAASuF,EAASC,GAI/B,MAAMC,GAFND,EAAOE,OAAOF,GAAMG,QAAQ,OAAQ,KAEhBC,MAAM,eACpB5I,EAAO,CAAEnB,OAAQ,KAAMN,SAAU,IACvC,IAAIgJ,EAAUvH,EACV6I,EAAS,EAEb,IAAK,MAAMC,KAASL,EAClB,GAAKK,GAAmB,MAAVA,EAEd,GAAc,MAAVA,EAAe,CACjB,MAAM7H,EAAQ,CAAEpC,OAAQ0I,EAAShJ,SAAU,IAC3CgJ,EAAQhJ,SAASF,KAAK4C,GACtBsG,EAAUtG,CACZ,MAAO,GAAc,MAAV6H,EAAe,CAExBvB,EAAUA,EAAQ1I,OAClB,MAAMoC,EAAQ,CAAEpC,OAAQ0I,EAAShJ,SAAU,IAC3CgJ,EAAQhJ,SAASF,KAAK4C,GACtBsG,EAAUtG,CACZ,MAAO,GAAc,MAAV6H,GAGT,GADAvB,EAAUA,EAAQ1I,OACF,OAAZ0I,EAAkB,UACjB,CAEL,MAAMwB,EAAWD,EAAMF,MAAM,KACL,IAApBG,EAASvK,OACPsK,EAAME,WAAW,MACnBzB,EAAQpI,MAAQ,GAChBoI,EAAQlI,aAAe4J,WAAWF,EAAS,MAE3CxB,EAAQpI,MAAQ4J,EAAS,GACzBxB,EAAQlI,aAAe,MAEI,IAApB0J,EAASvK,QAClB+I,EAAQpI,MAAQ4J,EAAS,GACzBxB,EAAQlI,aAAe4J,WAAWF,EAAS,MAE3CG,QAAQC,KAAKL,EAAO,gDACpBvB,EAAQpI,MAAQ4J,EAAS,IAAM,GAC/BxB,EAAQlI,aAAe4J,WAAWF,EAASA,EAASvK,OAAS,KAE/D+I,EAAQtI,GAAK4J,GACf,CAMF,OAFe,MAAX7I,EAAKf,KAAYe,EAAKf,GAAK4J,GAExB7I,CACT,QC3De,SACboJ,GACAC,OACEA,EAAS,OAAMC,MACfA,EAAQ,IAAGC,OACXA,EAAS,IAAGC,OACZA,EAAS,CAAEC,IAAK,GAAIC,MAAO,IAAKC,OAAQ,GAAIC,KAAM,IAAIC,aACtDA,EAAe,GAAEC,YACjBA,EAAc,EAACC,WACfA,EAAa,QAAOC,UACpBA,GAAY,EAAIC,aAChBA,GAAe,EAAIC,iBACnBA,EAAmB,CAACpK,EAAGqK,IACrB,GAAGrK,EAAEZ,WAAa,4BAA4BiL,GAAKC,QAAQ,KAAIC,YACjEA,EAAc,UAASC,WACvBA,EAAa,EAACC,cACdA,EAAgB,GAAEC,gBAClBA,EAAkB,UAASC,eAC3BA,EAAiB,KACf,CAAA,GAIJ,MAAMC,EAAY9N,GAAmB,iBAANA,GAAkB+N,OAAOzG,SAAStH,GAGjE,SAASgO,EAAoBhL,GAAMiL,OAAEA,EAAS,QAAW,CAAA,GACvD,OAAO,SAAmBC,GACxB,IAAI3F,EAAIvF,EAAKQ,IAAI0K,GACjB,IAAK3F,EAAG,OAAO,EAGf,GAAe,MAAX0F,GAA8B,SAAXA,GAAqB,MAAO1F,EACjD,OAAOwF,OAAOxF,EAAEzI,GAAK,GAEvB,GAAe,OAAXmO,GAA+B,SAAXA,GAAqB,OAAQ1F,EACnD,OAAOwF,OAAOxF,EAAEtC,IAAM,GAIxB,IAAI2C,EAAM,EACV,KAAOL,GAAmB,MAAdA,EAAErG,UACZ0G,GAAOmF,OAAOxF,EAAE9F,cAAgB,GAChC8F,EAAIvF,EAAKQ,IAAI+E,EAAErG,UAEjB,OAAO0G,CACT,CACF,CAGA,GAAe,SAAX6D,EAAmB,CAErB,MAAM0B,EAAUC,EAAmBC,EAAY7B,IACzC8B,EAAaH,EAAQ/D,iBACrBmE,EAAWJ,EAAQ5E,eACnBiF,EAAOF,EAAW3G,OAAQzE,GAAMA,EAAER,OAGlCM,EAAO,IAAIC,IAAIqL,EAAW9L,IAAKU,GAAM,CAACA,EAAEd,OAAQc,KAChDuL,EAAU,IAAIxL,IAAIuL,EAAKhM,IAAKU,GAAM,CAACA,EAAEd,OAAQc,KAC7CwL,EAAa,IAAIzL,IAAIuL,EAAKhM,IAAKU,GAAM,CAACA,EAAEZ,UAAWY,KACnDyL,EAAYX,EAAoBhL,EAAM,CAAEiL,OAAQ,OAEhDW,EAAOC,EAAGlL,IAAI2K,EAAapL,GAAMA,EAAEgD,IACnC4I,EAAOD,EAAGjF,IAAI0E,EAAapL,GAAMA,EAAEgD,IACnC6I,EAAOF,EAAGlL,IAAI2K,EAAapL,GAAMA,EAAE+C,IAEnC+I,EAASH,EACZI,cACAC,OAAO,CAACJ,EAAO,EAAGF,EAAO,IACzBO,MAAM,CAACvC,EAAOC,IAAKF,EAASC,EAAOG,SAEhCqC,EAASP,EACZI,cACAC,OAAO,CAAC,EAAGH,IACXI,MAAM,CAACvC,EAAOI,KAAMN,EAAQE,EAAOE,QAEhCuC,EAAMR,EACTS,OAAO,OACPC,KAAK,QAAS7C,GACd6C,KAAK,SAAU5C,GACf4C,KAAK,cAAe,cACpBA,KAAK,YAAa,IAEfC,EAAQH,EAAII,OAAO,KAGnBC,EAAcL,EAAII,OAAO,KAAKF,KAAK,QAAS,0BAC5CI,EAAaN,EAAII,OAAO,KAAKF,KAAK,QAAS,yBAEjDC,EACGI,UAAU,UACVxJ,KAAKkI,GACLuB,KAAK,QACLN,KAAK,KAAOrM,GAAMkM,EAAOlM,EAAE6C,KAC3BwJ,KAAK,KAAOrM,GAAM8L,EAAO9L,EAAE8C,KAC3BuJ,KAAK,KAAOrM,GAAMkM,EAAOlM,EAAE+C,KAC3BsJ,KAAK,KAAOrM,GAAM8L,EAAO9L,EAAEgD,KAC3BqJ,KAAK,SAAU,QACfA,KAAK,eAAgBrC,GAExBsC,EACGI,UAAU,UACVxJ,KAAKmI,GACLsB,KAAK,QACLN,KAAK,KAAOrM,GAAMkM,EAAOlM,EAAE6C,KAC3BwJ,KAAK,KAAOrM,GAAM8L,EAAO9L,EAAE8C,KAC3BuJ,KAAK,KAAOrM,GAAMkM,EAAOlM,EAAE+C,KAC3BsJ,KAAK,KAAOrM,GAAM8L,EAAO9L,EAAEgD,KAC3BqJ,KAAK,SAAU,QACfA,KAAK,eAAgBrC,GAGxB,MAAM4C,EAAUN,EACbI,UAAU,YACVxJ,KAAKoI,GACLqB,KAAK,UACLN,KAAK,KAAOrM,GAAMkM,EAAOlM,EAAE+C,KAC3BsJ,KAAK,KAAOrM,GAAM8L,EAAO9L,EAAEgD,KAC3BqJ,KAAK,IAAK,GACVA,KAAK,OAAQ,SAqBhB,GAlBIlC,GACFyC,EACGL,OAAO,SACP7D,KAAM1I,GAAMoK,EAAiBpK,EAAGyL,EAAUzL,EAAEd,UAIjD0N,EACGC,GAAG,aAAc,SAASC,EAAQ9M,GACjC+M,EAAa/M,EAAEd,OAAQuN,EAAYlC,EAAaC,GAChDmB,EAAGqB,OAAOC,MAAMZ,KAAK,IAAK,EAC5B,GACCQ,GAAG,aAAc,WAChBJ,EAAWC,UAAU,KAAKQ,SAC1BvB,EAAGqB,OAAOC,MAAMZ,KAAK,IAAK,EAC5B,GAGEnC,EAAW,CACb,MAAMiD,EAAShB,EACZI,OAAO,KACPF,KAAK,QAAS,gBACdK,UAAU,QACVxJ,KAAKoI,GACLqB,KAAK,QACLN,KAAK,IAAMrM,GAAMkM,EAAOlM,EAAE+C,IAAM,GAChCsJ,KAAK,IAAMrM,GAAM8L,EAAO9L,EAAEgD,KAC1BqJ,KAAK,KAAM,UACXA,KAAK,YAAa,IAClB3D,KAAM1I,GAAMA,EAAEZ,WAAWyJ,QAAQ,KAAM,MAAQ,IAE9CsB,GACFgD,EACGZ,OAAO,SACP7D,KAAM1I,GAAMoK,EAAiBpK,EAAGyL,EAAUzL,EAAEd,UAGjDiO,EACGN,GAAG,aAAc,SAASC,EAAQ9M,GACjC+M,EAAa/M,EAAEd,OAAQuN,EAAYlC,EAAaC,GAChDmB,EAAGqB,OAAOC,MAAMZ,KAAK,cAAe,IACtC,GACCQ,GAAG,aAAc,WAChBJ,EAAWC,UAAU,KAAKQ,SAC1BvB,EAAGqB,OAAOC,MAAMZ,KAAK,cAAe,KACtC,EACJ,CAGA,GAAI5B,GAAiBA,EAAc/L,OAAQ,CAC1B,IAAI0O,IACjB,IACK3C,EAAchG,OAAOmG,GAAUtL,IAAKH,GAAOoM,EAAQjL,IAAInB,OACvDsL,EACAhG,OAAQ3H,IAAO8N,EAAS9N,IACxBwC,IAAK+N,GAAO7B,EAAWlL,IAAI+M,KAC9B5I,OAAOE,UAEJjE,QAAS4M,IACdP,EAAaO,EAAIpO,OAAQsN,EAAa9B,EAAiBC,IAE3D,CAGA,SAASoC,EAAa/B,EAAOuC,EAAOC,EAAQhE,GAC1C+D,EAAMb,UAAU,KAAKQ,SACrB,IAAIpI,EAAMhF,EAAKQ,IAAI0K,GACnB,KAAOlG,GAAuB,MAAhBA,EAAI9F,UAAkB,CAClC,MAAMD,EAASe,EAAKQ,IAAIwE,EAAI9F,UAC5B,IAAKD,EAAQ,MAGbwO,EACGhB,OAAO,QACPF,KAAK,KAAMH,EAAOpH,EAAIjC,KACtBwJ,KAAK,KAAMH,EAAOpH,EAAIjC,KACtBwJ,KAAK,KAAMP,EAAO/M,EAAO+D,KACzBuJ,KAAK,KAAMP,EAAOhH,EAAIhC,KACtBuJ,KAAK,SAAUmB,GACfnB,KAAK,eAAgB7C,GACrB6C,KAAK,iBAAkB,SAG1BkB,EACGhB,OAAO,QACPF,KAAK,KAAMH,EAAOpH,EAAIjC,KACtBwJ,KAAK,KAAMH,EAAOpH,EAAI/B,KACtBsJ,KAAK,KAAMP,EAAOhH,EAAIhC,KACtBuJ,KAAK,KAAMP,EAAOhH,EAAI9B,KACtBqJ,KAAK,SAAUmB,GACfnB,KAAK,eAAgB7C,GACrB6C,KAAK,iBAAkB,SAE1BvH,EAAM/F,CACR,CACF,CAEA,OAAOoN,EAAI9N,MACb,CAAO,GAAe,WAAXkL,EAAqB,CAE9B,GAAIC,IAAUC,EACZ,MAAM,IAAIgE,MAAM,uDAElB,MACMC,EAAMC,EADOxC,EAAY7B,GACS,CACtClH,cAAe,MACfC,UAAW,QAKPuL,EAAuB,UADZ3D,EAEjB,GAAiB,UAFAA,GAEwB,UAFxBA,EAGf,MAAM,IAAIwD,MAAM,gDAKlB,MAAMI,EAAQ,EACRC,EAAU,EAIVC,GADYpC,EAAGlL,IAAIiN,EAAIxK,KAAOlD,GAAMA,EAAEpD,IAAM,GAClB,EAAImN,EAC9BiE,EAAIxE,EACRyE,EAAIxE,EACAyE,EAAUF,EAAI,EAClBG,EAAUF,EAAI,EAEVG,GAAezC,EAClBI,cACAC,OAAO,EAAE+B,EAAaA,IACtB9B,MAAM,CAAC,EAAG+B,IACPK,GAAe1C,EAClBI,cACAC,OAAO,EAAE+B,EAAaA,IACtB9B,MAAM,CAACgC,EAAG,IAEPK,GAAY1R,GAAMA,GAAKoR,GAAK,EAAID,IAGhCjO,GAAO,IAAIC,IAAI2N,EAAIxK,KAAK5D,IAAKU,GAAM,CAACA,EAAEd,OAAQc,KAC9CsL,GAAOoC,EAAIxK,KAAKuB,OAAQzE,GAAMA,EAAER,OAChC+O,GAAUjD,GAAK5M,OAASiN,EAAGlL,IAAI6K,GAAOtL,GAAMA,EAAEpD,GAAK,EACnD6O,GAAYX,EAAoBhL,GAAM,CAAEiL,OAAQ,MAChDQ,GAAU,IAAIxL,IAAIuL,GAAKhM,IAAKU,GAAM,CAACA,EAAEd,OAAQc,KAC7CwL,GAAa,IAAIzL,IAAIuL,GAAKhM,IAAKU,GAAM,CAACA,EAAEZ,UAAWY,KASzD,SAASwO,GAAe3L,EAAIC,EAAIC,EAAIC,GAClC,MAAMyL,EAAKL,GAAavL,GACtB6L,EAAKL,GAAavL,GAGd6L,EAFKP,GAAarL,GAER0L,EACdG,EAFKP,GAAarL,GAER0L,EACNG,EAAM9R,KAAK+R,MAAMH,EAAIC,IAAO,EAC5B/R,EAAIE,KAAK0D,IAAI,GAAIoO,EAAMf,GAAWe,GACxC,MAAO,CAAEJ,KAAIC,KAAIK,IAAKN,EAAKE,EAAK9R,EAAGmS,IAAKN,EAAKE,EAAK/R,EAAGgS,MACvD,CAGA,MAAM1C,GAAMR,EACTS,OAAO,OACPC,KAAK,QAAS2B,GACd3B,KAAK,SAAU4B,GACf5B,KAAK,cAAe,cACpBA,KAAK,YAAa,IAEfC,GAAQH,GAAII,OAAO,KAGnB0C,GAAc9C,GAAII,OAAO,KAAKF,KAAK,QAAS,sBAC5C6C,GAAa/C,GAAII,OAAO,KAAKF,KAAK,QAAS,qBAC3C8C,GAAahD,GAAII,OAAO,KAAKF,KAAK,QAAS,qBAC3C+C,GAAYjD,GAAII,OAAO,KAAKF,KAAK,QAAS,oBAGhDC,GACGC,OAAO,KACPF,KAAK,QAAS,cACdK,UAAU,QACVxJ,KAAKwK,EAAItK,MACTuJ,KAAK,QACLN,KAAK,IAAMrM,GACC,MAAXA,EAAE7B,MACEkR,EACAnB,EACAC,EACAG,GAAStO,EAAE5C,QACX4C,EAAEuD,MACFvD,EAAEwD,KAEF8L,EACApB,EACAC,EACAG,GAAStO,EAAE5C,QACX4C,EAAEuD,MACFvD,EAAEwD,IACFxD,EAAE7B,QAGPkO,KAAK,OAAQ,QACbA,KAAK,SAAU,QACfA,KAAK,eAAgBrC,GAGxBsC,GACGC,OAAO,KACPF,KAAK,QAAS,eACdK,UAAU,QACVxJ,KAAKwK,EAAInL,OACToK,KAAK,QACL4C,KAAK,SAAStN,EAAGuN,GAEhB,MAAM3M,EAAKZ,EAAEY,GACXC,EAAKb,EAAEa,GAGH2M,GA5ESC,EA4EOzN,GA1EXW,SAAW8M,EAAMxQ,QAAUwQ,EAAMtH,KAAO,KAFvD,IAAmBsH,EA6Ef,MAAMrR,EAAc,MAAPoR,EAAc3P,GAAKQ,IAAImP,QAAOjH,EACrCmH,KAAgBtR,IAAQA,EAAKmB,OAGnC,IAAIuD,EAAKd,EAAEc,GACTC,EAAKf,EAAEe,GAGL4K,GAAW+B,IACb5M,EAAKwL,GAAUxR,KAAKC,IAAIqB,EAAKoB,OAC7BuD,EAAKuL,GAAUxR,KAAKG,IAAImB,EAAKoB,QAI/B,MAAMgP,GAAEA,EAAEC,GAAEA,EAAEK,IAAEA,EAAGC,IAAEA,GAAcR,GAAe3L,EAAIC,EAAIC,EAAIC,GAE9D2I,EAAGqB,OAAOC,MACPZ,KAAK,KAAMoC,GACXpC,KAAK,KAAMqC,GACXrC,KAAK,KAAM0C,GACX1C,KAAK,KAAM2C,GACX3C,KAAK,SAAU,QACfA,KAAK,eAAgBrC,EAC1B,GAGF,MAAM4C,GAAUN,GACbC,OAAO,KACPF,KAAK,QAAS,kBACdK,UAAU,UACVxJ,KAAKoI,IACLqB,KAAK,UACL4C,KAAK,SAASvP,EAAGwP,GAEhB,MAAM1S,EAAI8Q,EAAUW,GAAUxR,KAAKC,IAAIgD,EAAEP,OAASO,EAAElD,EAC9CG,EAAI2Q,EAAUW,GAAUxR,KAAKG,IAAI8C,EAAEP,OAASO,EAAE/C,EAEpD0O,EAAGqB,OAAOC,MACPZ,KAAK,KAAM+B,GAAatR,IACxBuP,KAAK,KAAMgC,GAAapR,IACxBoP,KAAK,IAAKwB,GACVxB,KAAK,OAAQ,SACbA,KAAK,SAAU,SACfA,KAAK,eAAgB,IAC1B,GAEElC,GACFyC,GACGL,OAAO,SACP7D,KAAM1I,GAAMoK,EAAiBpK,EAAGyL,GAAUzL,EAAEd,UAIjD,MAAMsF,GAAO1H,GAAoB,iBAANA,GAAkBA,EAAIA,EAC3C8S,GAAe,IAAI7P,IAAI2N,EAAInL,MAAMjD,IAAI2C,GAAK,CAACuC,GAAIvC,EAAEW,SAAWX,EAAE/C,QAAU+C,EAAEmG,KAAMnG,KAChF4N,GAAa,IAAI9P,IAAI2N,EAAIpJ,WAAWhF,IAAII,GAAK,CAAC8E,GAAI9E,EAAEkD,SAAUlD,KAKpE,GAAIwK,EAAW,CACb,MAAMiD,GAASb,GACZC,OAAO,KACPF,KAAK,QAAS,gBACdK,UAAU,WACVxJ,KAAKoI,IACLqB,KAAK,KACLN,KAAK,QAAS,SACdA,KAAK,YAAcrM,IAIlB,MAAMpD,EAAIgR,EAAUW,GAAUvO,EAAEpD,EAC1BE,EAAIF,EAAIG,KAAKC,IAAIgD,EAAEP,OACnBxC,EAAIL,EAAIG,KAAKG,IAAI8C,EAAEP,OACzB,MAAO,aAAa2O,GAAatR,MAAMuR,GAAapR,QAErDsS,KAAK,SAASvP,GAEb,IAAIP,EAAoB,KAAVO,EAAEP,MAAe1C,KAAKS,GAChCsS,EAAO,GACPC,EAAS,QACT/P,EAAEP,MAAQ1C,KAAKS,GAAK,GAAKwC,EAAEP,MAAS,EAAI1C,KAAKS,GAAM,IACrDiC,GAAS,IACTqQ,IAAQ,EACRC,EAAS,OAEXpE,EAAGqB,OAAOC,MACPV,OAAO,KACPF,KAAK,YAAa,UAAU5M,MAC5B8M,OAAO,QACPF,KAAK,IAAKyD,GACVzD,KAAK,qBAAsB,UAC3BA,KAAK,cAAe0D,GACpB1D,KAAK,YAAa,IAClBA,KAAK,OAAQ,SACb3D,KAAM1I,GAAMA,EAAEZ,WAAWyJ,QAAQ,KAAM,MAAQ,GACpD,GAEEsB,GACFgD,GACGZ,OAAO,SACP7D,KAAM1I,GAAMoK,EAAiBpK,EAAGyL,GAAUzL,EAAEd,UAIjDiO,GACGN,GAAG,aAAc,SAASC,EAAQ9M,GACjCgQ,GAAehQ,EAAGmP,GAAYC,GAAW7E,EAAaC,GACtDmB,EAAGqB,OAAOC,MAAMD,OAAO,QAAQX,KAAK,cAAe,IACrD,GACCQ,GAAG,aAAc,WAChBsC,GAAWzC,UAAU,KAAKQ,SAC1BkC,GAAU1C,UAAU,KAAKQ,SACzBvB,EAAGqB,OAAOC,MAAMD,OAAO,QAAQX,KAAK,cAAe,KACrD,EACJ,CAGA,SAAS2D,GACPC,EACAC,EACAC,EACA3C,EAAS,UACThE,EAAQ,GAGR0G,EAAUxD,UAAU,KAAKQ,SACzBiD,EAASzD,UAAU,KAAKQ,SAExB,IAAIpI,EAAwB,iBAAXmL,EAAsBnQ,GAAKQ,IAAI2P,GAAUA,EAC1D,IAAKnL,EAAK,OAEV,IAAIzB,GAAQ,EACZ,KAAOyB,GAAuB,MAAhBA,EAAI9F,UAAkB,CAElC,MAAMiD,EAAI2N,GAAatP,IAAIkE,GAAIM,EAAI5F,SACnC,GAAI+C,EAAG,CACL,MAAMmO,EAAKnO,EAAEY,GACXwN,EAAKpO,EAAEa,GACT,IAAIpG,EAAKuF,EAAEc,GACTpG,EAAKsF,EAAEe,GACT,GAAI4K,GAAWvK,GAASyB,EAAItF,MAAO,CACjC,MAAM5C,EAAI2R,GACV7R,EAAKE,EAAIG,KAAKC,IAAI8H,EAAIrF,OACtB9C,EAAKC,EAAIG,KAAKG,IAAI4H,EAAIrF,MACxB,CACA,MAAMgP,GAAEA,EAAEC,GAAEA,EAAEK,IAAEA,EAAGC,IAAEA,GAAQR,GAAe4B,EAAIC,EAAI3T,EAAIC,GACxDuT,EACG3D,OAAO,QACPF,KAAK,KAAMoC,GACXpC,KAAK,KAAMqC,GACXrC,KAAK,KAAM0C,GACX1C,KAAK,KAAM2C,GACX3C,KAAK,SAAUmB,GACfnB,KAAK,eAAgB7C,GACrB6C,KAAK,iBAAkB,QAC5B,CAGA,MAAM3M,EAAImQ,GAAWvP,IAAIkE,GAAIM,EAAI5F,SACjC,SAASoR,EAAkBC,GACzB,MAAMC,EAAIlC,GAASiC,EAAInT,QACvB,OAAoB,MAAbmT,EAAIpS,MACPkR,EAAenB,EAASC,EAASqC,EAAGD,EAAIhN,MAAOgN,EAAI/M,KACnD8L,EAAoBpB,EAASC,EAASqC,EAAGD,EAAIhN,MAAOgN,EAAI/M,IAAK+M,EAAIpS,MACvE,CAEIuB,GACFyQ,EACG5D,OAAO,QACPF,KAAK,IAAKiE,EAAkB5Q,IAC5B2M,KAAK,OAAQ,QACbA,KAAK,SAAUmB,GACfnB,KAAK,eAAgB7C,GAG1BnG,GAAQ,EACRyB,EAAMhF,GAAKQ,IAAIwE,EAAI9F,SACrB,CACF,CAcA,GAXA4N,GACGC,GAAG,aAAc,SAASC,EAAQ9M,GACjCgQ,GAAehQ,EAAGmP,GAAYC,GAAW7E,EAAaC,GACtDmB,EAAGqB,OAAOC,MAAMZ,KAAK,IAAKwB,EAAQ,EACpC,GACChB,GAAG,aAAc,WAChBsC,GAAWzC,UAAU,KAAKQ,SAC1BkC,GAAU1C,UAAU,KAAKQ,SACzBvB,EAAGqB,OAAOC,MAAMZ,KAAK,IAAKwB,EAC5B,GAEEpD,GAAiBA,EAAc/L,OAAQ,CAC1B,IAAI0O,IACjB,IACK3C,EAAchG,OAAOmG,GAAUtL,IAAKH,GAAOoM,GAAQjL,IAAInB,OACvDsL,EACAhG,OAAQ3H,IAAO8N,EAAS9N,IACxBwC,IAAK+N,GAAO7B,GAAWlL,IAAI+M,KAC9B5I,OAAOE,UAGJjE,QAAS4M,IACd0C,GACE1C,EAAIpO,OACJ+P,GACAC,GACAxE,EACAC,IAGN,CAEA,OAAOwB,GAAI9N,MACb,CAAO,GAAe,aAAXkL,EAAuB,CAEhC,MACMkH,GAAgBC,EADHvF,EAAY7B,IAGzB0E,GAAIxE,EACJyE,GAAIxE,EAEJkH,GAAUhF,EAAGiF,OAAOH,GAAcvN,KAAOlD,GAAMA,EAAElD,GACjD+T,GAAUlF,EAAGiF,OAAOH,GAAcvN,KAAOlD,GAAMA,EAAE/C,GACjD4O,GAAO9O,KAAK0D,IAAI1D,KAAK+T,IAAIH,GAAQ,IAAK5T,KAAK+T,IAAIH,GAAQ,KACvDjF,GAAO3O,KAAK0D,IAAI1D,KAAK+T,IAAID,GAAQ,IAAK9T,KAAK+T,IAAID,GAAQ,KAEvDE,GADYhU,KAAK0D,IAAIoL,GAAMH,IACD,EAAI3B,EAE9BiH,GAAerF,EAClBI,cACAC,OAAO,EAAE+E,GAAaA,KACtB9E,MAAM,CAAC,EAAG+B,KACPiD,GAAetF,EAClBI,cACAC,OAAO,EAAE+E,GAAaA,KACtB9E,MAAM,CAACgC,GAAG,IAEP9B,GAAMR,EACTS,OAAO,OACPC,KAAK,QAAS2B,IACd3B,KAAK,SAAU4B,IACf5B,KAAK,cAAe,cACpBA,KAAK,YAAa,IAEfC,GAAQH,GAAII,OAAO,KACnBC,GAAcL,GAAII,OAAO,KAAKF,KAAK,QAAS,0BAC5CI,GAAaN,GAAII,OAAO,KAAKF,KAAK,QAAS,yBAEjDC,GACGC,OAAO,KACPF,KAAK,QAAS,eACdK,UAAU,QACVxJ,KAAKuN,GAAcxI,OACnB0E,KAAK,QACLN,KAAK,KAAOrM,GAAMgR,GAAahR,EAAE+C,KACjCsJ,KAAK,KAAOrM,GAAMiR,GAAajR,EAAEgD,KACjCqJ,KAAK,KAAOrM,GAAMgR,GAAahR,EAAEqI,KACjCgE,KAAK,KAAOrM,GAAMiR,GAAajR,EAAEsI,KACjC+D,KAAK,eAAgBrC,GACrBqC,KAAK,SAAU,QAElB,MAAM6E,GAAQ5E,GACXC,OAAO,KACPF,KAAK,QAAS,gBACdK,UAAU,UACVxJ,KAAKuN,GAAcvN,MACnByJ,KAAK,UACLN,KAAK,QAAS,OACdA,KAAK,IAAMrM,GAAOA,EAAER,MAAQ,EAAI,GAChC6M,KAAK,KAAOrM,GAAMgR,GAAahR,EAAElD,IACjCuP,KAAK,KAAOrM,GAAMiR,GAAajR,EAAE/C,IACjCoP,KAAK,SAAU,SACfA,KAAK,eAAgB,GACrBA,KAAK,OAASrM,GAAOA,EAAER,MAAQ,QAAU,SAEtCM,GAAO,IAAIC,IAAI0Q,GAAcvN,KAAK5D,IAAKU,GAAM,CAACA,EAAEd,OAAQc,KACxDuL,GAAU,IAAIxL,IAClB0Q,GAAcvN,KAAKuB,OAAQzE,GAAMA,EAAER,OAAOF,IAAKU,GAAM,CAACA,EAAEd,OAAQc,KAE5DwL,GAAa,IAAIzL,IACrB0Q,GAAcvN,KAAKuB,OAAQzE,GAAMA,EAAER,OAAOF,IAAKU,GAAM,CAACA,EAAEZ,UAAWY,KAE/DyL,GAAYX,EAAoBhL,IAElCqK,GACF+G,GACGzM,OAAQzE,GAAMA,EAAER,OAChB+M,OAAO,SACP7D,KAAM1I,GAAMoK,EAAiBpK,EAAGyL,GAAUzL,EAAEd,UAGjD,MAAMiS,GAAW,IAAIpR,IACfqR,GAAY,IAAIrR,IAAI0Q,GAAcvN,KAAK5D,IAAKU,GAAM,CAACA,EAAEd,OAAQc,KAMnE,GALAyQ,GAAcxI,MAAMvH,QAAS2Q,IAC3B,MAAMC,EAAUF,GAAU9Q,IAAI+Q,EAAKjJ,KAC/BkJ,GAAS9R,OAAO2R,GAASxQ,IAAI0Q,EAAKjJ,IAAKiJ,KAGzCnH,EAAW,CACb,MAAMqH,GAAejF,GAClBC,OAAO,KACPF,KAAK,QAAS,gBACdK,UAAU,KACVxJ,KAAKuN,GAAcvN,KAAKuB,OAAQzE,GAAMA,EAAER,QACxCmN,KAAK,KACLN,KAAK,YAAcrM,GAGX,aAFGgR,GAAahR,EAAElD,MACfmU,GAAajR,EAAE/C,OAG1BsS,KAAK,SAASvP,GACb,MAAMqR,EAAOF,GAAS7Q,IAAIN,EAAEd,QAC5B,IAAKmS,EAAM,OAEX,MAAMtO,EAAKiO,GAAaK,EAAKtO,IACvBC,EAAKiO,GAAaI,EAAKrO,IAIvB2L,EAHKqC,GAAaK,EAAKhJ,IAGbtF,EACV6L,EAHKqC,GAAaI,EAAK/I,IAGbtF,EAChB,IAAIvD,EAA8B,IAArB1C,KAAKiE,MAAM4N,EAAID,GAAa5R,KAAKS,GAE1CgU,GAAU,GACVzB,EAAS,OACTtQ,EAAQ,IAAMA,SAChBA,GAAS,IACTsQ,EAAS,QACTyB,EAAU,IAGZ7F,EAAGqB,OAAOC,MACPV,OAAO,KACPF,KAAK,YAAa,UAAU5M,MAC5B8M,OAAO,QACPF,KAAK,IAAKmF,GACVnF,KAAK,qBAAsB,UAC3BA,KAAK,cAAe0D,GACpB1D,KAAK,YAAa,IAClBA,KAAK,OAAQ,SACb3D,KAAK1I,EAAEZ,WAAWyJ,QAAQ,KAAM,MAAQ,GAC7C,GAEEsB,GACFoH,GACGhF,OAAO,SACP7D,KAAM1I,GAAMoK,EAAiBpK,EAAGyL,GAAUzL,EAAEd,UAGjDqS,GACG1E,GAAG,aAAc,SAASC,EAAQ9M,GACjCyR,GAAiBzR,EAAEd,OAAQuN,GAAYlC,EAAaC,GACpDmB,EAAGqB,OAAOC,MAAMD,OAAO,QAAQX,KAAK,cAAe,IACrD,GACCQ,GAAG,aAAc,WAChBJ,GAAWC,UAAU,KAAKQ,SAC1BvB,EAAGqB,OAAOC,MAAMD,OAAO,QAAQX,KAAK,cAAe,KACrD,EACJ,CAaA,GAXA6E,GACGzM,OAAQzE,GAAMA,EAAER,OAChBqN,GAAG,aAAc,SAASC,EAAQ9M,GACjCyR,GAAiBzR,EAAEd,OAAQuN,GAAYlC,EAAaC,GACpDmB,EAAGqB,OAAOC,MAAMZ,KAAK,IAAK,EAC5B,GACCQ,GAAG,aAAc,WAChBJ,GAAWC,UAAU,KAAKQ,SAC1BvB,EAAGqB,OAAOC,MAAMZ,KAAK,IAAK,EAC5B,GAEE5B,GAAiBA,EAAc/L,OAAQ,CAC1B,IAAI0O,IACjB,IACK3C,EAAchG,OAAOmG,GAAUtL,IAAKH,GAAOoM,GAAQjL,IAAInB,OACvDsL,EACAhG,OAAQ3H,IAAO8N,EAAS9N,IACxBwC,IAAK+N,GAAO7B,GAAWlL,IAAI+M,KAC9B5I,OAAOE,UAEJjE,QAAS4M,IACdmE,GACEnE,EAAIpO,OACJsN,GACA9B,EACAC,IAGN,CAEA,SAAS8G,GAAiBzG,EAAOuC,EAAOC,EAAQhE,GAC9C,MAAMkI,EAAgB,IAAI3R,IAAI0Q,GAAcxI,MAAM3I,IAAKqS,GAAM,CAACA,EAAEvJ,IAAKuJ,KACrEpE,EAAMb,UAAU,KAAKQ,SACrB,IAAIpI,EAAMhF,GAAKQ,IAAI0K,GACnB,KAAOlG,GAAuB,MAAhBA,EAAI9F,UAAkB,CAClC,MAAM2S,EAAID,EAAcpR,IAAIwE,EAAI5F,QAC5ByS,GACFpE,EACGhB,OAAO,QACPF,KAAK,KAAM2E,GAAaW,EAAE5O,KAC1BsJ,KAAK,KAAM4E,GAAaU,EAAE3O,KAC1BqJ,KAAK,KAAM2E,GAAaW,EAAEtJ,KAC1BgE,KAAK,KAAM4E,GAAaU,EAAErJ,KAC1B+D,KAAK,SAAUmB,GACfnB,KAAK,eAAgB7C,GACrB6C,KAAK,iBAAkB,SAE5BvH,EAAMhF,GAAKQ,IAAIwE,EAAI9F,SACrB,CACF,CAEA,OAAOmN,GAAI9N,MACb,CACE,MAAM,IAAIoP,MACR,gEAGN"}
1
+ {"version":3,"file":"drawPhylogeny.umd.js","sources":["../src/radial/polarToCartesian.js","../src/radial/describeArc.js","../src/radial/describeArcSweep.js","../src/utils/preorder.js","../src/utils/fortify.js","../src/radial/radialData.js","../src/radial/fanAngles.js","../src/radial/radialLayout.js","../src/radial/getRadiiFromPd.js","../src/radial/getRadii.js","../src/radial/getArcsFan.js","../src/radial/getArcs.js","../src/radial/getChildArcsFan.js","../src/radial/getChildArcs.js","../src/rectangle/getHorizontal.js","../src/utils/mean.js","../src/rectangle/rectangleLayout.js","../src/rectangle/getVertical.js","../src/rectangle/getChildVerticals.js","../src/unrooted/equalAngleLayout.js","../src/utils/numTips.js","../src/unrooted/unrooted.js","../src/utils/edges.js","../src/utils/readTree.js","../src/plot/drawPhylogeny.js"],"sourcesContent":["// src/radial/polarToCartesian.js\nexport default function (cx, cy, r, t) {\n return { x: cx + r * Math.cos(t), y: cy - r * Math.sin(t) };\n}\n","import polarToCartesian from \"./polarToCartesian.js\";\n\n/**\n * Draw the shortest arc between startAngle and endAngle (radians), CCW.\n * If the CW path is shorter, swap start/end so the CCW path is still shortest.\n * Works with Y-inverted screen coords (polarToCartesian already inverts Y).\n */\nexport default function describeArc(cx, cy, radius, startAngle, endAngle) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n let a0 = norm(startAngle);\n let a1 = norm(endAngle);\n\n // CCW and CW spans\n const ccw = (a1 - a0 + TAU) % TAU;\n const cw = (a0 - a1 + TAU) % TAU;\n\n // Ensure we always take the shorter span *in CCW* by swapping if needed\n if (cw < ccw) {\n const tmp = a0; a0 = a1; a1 = tmp;\n }\n\n const delta = (a1 - a0 + TAU) % TAU; // now the shorter CCW span\n if (delta < 1e-9) {\n const p = polarToCartesian(cx, cy, radius, a0);\n return `M ${p.x} ${p.y}`; // degenerate span → no arc\n }\n\n const largeArcFlag = delta > Math.PI ? 1 : 0; // should be 0 for “shortest”, but keep for safety\n const sweepFlag = 0; // CCW\n\n const p0 = polarToCartesian(cx, cy, radius, a0);\n const p1 = polarToCartesian(cx, cy, radius, a1);\n\n return `M ${p0.x} ${p0.y} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${p1.x} ${p1.y}`;\n}\n\n","// src/radial/describeArcSweep.js\nconst TAU = Math.PI * 2;\nconst norm = (t) => ((t % TAU) + TAU) % TAU;\n\nexport default function describeArcSweep(cx, cy, r, a0, a1, sweep /*0=CCW,1=CW*/) {\n const delta = sweep === 0 ? norm(a1 - a0) : norm(a0 - a1);\n if (!(r > 0) || delta < 1e-9) return \"\";\n const largeArcFlag = delta > Math.PI ? 1 : 0;\n\n const x0 = cx + r * Math.cos(a0), y0 = cy - r * Math.sin(a0);\n const x1 = cx + r * Math.cos(a1), y1 = cy - r * Math.sin(a1);\n\n return `M ${x0} ${y0} A ${r} ${r} 0 ${largeArcFlag} ${sweep} ${x1} ${y1}`;\n}\n\n","/**\n * Recursive function for pre-order traversal of tree (returns array)\n */\nexport function preorder(node, list = []) {\n list.push(node);\n for (let i = 0; i < (node.children?.length || 0); i++) {\n list = preorder(node.children[i], list);\n }\n return list;\n}\n\n/**\n * Iterative generator traversals (avoid recursion limits on large trees)\n */\nexport function* preorderIter(root) {\n const stack = [root];\n while (stack.length) {\n const n = stack.pop();\n yield n;\n if (n.children) for (let i = n.children.length - 1; i >= 0; --i) stack.push(n.children[i]);\n }\n}\n\nexport function* postorderIter(root) {\n const stack = [[root, 0]];\n while (stack.length) {\n const top = stack[stack.length - 1];\n const [n, i] = top;\n if (!n.children || i >= n.children.length) {\n stack.pop();\n yield n;\n } else {\n top[1] = i + 1;\n stack.push([n.children[i], 0]);\n }\n }\n}\n\n","import { preorder } from \"./preorder.js\"\n\n/**\n * Convert parsed Newick tree from readTree() into data\n * frame.\n * this is akin to a \"phylo\" object in R.\n */\n\nexport default function (tree, sort = true) {\n var df = [];\n\n for (const node of preorder(tree)) {\n if (node.parent === null) {\n df.push({\n 'parentId': null,\n 'parentLabel': null,\n 'thisId': node.id,\n 'thisLabel': node.label,\n 'children': node.children.map(x => x.id),\n 'branchLength': 0.,\n 'isTip': false,\n 'x': node.x,\n 'y': node.y,\n 'angle': node.angle\n })\n }\n else {\n df.push({\n 'parentId': node.parent.id,\n 'parentLabel': node.parent.label,\n 'thisId': node.id,\n 'thisLabel': node.label,\n 'children': node.children.map(x => x.id),\n 'branchLength': node.branchLength,\n 'isTip': (node.children.length == 0),\n 'x': node.x,\n 'y': node.y,\n 'angle': node.angle\n })\n }\n }\n\n if (sort) {\n df = df.sort(function (a, b) {\n return a.thisId - b.thisId;\n })\n }\n return (df);\n}\n","import fortify from \"../utils/fortify.js\"\n\n/**\n * Compute per-node polar coordinates for radial layout:\n * - Tip angles: evenly spaced 0..2π in tip DFS order\n * - Internal angles: circular mean of child angles\n * - Radii: cumulative branch length from root\n * - x,y: cartesian projection\n *\n * Returns the fortified array with added {angle, r, x, y}.\n */\nexport default function radialData(node) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n\n const pd = fortify(node, /*sort*/ true);\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const kids = new Map(pd.map(d => [d.thisId, d.children || []]));\n\n // Find root id\n let root = null;\n for (const d of pd) {\n if (d.parentId == null) { root = d.thisId; break; }\n }\n\n // Collect tip ids in DFS left->right order to preserve input ordering\n const tipIds = [];\n (function dfs(id) {\n const c = kids.get(id) || [];\n if (c.length === 0) {\n tipIds.push(id);\n return;\n }\n for (const ch of c) dfs(ch);\n })(root);\n\n // Assign tip angles evenly spaced 0..2π\n const N = Math.max(1, tipIds.length);\n const angle = new Map();\n tipIds.forEach((id, i) => {\n angle.set(id, (i / N) * TAU);\n });\n\n // Internal node angles: circular mean of child angles (post-order)\n (function setInternalAngles(id) {\n const c = kids.get(id) || [];\n for (const ch of c) setInternalAngles(ch);\n if (c.length > 0) {\n let sx = 0, sy = 0;\n for (const ch of c) {\n const th = angle.get(ch);\n sx += Math.cos(th);\n sy += Math.sin(th);\n }\n angle.set(id, norm(Math.atan2(sy, sx)));\n }\n })(root);\n\n // Radii: cumulative branch lengths from root (root r=0)\n const radius = new Map();\n radius.set(root, 0);\n (function setR(id) {\n const c = kids.get(id) || [];\n const r0 = radius.get(id) || 0;\n for (const ch of c) {\n const child = byId.get(ch);\n const bl = child?.branchLength ?? 0;\n radius.set(ch, r0 + bl);\n setR(ch);\n }\n })(root);\n\n // Enrich pd rows with angle, r, x, y\n for (const d of pd) {\n const th = angle.get(d.thisId) ?? 0;\n const r = radius.get(d.thisId) ?? 0;\n d.angle = th;\n d.r = r;\n d.x = r * Math.cos(th);\n d.y = r * Math.sin(th);\n }\n\n return pd;\n}\n","// APE-like \"fan\" angles: tips evenly spaced with open-angle gap & rotation,\n// internal nodes = arithmetic mean of unwrapped child angles.\nconst TAU = Math.PI * 2;\nconst norm = (t) => ((t % TAU) + TAU) % TAU;\n\nfunction unwrapAround(ref, a) {\n let x = a;\n while (x < ref - Math.PI) x += TAU;\n while (x > ref + Math.PI) x -= TAU;\n return x;\n}\n\nexport default function fanAngles(pd, opts = {}) {\n const { openAngleDeg = 0, rotateDeg = 0 } = opts;\n const gap = (openAngleDeg / 360) * TAU;\n const rot = (rotateDeg / 360) * TAU;\n\n // root + children index\n let root = null;\n const kids = new Map(pd.map((d) => [d.thisId, d.children || []]));\n for (const d of pd) if (d.parentId == null) { root = d.thisId; break; }\n\n // tip order (DFS left→right like your fortify)\n const tipIds = [];\n (function dfs(id) {\n const c = kids.get(id) || [];\n if (!c.length) { tipIds.push(id); return; }\n for (const ch of c) dfs(ch);\n })(root);\n\n const N = Math.max(1, tipIds.length);\n const maxA = TAU * (1 - 1 / N) - gap; // note: no last-step overlap\n const step = N > 1 ? maxA / (N - 1) : 0;\n\n const angle = new Map();\n tipIds.forEach((id, i) => angle.set(id, norm(i * step + rot)));\n\n // internal nodes: arithmetic mean of child angles (unwrapped)\n (function setInternal(id) {\n const c = kids.get(id) || [];\n for (const ch of c) setInternal(ch);\n if (c.length) {\n const a0 = angle.get(c[0]);\n const arr = c.map((ch) => unwrapAround(a0, angle.get(ch)));\n angle.set(id, norm(arr.reduce((s, v) => s + v, 0) / arr.length));\n }\n })(root);\n\n return angle;\n}\n\n","import radialData from \"./radialData.js\";\nimport getRadii from \"./getRadii.js\";\nimport getRadiiFromPd from \"./getRadiiFromPd.js\";\nimport getArcs from \"./getArcs.js\"; // your existing \"shortest-arc\"\nimport getArcsFan from \"./getArcsFan.js\"; // new\nimport fanAngles from \"./fanAngles.js\";\nimport getChildArcs from \"./getChildArcs.js\"; // optional existing helper\nimport getChildArcsFan from \"./getChildArcsFan.js\"; // optional existing helper\n\n/**\n * radialLayout(node, opts?)\n * opts:\n * - angleStrategy: \"cmean\" (default, your current) | \"fan\" (APE-like)\n * - arcsStyle: \"shortest\" (default) | \"fan\" (block arcs, supports wrap)\n * - openAngleDeg: number (gap wedge for \"fan\" angles)\n * - rotateDeg: number (rotation for \"fan\" angles)\n */\nexport default function radialLayout(node, opts = {}) {\n const {\n // default fan\n angleStrategy = \"fan\",\n arcsStyle = \"fan\",\n openAngleDeg = 0,\n rotateDeg = 0\n } = opts;\n\n // Start with your current enriched nodes (angle + r from radialData)\n const pd = radialData(node);\n\n if (angleStrategy === \"fan\") {\n // overwrite angles with APE-like fan angles; keep radii r as-is\n const angleMap = fanAngles(pd, { openAngleDeg, rotateDeg });\n for (const d of pd) {\n const a = angleMap.get(d.thisId);\n if (a != null) {\n d.angle = a;\n d.x = d.r * Math.cos(a);\n d.y = d.r * Math.sin(a);\n }\n }\n }\n\n // Build spokes using the angles currently on pd\n const radii = (angleStrategy === \"fan\")\n ? getRadiiFromPd(pd)\n : getRadii(node);\n\n // Choose arc builder\n const arcs = (arcsStyle === \"fan\")\n ? getArcsFan(pd)\n : getArcs(pd);\n\n // per-child arcs for half-arc highlighting if you already use them\n let child_arcs = [];\n if (arcsStyle === \"fan\") {\n child_arcs = getChildArcsFan(pd);\n } else {\n child_arcs = getChildArcs(pd);\n }\n\n return { data: pd, radii, arcs, child_arcs };\n}\n\n","// src/radial/getRadiiFromPd.js\n/**\n * Build per-edge spokes using the *current* pd angles/r.\n * Output: [{ parentId, childId, x0,y0,x1,y1, isTip }]\n */\nexport default function getRadiiFromPd(pd) {\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const root = pd.find(d => d.parentId == null)?.thisId;\n\n const segments = [];\n for (const d of pd) {\n if (d.thisId === root) continue;\n const parent = byId.get(d.parentId);\n if (!parent) continue;\n\n const theta = d.angle;\n const r0 = parent.r, r1 = d.r;\n\n segments.push({\n parentId: parent.thisId,\n childId: d.thisId,\n x0: r0 * Math.cos(theta),\n y0: r0 * Math.sin(theta),\n x1: r1 * Math.cos(theta),\n y1: r1 * Math.sin(theta),\n isTip: !!d.isTip\n });\n }\n return segments;\n}\n\n","import radialData from \"./radialData.js\";\n\n/**\n * Per-edge radial segments (for highlighting and drawing).\n * For each non-root node, draw a radial line from the parent radius to the child radius\n * at the CHILD'S angle.\n *\n * Output: [{ parentId, childId, x0, y0, x1, y1, isTip }]\n */\nexport default function getRadii(node) {\n const data = radialData(node);\n const byId = new Map(data.map(d => [d.thisId, d]));\n const root = data.find(d => d.parentId == null)?.thisId;\n\n const segments = [];\n for (const d of data) {\n if (d.thisId === root) continue;\n const parent = byId.get(d.parentId);\n if (!parent) continue;\n\n const theta = d.angle;\n const r0 = parent.r;\n const r1 = d.r;\n\n segments.push({\n parentId: parent.thisId,\n childId: d.thisId,\n x0: r0 * Math.cos(theta),\n y0: r0 * Math.sin(theta),\n x1: r1 * Math.cos(theta),\n y1: r1 * Math.sin(theta),\n isTip: !!d.isTip\n });\n }\n return segments;\n}\n\n","/**\n * Build APE-like block arcs per internal parent:\n * radius = parent.r\n * start = first child's angle\n * end = last child's angle\n * sweep = 0 (CCW) if end>=start; 1 (CW) if wrapped across 2π\n *\n * @param {Array} pd nodes with {thisId,parentId,children,angle,r}\n * @returns {Array} [{parentId,thisId,radius,start,end,sweep}]\n */\nexport default function getArcsFan(pd) {\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const arcs = [];\n\n for (const p of pd) {\n const c = p.children || [];\n if (c.length < 2 || !(p.r > 0)) continue;\n\n const first = byId.get(c[0])?.angle;\n const last = byId.get(c[c.length - 1])?.angle;\n if (first == null || last == null) continue;\n\n const start = first;\n const end = last;\n const sweep = end >= start ? 0 : 1; // CW if wrapped\n\n arcs.push({\n parentId: p.parentId,\n thisId: p.thisId,\n radius: p.r,\n start,\n end,\n sweep\n });\n }\n return arcs;\n}\n\n","/**\n * Build arc descriptors for each internal parent:\n * - One arc per internal node at radius = parent.r\n * - Start/end angles choose the *shortest* wrap-aware span covering the children\n * - Skips degenerate spans (delta ~ 0)\n */\nexport default function getArcs(pd) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n const EPS = 1e-6;\n\n // Quick lookups\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const childrenByParent = new Map();\n let root = null;\n\n for (const d of pd) {\n if (d.parentId == null) root = d.thisId;\n if (!childrenByParent.has(d.parentId)) childrenByParent.set(d.parentId, []);\n childrenByParent.get(d.parentId).push(d);\n }\n\n const arcs = [];\n\n for (const parent of pd) {\n const pid = parent.thisId;\n if (pid === root) continue; // no arc above root\n const kids = childrenByParent.get(pid) || [];\n if (kids.length < 2) continue; // need at least two children\n\n // Collect & sort child angles\n const A = kids.map(k => norm(k.angle)).sort((a, b) => a - b);\n const aMin = A[0], aMax = A[A.length - 1];\n\n // Two candidate spans: direct (aMin -> aMax) and wrapped (aMax -> aMin across 2π)\n const direct = aMax - aMin;\n const wrapped = TAU - direct;\n\n // Choose the shorter span. We'll draw **CCW** (sweepFlag = 0) in describeArc.\n let start, end, span;\n if (direct <= wrapped) {\n start = aMin;\n end = aMax;\n span = direct;\n } else {\n // wrapped is shorter: go CCW from aMax up through 2π to aMin\n start = aMax;\n end = aMin;\n span = wrapped;\n }\n\n if (span < EPS || !isFinite(parent.r) || parent.r <= 0) continue;\n\n arcs.push({\n start,\n end,\n radius: parent.r,\n thisId: pid,\n parentId: parent.parentId\n });\n }\n\n return arcs;\n}\n\n","export default function getChildArcsFan(pd) {\n const TAU = Math.PI * 2;\n const norm = (t) => ((t % TAU) + TAU) % TAU;\n\n const ccwDelta = (a0, a1) => (a1 - a0 + TAU) % TAU;\n const cwDelta = (a0, a1) => (a0 - a1 + TAU) % TAU;\n\n // Circular midpoint that travels CCW from a -> b by half the CCW span\n function midCCW(a, b) {\n const d = (b - a + TAU) % TAU; // CCW delta in [0, 2π)\n return norm(a + d / 2);\n }\n\n const key = (x) => (typeof x === \"string\" ? +x : x);\n\n const byId = new Map(pd.map(d => [key(d.thisId), d]));\n const childrenByParent = new Map(\n pd.map(d => [\n key(d.thisId),\n // normalize children to numeric IDs; drop anything we can't resolve\n (d.children || [])\n .map(ch => (typeof ch === \"object\" ? ch.thisId : ch))\n .map(key)\n .filter(id => byId.has(id))\n ])\n );\n\n const child_arcs = [];\n\n for (const parentRaw of pd) {\n const pid = key(parentRaw.thisId);\n const kids = childrenByParent.get(pid) || [];\n if (kids.length < 2) continue;\n\n // Sort children by angle (normalized) around the circle\n const A = kids\n .map(id => {\n const node = byId.get(id);\n return node ? { id, a: norm(node.angle) } : null;\n })\n .filter(Boolean)\n .sort((u, v) => u.a - v.a);\n\n const N = A.length;\n if (N < 2) continue;\n\n const parent = byId.get(pid);\n const radius = parent?.r;\n if (!(radius > 0)) continue;\n\n for (let i = 0; i < N; i++) {\n const prev = A[(i - 1 + N) % N];\n const cur = A[i];\n const next = A[(i + 1) % N];\n\n // “fan” wedge around the child: midpoint(prev→cur) .. midpoint(cur→next), CCW\n const start = midCCW(prev.a, cur.a);\n const end = midCCW(cur.a, next.a);\n\n // Choose the **shorter** sweep (0 = CCW, 1 = CW in our math-angle convention)\n const ccw = ccwDelta(start, end);\n const cw = cwDelta(start, end);\n const sweep = ccw <= cw ? 0 : 1;\n\n child_arcs.push({\n parentId: pid,\n childId: cur.id,\n radius,\n start,\n end,\n sweep\n });\n }\n }\n\n return child_arcs;\n}\n\n","/**\n * Per-child \"half\" arcs for radial trees.\n *\n * For each non-root node (child), emit an arc at the PARENT's radius that\n * spans between the parent's angle and the child's angle. This is the arc\n * segment that meets the child's spoke and is ideal for root→tip highlighting.\n *\n * Input: pd — the array returned by radialData(node) (each row has .thisId, .parentId, .angle, .r)\n * Output: [{ parentId, childId, radius, start, end }]\n */\nexport default function getChildArcs(pd) {\n const byId = new Map(pd.map(d => [d.thisId, d]));\n const arcs = [];\n\n for (const child of pd) {\n if (child.parentId == null) continue; // skip root\n const parent = byId.get(child.parentId);\n if (!parent) continue;\n\n arcs.push({\n parentId: parent.thisId,\n childId: child.thisId,\n radius: parent.r, // draw on the parent's circle\n start: parent.angle, // start at parent's angle\n end: child.angle // end at child's angle (describeArc will choose the shortest CCW span)\n });\n }\n\n return arcs;\n}\n\n","import mean from \"../utils/mean.js\"\nimport fortify from \"../utils/fortify.js\"\n\n/**\n * Rectangle layout: compute per-node x0,x1 and y0=y1\n * - Tip y is assigned by input order (preserves ladderize/order)\n * - Internal node y is mean of child y's\n * - x1 accumulates branch lengths from root\n */\n\nexport default function getHorizontal(node) {\n const pd = fortify(node);\n\n // Fast lookup from id -> pd index\n const idIndex = new Map(pd.map((d, i) => [d.thisId, i]));\n\n // 1) Leaf order from the INPUT TREE (respects your child order / ladderize)\n const leafIds = [];\n (function dfs(n) {\n if (!n.children || n.children.length === 0) { leafIds.push(n.id); return; }\n n.children.forEach(dfs);\n })(node);\n\n // Map each leaf id to a vertical slot (1..N)\n const tipSlot = new Map(leafIds.map((id, i) => [id, i + 1]));\n\n // 2) Set Y for tips directly from that order; internal node Y via children mean\n (function setY(n) {\n const i = idIndex.get(n.id);\n if (!n.children || n.children.length === 0) {\n const y = tipSlot.get(n.id);\n pd[i].y0 = y; pd[i].y1 = y;\n return y;\n }\n const ys = n.children.map(setY);\n const y = mean(ys);\n pd[i].y0 = y; pd[i].y1 = y;\n return y;\n })(node);\n\n // 3) Set X by accumulating branch lengths down the tree\n (function setX(n, xParent) {\n const i = idIndex.get(n.id);\n const bl = pd[i].branchLength ?? 0;\n const x0 = xParent ?? 0;\n const x1 = x0 + bl;\n pd[i].x0 = x0; pd[i].x1 = x1;\n if (n.children && n.children.length) n.children.forEach(c => setX(c, x1));\n })(node, 0);\n\n // Clean up: remove fields not needed downstream without triggering no-unused-vars\n return pd.map((row) => {\n const { y: _y, x: _x, angle: _angle, ...item } = row;\n return item;\n });\n}\n\n","/**\n * Iterable mean\n * Poached from https://github.com/d3/d3-array/blob/master/src/mean.js\n * (Other array means buggered up the tree)\n */\n\nexport default function (values, valueof) {\n let count = 0;\n let sum = 0;\n if (valueof === undefined) {\n for (let value of values) {\n if (value != null && (value = +value) >= value) {\n ++count, sum += value;\n }\n }\n } else {\n let index = -1;\n for (let value of values) {\n if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {\n ++count, sum += value;\n }\n }\n }\n if (count) return sum / count;\n}\n","import getHorizontal from \"./getHorizontal.js\"\nimport getVertical from \"./getVertical.js\"\nimport getChildVerticals from \"./getChildVerticals.js\"\n\n/**\n * Rectangle layout wrapper.\n * Returns:\n * - data: per-node rows (x0,x1,y0=y1,...)\n * - vertical_lines: single spanning vertical per parent (baseline draw)\n * - child_vertical_lines: one vertical per edge (for highlighting)\n * - horizontal_lines: per-edge child horizontals (x0->x1 at y), with labels & tip flags\n */\nexport default function rectangleLayout(node) {\n const data = getHorizontal(node); // per-node\n const vertical_lines = getVertical(node); // parent spans\n const child_vertical_lines = getChildVerticals(node); // per-edge verticals\n\n // IMPORTANT: include y0 & y1, and carry isTip/labels for the renderer\n const byId = new Map(data.map(d => [d.thisId, d]));\n const horizontal_lines = data\n .filter(d => d.parentId != null)\n .map(d => ({\n parentId: d.parentId,\n childId: d.thisId,\n thisId: d.thisId,\n thisLabel: d.thisLabel,\n isTip: d.isTip,\n x0: d.x0,\n x1: d.x1,\n y0: d.y0,\n y1: d.y0\n }));\n\n return { data, vertical_lines, child_vertical_lines, horizontal_lines };\n}\n\n","import getHorizontal from \"./getHorizontal.js\"\n\nexport default function getVertical(node) {\n const data = getHorizontal(node);\n\n // Group rows by parentId (children that share a parent)\n const byParent = new Map();\n for (const row of data) {\n if (row.parentId == null) continue;\n const a = byParent.get(row.parentId);\n if (a) a.push(row); else byParent.set(row.parentId, [row]);\n }\n\n const verticals = [];\n for (const [parentId, kids] of byParent.entries()) {\n if (!kids.length) continue;\n // Works for binary and multifurcations:\n const yvals = kids.map(d => d.y0);\n const y0 = Math.min(...yvals);\n const y1 = Math.max(...yvals);\n // All children share the same junction x (their x0)\n const x = kids[0].x0;\n\n verticals.push({\n parentId,\n x0: x,\n x1: x,\n y0,\n y1,\n heights: y1 - y0\n });\n }\n\n return verticals;\n}\n\n","import getHorizontal from \"./getHorizontal.js\";\n\n/**\n * Build per-child vertical segments for a rectangular tree:\n * For each non-root node (child), draw a vertical from (parent.x, child.y) to (parent.x, parent.y).\n * This yields exactly one vertical per edge (child->parent), making highlighting trivial.\n *\n * Returns an array of:\n * {\n * parentId: number,\n * childId: number,\n * x: number, // x of the parent junction\n * y0: number, // min(child.y, parent.y)\n * y1: number, // max(child.y, parent.y)\n * }\n */\nexport default function getChildVerticals(node) {\n const data = getHorizontal(node); // has parentId, thisId, x0,x1,y0=y1\n\n // Build a quick index to access parent's y by id\n const byId = new Map(data.map(d => [d.thisId, d]));\n\n const childVerticals = [];\n\n for (const d of data) {\n if (d.parentId == null) continue;\n const parent = byId.get(d.parentId);\n if (!parent) continue;\n\n const x = d.x0; // child’s vertical sits at parent.x == child.x0\n const yc = d.y0; // child y\n const yp = parent.y0; // parent y\n const y0 = Math.min(yc, yp);\n const y1 = Math.max(yc, yp);\n\n childVerticals.push({\n parentId: d.parentId,\n childId: d.thisId,\n x,\n y0,\n y1\n });\n }\n\n return childVerticals;\n}\n\n","import numTips from \"../utils/numTips.js\"\n\n/**\n * Equal-angle layout for unrooted trees.\n * - Precomputes ntips in O(n) to avoid repeated subtree counts\n * - Uses angles in \"π units\" (0..2) to match existing API\n * - Populates x,y positions from branchLength and angle\n */\n\nfunction annotateTipCounts(root) {\n (function post(n) {\n if (!n.children || n.children.length === 0) {\n n.ntips = 1; return 1;\n }\n let sum = 0;\n for (const c of n.children) sum += post(c);\n n.ntips = sum;\n return sum;\n })(root);\n return root;\n}\n\nfunction equalAngleLayout(node) {\n if (node.parent === null) {\n annotateTipCounts(node);\n node.start = 0.; // guarantees no arcs overlap 0\n node.end = 2.; // *π\n node.angle = 0.; // irrelevant at root\n node.ntips = numTips(node); // safe (already computed), left for compatibility\n node.x = 0;\n node.y = 0;\n }\n\n let lastStart = node.start;\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const arc = (node.end - node.start) * (child.ntips / node.ntips);\n\n child.start = lastStart;\n child.end = lastStart + arc;\n\n // bisect the arc in π-units\n child.angle = child.start + (child.end - child.start) / 2.;\n lastStart = child.end;\n\n // map to coordinates (convert π-units to radians by multiplying by Math.PI)\n const theta = child.angle * Math.PI;\n const bl = (child.branchLength ?? 0);\n child.x = node.x + bl * Math.sin(theta);\n child.y = node.y + bl * Math.cos(theta);\n\n equalAngleLayout(child);\n }\n\n return node;\n}\n\nexport default equalAngleLayout\n\n","/**\n * Recursive function for breadth-first search of a tree\n * the root node is visited first.\n */\n\nfunction levelorder(root) {\n const queue = [root], result = [];\n while (queue.length) {\n const curnode = queue.shift(); // <- FIFO\n result.push(curnode);\n for (const child of curnode.children) queue.push(child);\n }\n return result;\n}\n\n\n/**\n * Count the number of tips that descend from this node\n */\n\nexport default function(thisnode) {\n var result = 0;\n for (const node of levelorder(thisnode)) {\n if (node.children.length == 0) result++;\n }\n return (result);\n}\n","import edges from \"../utils/edges.js\";\nimport fortify from \"../utils/fortify.js\";\nimport equalAngleLayout from \"./equalAngleLayout.js\";\n\n/**\n * Simple wrapper function for equalAngleLayout()\n */\n\nexport default function (node) {\n var data = {};\n // use the Felsenstein equal angle layout algorithm\n var eq = fortify(equalAngleLayout(node));\n data.data = eq;\n // make the edges dataset\n data.edges = edges(eq);\n\n return data;\n}\n","/**\n * Convert parsed Newick tree from fortify() into data frame of edges\n * this is akin to a \"phylo\" object in R, where thisID and parentId\n * are the $edge slot. I think.\n */\n\nexport default function edges(df, rectangular = false) {\n const rows = [...df].sort((a, b) => a.thisId - b.thisId);\n const byId = new Map(rows.map((r) => [r.thisId, r]));\n const result = [];\n\n for (const row of rows) {\n if (row.parentId == null) continue;\n const parent = byId.get(row.parentId);\n if (!parent) continue;\n\n if (rectangular) {\n result.push({ x1: row.x, y1: row.y, id1: row.thisId, x2: parent.x, y2: row.y, id2: undefined });\n result.push({ x1: parent.x, y1: row.y, id1: undefined, x2: parent.x, y2: parent.y, id2: row.parentId });\n } else {\n result.push({ x1: row.x, y1: row.y, id1: row.thisId, x2: parent.x, y2: parent.y, id2: row.parentId });\n }\n }\n return result;\n}\n\n","/**\n * Parse a Newick tree string into a doubly-linked list of JS Objects.\n * Assigns labels, branch lengths, and node IDs (tips before internals if input emits them that way).\n *\n * Notes / limitations:\n * - Quoted labels and NHX annotations are not fully supported.\n * - Branch lengths in scientific notation are supported (parseFloat).\n */\n\nexport default function readTree(text) {\n // Remove all whitespace (space, tabs, newlines)\n text = String(text).replace(/\\s+/g, '');\n\n const tokens = text.split(/(;|\\(|\\)|,)/);\n const root = { parent: null, children: [] };\n let curnode = root;\n let nodeId = 0;\n\n for (const token of tokens) {\n if (!token || token === ';') continue;\n\n if (token === '(') {\n const child = { parent: curnode, children: [] };\n curnode.children.push(child);\n curnode = child; // descend\n } else if (token === ',') {\n // back to parent, then create sibling\n curnode = curnode.parent;\n const child = { parent: curnode, children: [] };\n curnode.children.push(child);\n curnode = child;\n } else if (token === ')') {\n // ascend one level\n curnode = curnode.parent;\n if (curnode === null) break;\n } else {\n // label/branch-length chunk (e.g., \"A:0.01\" or \"A\")\n const nodeinfo = token.split(':');\n if (nodeinfo.length === 1) {\n if (token.startsWith(':')) {\n curnode.label = '';\n curnode.branchLength = parseFloat(nodeinfo[0]);\n } else {\n curnode.label = nodeinfo[0];\n curnode.branchLength = null;\n }\n } else if (nodeinfo.length === 2) {\n curnode.label = nodeinfo[0];\n curnode.branchLength = parseFloat(nodeinfo[1]);\n } else {\n console.warn(token, \"Unhandled token with multiple ':' characters\");\n curnode.label = nodeinfo[0] || '';\n curnode.branchLength = parseFloat(nodeinfo[nodeinfo.length - 1]);\n }\n curnode.id = nodeId++; // assign then increment\n }\n }\n\n // Ensure root has an id if not assigned during parsing\n if (root.id == null) root.id = nodeId;\n\n return root;\n}\n\n","import * as d3 from \"d3\";\nimport * as lw from \"../index.js\";\n\nexport default function drawPhylogeny(\n treeText,\n {\n layout = \"rect\", // rect/radial/unrooted\n width = 800,\n height = 800,\n margin = { top: 20, right: 300, bottom: 20, left: 50 },\n radialMargin = 80,\n strokeWidth = 1, // for the phylogeny branches\n radialMode = \"outer\", // \"outer\" (co-circular tips) or \"phylo\" (true terminals)\n tipLabels = true,\n showTooltips = true,\n tooltipFormatter = (d, rtt) =>\n `${d.thisLabel ?? \"(unnamed)\"}\\nroot→tip: ${(+rtt).toFixed(4)}`,\n hoverStroke = \"#1f77b4\",\n hoverWidth = 3,\n highlightTips = [], // array of tip labels or ids for static highlight (optional)\n highlightStroke = \"#e63946\",\n highlightWidth = 2.5\n } = {}\n) {\n\n // shared helpers\n const isNumber = (x) => typeof x === \"number\" && Number.isFinite(x);\n // Works for both radial (uses `r`) and rect (uses `x1`).\n // Falls back to summing branchLength up to the root if neither is present.\n function makeRootToTipGetter(byId, { prefer = \"auto\" } = {}) {\n return function rootToTip(tipId) {\n let n = byId.get(tipId);\n if (!n) return 0;\n\n // Prefer explicit cumulative fields if present\n if (prefer === \"r\" || (prefer === \"auto\" && \"r\" in n)) {\n return Number(n.r ?? 0);\n }\n if (prefer === \"x1\" || (prefer === \"auto\" && \"x1\" in n)) {\n return Number(n.x1 ?? 0);\n }\n\n // Fallback: sum branchLength up the ancestry\n let sum = 0;\n while (n && n.parentId != null) {\n sum += Number(n.branchLength || 0); // null/undefined → 0\n n = byId.get(n.parentId);\n }\n return sum;\n };\n }\n\n\n if (layout === \"rect\") {\n // RECTANGULAR LAYOUT\n const tree_df = lw.rectangleLayout(lw.readTree(treeText));\n const horizontal = tree_df.horizontal_lines;\n const vertical = tree_df.vertical_lines;\n const tips = horizontal.filter((d) => d.isTip);\n\n // indices & root→tip getter\n const byId = new Map(horizontal.map((d) => [d.thisId, d]));\n const tipById = new Map(tips.map((d) => [d.thisId, d]));\n const tipByLabel = new Map(tips.map((d) => [d.thisLabel, d]));\n const rootToTip = makeRootToTipGetter(byId, { prefer: \"x1\" });\n\n const maxY = d3.max(horizontal, (d) => d.y1);\n const minY = d3.min(horizontal, (d) => d.y1);\n const maxX = d3.max(horizontal, (d) => d.x1);\n\n const yScale = d3\n .scaleLinear()\n .domain([minY - 1, maxY + 1])\n .range([margin.top, height - margin.bottom]);\n\n const xScale = d3\n .scaleLinear()\n .domain([0, maxX])\n .range([margin.left, width - margin.right]);\n\n const svg = d3\n .create(\"svg\")\n .attr(\"width\", width)\n .attr(\"height\", height)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"font-size\", 10);\n\n const group = svg.append(\"g\");\n\n // layers for highlight/hover\n const staticLayer = svg.append(\"g\").attr(\"class\", \"phylo_static_highlight\");\n const hoverLayer = svg.append(\"g\").attr(\"class\", \"phylo_hover_highlight\");\n\n group\n .selectAll(\".hline\")\n .data(horizontal)\n .join(\"line\")\n .attr(\"x1\", (d) => xScale(d.x0))\n .attr(\"y1\", (d) => yScale(d.y0))\n .attr(\"x2\", (d) => xScale(d.x1))\n .attr(\"y2\", (d) => yScale(d.y1))\n .attr(\"stroke\", \"#555\")\n .attr(\"stroke-width\", strokeWidth);\n\n group\n .selectAll(\".vline\")\n .data(vertical)\n .join(\"line\")\n .attr(\"x1\", (d) => xScale(d.x0))\n .attr(\"y1\", (d) => yScale(d.y0))\n .attr(\"x2\", (d) => xScale(d.x1))\n .attr(\"y2\", (d) => yScale(d.y1))\n .attr(\"stroke\", \"#555\")\n .attr(\"stroke-width\", strokeWidth);\n\n // tip dots\n const tipDots = group\n .selectAll(\".tip-dot\")\n .data(tips)\n .join(\"circle\")\n .attr(\"cx\", (d) => xScale(d.x1))\n .attr(\"cy\", (d) => yScale(d.y1))\n .attr(\"r\", 2)\n .attr(\"fill\", \"black\");\n\n // tooltips for rect dots\n if (showTooltips) {\n tipDots\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n // interactive root→tip highlight (rect) on dot hover\n tipDots\n .on(\"mouseenter\", function(_event, d) {\n drawRectPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).attr(\"r\", 4);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).attr(\"r\", 2);\n });\n\n // labels\n if (tipLabels) {\n const labels = svg\n .append(\"g\")\n .attr(\"class\", \"phylo_labels\")\n .selectAll(\"text\")\n .data(tips)\n .join(\"text\")\n .attr(\"x\", (d) => xScale(d.x1) + 4)\n .attr(\"y\", (d) => yScale(d.y1))\n .attr(\"dy\", \"0.32em\")\n .attr(\"font-size\", 10)\n .text((d) => d.thisLabel?.replace(/_/g, \" \") ?? \"\");\n\n if (showTooltips) {\n labels\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n labels\n .on(\"mouseenter\", function(_event, d) {\n drawRectPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).attr(\"font-weight\", 600);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).attr(\"font-weight\", null);\n });\n }\n\n // static highlight by ids/labels\n if (highlightTips && highlightTips.length) {\n const chosen = new Set(\n [\n ...highlightTips.filter(isNumber).map((id) => tipById.get(id)),\n ...highlightTips\n .filter((x) => !isNumber(x))\n .map((lb) => tipByLabel.get(lb))\n ].filter(Boolean)\n );\n chosen.forEach((tip) => {\n drawRectPath(tip.thisId, staticLayer, highlightStroke, highlightWidth);\n });\n }\n\n // helper to draw root→tip for rect (both vertical+horizontal)\n function drawRectPath(tipId, layer, stroke, width) {\n layer.selectAll(\"*\").remove();\n let cur = byId.get(tipId);\n while (cur && cur.parentId != null) {\n const parent = byId.get(cur.parentId);\n if (!parent) break;\n\n // vertical at junction x0 from parent.y to child.y\n layer\n .append(\"line\")\n .attr(\"x1\", xScale(cur.x0))\n .attr(\"x2\", xScale(cur.x0))\n .attr(\"y1\", yScale(parent.y0))\n .attr(\"y2\", yScale(cur.y0))\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n\n // horizontal along child's y, from junction x0 to x1\n layer\n .append(\"line\")\n .attr(\"x1\", xScale(cur.x0))\n .attr(\"x2\", xScale(cur.x1))\n .attr(\"y1\", yScale(cur.y0))\n .attr(\"y2\", yScale(cur.y1))\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n\n cur = parent;\n }\n }\n\n return svg.node();\n } else if (layout === \"radial\") {\n // RADIAL LAYOUT\n if (width !== height) {\n throw new Error(\"width and height must be the same for radial layout\");\n }\n const parsedTree = lw.readTree(treeText);\n const rad = lw.radialLayout(parsedTree, {\n angleStrategy: \"fan\",\n arcsStyle: \"fan\"\n });\n\n // ===== MODE =====\n const TIP_MODE = radialMode; // \"phylo\" (shorten to original tips) or \"outer\" (project to one circle)\n const isOuter = TIP_MODE === \"outer\";\n if (TIP_MODE !== \"phylo\" && TIP_MODE !== \"outer\") {\n throw new Error(\"radialMode must be either 'phylo' or 'outer'\");\n }\n\n\n // visuals (0 = let spokes reach the dots)\n const DOT_R = 3;\n const END_CAP = 0;\n\n // ===== SCALES / BOUNDS =====\n const maxRadius = d3.max(rad.data, (d) => d.r) ?? 0;\n const scaleRadial = maxRadius + 2 * radialMargin;\n const w = width,\n h = height;\n const centerX = w / 2,\n centerY = h / 2;\n\n const xScaleRadial = d3\n .scaleLinear()\n .domain([-scaleRadial, scaleRadial])\n .range([0, w]);\n const yScaleRadial = d3\n .scaleLinear()\n .domain([-scaleRadial, scaleRadial])\n .range([h, 0]);\n\n const radiusPx = (r) => r * (w / (2 * scaleRadial));\n\n // ===== INDEXES / HELPERS =====\n const byId = new Map(rad.data.map((d) => [d.thisId, d]));\n const tips = rad.data.filter((d) => d.isTip);\n const tipMaxR = tips.length ? d3.max(tips, (d) => d.r) : 0;\n const rootToTip = makeRootToTipGetter(byId, { prefer: \"r\" });\n const tipById = new Map(tips.map((d) => [d.thisId, d])); // HILITE:\n const tipByLabel = new Map(tips.map((d) => [d.thisLabel, d])); // HILITE:\n\n // Robust child-id extractor (handles multiple shapes)\n function childIdOf(spoke) {\n // prefer explicit child id fields; fall back to thisId; last-ditch id1 (seen in some edge shapes)\n return spoke.childId ?? spoke.thisId ?? spoke.id1 ?? null;\n }\n\n // Shorten the *screen-space* end of a spoke by END_CAP px\n function shortenSpokePx(x0, y0, x1, y1) {\n const X0 = xScaleRadial(x0),\n Y0 = yScaleRadial(y0);\n const X1 = xScaleRadial(x1),\n Y1 = yScaleRadial(y1);\n const dx = X1 - X0,\n dy = Y1 - Y0;\n const len = Math.hypot(dx, dy) || 1;\n const t = Math.max(0, (len - END_CAP) / len);\n return { X0, Y0, X1s: X0 + dx * t, Y1s: Y0 + dy * t, len };\n }\n\n // ===== SVG ROOT =====\n const svg = d3\n .create(\"svg\")\n .attr(\"width\", w)\n .attr(\"height\", h)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"font-size\", 10);\n\n const group = svg.append(\"g\");\n\n // overlay groups (drawn on top)\n const staticLines = svg.append(\"g\").attr(\"class\", \"phylo_static_lines\"); // HILITE:\n const staticArcs = svg.append(\"g\").attr(\"class\", \"phylo_static_arcs\"); // HILITE:\n const hoverLines = svg.append(\"g\").attr(\"class\", \"phylo_hover_lines\"); // HILITE:\n const hoverArcs = svg.append(\"g\").attr(\"class\", \"phylo_hover_arcs\"); // HILITE:\n\n // ===== ARCS (parent circles) =====\n group\n .append(\"g\")\n .attr(\"class\", \"phylo_arcs\")\n .selectAll(\"path\")\n .data(rad.arcs)\n .join(\"path\")\n .attr(\"d\", (d) =>\n d.sweep == null\n ? lw.describeArc(\n centerX,\n centerY,\n radiusPx(d.radius),\n d.start,\n d.end\n )\n : lw.describeArcSweep(\n centerX,\n centerY,\n radiusPx(d.radius),\n d.start,\n d.end,\n d.sweep\n )\n )\n .attr(\"fill\", \"none\")\n .attr(\"stroke\", \"#777\")\n .attr(\"stroke-width\", strokeWidth);\n\n // ===== RADII (spokes) =====\n group\n .append(\"g\")\n .attr(\"class\", \"phylo_radii\")\n .selectAll(\"line\")\n .data(rad.radii)\n .join(\"line\")\n .each(function(s, _i) {\n // parent end (data space)\n const x0 = s.x0,\n y0 = s.y0;\n\n // child end (data space), shape-agnostic\n const cid = childIdOf(s);\n const node = cid != null ? byId.get(cid) : undefined;\n const isTipSpoke = !!(node && node.isTip);\n\n // default to the original child endpoint from the spoke record\n let x1 = s.x1,\n y1 = s.y1;\n\n // In \"outer\" mode, project only *tip* spokes to the common circle\n if (isOuter && isTipSpoke) {\n x1 = tipMaxR * Math.cos(node.angle);\n y1 = tipMaxR * Math.sin(node.angle);\n }\n\n // Shorten in screen space so the spoke doesn’t pierce the dot (END_CAP can be 0)\n const { X0, Y0, X1s, Y1s, _len } = shortenSpokePx(x0, y0, x1, y1);\n\n d3.select(this)\n .attr(\"x1\", X0)\n .attr(\"y1\", Y0)\n .attr(\"x2\", X1s)\n .attr(\"y2\", Y1s)\n .attr(\"stroke\", \"#777\")\n .attr(\"stroke-width\", strokeWidth);\n });\n\n // ===== TIP DOTS =====\n const tipDots = group\n .append(\"g\")\n .attr(\"class\", \"phylo_tip_dots\")\n .selectAll(\"circle\")\n .data(tips)\n .join(\"circle\")\n .each(function(d, _i) {\n // dot at original tip (align) or projected circle (outer)\n const x = isOuter ? tipMaxR * Math.cos(d.angle) : d.x;\n const y = isOuter ? tipMaxR * Math.sin(d.angle) : d.y;\n\n d3.select(this)\n .attr(\"cx\", xScaleRadial(x))\n .attr(\"cy\", yScaleRadial(y))\n .attr(\"r\", DOT_R)\n .attr(\"fill\", \"black\")\n .attr(\"stroke\", \"black\")\n .attr(\"stroke-width\", 1.5);\n });\n\n if (showTooltips) {\n tipDots\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n // maps for fast lookup on hover (childId → spoke / arc)\n const key = (x) => (typeof x === \"string\" ? +x : x);\n const spokeByChild = new Map(rad.radii.map(s => [key(s.childId ?? s.thisId ?? s.id1), s]));\n const arcByChild = new Map(rad.child_arcs.map(a => [key(a.childId), a]));\n\n\n // ===== LABELS =====\n // Labels — make them follow the tip position used by the current mode\n if (tipLabels) {\n const labels = group\n .append(\"g\")\n .attr(\"class\", \"phylo_labels\")\n .selectAll(\"g.label\")\n .data(tips)\n .join(\"g\")\n .attr(\"class\", \"label\")\n .attr(\"transform\", (d) => {\n // same tip position rule as dots/spokes:\n // - \"outer\": snap to common ring (tipMaxR)\n // - otherwise (e.g. \"align\"/\"phylo\"): true tip radius\n const r = isOuter ? tipMaxR : d.r;\n const x = r * Math.cos(d.angle);\n const y = r * Math.sin(d.angle);\n return `translate(${xScaleRadial(x)},${yScaleRadial(y)})`;\n })\n .each(function(d) {\n // rotate so text reads outward; flip when on the left side\n let angle = (-d.angle * 180) / Math.PI;\n let xoff = 10; // radial padding for text (px)\n let anchor = \"start\";\n if (d.angle > Math.PI / 2 && d.angle < (3 * Math.PI) / 2) {\n angle += 180;\n xoff *= -1;\n anchor = \"end\";\n }\n d3.select(this)\n .append(\"g\")\n .attr(\"transform\", `rotate(${angle})`)\n .append(\"text\")\n .attr(\"x\", xoff)\n .attr(\"alignment-baseline\", \"middle\")\n .attr(\"text-anchor\", anchor)\n .attr(\"font-size\", 10)\n .attr(\"fill\", \"black\")\n .text((d) => d.thisLabel?.replace(/_/g, \" \") ?? \"\");\n });\n\n if (showTooltips) {\n labels\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n // label hover\n labels\n .on(\"mouseenter\", function(_event, d) {\n drawRadialPath(d, hoverLines, hoverArcs, hoverStroke, hoverWidth);\n d3.select(this).select(\"text\").attr(\"font-weight\", 600);\n })\n .on(\"mouseleave\", function() {\n hoverLines.selectAll(\"*\").remove();\n hoverArcs.selectAll(\"*\").remove();\n d3.select(this).select(\"text\").attr(\"font-weight\", null);\n });\n }\n\n // draw (overlay) the root→tip path: spokes + arcs (half-arc per child)\n function drawRadialPath(\n target,\n lineLayer,\n arcLayer,\n stroke = \"#1f77b4\",\n width = 3\n ) {\n // target may be a tip node *or* a numeric tip id\n lineLayer.selectAll(\"*\").remove();\n arcLayer.selectAll(\"*\").remove();\n\n let cur = (typeof target === \"number\" || typeof target === \"string\")\n ? byId.get(target)\n : target;\n\n if (!cur) return;\n\n let first = true;\n while (cur && cur.parentId != null) {\n // ----- spoke (parent → child) -----\n const s = spokeByChild.get(key(cur.thisId));\n if (s) {\n const px = s.x0,\n py = s.y0;\n let cx = s.x1,\n cy = s.y1;\n if (isOuter && first && cur.isTip) {\n const r = tipMaxR;\n cx = r * Math.cos(cur.angle);\n cy = r * Math.sin(cur.angle);\n }\n const { X0, Y0, X1s, Y1s } = shortenSpokePx(px, py, cx, cy);\n lineLayer\n .append(\"line\")\n .attr(\"x1\", X0)\n .attr(\"y1\", Y0)\n .attr(\"x2\", X1s)\n .attr(\"y2\", Y1s)\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n }\n\n // ----- half-arc at parent radius (parent.angle → child.angle) -----\n const a = arcByChild.get(key(cur.thisId));\n function pathFromArcRecord(rec) {\n const R = radiusPx(rec.radius);\n return rec.sweep == null\n ? lw.describeArc(centerX, centerY, R, rec.start, rec.end)\n : lw.describeArcSweep(centerX, centerY, R, rec.start, rec.end, rec.sweep);\n }\n\n if (a) {\n arcLayer\n .append(\"path\")\n .attr(\"d\", pathFromArcRecord(a))\n .attr(\"fill\", \"none\")\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width);\n }\n\n first = false;\n cur = byId.get(cur.parentId);\n }\n }\n\n // tip dot hover\n tipDots\n .on(\"mouseenter\", function(_event, d) {\n drawRadialPath(d, hoverLines, hoverArcs, hoverStroke, hoverWidth);\n d3.select(this).attr(\"r\", DOT_R + 2);\n })\n .on(\"mouseleave\", function() {\n hoverLines.selectAll(\"*\").remove();\n hoverArcs.selectAll(\"*\").remove();\n d3.select(this).attr(\"r\", DOT_R);\n });\n\n if (highlightTips && highlightTips.length) {\n const chosen = new Set(\n [\n ...highlightTips.filter(isNumber).map((id) => tipById.get(id)),\n ...highlightTips\n .filter((x) => !isNumber(x))\n .map((lb) => tipByLabel.get(lb))\n ].filter(Boolean)\n );\n\n chosen.forEach((tip) => {\n drawRadialPath(\n tip.thisId,\n staticLines,\n staticArcs,\n highlightStroke,\n highlightWidth\n );\n });\n }\n\n return svg.node();\n } else if (layout === \"unrooted\") {\n // UNROOTED LAYOUT\n const parsedTree = lw.readTree(treeText);\n const unrootedPhylo = lw.unrooted(parsedTree);\n\n const w = width;\n const h = height;\n\n const xExtent = d3.extent(unrootedPhylo.data, (d) => d.x);\n const yExtent = d3.extent(unrootedPhylo.data, (d) => d.y);\n const maxX = Math.max(Math.abs(xExtent[0]), Math.abs(xExtent[1]));\n const maxY = Math.max(Math.abs(yExtent[0]), Math.abs(yExtent[1]));\n const maxRadius = Math.max(maxX, maxY);\n const scaleUnroot = maxRadius + 2 * radialMargin;\n\n const xScaleUnroot = d3\n .scaleLinear()\n .domain([-scaleUnroot, scaleUnroot])\n .range([0, w]);\n const yScaleUnroot = d3\n .scaleLinear()\n .domain([-scaleUnroot, scaleUnroot])\n .range([h, 0]);\n\n const svg = d3\n .create(\"svg\")\n .attr(\"width\", w)\n .attr(\"height\", h)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"font-size\", 10);\n\n const group = svg.append(\"g\");\n const staticLayer = svg.append(\"g\").attr(\"class\", \"phylo_static_highlight\");\n const hoverLayer = svg.append(\"g\").attr(\"class\", \"phylo_hover_highlight\");\n\n group\n .append(\"g\")\n .attr(\"class\", \"phylo_lines\")\n .selectAll(\"line\")\n .data(unrootedPhylo.edges)\n .join(\"line\")\n .attr(\"x1\", (d) => xScaleUnroot(d.x1))\n .attr(\"y1\", (d) => yScaleUnroot(d.y1))\n .attr(\"x2\", (d) => xScaleUnroot(d.x2))\n .attr(\"y2\", (d) => yScaleUnroot(d.y2))\n .attr(\"stroke-width\", strokeWidth)\n .attr(\"stroke\", \"#777\");\n\n const nodes = group\n .append(\"g\")\n .attr(\"class\", \"phylo_points\")\n .selectAll(\"circle\")\n .data(unrootedPhylo.data)\n .join(\"circle\")\n .attr(\"class\", \"dot\")\n .attr(\"r\", (d) => (d.isTip ? 4 : 0))\n .attr(\"cx\", (d) => xScaleUnroot(d.x))\n .attr(\"cy\", (d) => yScaleUnroot(d.y))\n .attr(\"stroke\", \"black\")\n .attr(\"stroke-width\", 2)\n .attr(\"fill\", (d) => (d.isTip ? \"black\" : \"white\"));\n\n const byId = new Map(unrootedPhylo.data.map((d) => [d.thisId, d]));\n const tipById = new Map(\n unrootedPhylo.data.filter((d) => d.isTip).map((d) => [d.thisId, d])\n );\n const tipByLabel = new Map(\n unrootedPhylo.data.filter((d) => d.isTip).map((d) => [d.thisLabel, d])\n );\n const rootToTip = makeRootToTipGetter(byId);\n\n if (showTooltips) {\n nodes\n .filter((d) => d.isTip)\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n const tipEdges = new Map();\n const nodesById = new Map(unrootedPhylo.data.map((d) => [d.thisId, d]));\n unrootedPhylo.edges.forEach((edge) => {\n const tipNode = nodesById.get(edge.id1);\n if (tipNode?.isTip) tipEdges.set(edge.id1, edge);\n });\n\n if (tipLabels) {\n const tipLabelsSel = group\n .append(\"g\")\n .attr(\"class\", \"phylo_labels\")\n .selectAll(\"g\")\n .data(unrootedPhylo.data.filter((d) => d.isTip))\n .join(\"g\")\n .attr(\"transform\", (d) => {\n const x = xScaleUnroot(d.x);\n const y = yScaleUnroot(d.y);\n return `translate(${x},${y})`;\n })\n .each(function(d) {\n const edge = tipEdges.get(d.thisId);\n if (!edge) return;\n\n const x1 = xScaleUnroot(edge.x1);\n const y1 = yScaleUnroot(edge.y1);\n const x2 = xScaleUnroot(edge.x2);\n const y2 = yScaleUnroot(edge.y2);\n\n const dx = x2 - x1;\n const dy = y2 - y1;\n let angle = (Math.atan2(dy, dx) * 180) / Math.PI;\n\n let xOffset = -10;\n let anchor = \"end\";\n if (angle > 90 || angle < -90) {\n angle += 180;\n anchor = \"start\";\n xOffset = 10;\n }\n\n d3.select(this)\n .append(\"g\")\n .attr(\"transform\", `rotate(${angle})`)\n .append(\"text\")\n .attr(\"x\", xOffset)\n .attr(\"alignment-baseline\", \"middle\")\n .attr(\"text-anchor\", anchor)\n .attr(\"font-size\", 10)\n .attr(\"fill\", \"black\")\n .text(d.thisLabel?.replace(/_/g, \" \") ?? \"\");\n });\n\n if (showTooltips) {\n tipLabelsSel\n .append(\"title\")\n .text((d) => tooltipFormatter(d, rootToTip(d.thisId)));\n }\n\n tipLabelsSel\n .on(\"mouseenter\", function(_event, d) {\n drawUnrootedPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).select(\"text\").attr(\"font-weight\", 600);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).select(\"text\").attr(\"font-weight\", null);\n });\n }\n\n nodes\n .filter((d) => d.isTip)\n .on(\"mouseenter\", function(_event, d) {\n drawUnrootedPath(d.thisId, hoverLayer, hoverStroke, hoverWidth);\n d3.select(this).attr(\"r\", 6);\n })\n .on(\"mouseleave\", function() {\n hoverLayer.selectAll(\"*\").remove();\n d3.select(this).attr(\"r\", 4);\n });\n\n if (highlightTips && highlightTips.length) {\n const chosen = new Set(\n [\n ...highlightTips.filter(isNumber).map((id) => tipById.get(id)),\n ...highlightTips\n .filter((x) => !isNumber(x))\n .map((lb) => tipByLabel.get(lb))\n ].filter(Boolean)\n );\n chosen.forEach((tip) => {\n drawUnrootedPath(\n tip.thisId,\n staticLayer,\n highlightStroke,\n highlightWidth\n );\n });\n }\n\n function drawUnrootedPath(tipId, layer, stroke, width) {\n const edgeFromChild = new Map(unrootedPhylo.edges.map((e) => [e.id1, e]));\n layer.selectAll(\"*\").remove();\n let cur = byId.get(tipId);\n while (cur && cur.parentId != null) {\n const e = edgeFromChild.get(cur.thisId);\n if (e) {\n layer\n .append(\"line\")\n .attr(\"x1\", xScaleUnroot(e.x1))\n .attr(\"y1\", yScaleUnroot(e.y1))\n .attr(\"x2\", xScaleUnroot(e.x2))\n .attr(\"y2\", yScaleUnroot(e.y2))\n .attr(\"stroke\", stroke)\n .attr(\"stroke-width\", width)\n .attr(\"stroke-linecap\", \"round\");\n }\n cur = byId.get(cur.parentId);\n }\n }\n\n return svg.node();\n } else {\n throw new Error(\n \"Unsupported layout type. Use 'rect', 'radial', or 'unrooted'.\"\n );\n }\n}\n"],"names":["polarToCartesian","cx","cy","r","t","x","Math","cos","y","sin","describeArc","radius","startAngle","endAngle","TAU","PI","norm","a0","a1","tmp","delta","p","largeArcFlag","p0","p1","describeArcSweep","sweep","preorder","node","list","push","i","children","length","fortify","tree","sort","df","parent","parentId","parentLabel","thisId","id","thisLabel","label","map","branchLength","isTip","angle","a","b","radialData","pd","byId","Map","d","kids","root","tipIds","dfs","c","get","ch","N","max","forEach","set","setInternalAngles","sx","sy","th","atan2","setR","r0","child","bl","fanAngles","opts","openAngleDeg","rotateDeg","gap","rot","step","setInternal","arr","ref","unwrapAround","reduce","s","v","radialLayout","angleStrategy","arcsStyle","angleMap","radii","find","segments","theta","r1","childId","x0","y0","x1","y1","getRadiiFromPd","data","getRadii","arcs","first","last","start","end","getArcsFan","childrenByParent","has","pid","A","k","aMin","aMax","direct","wrapped","span","isFinite","getArcs","child_arcs","ccwDelta","cwDelta","midCCW","key","filter","parentRaw","Boolean","u","prev","cur","next","getChildArcsFan","getChildArcs","getHorizontal","idIndex","leafIds","n","tipSlot","setY","values","count","sum","value","mean","setX","xParent","row","_y","_x","_angle","item","rectangleLayout","vertical_lines","byParent","verticals","entries","yvals","min","heights","getVertical","child_vertical_lines","childVerticals","yc","yp","getChildVerticals","horizontal_lines","equalAngleLayout","post","ntips","thisnode","result","queue","curnode","shift","levelorder","numTips","lastStart","arc","unrooted","eq","edges","rectangular","rows","id1","x2","y2","id2","undefined","readTree","text","tokens","String","replace","split","nodeId","token","nodeinfo","startsWith","parseFloat","console","warn","treeText","layout","width","height","margin","top","right","bottom","left","radialMargin","strokeWidth","radialMode","tipLabels","showTooltips","tooltipFormatter","rtt","toFixed","hoverStroke","hoverWidth","highlightTips","highlightStroke","highlightWidth","isNumber","Number","makeRootToTipGetter","prefer","tipId","tree_df","lw.rectangleLayout","lw.readTree","horizontal","vertical","tips","tipById","tipByLabel","rootToTip","maxY","d3","minY","maxX","yScale","scaleLinear","domain","range","xScale","svg","create","attr","group","append","staticLayer","hoverLayer","selectAll","join","tipDots","on","_event","drawRectPath","select","this","remove","labels","Set","lb","tip","layer","stroke","Error","rad","lw.radialLayout","isOuter","DOT_R","END_CAP","scaleRadial","w","h","centerX","centerY","xScaleRadial","yScaleRadial","radiusPx","tipMaxR","shortenSpokePx","X0","Y0","dx","dy","len","hypot","X1s","Y1s","staticLines","staticArcs","hoverLines","hoverArcs","lw.describeArc","lw.describeArcSweep","each","_i","cid","spoke","isTipSpoke","spokeByChild","arcByChild","xoff","anchor","drawRadialPath","target","lineLayer","arcLayer","px","py","pathFromArcRecord","rec","R","unrootedPhylo","lw.unrooted","xExtent","extent","yExtent","abs","scaleUnroot","xScaleUnroot","yScaleUnroot","nodes","tipEdges","nodesById","edge","tipNode","tipLabelsSel","xOffset","drawUnrootedPath","edgeFromChild","e"],"mappings":"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"}