@niivue/niivue 0.45.0 → 0.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- (function(Ut,at){typeof exports=="object"&&typeof module<"u"?at(exports):typeof define=="function"&&define.amd?define(["exports"],at):(Ut=typeof globalThis<"u"?globalThis:Ut||self,at(Ut.niivue={}))})(this,function(exports){"use strict";var zp=Object.defineProperty;var Qp=(Ut,at,bt)=>at in Ut?zp(Ut,at,{enumerable:!0,configurable:!0,writable:!0,value:bt}):Ut[at]=bt;var d0=(Ut,at,bt)=>Qp(Ut,typeof at!="symbol"?at+"":at,bt);function _mergeNamespaces(C,u){for(var h=0;h<u.length;h++){const g=u[h];if(typeof g!="string"&&!Array.isArray(g)){for(const p in g)if(p!=="default"&&!(p in C)){const w=Object.getOwnPropertyDescriptor(g,p);w&&Object.defineProperty(C,p,w.get?w:{enumerable:!0,get:()=>g[p]})}}}return Object.freeze(Object.defineProperty(C,Symbol.toStringTag,{value:"Module"}))}var EPSILON=1e-6,ARRAY_TYPE=typeof Float32Array<"u"?Float32Array:Array;Math.hypot||(Math.hypot=function(){for(var C=0,u=arguments.length;u--;)C+=arguments[u]*arguments[u];return Math.sqrt(C)});function create$4(){var C=new ARRAY_TYPE(9);return ARRAY_TYPE!=Float32Array&&(C[1]=0,C[2]=0,C[3]=0,C[5]=0,C[6]=0,C[7]=0),C[0]=1,C[4]=1,C[8]=1,C}function fromValues$4(C,u,h,g,p,w,T,E,L){var U=new ARRAY_TYPE(9);return U[0]=C,U[1]=u,U[2]=h,U[3]=g,U[4]=p,U[5]=w,U[6]=T,U[7]=E,U[8]=L,U}function multiply$1(C,u,h){var g=u[0],p=u[1],w=u[2],T=u[3],E=u[4],L=u[5],U=u[6],O=u[7],d=u[8],M=h[0],e=h[1],b=h[2],z=h[3],W=h[4],o0=h[5],J=h[6],K=h[7],y=h[8];return C[0]=M*g+e*T+b*U,C[1]=M*p+e*E+b*O,C[2]=M*w+e*L+b*d,C[3]=z*g+W*T+o0*U,C[4]=z*p+W*E+o0*O,C[5]=z*w+W*L+o0*d,C[6]=J*g+K*T+y*U,C[7]=J*p+K*E+y*O,C[8]=J*w+K*L+y*d,C}function create$3(){var C=new ARRAY_TYPE(16);return ARRAY_TYPE!=Float32Array&&(C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[6]=0,C[7]=0,C[8]=0,C[9]=0,C[11]=0,C[12]=0,C[13]=0,C[14]=0),C[0]=1,C[5]=1,C[10]=1,C[15]=1,C}function clone$2(C){var u=new ARRAY_TYPE(16);return u[0]=C[0],u[1]=C[1],u[2]=C[2],u[3]=C[3],u[4]=C[4],u[5]=C[5],u[6]=C[6],u[7]=C[7],u[8]=C[8],u[9]=C[9],u[10]=C[10],u[11]=C[11],u[12]=C[12],u[13]=C[13],u[14]=C[14],u[15]=C[15],u}function copy$1(C,u){return C[0]=u[0],C[1]=u[1],C[2]=u[2],C[3]=u[3],C[4]=u[4],C[5]=u[5],C[6]=u[6],C[7]=u[7],C[8]=u[8],C[9]=u[9],C[10]=u[10],C[11]=u[11],C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15],C}function fromValues$3(C,u,h,g,p,w,T,E,L,U,O,d,M,e,b,z){var W=new ARRAY_TYPE(16);return W[0]=C,W[1]=u,W[2]=h,W[3]=g,W[4]=p,W[5]=w,W[6]=T,W[7]=E,W[8]=L,W[9]=U,W[10]=O,W[11]=d,W[12]=M,W[13]=e,W[14]=b,W[15]=z,W}function identity$1(C){return C[0]=1,C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[5]=1,C[6]=0,C[7]=0,C[8]=0,C[9]=0,C[10]=1,C[11]=0,C[12]=0,C[13]=0,C[14]=0,C[15]=1,C}function transpose(C,u){if(C===u){var h=u[1],g=u[2],p=u[3],w=u[6],T=u[7],E=u[11];C[1]=u[4],C[2]=u[8],C[3]=u[12],C[4]=h,C[6]=u[9],C[7]=u[13],C[8]=g,C[9]=w,C[11]=u[14],C[12]=p,C[13]=T,C[14]=E}else C[0]=u[0],C[1]=u[4],C[2]=u[8],C[3]=u[12],C[4]=u[1],C[5]=u[5],C[6]=u[9],C[7]=u[13],C[8]=u[2],C[9]=u[6],C[10]=u[10],C[11]=u[14],C[12]=u[3],C[13]=u[7],C[14]=u[11],C[15]=u[15];return C}function invert(C,u){var h=u[0],g=u[1],p=u[2],w=u[3],T=u[4],E=u[5],L=u[6],U=u[7],O=u[8],d=u[9],M=u[10],e=u[11],b=u[12],z=u[13],W=u[14],o0=u[15],J=h*E-g*T,K=h*L-p*T,y=h*U-w*T,Z=g*L-p*E,X=g*U-w*E,e0=p*U-w*L,s0=O*z-d*b,j=O*W-M*b,f=O*o0-e*b,g0=d*W-M*z,c0=d*o0-e*z,A0=M*o0-e*W,v0=J*A0-K*c0+y*g0+Z*f-X*j+e0*s0;return v0?(v0=1/v0,C[0]=(E*A0-L*c0+U*g0)*v0,C[1]=(p*c0-g*A0-w*g0)*v0,C[2]=(z*e0-W*X+o0*Z)*v0,C[3]=(M*X-d*e0-e*Z)*v0,C[4]=(L*f-T*A0-U*j)*v0,C[5]=(h*A0-p*f+w*j)*v0,C[6]=(W*y-b*e0-o0*K)*v0,C[7]=(O*e0-M*y+e*K)*v0,C[8]=(T*c0-E*f+U*s0)*v0,C[9]=(g*f-h*c0-w*s0)*v0,C[10]=(b*X-z*y+o0*J)*v0,C[11]=(d*y-O*X-e*J)*v0,C[12]=(E*j-T*g0-L*s0)*v0,C[13]=(h*g0-g*j+p*s0)*v0,C[14]=(z*K-b*Z-W*J)*v0,C[15]=(O*Z-d*K+M*J)*v0,C):null}function multiply(C,u,h){var g=u[0],p=u[1],w=u[2],T=u[3],E=u[4],L=u[5],U=u[6],O=u[7],d=u[8],M=u[9],e=u[10],b=u[11],z=u[12],W=u[13],o0=u[14],J=u[15],K=h[0],y=h[1],Z=h[2],X=h[3];return C[0]=K*g+y*E+Z*d+X*z,C[1]=K*p+y*L+Z*M+X*W,C[2]=K*w+y*U+Z*e+X*o0,C[3]=K*T+y*O+Z*b+X*J,K=h[4],y=h[5],Z=h[6],X=h[7],C[4]=K*g+y*E+Z*d+X*z,C[5]=K*p+y*L+Z*M+X*W,C[6]=K*w+y*U+Z*e+X*o0,C[7]=K*T+y*O+Z*b+X*J,K=h[8],y=h[9],Z=h[10],X=h[11],C[8]=K*g+y*E+Z*d+X*z,C[9]=K*p+y*L+Z*M+X*W,C[10]=K*w+y*U+Z*e+X*o0,C[11]=K*T+y*O+Z*b+X*J,K=h[12],y=h[13],Z=h[14],X=h[15],C[12]=K*g+y*E+Z*d+X*z,C[13]=K*p+y*L+Z*M+X*W,C[14]=K*w+y*U+Z*e+X*o0,C[15]=K*T+y*O+Z*b+X*J,C}function translate(C,u,h){var g=h[0],p=h[1],w=h[2],T,E,L,U,O,d,M,e,b,z,W,o0;return u===C?(C[12]=u[0]*g+u[4]*p+u[8]*w+u[12],C[13]=u[1]*g+u[5]*p+u[9]*w+u[13],C[14]=u[2]*g+u[6]*p+u[10]*w+u[14],C[15]=u[3]*g+u[7]*p+u[11]*w+u[15]):(T=u[0],E=u[1],L=u[2],U=u[3],O=u[4],d=u[5],M=u[6],e=u[7],b=u[8],z=u[9],W=u[10],o0=u[11],C[0]=T,C[1]=E,C[2]=L,C[3]=U,C[4]=O,C[5]=d,C[6]=M,C[7]=e,C[8]=b,C[9]=z,C[10]=W,C[11]=o0,C[12]=T*g+O*p+b*w+u[12],C[13]=E*g+d*p+z*w+u[13],C[14]=L*g+M*p+W*w+u[14],C[15]=U*g+e*p+o0*w+u[15]),C}function scale$3(C,u,h){var g=h[0],p=h[1],w=h[2];return C[0]=u[0]*g,C[1]=u[1]*g,C[2]=u[2]*g,C[3]=u[3]*g,C[4]=u[4]*p,C[5]=u[5]*p,C[6]=u[6]*p,C[7]=u[7]*p,C[8]=u[8]*w,C[9]=u[9]*w,C[10]=u[10]*w,C[11]=u[11]*w,C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15],C}function rotateX(C,u,h){var g=Math.sin(h),p=Math.cos(h),w=u[4],T=u[5],E=u[6],L=u[7],U=u[8],O=u[9],d=u[10],M=u[11];return u!==C&&(C[0]=u[0],C[1]=u[1],C[2]=u[2],C[3]=u[3],C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15]),C[4]=w*p+U*g,C[5]=T*p+O*g,C[6]=E*p+d*g,C[7]=L*p+M*g,C[8]=U*p-w*g,C[9]=O*p-T*g,C[10]=d*p-E*g,C[11]=M*p-L*g,C}function rotateZ(C,u,h){var g=Math.sin(h),p=Math.cos(h),w=u[0],T=u[1],E=u[2],L=u[3],U=u[4],O=u[5],d=u[6],M=u[7];return u!==C&&(C[8]=u[8],C[9]=u[9],C[10]=u[10],C[11]=u[11],C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15]),C[0]=w*p+U*g,C[1]=T*p+O*g,C[2]=E*p+d*g,C[3]=L*p+M*g,C[4]=U*p-w*g,C[5]=O*p-T*g,C[6]=d*p-E*g,C[7]=M*p-L*g,C}function fromRotation(C,u,h){var g=h[0],p=h[1],w=h[2],T=Math.hypot(g,p,w),E,L,U;return T<EPSILON?null:(T=1/T,g*=T,p*=T,w*=T,E=Math.sin(u),L=Math.cos(u),U=1-L,C[0]=g*g*U+L,C[1]=p*g*U+w*E,C[2]=w*g*U-p*E,C[3]=0,C[4]=g*p*U-w*E,C[5]=p*p*U+L,C[6]=w*p*U+g*E,C[7]=0,C[8]=g*w*U+p*E,C[9]=p*w*U-g*E,C[10]=w*w*U+L,C[11]=0,C[12]=0,C[13]=0,C[14]=0,C[15]=1,C)}function orthoNO(C,u,h,g,p,w,T){var E=1/(u-h),L=1/(g-p),U=1/(w-T);return C[0]=-2*E,C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[5]=-2*L,C[6]=0,C[7]=0,C[8]=0,C[9]=0,C[10]=2*U,C[11]=0,C[12]=(u+h)*E,C[13]=(p+g)*L,C[14]=(T+w)*U,C[15]=1,C}var ortho=orthoNO;function multiplyScalar(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C[2]=u[2]*h,C[3]=u[3]*h,C[4]=u[4]*h,C[5]=u[5]*h,C[6]=u[6]*h,C[7]=u[7]*h,C[8]=u[8]*h,C[9]=u[9]*h,C[10]=u[10]*h,C[11]=u[11]*h,C[12]=u[12]*h,C[13]=u[13]*h,C[14]=u[14]*h,C[15]=u[15]*h,C}var mul=multiply;function create$2(){var C=new ARRAY_TYPE(3);return ARRAY_TYPE!=Float32Array&&(C[0]=0,C[1]=0,C[2]=0),C}function clone$1(C){var u=new ARRAY_TYPE(3);return u[0]=C[0],u[1]=C[1],u[2]=C[2],u}function length$1(C){var u=C[0],h=C[1],g=C[2];return Math.hypot(u,h,g)}function fromValues$2(C,u,h){var g=new ARRAY_TYPE(3);return g[0]=C,g[1]=u,g[2]=h,g}function copy(C,u){return C[0]=u[0],C[1]=u[1],C[2]=u[2],C}function add$1(C,u,h){return C[0]=u[0]+h[0],C[1]=u[1]+h[1],C[2]=u[2]+h[2],C}function subtract$1(C,u,h){return C[0]=u[0]-h[0],C[1]=u[1]-h[1],C[2]=u[2]-h[2],C}function min$j(C,u,h){return C[0]=Math.min(u[0],h[0]),C[1]=Math.min(u[1],h[1]),C[2]=Math.min(u[2],h[2]),C}function max$l(C,u,h){return C[0]=Math.max(u[0],h[0]),C[1]=Math.max(u[1],h[1]),C[2]=Math.max(u[2],h[2]),C}function scale$2(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C[2]=u[2]*h,C}function negate(C,u){return C[0]=-u[0],C[1]=-u[1],C[2]=-u[2],C}function normalize$1(C,u){var h=u[0],g=u[1],p=u[2],w=h*h+g*g+p*p;return w>0&&(w=1/Math.sqrt(w)),C[0]=u[0]*w,C[1]=u[1]*w,C[2]=u[2]*w,C}function dot(C,u){return C[0]*u[0]+C[1]*u[1]+C[2]*u[2]}function cross(C,u,h){var g=u[0],p=u[1],w=u[2],T=h[0],E=h[1],L=h[2];return C[0]=p*L-w*E,C[1]=w*T-g*L,C[2]=g*E-p*T,C}function lerp(C,u,h,g){var p=u[0],w=u[1],T=u[2];return C[0]=p+g*(h[0]-p),C[1]=w+g*(h[1]-w),C[2]=T+g*(h[2]-T),C}function angle(C,u){var h=C[0],g=C[1],p=C[2],w=u[0],T=u[1],E=u[2],L=Math.sqrt(h*h+g*g+p*p),U=Math.sqrt(w*w+T*T+E*E),O=L*U,d=O&&dot(C,u)/O;return Math.acos(Math.min(Math.max(d,-1),1))}var sub$1=subtract$1,len=length$1;(function(){var C=create$2();return function(u,h,g,p,w,T){var E,L;for(h||(h=3),g||(g=0),p?L=Math.min(p*h+g,u.length):L=u.length,E=g;E<L;E+=h)C[0]=u[E],C[1]=u[E+1],C[2]=u[E+2],w(C,C,T),u[E]=C[0],u[E+1]=C[1],u[E+2]=C[2];return u}})();function create$1(){var C=new ARRAY_TYPE(4);return ARRAY_TYPE!=Float32Array&&(C[0]=0,C[1]=0,C[2]=0,C[3]=0),C}function clone(C){var u=new ARRAY_TYPE(4);return u[0]=C[0],u[1]=C[1],u[2]=C[2],u[3]=C[3],u}function fromValues$1(C,u,h,g){var p=new ARRAY_TYPE(4);return p[0]=C,p[1]=u,p[2]=h,p[3]=g,p}function add(C,u,h){return C[0]=u[0]+h[0],C[1]=u[1]+h[1],C[2]=u[2]+h[2],C[3]=u[3]+h[3],C}function subtract(C,u,h){return C[0]=u[0]-h[0],C[1]=u[1]-h[1],C[2]=u[2]-h[2],C[3]=u[3]-h[3],C}function scale$1(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C[2]=u[2]*h,C[3]=u[3]*h,C}function transformMat4(C,u,h){var g=u[0],p=u[1],w=u[2],T=u[3];return C[0]=h[0]*g+h[4]*p+h[8]*w+h[12]*T,C[1]=h[1]*g+h[5]*p+h[9]*w+h[13]*T,C[2]=h[2]*g+h[6]*p+h[10]*w+h[14]*T,C[3]=h[3]*g+h[7]*p+h[11]*w+h[15]*T,C}var sub=subtract;(function(){var C=create$1();return function(u,h,g,p,w,T){var E,L;for(h||(h=4),g||(g=0),p?L=Math.min(p*h+g,u.length):L=u.length,E=g;E<L;E+=h)C[0]=u[E],C[1]=u[E+1],C[2]=u[E+2],C[3]=u[E+3],w(C,C,T),u[E]=C[0],u[E+1]=C[1],u[E+2]=C[2],u[E+3]=C[3];return u}})();function create(){var C=new ARRAY_TYPE(2);return ARRAY_TYPE!=Float32Array&&(C[0]=0,C[1]=0),C}function fromValues(C,u){var h=new ARRAY_TYPE(2);return h[0]=C,h[1]=u,h}function scale(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C}function length(C){var u=C[0],h=C[1];return Math.hypot(u,h)}function normalize(C,u){var h=u[0],g=u[1],p=h*h+g*g;return p>0&&(p=1/Math.sqrt(p)),C[0]=u[0]*p,C[1]=u[1]*p,C}(function(){var C=create();return function(u,h,g,p,w,T){var E,L;for(h||(h=2),g||(g=0),p?L=Math.min(p*h+g,u.length):L=u.length,E=g;E<L;E+=h)C[0]=u[E],C[1]=u[E+1],w(C,C,T),u[E]=C[0],u[E+1]=C[1];return u}})();const version="0.45.0",Ut=class Ut{constructor({name:u="niivue",level:h="info"}={}){d0(this,"level");d0(this,"name");this.name=`${u}`,this.level=h}debug(...u){Ut.levels[this.level]>Ut.levels.debug||console.debug(`${this.name}-debug`,...u)}info(...u){Ut.levels[this.level]>Ut.levels.info||console.info(`${this.name}-info`,...u)}warn(...u){Ut.levels[this.level]>Ut.levels.warn||console.warn(`${this.name}-warn`,...u)}error(...u){Ut.levels[this.level]>Ut.levels.error||console.error(`${this.name}-error`,...u)}fatal(...u){Ut.levels[this.level]>Ut.levels.fatal||console.error(`${this.name}-fatal`,...u)}setLogLevel(u){this.level=u}setName(u){this.name=u}};d0(Ut,"levels",{debug:0,info:1,warn:2,error:3,fatal:4,silent:1/0});let Log=Ut;const log=new Log({name:"niivue",level:"info"}),compileShader=function(C,u,h){const g=C.createShader(C.VERTEX_SHADER);C.shaderSource(g,u),C.compileShader(g);const p=C.createShader(C.FRAGMENT_SHADER);C.shaderSource(p,h),C.compileShader(p);const w=C.createProgram();if(C.attachShader(w,g),C.attachShader(w,p),C.linkProgram(w),!C.getProgramParameter(w,C.LINK_STATUS))throw log.error(C.getProgramInfoLog(w)),new Error("Shader failed to link, see console for log");return w};class Shader{constructor(u,h,g){d0(this,"program");d0(this,"uniforms",{});d0(this,"isMatcap");this.program=compileShader(u,h,g);const p=/uniform[^;]+[ ](\w+);/g,w=/uniform[^;]+[ ](\w+);/,T=h.match(p),E=g.match(p);T&&T.forEach(L=>{const U=L.match(w);this.uniforms[U[1]]=-1}),E&&E.forEach(L=>{const U=L.match(w);this.uniforms[U[1]]=-1});for(const L in this.uniforms)this.uniforms[L]=u.getUniformLocation(this.program,L)}use(u){u.useProgram(this.program)}}const vertRenderShader=`#version 300 es
1
+ (function(Ut,at){typeof exports=="object"&&typeof module<"u"?at(exports):typeof define=="function"&&define.amd?define(["exports"],at):(Ut=typeof globalThis<"u"?globalThis:Ut||self,at(Ut.niivue={}))})(this,function(exports){"use strict";var zp=Object.defineProperty;var Qp=(Ut,at,bt)=>at in Ut?zp(Ut,at,{enumerable:!0,configurable:!0,writable:!0,value:bt}):Ut[at]=bt;var d0=(Ut,at,bt)=>Qp(Ut,typeof at!="symbol"?at+"":at,bt);function _mergeNamespaces(C,u){for(var h=0;h<u.length;h++){const g=u[h];if(typeof g!="string"&&!Array.isArray(g)){for(const p in g)if(p!=="default"&&!(p in C)){const w=Object.getOwnPropertyDescriptor(g,p);w&&Object.defineProperty(C,p,w.get?w:{enumerable:!0,get:()=>g[p]})}}}return Object.freeze(Object.defineProperty(C,Symbol.toStringTag,{value:"Module"}))}var EPSILON=1e-6,ARRAY_TYPE=typeof Float32Array<"u"?Float32Array:Array;Math.hypot||(Math.hypot=function(){for(var C=0,u=arguments.length;u--;)C+=arguments[u]*arguments[u];return Math.sqrt(C)});function create$4(){var C=new ARRAY_TYPE(9);return ARRAY_TYPE!=Float32Array&&(C[1]=0,C[2]=0,C[3]=0,C[5]=0,C[6]=0,C[7]=0),C[0]=1,C[4]=1,C[8]=1,C}function fromValues$4(C,u,h,g,p,w,T,E,L){var U=new ARRAY_TYPE(9);return U[0]=C,U[1]=u,U[2]=h,U[3]=g,U[4]=p,U[5]=w,U[6]=T,U[7]=E,U[8]=L,U}function multiply$1(C,u,h){var g=u[0],p=u[1],w=u[2],T=u[3],E=u[4],L=u[5],U=u[6],O=u[7],d=u[8],M=h[0],e=h[1],b=h[2],z=h[3],W=h[4],o0=h[5],J=h[6],K=h[7],y=h[8];return C[0]=M*g+e*T+b*U,C[1]=M*p+e*E+b*O,C[2]=M*w+e*L+b*d,C[3]=z*g+W*T+o0*U,C[4]=z*p+W*E+o0*O,C[5]=z*w+W*L+o0*d,C[6]=J*g+K*T+y*U,C[7]=J*p+K*E+y*O,C[8]=J*w+K*L+y*d,C}function create$3(){var C=new ARRAY_TYPE(16);return ARRAY_TYPE!=Float32Array&&(C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[6]=0,C[7]=0,C[8]=0,C[9]=0,C[11]=0,C[12]=0,C[13]=0,C[14]=0),C[0]=1,C[5]=1,C[10]=1,C[15]=1,C}function clone$2(C){var u=new ARRAY_TYPE(16);return u[0]=C[0],u[1]=C[1],u[2]=C[2],u[3]=C[3],u[4]=C[4],u[5]=C[5],u[6]=C[6],u[7]=C[7],u[8]=C[8],u[9]=C[9],u[10]=C[10],u[11]=C[11],u[12]=C[12],u[13]=C[13],u[14]=C[14],u[15]=C[15],u}function copy$1(C,u){return C[0]=u[0],C[1]=u[1],C[2]=u[2],C[3]=u[3],C[4]=u[4],C[5]=u[5],C[6]=u[6],C[7]=u[7],C[8]=u[8],C[9]=u[9],C[10]=u[10],C[11]=u[11],C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15],C}function fromValues$3(C,u,h,g,p,w,T,E,L,U,O,d,M,e,b,z){var W=new ARRAY_TYPE(16);return W[0]=C,W[1]=u,W[2]=h,W[3]=g,W[4]=p,W[5]=w,W[6]=T,W[7]=E,W[8]=L,W[9]=U,W[10]=O,W[11]=d,W[12]=M,W[13]=e,W[14]=b,W[15]=z,W}function identity$1(C){return C[0]=1,C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[5]=1,C[6]=0,C[7]=0,C[8]=0,C[9]=0,C[10]=1,C[11]=0,C[12]=0,C[13]=0,C[14]=0,C[15]=1,C}function transpose(C,u){if(C===u){var h=u[1],g=u[2],p=u[3],w=u[6],T=u[7],E=u[11];C[1]=u[4],C[2]=u[8],C[3]=u[12],C[4]=h,C[6]=u[9],C[7]=u[13],C[8]=g,C[9]=w,C[11]=u[14],C[12]=p,C[13]=T,C[14]=E}else C[0]=u[0],C[1]=u[4],C[2]=u[8],C[3]=u[12],C[4]=u[1],C[5]=u[5],C[6]=u[9],C[7]=u[13],C[8]=u[2],C[9]=u[6],C[10]=u[10],C[11]=u[14],C[12]=u[3],C[13]=u[7],C[14]=u[11],C[15]=u[15];return C}function invert(C,u){var h=u[0],g=u[1],p=u[2],w=u[3],T=u[4],E=u[5],L=u[6],U=u[7],O=u[8],d=u[9],M=u[10],e=u[11],b=u[12],z=u[13],W=u[14],o0=u[15],J=h*E-g*T,K=h*L-p*T,y=h*U-w*T,Z=g*L-p*E,X=g*U-w*E,e0=p*U-w*L,s0=O*z-d*b,j=O*W-M*b,f=O*o0-e*b,g0=d*W-M*z,c0=d*o0-e*z,A0=M*o0-e*W,v0=J*A0-K*c0+y*g0+Z*f-X*j+e0*s0;return v0?(v0=1/v0,C[0]=(E*A0-L*c0+U*g0)*v0,C[1]=(p*c0-g*A0-w*g0)*v0,C[2]=(z*e0-W*X+o0*Z)*v0,C[3]=(M*X-d*e0-e*Z)*v0,C[4]=(L*f-T*A0-U*j)*v0,C[5]=(h*A0-p*f+w*j)*v0,C[6]=(W*y-b*e0-o0*K)*v0,C[7]=(O*e0-M*y+e*K)*v0,C[8]=(T*c0-E*f+U*s0)*v0,C[9]=(g*f-h*c0-w*s0)*v0,C[10]=(b*X-z*y+o0*J)*v0,C[11]=(d*y-O*X-e*J)*v0,C[12]=(E*j-T*g0-L*s0)*v0,C[13]=(h*g0-g*j+p*s0)*v0,C[14]=(z*K-b*Z-W*J)*v0,C[15]=(O*Z-d*K+M*J)*v0,C):null}function multiply(C,u,h){var g=u[0],p=u[1],w=u[2],T=u[3],E=u[4],L=u[5],U=u[6],O=u[7],d=u[8],M=u[9],e=u[10],b=u[11],z=u[12],W=u[13],o0=u[14],J=u[15],K=h[0],y=h[1],Z=h[2],X=h[3];return C[0]=K*g+y*E+Z*d+X*z,C[1]=K*p+y*L+Z*M+X*W,C[2]=K*w+y*U+Z*e+X*o0,C[3]=K*T+y*O+Z*b+X*J,K=h[4],y=h[5],Z=h[6],X=h[7],C[4]=K*g+y*E+Z*d+X*z,C[5]=K*p+y*L+Z*M+X*W,C[6]=K*w+y*U+Z*e+X*o0,C[7]=K*T+y*O+Z*b+X*J,K=h[8],y=h[9],Z=h[10],X=h[11],C[8]=K*g+y*E+Z*d+X*z,C[9]=K*p+y*L+Z*M+X*W,C[10]=K*w+y*U+Z*e+X*o0,C[11]=K*T+y*O+Z*b+X*J,K=h[12],y=h[13],Z=h[14],X=h[15],C[12]=K*g+y*E+Z*d+X*z,C[13]=K*p+y*L+Z*M+X*W,C[14]=K*w+y*U+Z*e+X*o0,C[15]=K*T+y*O+Z*b+X*J,C}function translate(C,u,h){var g=h[0],p=h[1],w=h[2],T,E,L,U,O,d,M,e,b,z,W,o0;return u===C?(C[12]=u[0]*g+u[4]*p+u[8]*w+u[12],C[13]=u[1]*g+u[5]*p+u[9]*w+u[13],C[14]=u[2]*g+u[6]*p+u[10]*w+u[14],C[15]=u[3]*g+u[7]*p+u[11]*w+u[15]):(T=u[0],E=u[1],L=u[2],U=u[3],O=u[4],d=u[5],M=u[6],e=u[7],b=u[8],z=u[9],W=u[10],o0=u[11],C[0]=T,C[1]=E,C[2]=L,C[3]=U,C[4]=O,C[5]=d,C[6]=M,C[7]=e,C[8]=b,C[9]=z,C[10]=W,C[11]=o0,C[12]=T*g+O*p+b*w+u[12],C[13]=E*g+d*p+z*w+u[13],C[14]=L*g+M*p+W*w+u[14],C[15]=U*g+e*p+o0*w+u[15]),C}function scale$3(C,u,h){var g=h[0],p=h[1],w=h[2];return C[0]=u[0]*g,C[1]=u[1]*g,C[2]=u[2]*g,C[3]=u[3]*g,C[4]=u[4]*p,C[5]=u[5]*p,C[6]=u[6]*p,C[7]=u[7]*p,C[8]=u[8]*w,C[9]=u[9]*w,C[10]=u[10]*w,C[11]=u[11]*w,C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15],C}function rotateX(C,u,h){var g=Math.sin(h),p=Math.cos(h),w=u[4],T=u[5],E=u[6],L=u[7],U=u[8],O=u[9],d=u[10],M=u[11];return u!==C&&(C[0]=u[0],C[1]=u[1],C[2]=u[2],C[3]=u[3],C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15]),C[4]=w*p+U*g,C[5]=T*p+O*g,C[6]=E*p+d*g,C[7]=L*p+M*g,C[8]=U*p-w*g,C[9]=O*p-T*g,C[10]=d*p-E*g,C[11]=M*p-L*g,C}function rotateZ(C,u,h){var g=Math.sin(h),p=Math.cos(h),w=u[0],T=u[1],E=u[2],L=u[3],U=u[4],O=u[5],d=u[6],M=u[7];return u!==C&&(C[8]=u[8],C[9]=u[9],C[10]=u[10],C[11]=u[11],C[12]=u[12],C[13]=u[13],C[14]=u[14],C[15]=u[15]),C[0]=w*p+U*g,C[1]=T*p+O*g,C[2]=E*p+d*g,C[3]=L*p+M*g,C[4]=U*p-w*g,C[5]=O*p-T*g,C[6]=d*p-E*g,C[7]=M*p-L*g,C}function fromRotation(C,u,h){var g=h[0],p=h[1],w=h[2],T=Math.hypot(g,p,w),E,L,U;return T<EPSILON?null:(T=1/T,g*=T,p*=T,w*=T,E=Math.sin(u),L=Math.cos(u),U=1-L,C[0]=g*g*U+L,C[1]=p*g*U+w*E,C[2]=w*g*U-p*E,C[3]=0,C[4]=g*p*U-w*E,C[5]=p*p*U+L,C[6]=w*p*U+g*E,C[7]=0,C[8]=g*w*U+p*E,C[9]=p*w*U-g*E,C[10]=w*w*U+L,C[11]=0,C[12]=0,C[13]=0,C[14]=0,C[15]=1,C)}function orthoNO(C,u,h,g,p,w,T){var E=1/(u-h),L=1/(g-p),U=1/(w-T);return C[0]=-2*E,C[1]=0,C[2]=0,C[3]=0,C[4]=0,C[5]=-2*L,C[6]=0,C[7]=0,C[8]=0,C[9]=0,C[10]=2*U,C[11]=0,C[12]=(u+h)*E,C[13]=(p+g)*L,C[14]=(T+w)*U,C[15]=1,C}var ortho=orthoNO;function multiplyScalar(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C[2]=u[2]*h,C[3]=u[3]*h,C[4]=u[4]*h,C[5]=u[5]*h,C[6]=u[6]*h,C[7]=u[7]*h,C[8]=u[8]*h,C[9]=u[9]*h,C[10]=u[10]*h,C[11]=u[11]*h,C[12]=u[12]*h,C[13]=u[13]*h,C[14]=u[14]*h,C[15]=u[15]*h,C}var mul=multiply;function create$2(){var C=new ARRAY_TYPE(3);return ARRAY_TYPE!=Float32Array&&(C[0]=0,C[1]=0,C[2]=0),C}function clone$1(C){var u=new ARRAY_TYPE(3);return u[0]=C[0],u[1]=C[1],u[2]=C[2],u}function length$1(C){var u=C[0],h=C[1],g=C[2];return Math.hypot(u,h,g)}function fromValues$2(C,u,h){var g=new ARRAY_TYPE(3);return g[0]=C,g[1]=u,g[2]=h,g}function copy(C,u){return C[0]=u[0],C[1]=u[1],C[2]=u[2],C}function add$1(C,u,h){return C[0]=u[0]+h[0],C[1]=u[1]+h[1],C[2]=u[2]+h[2],C}function subtract$1(C,u,h){return C[0]=u[0]-h[0],C[1]=u[1]-h[1],C[2]=u[2]-h[2],C}function min$j(C,u,h){return C[0]=Math.min(u[0],h[0]),C[1]=Math.min(u[1],h[1]),C[2]=Math.min(u[2],h[2]),C}function max$l(C,u,h){return C[0]=Math.max(u[0],h[0]),C[1]=Math.max(u[1],h[1]),C[2]=Math.max(u[2],h[2]),C}function scale$2(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C[2]=u[2]*h,C}function negate(C,u){return C[0]=-u[0],C[1]=-u[1],C[2]=-u[2],C}function normalize$1(C,u){var h=u[0],g=u[1],p=u[2],w=h*h+g*g+p*p;return w>0&&(w=1/Math.sqrt(w)),C[0]=u[0]*w,C[1]=u[1]*w,C[2]=u[2]*w,C}function dot(C,u){return C[0]*u[0]+C[1]*u[1]+C[2]*u[2]}function cross(C,u,h){var g=u[0],p=u[1],w=u[2],T=h[0],E=h[1],L=h[2];return C[0]=p*L-w*E,C[1]=w*T-g*L,C[2]=g*E-p*T,C}function lerp(C,u,h,g){var p=u[0],w=u[1],T=u[2];return C[0]=p+g*(h[0]-p),C[1]=w+g*(h[1]-w),C[2]=T+g*(h[2]-T),C}function angle(C,u){var h=C[0],g=C[1],p=C[2],w=u[0],T=u[1],E=u[2],L=Math.sqrt(h*h+g*g+p*p),U=Math.sqrt(w*w+T*T+E*E),O=L*U,d=O&&dot(C,u)/O;return Math.acos(Math.min(Math.max(d,-1),1))}var sub$1=subtract$1,len=length$1;(function(){var C=create$2();return function(u,h,g,p,w,T){var E,L;for(h||(h=3),g||(g=0),p?L=Math.min(p*h+g,u.length):L=u.length,E=g;E<L;E+=h)C[0]=u[E],C[1]=u[E+1],C[2]=u[E+2],w(C,C,T),u[E]=C[0],u[E+1]=C[1],u[E+2]=C[2];return u}})();function create$1(){var C=new ARRAY_TYPE(4);return ARRAY_TYPE!=Float32Array&&(C[0]=0,C[1]=0,C[2]=0,C[3]=0),C}function clone(C){var u=new ARRAY_TYPE(4);return u[0]=C[0],u[1]=C[1],u[2]=C[2],u[3]=C[3],u}function fromValues$1(C,u,h,g){var p=new ARRAY_TYPE(4);return p[0]=C,p[1]=u,p[2]=h,p[3]=g,p}function add(C,u,h){return C[0]=u[0]+h[0],C[1]=u[1]+h[1],C[2]=u[2]+h[2],C[3]=u[3]+h[3],C}function subtract(C,u,h){return C[0]=u[0]-h[0],C[1]=u[1]-h[1],C[2]=u[2]-h[2],C[3]=u[3]-h[3],C}function scale$1(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C[2]=u[2]*h,C[3]=u[3]*h,C}function transformMat4(C,u,h){var g=u[0],p=u[1],w=u[2],T=u[3];return C[0]=h[0]*g+h[4]*p+h[8]*w+h[12]*T,C[1]=h[1]*g+h[5]*p+h[9]*w+h[13]*T,C[2]=h[2]*g+h[6]*p+h[10]*w+h[14]*T,C[3]=h[3]*g+h[7]*p+h[11]*w+h[15]*T,C}var sub=subtract;(function(){var C=create$1();return function(u,h,g,p,w,T){var E,L;for(h||(h=4),g||(g=0),p?L=Math.min(p*h+g,u.length):L=u.length,E=g;E<L;E+=h)C[0]=u[E],C[1]=u[E+1],C[2]=u[E+2],C[3]=u[E+3],w(C,C,T),u[E]=C[0],u[E+1]=C[1],u[E+2]=C[2],u[E+3]=C[3];return u}})();function create(){var C=new ARRAY_TYPE(2);return ARRAY_TYPE!=Float32Array&&(C[0]=0,C[1]=0),C}function fromValues(C,u){var h=new ARRAY_TYPE(2);return h[0]=C,h[1]=u,h}function scale(C,u,h){return C[0]=u[0]*h,C[1]=u[1]*h,C}function length(C){var u=C[0],h=C[1];return Math.hypot(u,h)}function normalize(C,u){var h=u[0],g=u[1],p=h*h+g*g;return p>0&&(p=1/Math.sqrt(p)),C[0]=u[0]*p,C[1]=u[1]*p,C}(function(){var C=create();return function(u,h,g,p,w,T){var E,L;for(h||(h=2),g||(g=0),p?L=Math.min(p*h+g,u.length):L=u.length,E=g;E<L;E+=h)C[0]=u[E],C[1]=u[E+1],w(C,C,T),u[E]=C[0],u[E+1]=C[1];return u}})();const version="0.45.1",Ut=class Ut{constructor({name:u="niivue",level:h="info"}={}){d0(this,"level");d0(this,"name");this.name=`${u}`,this.level=h}debug(...u){Ut.levels[this.level]>Ut.levels.debug||console.debug(`${this.name}-debug`,...u)}info(...u){Ut.levels[this.level]>Ut.levels.info||console.info(`${this.name}-info`,...u)}warn(...u){Ut.levels[this.level]>Ut.levels.warn||console.warn(`${this.name}-warn`,...u)}error(...u){Ut.levels[this.level]>Ut.levels.error||console.error(`${this.name}-error`,...u)}fatal(...u){Ut.levels[this.level]>Ut.levels.fatal||console.error(`${this.name}-fatal`,...u)}setLogLevel(u){this.level=u}setName(u){this.name=u}};d0(Ut,"levels",{debug:0,info:1,warn:2,error:3,fatal:4,silent:1/0});let Log=Ut;const log=new Log({name:"niivue",level:"info"}),compileShader=function(C,u,h){const g=C.createShader(C.VERTEX_SHADER);C.shaderSource(g,u),C.compileShader(g);const p=C.createShader(C.FRAGMENT_SHADER);C.shaderSource(p,h),C.compileShader(p);const w=C.createProgram();if(C.attachShader(w,g),C.attachShader(w,p),C.linkProgram(w),!C.getProgramParameter(w,C.LINK_STATUS))throw log.error(C.getProgramInfoLog(w)),new Error("Shader failed to link, see console for log");return w};class Shader{constructor(u,h,g){d0(this,"program");d0(this,"uniforms",{});d0(this,"isMatcap");this.program=compileShader(u,h,g);const p=/uniform[^;]+[ ](\w+);/g,w=/uniform[^;]+[ ](\w+);/,T=h.match(p),E=g.match(p);T&&T.forEach(L=>{const U=L.match(w);this.uniforms[U[1]]=-1}),E&&E.forEach(L=>{const U=L.match(w);this.uniforms[U[1]]=-1});for(const L in this.uniforms)this.uniforms[L]=u.getUniformLocation(this.program,L)}use(u){u.useProgram(this.program)}}const vertRenderShader=`#version 300 es
2
2
  #line 4
3
3
  layout(location=0) in vec3 pos;
4
4
  layout(location=1) in vec3 texCoords;
@@ -1736,7 +1736,7 @@ If this abort() is unexpected, build with -s ASSERTIONS=1 which can give more in
1736
1736
  `);E[0].startsWith("NRRD")||alert("Invalid NRRD image");const L=E.length;let U=!1,O=!1,d=!1;const M=fromValues$4(NaN,0,0,0,1,0,0,0,1),e=fromValues$2(0,0,0);let b=create$4();for(let z=1;z<L;z++){let W=E[z];if(W[0]==="#")continue;W=W.toLowerCase();const o0=W.split(":");if(o0.length<2)continue;const J=o0[0].trim();let K=o0[1].trim();switch(K=K.replaceAll(")"," "),K=K.replaceAll("("," "),K=K.trim(),J){case"data file":d=!0;break;case"encoding":K.includes("raw")?U=!1:K.includes("gz")?U=!0:alert("Unsupported NRRD encoding");break;case"type":switch(K){case"uchar":case"unsigned char":case"uint8":case"uint8_t":g.numBitsPerVoxel=8,g.datatypeCode=NiiDataType.DT_UINT8;break;case"signed char":case"int8":case"int8_t":g.numBitsPerVoxel=8,g.datatypeCode=NiiDataType.DT_INT8;break;case"short":case"short int":case"signed short":case"signed short int":case"int16":case"int16_t":g.numBitsPerVoxel=16,g.datatypeCode=NiiDataType.DT_INT16;break;case"ushort":case"unsigned short":case"unsigned short int":case"uint16":case"uint16_t":g.numBitsPerVoxel=16,g.datatypeCode=NiiDataType.DT_UINT16;break;case"int":case"signed int":case"int32":case"int32_t":g.numBitsPerVoxel=32,g.datatypeCode=NiiDataType.DT_INT32;break;case"uint":case"unsigned int":case"uint32":case"uint32_t":g.numBitsPerVoxel=32,g.datatypeCode=NiiDataType.DT_UINT32;break;case"float":g.numBitsPerVoxel=32,g.datatypeCode=NiiDataType.DT_FLOAT32;break;case"double":g.numBitsPerVoxel=64,g.datatypeCode=NiiDataType.DT_FLOAT64;break;default:throw new Error("Unsupported NRRD data type: "+K)}break;case"spacings":{const y=K.split(/[ ,]+/);for(let Z=0;Z<y.length;Z++)g.pixDims[Z+1]=parseFloat(y[Z])}break;case"sizes":{const y=K.split(/[ ,]+/);g.dims[0]=y.length;for(let Z=0;Z<y.length;Z++)g.dims[Z+1]=parseInt(y[Z])}break;case"endian":K.includes("little")?g.littleEndian=!0:K.includes("big")&&(g.littleEndian=!1);break;case"space directions":{const y=K.split(/[ ,]+/);if(y.length!==9)break;for(let Z=0;Z<9;Z++)M[Z]=parseFloat(y[Z])}break;case"space origin":{const y=K.split(/[ ,]+/);if(y.length!==3)break;e[0]=parseFloat(y[0]),e[1]=parseFloat(y[1]),e[2]=parseFloat(y[2])}break;case"space units":K.includes("microns")&&(O=!0);break;case"space":K.includes("right-anterior-superior")||K.includes("ras")?b=fromValues$4(1,0,0,0,1,0,0,0,1):K.includes("left-anterior-superior")||K.includes("las")?b=fromValues$4(-1,0,0,0,1,0,0,0,1):K.includes("left-posterior-superior")||K.includes("lps")?b=fromValues$4(-1,0,0,0,-1,0,0,0,1):log.warn("Unsupported NRRD space value:",K);break;default:log.warn("Unknown:",J)}}if(!isNaN(M[0])){this.hdr.sform_code=2,O&&(multiplyScalar(M,M,.001),e[0]*=.001,e[1]*=.001,e[2]*=.001),b[0]<0&&(e[0]=-e[0]),b[4]<0&&(e[1]=-e[1]),b[8]<0&&(e[2]=-e[2]),multiply$1(M,b,M);const z=fromValues$3(M[0],M[3],M[6],e[0],M[1],M[4],M[7],e[1],M[2],M[5],M[8],e[2],0,0,0,1),W=this.vox2mm([0,0,0],z),o0=this.vox2mm([1,0,0],z);subtract$1(o0,o0,W);const J=this.vox2mm([0,1,0],z);subtract$1(J,J,W);const K=this.vox2mm([0,0,1],z);subtract$1(K,K,W),g.pixDims[1]=length$1(o0),g.pixDims[2]=length$1(J),g.pixDims[3]=length$1(K),g.affine=[[z[0],z[1],z[2],z[3]],[z[4],z[5],z[6],z[7]],[z[8],z[9],z[10],z[11]],[0,0,0,1]]}return d&&h?h.slice(0):(d&&log.warn("Missing data: NRRD header describes detached data file but only one URL provided"),U?decompressSync$1(new Uint8Array(u.slice(g.vox_offset))).buffer:u.slice(g.vox_offset))}calculateRAS(){if(!this.hdr)throw new Error("hdr not set");const u=this.hdr.affine,h=this.hdr,g=fromValues$4(Math.abs(u[0][0]),Math.abs(u[0][1]),Math.abs(u[0][2]),Math.abs(u[1][0]),Math.abs(u[1][1]),Math.abs(u[1][2]),Math.abs(u[2][0]),Math.abs(u[2][1]),Math.abs(u[2][2])),p=[1,1,1];g[3]>g[0]&&(p[0]=2),g[6]>g[0]&&g[6]>g[3]&&(p[0]=3),p[1]=1,p[0]===1?g[4]>g[7]?p[1]=2:p[1]=3:p[0]===2?g[1]>g[7]?p[1]=1:p[1]=3:g[1]>g[4]?p[1]=1:p[1]=2,p[2]=6-p[1]-p[0];let w=[1,2,3];w[p[0]-1]=1,w[p[1]-1]=2,w[p[2]-1]=3;let T=fromValues$3(u[0][0],u[0][1],u[0][2],u[0][3],u[1][0],u[1][1],u[1][2],u[1][3],u[2][0],u[2][1],u[2][2],u[2][3],0,0,0,1);this.mm000=this.vox2mm([-.5,-.5,-.5],T),this.mm100=this.vox2mm([h.dims[1]-.5,-.5,-.5],T),this.mm010=this.vox2mm([-.5,h.dims[2]-.5,-.5],T),this.mm001=this.vox2mm([-.5,-.5,h.dims[3]-.5],T);const E=create$3();copy$1(E,T);for(let W=0;W<3;W++)for(let o0=0;o0<3;o0++)E[W*4+o0]=T[W*4+w[o0]-1];const L=[0,0,0];E[0]<0&&(L[0]=1),E[5]<0&&(L[1]=1),E[10]<0&&(L[2]=1),this.dimsRAS=[h.dims[0],h.dims[w[0]],h.dims[w[1]],h.dims[w[2]]],this.pixDimsRAS=[h.pixDims[0],h.pixDims[w[0]],h.pixDims[w[1]],h.pixDims[w[2]]],this.permRAS=w.slice();for(let W=0;W<3;W++)L[W]===1&&(this.permRAS[W]=-this.permRAS[W]);if(this.arrayEquals(w,[1,2,3])&&this.arrayEquals(L,[0,0,0])){this.toRAS=create$3(),this.matRAS=clone$2(T),this.calculateOblique(),this.img2RASstep=[1,this.dimsRAS[1],this.dimsRAS[1]*this.dimsRAS[2]],this.img2RASstart=[0,0,0];return}identity$1(T),T[0+0*4]=1-L[0]*2,T[1+1*4]=1-L[1]*2,T[2+2*4]=1-L[2]*2,T[3+0*4]=(h.dims[w[0]]-1)*L[0],T[3+1*4]=(h.dims[w[1]]-1)*L[1],T[3+2*4]=(h.dims[w[2]]-1)*L[2];const U=create$3();invert(U,T),multiply(U,U,E),this.matRAS=clone$2(U),T=fromValues$3(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1),T[w[0]-1+0*4]=-L[0]*2+1,T[w[1]-1+1*4]=-L[1]*2+1,T[w[2]-1+2*4]=-L[2]*2+1,T[3+0*4]=L[0],T[3+1*4]=L[1],T[3+2*4]=L[2],this.toRAS=clone$2(T),T[3]=0,T[7]=0,T[11]=0,T[12]=0,(this.permRAS[0]===-1||this.permRAS[1]===-1||this.permRAS[2]===-1)&&(T[12]=h.dims[1]-1),T[13]=0,(this.permRAS[0]===-2||this.permRAS[1]===-2||this.permRAS[2]===-2)&&(T[13]=h.dims[2]-1),T[14]=0,(this.permRAS[0]===-3||this.permRAS[1]===-3||this.permRAS[2]===-3)&&(T[14]=h.dims[3]-1),this.toRASvox=clone$2(T),log.debug(this.hdr.dims),log.debug(this.dimsRAS);const O=this.hdr;w=this.permRAS;const d=[Math.abs(w[0]),Math.abs(w[1]),Math.abs(w[2])],M=[O.dims[d[0]],O.dims[d[1]],O.dims[d[2]]],e=[1,O.dims[1],O.dims[1]*O.dims[2]],b=[e[d[0]-1],e[d[1]-1],e[d[2]-1]],z=[0,0,0];for(let W=0;W<3;W++)w[W]<0&&(z[W]=b[W]*(M[W]-1),b[W]=-b[W]);this.img2RASstep=b,this.img2RASstart=z,this.calculateOblique()}img2RAS(){if(!this.permRAS)throw new Error("permRAS undefined");if(!this.img)throw new Error("img undefined");if(!this.hdr)throw new Error("hdr undefined");const u=this.permRAS.slice();if(u[0]===1&&u[1]===2&&u[2]===3)return this.img;const h=this.hdr,g=this.img.slice(0,h.dims[1]*h.dims[1]*h.dims[2]*h.dims[3]),p=[Math.abs(u[0]),Math.abs(u[1]),Math.abs(u[2])],w=[h.dims[p[0]],h.dims[p[1]],h.dims[p[2]]],T=[1,h.dims[1],h.dims[1]*h.dims[2]],E=[T[p[0]-1],T[p[1]-1],T[p[2]-1]],L=[0,0,0];for(let O=0;O<3;O++)u[O]<0&&(L[O]=E[O]*(w[O]-1),E[O]=-E[O]);let U=0;for(let O=0;O<w[2];O++){const d=L[2]+O*E[2];for(let M=0;M<w[1];M++){const e=L[1]+M*E[1];for(let b=0;b<w[0];b++){const z=L[0]+b*E[0];g[U]=this.img[z+e+d],U++}}}return g}vox2mm(u,h){const g=clone$2(h);transpose(g,g);const p=fromValues$1(u[0],u[1],u[2],1);return transformMat4(p,p,g),fromValues$2(p[0],p[1],p[2])}mm2vox(u,h=!1){if(!this.matRAS)throw new Error("matRAS undefined");const g=clone$2(this.matRAS),p=clone$2(g);transpose(p,g),invert(p,p);const w=fromValues$1(u[0],u[1],u[2],1);transformMat4(w,w,p);const T=fromValues$2(w[0],w[1],w[2]);return h?T:[Math.round(T[0]),Math.round(T[1]),Math.round(T[2])]}arrayEquals(u,h){return Array.isArray(u)&&Array.isArray(h)&&u.length===h.length&&u.every((g,p)=>g===h[p])}setColormap(u){this._colormap=u,this.calMinMax(),this.onColormapChange&&this.onColormapChange(this)}setColormapLabel(u){this.colormapLabel=cmapper.makeLabelLut(u)}async setColormapLabelFromUrl(u){this.colormapLabel=await cmapper.makeLabelLutFromUrl(u)}get colormap(){return this._colormap}get colorMap(){return this._colormap}set colormap(u){this.setColormap(u)}set colorMap(u){this.setColormap(u)}get opacity(){return this._opacity}set opacity(u){this._opacity=u,this.onOpacityChange&&this.onOpacityChange(this)}calMinMax(){if(!this.hdr)throw new Error("hdr undefined");if(!this.img)throw new Error("img undefined");let u=Number.POSITIVE_INFINITY,h=Number.NEGATIVE_INFINITY,g=0,p=0;const w=this.img.length,T=this.img.constructor!==Float64Array&&this.img.constructor!==Float32Array&&this.ignoreZeroVoxels;if(T)for(let e0=0;e0<w;e0++)u=Math.min(this.img[e0],u),h=Math.max(this.img[e0],h),this.img[e0]===0&&g++;else for(let e0=0;e0<w;e0++){if(isNaN(this.img[e0])){p++;continue}this.img[e0]===0&&(g++,this.ignoreZeroVoxels)||(u=Math.min(this.img[e0],u),h=Math.max(this.img[e0],h))}this.ignoreZeroVoxels&&u===h&&g>0&&(u=0);const E=this.intensityRaw2Scaled(u),L=this.intensityRaw2Scaled(h),U=cmapper.colormapFromKey(this._colormap);let O=0,d=0;if(U.min!==void 0&&(O=U.min),U.max!==void 0&&(d=U.max),O===d&&this.trustCalMinMax&&isFinite(this.hdr.cal_min)&&isFinite(this.hdr.cal_max)&&this.hdr.cal_max>this.hdr.cal_min)return this.cal_min=this.hdr.cal_min,this.cal_max=this.hdr.cal_max,this.robust_min=this.cal_min,this.robust_max=this.cal_max,this.global_min=E,this.global_max=L,[this.hdr.cal_min,this.hdr.cal_max,this.hdr.cal_min,this.hdr.cal_max];if(O!==d)return this.cal_min=O,this.cal_max=d,this.robust_min=this.cal_min,this.robust_max=this.cal_max,[O,d,O,d];const M=100*g/w;let e=!1;M>60&&!this.ignoreZeroVoxels&&(log.warn(`${Math.round(M)}% of voxels are zero: ignoring zeros for cal_max`),e=!0,this.ignoreZeroVoxels=!0),this.ignoreZeroVoxels||(g=0),g+=p;const b=Math.round((w-g)*this.percentileFrac);if(b<1||u===h)return log.debug("no variability in image intensity?"),this.cal_min=E,this.cal_max=L,this.robust_min=this.cal_min,this.robust_max=this.cal_max,this.global_min=E,this.global_max=L,[E,L,E,L];const z=1001,W=(z-1)/(h-u),o0=new Array(z);for(let e0=0;e0<z;e0++)o0[e0]=0;if(T)for(let e0=0;e0<w;e0++)o0[Math.round((this.img[e0]-u)*W)]++;else if(this.ignoreZeroVoxels)for(let e0=0;e0<w;e0++)this.img[e0]!==0&&(isNaN(this.img[e0])||o0[Math.round((this.img[e0]-u)*W)]++);else for(let e0=0;e0<w;e0++)isNaN(this.img[e0])||o0[Math.round((this.img[e0]-u)*W)]++;let J=0,K=0;for(;J<b;)J+=o0[K],K++;K--,J=0;let y=z;for(;J<b;)y--,J+=o0[y];if(K===y){let e0=-1;for(;e0!==0;)K>0&&(K--,o0[K]>0&&(e0=0)),e0!==0&&y<z-1&&(y++,o0[y]>0&&(e0=0)),K===0&&y===z-1&&(e0=0)}let Z=this.intensityRaw2Scaled(K/W+u),X=this.intensityRaw2Scaled(y/W+u);return this.hdr.cal_min<this.hdr.cal_max&&this.hdr.cal_min>=E&&this.hdr.cal_max<=L&&(Z=this.hdr.cal_min,X=this.hdr.cal_max),e&&(Z=Math.min(Z,0)),this.cal_min=Z,this.cal_max=X,this.robust_min=this.cal_min,this.robust_max=this.cal_max,this.global_min=E,this.global_max=L,[Z,X,E,L]}intensityRaw2Scaled(u){if(!this.hdr)throw new Error("hdr undefined");return this.hdr.scl_slope===0&&(this.hdr.scl_slope=1),u*this.hdr.scl_slope+this.hdr.scl_inter}intensityScaled2Raw(u){if(!this.hdr)throw new Error("hdr undefined");return this.hdr.scl_slope===0&&(this.hdr.scl_slope=1),(u-this.hdr.scl_inter)/this.hdr.scl_slope}saveToUint8Array(u,h=null){if(!this.hdr)throw new Error("hdr undefined");if(!this.img)throw new Error("img undefined");const g=h!==null,p=hdrToArrayBuffer(this.hdr,g),w=new Uint8Array(4);let T=new Uint8Array(this.img.buffer);g&&(T=new Uint8Array(h.buffer));const E=new Uint8Array(p.length+w.length+T.length);E.set(p),E.set(w,p.length),E.set(T,p.length+w.length);let L=null;return u.endsWith(".gz")?L=gzipSync$1(E,{filename:u,mtime:Date.now(),level:6}):L=E,L}saveToDisk(u="",h=null){const g=this.saveToUint8Array(u,h);if(u==="")return log.debug("saveToDisk: empty file name, returning data as Uint8Array rather than triggering download"),g;const p=new Blob([g.buffer],{type:"application/octet-stream"}),w=URL.createObjectURL(p),T=document.createElement("a");return T.setAttribute("href",w),T.setAttribute("download",u),T.style.visibility="hidden",document.body.appendChild(T),T.click(),document.body.removeChild(T),g}static async fetchDicomData(u,h={}){if(u==="")throw Error("url must not be empty");let p=/^(?:[a-z+]+:)?\/\//i.test(u)?u:new URL(u,window.location.href);/(?:.([^.]+))?$/.exec(p.pathname)||(p=new URL("niivue-manifest.txt",u));let E=await fetch(p,{headers:h});if(!E.ok)throw Error(E.statusText);const U=(await E.text()).split(`
1737
1737
  `),d=/(.*\/).*/.exec(p)[0],M=[];for(const e of U){const b=new URL(e,d);if(E=await fetch(b,{headers:h}),!E.ok)throw Error(E.statusText);const z=await E.arrayBuffer();M.push(z)}return M}static async fetchPartial(u,h,g={}){try{return await fetch(u,{headers:{range:`bytes=0-'${h}`,stream:"true",...g}})}catch(p){return log.error(p),log.error("fetchPartial failed, trying again without range header"),await fetch(u,{headers:g})}}static async loadFromUrl({url:u="",urlImgData:h="",headers:g={},name:p="",colormap:w="gray",opacity:T=1,cal_min:E=NaN,cal_max:L=NaN,trustCalMinMax:U=!0,percentileFrac:O=.02,ignoreZeroVoxels:d=!1,useQFormNotSForm:M=!1,colormapNegative:e="",frame4D:b=0,isManifest:z=!1,limitFrames4D:W=NaN,imageType:o0=NVIMAGE_TYPE.UNKNOWN,colorbarVisible:J=!0,buffer:K=new ArrayBuffer(0)}={}){if(u==="")throw Error("url must not be empty");let y=null,Z=null;if(u instanceof Uint8Array&&(u=u.buffer),K.byteLength>0&&(u=K),u instanceof ArrayBuffer)if(Z=u,p!=="")u=p;else{u="array.nii";const f=new Uint8Array(Z);f[0]===31&&f[1]===139&&(u="array.nii.gz")}if(!isNaN(W)){let f=await this.fetchPartial(u,512,g);Z=await f.arrayBuffer();let g0=new Uint8Array(Z),c0=!1;g0[0]===31&&g0[1]===139&&(c0=!0,new Decompress$1(I0=>{g0=I0}).push(g0),Z=g0.buffer);let A0=g0[0]===92&&g0[1]===1;if(A0||(A0=g0[1]===92&&g0[0]===1),!A0)Z=null;else{const v0=nifti.readHeader(Z);if(v0===null)throw new Error("could not read nifti header");const I0=v0.numBitsPerVoxel/8;let T0=1;for(let oe=1;oe<4;oe++)v0.dims[oe]>1&&(T0*=v0.dims[oe]);let E0=1;for(let oe=4;oe<7;oe++)v0.dims[oe]>1&&(E0*=v0.dims[oe]);const M0=Math.max(Math.min(W,E0),1),U0=v0.vox_offset+M0*T0*I0;if(Z.byteLength<U0&&(f=await this.fetchPartial(u,U0,g),Z=await f.arrayBuffer(),c0)){let oe=new Uint8Array(Z);new Decompress$1(_e=>{oe=_e}).push(oe),Z=oe.buffer}Z.byteLength<U0?Z=null:Z=Z.slice(0,U0)}}if(!Z)if(z)Z=await NVImage.fetchDicomData(u,g),o0=NVIMAGE_TYPE.DCM_MANIFEST;else{const f=await fetch(u,{headers:g});if(!f.ok)throw Error(f.statusText);Z=await f.arrayBuffer()}const X=/(?:\.([^.]+))?$/;let e0="";p===""?e0=X.exec(u)[1]:e0=X.exec(p)[1],e0.toUpperCase()==="HEAD"&&h===""&&(h=u.substring(0,u.lastIndexOf("HEAD"))+"BRIK");let s0;if(p===""){try{s0=new URL(u).pathname.split("/")}catch{s0=u.split("/")}p=s0.slice(-1)[0],p.indexOf("?")>-1&&(p=p.slice(0,p.indexOf("?")))}let j=null;if(h.length>0){let f=await fetch(h,{headers:g});f.status===404&&h.lastIndexOf("BRIK")!==-1&&(f=await fetch(h+".gz",{headers:g})),j=await f.arrayBuffer()}if(!Z)throw new Error("Unable to load buffer properly from volume");return y=new NVImage(Z,p,w,T,j,E,L,U,O,d,M,e,b,o0),y.url=u,y.colorbarVisible=J,y}static readFileAsync(u,h=NaN){return new Promise((g,p)=>{const w=new FileReader;w.onload=()=>{u.name.lastIndexOf("gz")!==-1&&isNaN(h)?g(nifti.decompress(w.result)):g(w.result)},w.onerror=p,isNaN(h)?w.readAsArrayBuffer(u):w.readAsArrayBuffer(u.slice(0,h))})}static async loadFromFile({file:u,name:h="",colormap:g="gray",opacity:p=1,urlImgData:w=null,cal_min:T=NaN,cal_max:E=NaN,trustCalMinMax:L=!0,percentileFrac:U=.02,ignoreZeroVoxels:O=!1,useQFormNotSForm:d=!1,colormapNegative:M="",frame4D:e=0,limitFrames4D:b=NaN,imageType:z=NVIMAGE_TYPE.UNKNOWN}){let W=null,o0=[];try{if(Array.isArray(u))for(let K=0;K<u.length;K++)o0.push(await this.readFileAsync(u[K]));else{if(isNaN(b))o0=await this.readFileAsync(u,b);else{o0=await this.readFileAsync(u,512);let K=new Uint8Array(o0),y=!1;K[0]===31&&K[1]===139&&(y=!0,new Decompress$1(e0=>{K=e0}).push(K),o0=K.buffer);let Z=K[0]===92&&K[1]===1;if(Z||(Z=K[1]===92&&K[0]===1),!Z)o0=await this.readFileAsync(u);else{const X=nifti.readHeader(o0);if(!X)throw new Error("could not read nifti header");const e0=X.numBitsPerVoxel/8;let s0=1;for(let c0=1;c0<4;c0++)X.dims[c0]>1&&(s0*=X.dims[c0]);let j=1;for(let c0=4;c0<7;c0++)X.dims[c0]>1&&(j*=X.dims[c0]);const f=Math.max(Math.min(b,j),1),g0=X.vox_offset+f*s0*e0;if(o0.byteLength<g0&&(o0=await this.readFileAsync(u,g0),y)){let c0=new Uint8Array(o0);new Decompress$1(v0=>{c0=v0}).push(c0),o0=c0.buffer}if(o0.byteLength<g0)throw new Error("failed to load image data (e.g. incompressible data)");o0=o0.slice(0,g0)}}h=u.name}let J=null;w&&(J=await this.readFileAsync(w)),W=new NVImage(o0,h,g,p,J,T,E,L,U,O,d,M,e,z),W.fileObject=u}catch(J){log.error(J),log.error(J)}if(W===null)throw new Error("could not build NVImage");return W}static createNiftiArray(u=[256,256,256],h=[1,1,1],g=[1,0,0,-128,0,1,0,-128,0,0,1,-128,0,0,0,1],p=2,w=new Uint8Array){const T=this.createNiftiHeader(u,h,g,p),E=hdrToArrayBuffer(T,!1);if(w.length<1)return E;const L=new Uint8Array(4),U=new Uint8Array(w.buffer),O=new Uint8Array(E.length+L.length+U.length);return O.set(E),O.set(L,E.length),O.set(U,E.length+L.length),O}static createNiftiHeader(u=[256,256,256],h=[1,1,1],g=[1,0,0,-128,0,1,0,-128,0,0,1,-128,0,0,0,1],p=2){const w=new nifti.NIFTI1;w.littleEndian=!0,w.dims=[3,1,1,1,0,0,0,0],w.dims[0]=Math.max(3,u.length);for(let E=0;E<u.length;E++)w.dims[E+1]=u[E];w.pixDims=[1,1,1,1,1,0,0,0];for(let E=0;E<u.length;E++)w.pixDims[E+1]=h[E];if(g.length===16){let E=0;for(let L=0;L<4;L++)for(let U=0;U<4;U++)w.affine[L][U]=g[E],E++}let T=8;return p===256||p===2?T=8:p===512||p===4?T=16:p===16||p===768||p===8?T=32:p===64?T=64:log.warn("Unsupported NIfTI datatypeCode: "+p),w.datatypeCode=p,w.numBitsPerVoxel=T,w.scl_inter=0,w.scl_slope=0,w.sform_code=2,w.magic="n+1",w.vox_offset=352,w}getVolumeData(u=[-1,0,0],h=[0,0,0],g="same"){let p=new Uint8Array;if(Math.min(...u)<0||Math.min(...h)<0)return[p,[0,0,0]];const w=this.dimsRAS.slice(1,4);for(let M=0;M<3;M++)if(u[M]=Math.min(u[M],w[M]-1),h[M]=Math.min(h[M],w[M]-1),h[M]<u[M]){const e=h[M];h[M]=u[M],u[M]=e}const T=[h[0]-u[0]+1,h[1]-u[1]+1,h[2]-u[2]+1],E=T[0]*T[1]*T[2];let L=this.hdr.datatypeCode;if(g==="uint8"?L=NiiDataType.DT_UINT8:(g==="float32"||g==="scaled"||g==="normalized"||g==="windowed")&&(L=NiiDataType.DT_FLOAT32),L===NiiDataType.DT_UINT8)p=new Uint8Array(E);else if(L===NiiDataType.DT_INT16)p=new Int16Array(E);else if(L===NiiDataType.DT_UINT16)p=new Uint16Array(E);else if(L===NiiDataType.DT_FLOAT32)p=new Float32Array(E);else if(L===NiiDataType.DT_FLOAT64)p=new Float64Array(E);else return log.error("getVolumeData unsupported datatype"),[p,[0,0,0]];const U=this.img2RASstep,O=this.img2RASstart;let d=0;for(let M=u[2];M<=h[2];M++){const e=O[2]+M*U[2];for(let b=u[1];b<=h[1];b++){const z=e+O[1]+b*U[1];for(let W=u[0];W<=h[0];W++){const o0=O[0]+W*U[0];p[d++]=this.img[o0+z]}}}if(g==="scaled"||g==="normalized"||g==="windowed")for(let M=0;M<p.length;M++)p[M]=p[M]*this.hdr.scl_slope+this.hdr.scl_inter;if(g==="normalized"||g==="windowed"){let M=this.cal_min,e=this.cal_max;g==="normalized"&&(M=this.global_min,e=this.global_max);const b=1/(e-M);for(let z=0;z<p.length;z++)p[z]=(p[z]-M)*b,p[z]=Math.max(Math.min(p[z],1),0)}return[p,T]}setVolumeData(u=[-1,0,0],h=[0,0,0],g=new Uint8Array){if(g.length<1||Math.min(...u)<0||Math.min(...h)<0)return;const p=this.dimsRAS.slice(1,4);for(let O=0;O<3;O++)if(u[O]=Math.min(u[O],p[O]-1),h[O]=Math.min(h[O],p[O]-1),h[O]<u[O]){const d=h[O];h[O]=u[O],u[O]=d}const w=[h[0]-u[0]+1,h[1]-u[1]+1,h[2]-u[2]+1],T=w[0]*w[1]*w[2];if(g.length<T){log.error("setVolumeData image does not have enough voxels");return}const E=this.img2RASstep,L=this.img2RASstart;let U=0;for(let O=u[2];O<=h[2];O++){const d=L[2]+O*E[2];for(let M=u[1];M<=h[1];M++){const e=d+L[1]+M*E[1];for(let b=u[0];b<=h[0];b++){const z=L[0]+b*E[0];this.img[z+e]=g[U++]}}}}static loadFromBase64({base64:u,name:h="",colormap:g="gray",opacity:p=1,cal_min:w=NaN,cal_max:T=NaN,trustCalMinMax:E=!0,percentileFrac:L=.02,ignoreZeroVoxels:U=!1,useQFormNotSForm:O=!1,colormapNegative:d="",frame4D:M=0,imageType:e=NVIMAGE_TYPE.UNKNOWN,cal_minNeg:b=NaN,cal_maxNeg:z=NaN,colorbarVisible:W=!0,colormapLabel:o0=null}){function J(y){const Z=window.atob(y),X=Z.length,e0=new Uint8Array(X);for(let s0=0;s0<X;s0++)e0[s0]=Z.charCodeAt(s0);return e0.buffer}let K=null;try{const y=J(u),Z=null;K=new NVImage(y,h,g,p,Z,w,T,E,L,U,O,d,M,e,b,z,W,o0)}catch(y){log.debug(y)}if(K===null)throw new Error("could not load NVImage");return K}clone(){const u=new NVImage;return u.id=v4(),u.hdr=Object.assign({},this.hdr),u.img=this.img.slice(),u.calculateRAS(),u.calMinMax(),u}zeroImage(){this.img.fill(0)}getImageMetadata(){if(!this.hdr)throw new Error("hdr undefined");const u=this.id,h=this.hdr.datatypeCode,g=this.hdr.dims,p=g[1],w=g[2],T=g[3],E=Math.max(1,g[4]),L=this.hdr.pixDims,U=L[1],O=L[2],d=L[3],M=L[4],e=Math.floor(this.hdr.numBitsPerVoxel/8);return{id:u,datatypeCode:h,nx:p,ny:w,nz:T,nt:E,dx:U,dy:O,dz:d,dt:M,bpv:e}}static zerosLike(u,h="same"){const g=u.clone();return g.zeroImage(),h==="uint8"&&(g.img=Uint8Array.from(g.img),g.hdr.datatypeCode=NiiDataType.DT_UINT8,g.hdr.numBitsPerVoxel=8),h==="float32"&&(g.img=Float32Array.from(g.img),g.hdr.datatypeCode=NiiDataType.DT_FLOAT32,g.hdr.numBitsPerVoxel=32),g}getValue(u,h,g,p=0,w=!1){if(!this.hdr)throw new Error("hdr undefined");if(!this.img)throw new Error("img undefined");const T=this.hdr.dims[1],E=this.hdr.dims[2],L=this.hdr.dims[3],U=this.permRAS.slice();if(U[0]!==1||U[1]!==2||U[2]!==3){const e=fromValues$1(u,h,g,1);transformMat4(e,e,this.toRASvox),u=e[0],h=e[1],g=e[2]}let O=u+h*T+g*T*E;if(this.hdr.datatypeCode===NiiDataType.DT_RGBA32)return O*=4,Math.round(this.img[O]*.21+this.img[O+1]*.72+this.img[O+2]*.07);if(this.hdr.datatypeCode===NiiDataType.DT_RGB24)return O*=3,Math.round(this.img[O]*.21+this.img[O+1]*.72+this.img[O+2]*.07);const d=p*T*E*L;let M=this.img[O+d];return w&&(M=this.imaginary[O+d]),this.hdr.scl_slope*M+this.hdr.scl_inter}toNiivueObject3D(u,h){const g=this.dimsRAS,p=this.matRAS,w=this.pixDimsRAS,T=-.5,E=-.5,L=-.5,U=g[1]-1+.5,O=g[2]-1+.5,d=g[3]-1+.5,M=this.vox2mm([T,E,L],p),e=this.vox2mm([T,O,L],p),b=this.vox2mm([T,E,d],p),z=this.vox2mm([T,O,d],p),W=this.vox2mm([U,E,L],p),o0=this.vox2mm([U,O,L],p),J=this.vox2mm([U,E,d],p),K=this.vox2mm([U,O,d],p),y=[...b,0,0,1,...J,1,0,1,...K,1,1,1,...z,0,1,1,...M,0,0,0,...e,0,1,0,...o0,1,1,0,...W,1,0,0],Z=h.createBuffer();h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,Z);const X=[0,3,2,2,1,0,4,7,6,6,5,4,5,6,2,2,3,5,4,0,1,1,7,4,7,1,2,2,6,7,4,5,3,3,0,4];h.bufferData(h.ELEMENT_ARRAY_BUFFER,new Uint16Array(X),h.STATIC_DRAW);const e0=h.createBuffer();h.bindBuffer(h.ARRAY_BUFFER,e0),h.bufferData(h.ARRAY_BUFFER,new Float32Array(y),h.STATIC_DRAW);const s0=h.createVertexArray();h.bindVertexArray(s0),h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,Z),h.bindBuffer(h.ARRAY_BUFFER,e0),h.enableVertexAttribArray(0),h.vertexAttribPointer(0,3,h.FLOAT,!1,24,0),h.enableVertexAttribArray(1),h.vertexAttribPointer(1,3,h.FLOAT,!1,24,12),h.bindVertexArray(null);const j=new NiivueObject3D(u,e0,h.TRIANGLES,X.length,Z,s0),f=getExtents([...b,...J,...K,...z,...M,...e,...o0,...W]);return j.extentsMin=f.min.slice(),j.extentsMax=f.max.slice(),j.furthestVertexFromOrigin=f.furthestVertexFromOrigin,j.originNegate=clone$1(f.origin),negate(j.originNegate,j.originNegate),j.fieldOfViewDeObliqueMM=[g[1]*w[1],g[2]*w[2],g[3]*w[3]],j}applyOptionsUpdate(u){this.hdr.cal_min=u.cal_min,this.hdr.cal_max=u.cal_max,Object.assign(this,u)}getImageOptions(){return NVImageFromUrlOptions("","",this.name,this._colormap,this.opacity,this.hdr.cal_min,this.hdr.cal_max,this.trustCalMinMax,this.percentileFrac,this.ignoreZeroVoxels,this.useQFormNotSForm,this.colormapNegative,this.frame4D,this.imageType)}toUint8Array(u=null){const h=u!==null,g=hdrToArrayBuffer({...this.hdr,vox_offset:352},h);let p=u;if(h){const L=this.permRAS;if(L[0]!==1||L[1]!==2||L[2]!==3){const U=this.hdr.dims,O=[0,0,0];for(let y=0;y<3;y++)for(let Z=0;Z<3;Z++)Math.abs(L[y])-1===Z&&(O[Z]=y*Math.sign(L[y]));let d=1;const M=[1,1,1],e=[!1,!1,!1];for(let y=0;y<O.length;y++)for(let Z=0;Z<O.length;Z++)Math.abs(O[Z])===y&&(M[Z]=d,(O[Z]<0||Object.is(O[Z],-0))&&(e[Z]=!0),d*=U[Z+1]);let b=NVUtilities.range(0,U[1]-1,1);e[0]&&(b=NVUtilities.range(U[1]-1,0,-1));for(let y=0;y<U[1];y++)b[y]*=M[0];let z=NVUtilities.range(0,U[2]-1,1);e[1]&&(z=NVUtilities.range(U[2]-1,0,-1));for(let y=0;y<U[2];y++)z[y]*=M[1];let W=NVUtilities.range(0,U[3]-1,1);e[2]&&(W=NVUtilities.range(U[3]-1,0,-1));for(let y=0;y<U[3];y++)W[y]*=M[2];const o0=new Uint8Array(u),J=new Uint8Array(U[1]*U[2]*U[3]);let K=0;for(let y=0;y<U[3];y++)for(let Z=0;Z<U[2];Z++)for(let X=0;X<U[1];X++)J[K]=o0[b[X]+z[Z]+W[y]],K++;p=J,log.debug("drawing bytes"),log.debug(p)}}const w=h?p:new Uint8Array(this.img.buffer),T=new Uint8Array(4),E=new Uint8Array(g.length+T.length+w.length);return E.set(g),E.set(T,g.length),E.set(w,g.length+T.length),E}convertVox2Frac(u){return fromValues$2((u[0]+.5)/this.dimsRAS[1],(u[1]+.5)/this.dimsRAS[2],(u[2]+.5)/this.dimsRAS[3])}convertFrac2Vox(u){return fromValues$2(Math.round(u[0]*this.dims[1]-.5),Math.round(u[1]*this.dims[2]-.5),Math.round(u[2]*this.dims[3]-.5))}convertFrac2MM(u,h=!1){const g=fromValues$1(u[0],u[1],u[2],1);return h?transformMat4(g,g,this.frac2mm):transformMat4(g,g,this.frac2mmOrtho),g}convertMM2Frac(u,h=!1){const g=fromValues$1(u[0],u[1],u[2],1),p=this.dimsRAS,w=fromValues$2(0,0,0);if(typeof p>"u")return w;if(!h){const E=clone$2(this.frac2mmOrtho);return invert(E,E),transformMat4(g,g,E),w[0]=g[0],w[1]=g[1],w[2]=g[2],w}if(p[1]<1||p[2]<1||p[3]<1)return w;const T=clone$2(this.matRAS);return invert(T,T),transpose(T,T),transformMat4(g,g,T),w[0]=(g[0]+.5)/p[1],w[1]=(g[1]+.5)/p[2],w[2]=(g[2]+.5)/p[3],w}}var SLICE_TYPE=(C=>(C[C.AXIAL=0]="AXIAL",C[C.CORONAL=1]="CORONAL",C[C.SAGITTAL=2]="SAGITTAL",C[C.MULTIPLANAR=3]="MULTIPLANAR",C[C.RENDER=4]="RENDER",C))(SLICE_TYPE||{}),SHOW_RENDER=(C=>(C[C.NEVER=0]="NEVER",C[C.ALWAYS=1]="ALWAYS",C[C.AUTO=2]="AUTO",C))(SHOW_RENDER||{}),MULTIPLANAR_TYPE=(C=>(C[C.AUTO=0]="AUTO",C[C.COLUMN=1]="COLUMN",C[C.GRID=2]="GRID",C[C.ROW=3]="ROW",C))(MULTIPLANAR_TYPE||{}),DRAG_MODE=(C=>(C[C.none=0]="none",C[C.contrast=1]="contrast",C[C.measurement=2]="measurement",C[C.pan=3]="pan",C[C.slicer3D=4]="slicer3D",C[C.callbackOnly=5]="callbackOnly",C))(DRAG_MODE||{});const DEFAULT_OPTIONS={textHeight:.06,colorbarHeight:.05,crosshairWidth:1,crosshairGap:0,rulerWidth:4,show3Dcrosshair:!1,backColor:[0,0,0,1],crosshairColor:[1,0,0,1],fontColor:[.5,.5,.5,1],selectionBoxColor:[1,1,1,.5],clipPlaneColor:[.7,0,.7,.5],clipThick:2,clipVolumeLow:[0,0,0],clipVolumeHigh:[1,1,1],rulerColor:[1,0,0,.8],colorbarMargin:.05,trustCalMinMax:!0,clipPlaneHotKey:"KeyC",viewModeHotKey:"KeyV",doubleTouchTimeout:500,longTouchTimeout:1e3,keyDebounceTime:50,isNearestInterpolation:!1,isResizeCanvas:!0,atlasOutline:0,isRuler:!1,isColorbar:!1,isOrientCube:!1,multiplanarPadPixels:0,multiplanarForceRender:!1,multiplanarEqualSize:!1,multiplanarShowRender:2,isRadiologicalConvention:!1,meshThicknessOn2D:1/0,dragMode:1,yoke3Dto2DZoom:!1,isDepthPickMesh:!1,isCornerOrientationText:!1,sagittalNoseLeft:!1,isSliceMM:!1,isV1SliceShader:!1,isHighResolutionCapable:!0,logLevel:"info",loadingText:"waiting for images...",isForceMouseClickToVoxelCenters:!1,dragAndDropEnabled:!0,drawingEnabled:!1,penValue:1,floodFillNeighbors:6,isFilledPen:!1,thumbnail:"",maxDrawUndoBitmaps:8,sliceType:3,meshXRay:0,isAntiAlias:null,limitFrames4D:NaN,isAdditiveBlend:!1,showLegend:!0,legendBackgroundColor:[.3,.3,.3,.5],legendTextColor:[1,1,1,1],multiplanarLayout:0,renderOverlayBlend:1,sliceMosaicString:"",centerMosaic:!1,clickToSegment:!1,clickToSegmentRadius:2,clickToSegmentSteps:10,clickToSegmentBright:!0},INITIAL_SCENE_DATA={gamma:1,azimuth:110,elevation:10,crosshairPos:create$2(),clipPlane:[0,0,0,0],clipPlaneDepthAziElev:[2,0,0],volScaleMultiplier:1,pan2Dxyzmm:create$1(),clipThick:2,clipVolumeLow:[0,0,0],clipVolumeHigh:[1,1,1]};class NVDocument{constructor(){d0(this,"data",{title:"Untitled document",imageOptionsArray:[],meshOptionsArray:[],opts:{...DEFAULT_OPTIONS},previewImageDataURL:"",labels:[],encodedImageBlobs:[],encodedDrawingBlob:""});d0(this,"scene");d0(this,"volumes",[]);d0(this,"meshDataObjects");d0(this,"meshes",[]);d0(this,"drawBitmap",null);d0(this,"imageOptionsMap",new Map);d0(this,"meshOptionsMap",new Map);this.scene={onAzimuthElevationChange:()=>{},onZoom3DChange:()=>{},sceneData:{...INITIAL_SCENE_DATA,pan2Dxyzmm:fromValues$1(0,0,0,1),crosshairPos:fromValues$2(.5,.5,.5)},get renderAzimuth(){return this.sceneData.azimuth},set renderAzimuth(u){this.sceneData.azimuth=u,this.onAzimuthElevationChange&&this.onAzimuthElevationChange(this.sceneData.azimuth,this.sceneData.elevation)},get renderElevation(){return this.sceneData.elevation},set renderElevation(u){this.sceneData.elevation=u,this.onAzimuthElevationChange&&this.onAzimuthElevationChange(this.sceneData.azimuth,this.sceneData.elevation)},get volScaleMultiplier(){return this.sceneData.volScaleMultiplier},set volScaleMultiplier(u){this.sceneData.volScaleMultiplier=u,this.onZoom3DChange(u)},get crosshairPos(){return this.sceneData.crosshairPos},set crosshairPos(u){this.sceneData.crosshairPos=u},get clipPlane(){return this.sceneData.clipPlane},set clipPlane(u){this.sceneData.clipPlane=u},get clipPlaneDepthAziElev(){return this.sceneData.clipPlaneDepthAziElev},set clipPlaneDepthAziElev(u){this.sceneData.clipPlaneDepthAziElev=u},get pan2Dxyzmm(){return this.sceneData.pan2Dxyzmm},set pan2Dxyzmm(u){this.sceneData.pan2Dxyzmm=u}}}get title(){return this.data.title}get previewImageDataURL(){return this.data.previewImageDataURL}set previewImageDataURL(u){this.data.previewImageDataURL=u}set title(u){this.data.title=u}get imageOptionsArray(){return this.data.imageOptionsArray}get encodedImageBlobs(){return this.data.encodedImageBlobs}get encodedDrawingBlob(){return this.data.encodedDrawingBlob}get opts(){return this.data.opts}set opts(u){this.data.opts={...u}}get labels(){return this.data.labels}set labels(u){this.data.labels=u}get customData(){return this.data.customData}set customData(u){this.data.customData=u}hasImage(u){return this.volumes.find(h=>h.id===u.id)!==void 0}hasImageFromUrl(u){return this.data.imageOptionsArray.find(h=>h.url===u)!==void 0}addImageOptions(u,h){if(!this.hasImage(u)&&!h.name)if(h.url){const p=/^(?:[a-z+]+:)?\/\//i.test(h.url)?new URL(h.url):new URL(h.url,window.location.href);h.name=p.pathname.split("/").pop(),h.name.toLowerCase().endsWith(".gz")&&(h.name=h.name.slice(0,-3)),h.name.toLowerCase().endsWith(".nii")||(h.name+=".nii")}else h.name="untitled.nii";h.imageType=NVIMAGE_TYPE.NII,this.data.imageOptionsArray.push(h),this.imageOptionsMap.set(u.id,this.data.imageOptionsArray.length-1)}removeImage(u){if(this.imageOptionsMap.has(u.id)){const h=this.imageOptionsMap.get(u.id);this.data.imageOptionsArray.length>h&&this.data.imageOptionsArray.splice(h,1),this.imageOptionsMap.delete(u.id)}this.volumes=this.volumes.filter(h=>h.id!==u.id)}getImageOptions(u){return this.imageOptionsMap.has(u.id)?this.data.imageOptionsArray[this.imageOptionsMap.get(u.id)]:null}json(){const u={encodedImageBlobs:[],previewImageDataURL:this.data.previewImageDataURL,imageOptionsMap:new Map},h=[];u.sceneData={...this.scene.sceneData},u.opts={...this.opts},this.opts.meshThicknessOn2D===1/0&&(u.opts.meshThicknessOn2D="infinity"),u.labels=[...this.data.labels];for(const p of u.labels)delete p.onClick;if(u.customData=this.customData,this.volumes.length){let p=this.imageOptionsArray[0];if(p||(log.debug("no image options for base image"),p={name:"",colormap:"gray",opacity:1,pairedImgData:null,cal_min:NaN,cal_max:NaN,trustCalMinMax:!0,percentileFrac:.02,ignoreZeroVoxels:!1,useQFormNotSForm:!1,colormapNegative:"",colormapLabel:null,imageType:NVIMAGE_TYPE.NII,frame4D:0,limitFrames4D:NaN,url:"",urlImageData:"",alphaThreshold:!1,cal_minNeg:NaN,cal_maxNeg:NaN,colorbarVisible:!0}),p.colormap=this.volumes[0].colormap,p.opacity=this.volumes[0].opacity,p.cal_max=this.volumes[0].cal_max||NaN,p.cal_min=this.volumes[0].cal_min||NaN,p){h.push(p);const w=NVUtilities.uint8tob64(this.volumes[0].toUint8Array());u.encodedImageBlobs.push(w),this.drawBitmap&&(u.encodedDrawingBlob=NVUtilities.uint8tob64(this.volumes[0].toUint8Array(this.drawBitmap))),u.imageOptionsMap.set(this.volumes[0].id,0)}else throw new Error("image options for base layer not found");for(let w=1;w<this.volumes.length;w++){const T=this.volumes[w];let E=this.getImageOptions(T);E===null?(log.warn("no options found for image, using default"),E={name:"",colormap:"gray",opacity:1,pairedImgData:null,cal_min:NaN,cal_max:NaN,trustCalMinMax:!0,percentileFrac:.02,ignoreZeroVoxels:!1,useQFormNotSForm:!1,colormapNegative:"",colormapLabel:null,imageType:NVIMAGE_TYPE.NII,frame4D:0,limitFrames4D:NaN,url:"",urlImageData:"",alphaThreshold:!1,cal_minNeg:NaN,cal_maxNeg:NaN,colorbarVisible:!0}):"imageType"in E||(E.imageType=NVIMAGE_TYPE.NII),E.colormap=T.colormap,E.colormapLabel=T.colormapLabel,E.opacity=T.opacity,E.cal_max=T.cal_max||NaN,E.cal_min=T.cal_min||NaN,h.push(E);const L=NVUtilities.uint8tob64(T.toUint8Array());u.encodedImageBlobs.push(L),u.imageOptionsMap.set(T.id,w)}}u.imageOptionsArray=[...h];const g=[];u.connectomes=[];for(const p of this.meshes){if(p.type===MeshType.CONNECTOME){u.connectomes.push(JSON.stringify(p.json()));continue}const w={pts:p.pts,tris:p.tris,name:p.name,rgba255:Uint8Array.from(p.rgba255),opacity:p.opacity,connectome:p.connectome,dpg:p.dpg,dps:p.dps,dpv:p.dpv,meshShaderIndex:p.meshShaderIndex,layers:p.layers.map(T=>({values:T.values,nFrame4D:T.nFrame4D,frame4D:0,outlineBorder:T.outlineBorder,global_min:T.global_min,global_max:T.global_max,cal_min:T.cal_min,cal_max:T.cal_max,opacity:T.opacity,colormap:T.colormap,colormapNegative:T.colormapNegative,colormapLabel:T.colormapLabel,useNegativeCmap:T.useNegativeCmap})),hasConnectome:p.hasConnectome,edgeColormap:p.edgeColormap,edgeColormapNegative:p.edgeColormapNegative,edgeMax:p.edgeMax,edgeMin:p.edgeMin,edges:p.edges&&Array.isArray(p.edges)?[...p.edges]:[],extentsMax:p.extentsMax,extentsMin:p.extentsMin,furthestVertexFromOrigin:p.furthestVertexFromOrigin,nodeColormap:p.nodeColormap,nodeColormapNegative:p.nodeColormapNegative,nodeMaxColor:p.nodeMaxColor,nodeMinColor:p.nodeMinColor,nodeScale:p.nodeScale,legendLineThickness:p.legendLineThickness,offsetPt0:p.offsetPt0,nodes:p.nodes};p.offsetPt0&&p.offsetPt0.length>0&&(w.offsetPt0=p.offsetPt0,w.fiberGroupColormap=p.fiberGroupColormap,w.fiberColor=p.fiberColor,w.fiberDither=p.fiberDither,w.fiberRadius=p.fiberRadius,w.colormap=p.colormap),g.push(w)}return u.meshesString=JSON.stringify(serialize(g)),u}async download(u,h){const g=this.json(),p=JSON.stringify(g),w=h?"application/gzip":"application/json";let T;h?T=await NVUtilities.compressStringToArrayBuffer(p):T=JSON.stringify(g),NVUtilities.download(T,u,w)}static deserializeMeshDataObjects(u){if(u.data.meshesString){u.meshDataObjects=deserialize(JSON.parse(u.data.meshesString));for(const h of u.meshDataObjects)for(const g of h.layers)"colorMap"in g&&(g.colormap=g.colorMap,delete g.colorMap),"colorMapNegative"in g&&(g.colormapNegative=g.colorMapNegative,delete g.colorMapNegative)}}static async loadFromUrl(u){const g=await(await fetch(u)).arrayBuffer();let p;if(NVUtilities.isArrayBufferCompressed(g)){const w=await NVUtilities.decompressArrayBuffer(g);p=JSON.parse(w)}else{const w=new TextDecoder;p=JSON.parse(w.decode(g))}return NVDocument.loadFromJSON(p)}static async loadFromFile(u){const h=await NVUtilities.readFileAsync(u);let g;const p=new NVDocument;return NVUtilities.isArrayBufferCompressed(h)?g=await NVUtilities.decompressArrayBuffer(h):g=new TextDecoder().decode(h),p.data=JSON.parse(g),p.data.opts.meshThicknessOn2D==="infinity"&&(p.data.opts.meshThicknessOn2D=1/0),p.scene.sceneData={...INITIAL_SCENE_DATA,...p.data.sceneData},NVDocument.deserializeMeshDataObjects(p),p}static loadFromJSON(u){const h=new NVDocument;return h.data=u,h.data.opts.meshThicknessOn2D==="infinity"&&(h.data.opts.meshThicknessOn2D=1/0),h.scene.sceneData={...INITIAL_SCENE_DATA,...u.sceneData},NVDocument.deserializeMeshDataObjects(h),h}}var LabelTextAlignment=(C=>(C.LEFT="left",C.RIGHT="right",C.CENTER="center",C))(LabelTextAlignment||{}),LabelLineTerminator=(C=>(C.NONE="none",C.CIRCLE="circle",C.RING="ring",C))(LabelLineTerminator||{}),LabelAnchorFlag=(C=>(C[C.NONE=0]="NONE",C[C.LEFT=1]="LEFT",C[C.CENTER=2]="CENTER",C[C.RIGHT=4]="RIGHT",C[C.TOP=8]="TOP",C[C.MIDDLE=16]="MIDDLE",C[C.BOTTOM=32]="BOTTOM",C))(LabelAnchorFlag||{}),LabelAnchorPoint=(C=>(C[C.NONE=0]="NONE",C[C.TOPLEFT=9]="TOPLEFT",C[C.TOPCENTER=10]="TOPCENTER",C[C.TOPRIGHT=12]="TOPRIGHT",C[C.MIDDLELEFT=17]="MIDDLELEFT",C[C.MIDDLECENTER=18]="MIDDLECENTER",C[C.MIDDLERIGHT=20]="MIDDLERIGHT",C[C.BOTTOMLEFT=33]="BOTTOMLEFT",C[C.BOTTOMCENTER=34]="BOTTOMCENTER",C[C.BOTTOMRIGHT=36]="BOTTOMRIGHT",C))(LabelAnchorPoint||{});class NVLabel3DStyle{constructor(u=[1,1,1,1],h=1,g="left",p=0,w=[0,0,0],T="none",E,L,U){d0(this,"textColor");d0(this,"textScale");d0(this,"textAlignment");d0(this,"lineWidth");d0(this,"lineColor");d0(this,"lineTerminator");d0(this,"bulletScale");d0(this,"bulletColor");d0(this,"backgroundColor");this.textColor=u,this.textScale=h,this.textAlignment=g,this.lineWidth=p,this.lineColor=w,this.lineTerminator=T,this.bulletScale=E,this.bulletColor=L,this.backgroundColor=U}}class NVLabel3D{constructor(u,h,g,p,w){d0(this,"text");d0(this,"style");d0(this,"points");d0(this,"anchor");d0(this,"onClick");this.text=u,this.style=h,this.points=g,this.anchor=p||0,this.onClick=w}}const defaultOptions={name:"untitled connectome",nodeColormap:"warm",nodeColormapNegative:"winter",nodeMinColor:0,nodeMaxColor:4,nodeScale:3,edgeColormap:"warm",edgeColormapNegative:"winter",edgeMin:2,edgeMax:6,edgeScale:1,legendLineThickness:0};class NVConnectome extends NVMesh{constructor(h,g){super(new Float32Array([]),new Uint32Array([]),g.name,new Uint8Array([]),1,!0,h,g);d0(this,"gl");d0(this,"nodesChanged");this.gl=h,this.type=MeshType.CONNECTOME,this.nodes&&this.updateLabels(),this.nodesChanged=new EventTarget}static convertLegacyConnectome(h){const g={nodes:[],edges:[],...defaultOptions};for(const w in h)if(w in defaultOptions){const T=w;g[T]=h[T]}const p=h.nodes;for(let w=0;w<p.names.length;w++)g.nodes.push({name:p.names[w],x:p.X[w],y:p.Y[w],z:p.Z[w],colorValue:p.Color[w],sizeValue:p.Size[w]});for(let w=0;w<p.names.length-1;w++)for(let T=w+1;T<p.names.length;T++){const E=h.edges[w*p.names.length+T];g.edges.push({first:w,second:T,colorValue:E})}return g}static convertFreeSurferConnectome(h,g="warm"){let p=!0;if("data_type"in h?h.data_type!=="fs_pointset"&&(p=!1):p=!1,"points"in h||(p=!1),!p)throw Error("not a valid FreeSurfer json pointset");const w=h.points.map(E=>({name:Array.isArray(E.comments)&&E.comments.length>0&&"text"in E.comments[0]?E.comments[0].text:"",x:E.coordinates.x,y:E.coordinates.y,z:E.coordinates.z,colorValue:1,sizeValue:1,metadata:E.comments}));return{...defaultOptions,nodeColormap:g,edgeColormap:g,nodes:w,edges:[]}}updateLabels(){const h=this.nodes;if(h&&h.length>0){const g=h.reduce((O,d)=>O.sizeValue>d.sizeValue?O:d).sizeValue,p=this.nodeMinColor?this.nodeMinColor:h.reduce((O,d)=>O.colorValue<d.colorValue?O:d).colorValue,w=this.nodeMaxColor?this.nodeMaxColor:h.reduce((O,d)=>O.colorValue>d.colorValue?O:d).colorValue,T=cmapper.colormap(this.nodeColormap,this.colormapInvert),E=cmapper.colormap(this.nodeColormapNegative,this.colormapInvert),L="nodeColormapNegative"in this,U=this.legendLineThickness?this.legendLineThickness:0;for(let O=0;O<h.length;O++){let d=h[O].colorValue,M=!1;if(L&&d<0&&(M=!0,d=-d),p<w){if(d<p){log.warn("color value lower than min");continue}d=(d-p)/(w-p)}else d=1;d=Math.round(Math.max(Math.min(255,d*255)))*4;let e=[T[d],T[d+1],T[d+2],255];M&&(e=[E[d],E[d+1],E[d+2],255]),e=e.map(b=>b/255),log.debug("adding label for ",h[O]),h[O].label=new NVLabel3D(h[O].name,{textColor:e,bulletScale:h[O].sizeValue/g,bulletColor:e,lineWidth:U,lineColor:e,textScale:1,textAlignment:LabelTextAlignment.LEFT,lineTerminator:LabelLineTerminator.NONE},[h[O].x,h[O].y,h[O].z]),log.debug("label for node:",h[O].label)}}}addConnectomeNode(h){if(log.debug("adding node",h),!this.nodes)throw new Error("nodes not defined");this.nodes.push(h),this.updateLabels(),this.nodesChanged.dispatchEvent(new CustomEvent("nodeAdded",{detail:{node:h}}))}deleteConnectomeNode(h){const g=this.nodes.indexOf(h),p=this.edges;p&&(this.edges=p.filter(w=>w.first!==g&&w.second!==g)),this.nodes=this.nodes.filter(w=>w!==h),this.updateLabels(),this.updateConnectome(this.gl),this.nodesChanged.dispatchEvent(new CustomEvent("nodeDeleted",{detail:{node:h}}))}updateConnectomeNodeByIndex(h,g){this.nodes[h]=g,this.updateLabels(),this.updateConnectome(this.gl),this.nodesChanged.dispatchEvent(new CustomEvent("nodeChanged",{detail:{node:g}}))}updateConnectomeNodeByPoint(h,g){const p=this.nodes;if(!p)throw new Error("Node to update does not exist");const w=p.find(E=>NVUtilities.arraysAreEqual([E.x,E.y,E.z],h));if(!w)throw new Error(`Node with point ${h} to update does not exist`);const T=p.findIndex(E=>E===w);this.updateConnectomeNodeByIndex(T,g)}addConnectomeEdge(h,g,p){const w=this.edges;let T=w.find(E=>(E.first===h||E.second===h)&&E.first+E.second===h+g);return T||(T={first:h,second:g,colorValue:p},w.push(T),this.updateConnectome(this.gl),T)}deleteConnectomeEdge(h,g){const p=this.edges,w=p.find(T=>(T.first===h||T.first===g)&&T.first+T.second===h+g);if(w)this.edges=p.filter(T=>T!==w);else throw new Error(`edge between ${h} and ${g} not found`);return this.updateConnectome(this.gl),w}findClosestConnectomeNode(h,g){const p=this.nodes;if(!p||p.length===0)return null;const w=p.map((T,E)=>({node:T,distance:Math.sqrt(Math.pow(T.x-h[0],2)+Math.pow(T.y-h[1],2)+Math.pow(T.z-h[2],2)),index:E})).filter(T=>T.distance<g).sort((T,E)=>T.distance-E.distance);return w.length>0?w[0].node:null}updateConnectome(h){const g=[],p=[],w=[];let T=cmapper.colormap(this.nodeColormap,this.colormapInvert),E=cmapper.colormap(this.nodeColormapNegative,this.colormapInvert),L="nodeColormapNegative"in this;if(this.nodeMinColor===void 0||this.nodeMaxColor===void 0)throw new Error("nodeMinColor or nodeMaxColor is undefined");if(this.edgeMin===void 0||this.edgeMax===void 0)throw new Error("edgeMin or edgeMax undefined");let U=this.nodeMinColor,O=this.nodeMaxColor;const d=this.nodes,M=d.length;for(let J=0;J<M;J++){const K=d[J].sizeValue*this.nodeScale;if(K<=0)continue;let y=d[J].colorValue,Z=!1;if(L&&y<0&&(Z=!0,y=-y),U<O){if(y<U)continue;y=(y-U)/(O-U)}else y=1;y=Math.round(Math.max(Math.min(255,y*255)))*4;let X=[T[y],T[y+1],T[y+2],255];Z&&(X=[E[y],E[y+1],E[y+2],255]);const e0=fromValues$2(d[J].x,d[J].y,d[J].z);NiivueObject3D.makeColoredSphere(p,g,w,K,e0,X)}T=cmapper.colormap(this.edgeColormap,this.colormapInvert),E=cmapper.colormap(this.edgeColormapNegative,this.colormapInvert),L="edgeColormapNegative"in this,U=this.edgeMin,O=this.edgeMax;const e=this.edges;for(const J of e){let K=J.colorValue;const y=L&&K<0;y&&(K=-K);const Z=K*this.edgeScale;if(Z<=0)continue;if(U<O){if(K<U)continue;K=(K-U)/(O-U)}else K=1;K=Math.round(Math.max(Math.min(255,K*255)))*4;let X=[T[K],T[K+1],T[K+2],255];y&&(X=[E[K],E[K+1],E[K+2],255]);const e0=fromValues$2(d[J.first].x,d[J.first].y,d[J.first].z),s0=fromValues$2(d[J.second].x,d[J.second].y,d[J.second].z);NiivueObject3D.makeColoredCylinder(p,g,w,e0,s0,Z,X)}const b=new Float32Array(p),z=new Uint32Array(g),W=NVMeshUtilities.getExtents(b);this.furthestVertexFromOrigin=W.mxDx,this.extentsMin=W.extentsMin,this.extentsMax=W.extentsMax;const o0=this.generatePosNormClr(b,z,new Uint8Array(w));h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,this.indexBuffer),h.bufferData(h.ELEMENT_ARRAY_BUFFER,Uint32Array.from(z),h.STATIC_DRAW),h.bindBuffer(h.ARRAY_BUFFER,this.vertexBuffer),h.bufferData(h.ARRAY_BUFFER,Float32Array.from(o0),h.STATIC_DRAW),this.indexCount=g.length}updateMesh(h){this.updateConnectome(h),this.updateLabels()}json(){const h={};for(const g in this)(g in defaultOptions||g==="nodes"||g==="edges")&&(h[g]=this[g]);return h}static async loadConnectomeFromUrl(h,g){const w=await(await fetch(g)).json();return new NVConnectome(h,w)}}function img2ras16(C){const u=C.hdr.dims,h=C.permRAS,g=u[1]*u[2]*u[3],p=new Int16Array(g),w=[0,0,0];for(let e=0;e<3;e++)for(let b=0;b<3;b++)Math.abs(h[e])-1===b&&(w[b]=e*Math.sign(h[e]));let T=1;const E=[1,1,1],L=[!1,!1,!1];for(let e=0;e<w.length;e++)for(let b=0;b<w.length;b++)Math.abs(w[b])===e&&(E[b]=T,(w[b]<0||Object.is(w[b],-0))&&(L[b]=!0),T*=u[b+1]);let U=NVUtilities.range(0,u[1]-1,1);L[0]&&(U=NVUtilities.range(u[1]-1,0,-1));for(let e=0;e<u[1];e++)U[e]*=E[0];let O=NVUtilities.range(0,u[2]-1,1);L[1]&&(O=NVUtilities.range(u[2]-1,0,-1));for(let e=0;e<u[2];e++)O[e]*=E[1];let d=NVUtilities.range(0,u[3]-1,1);L[2]&&(d=NVUtilities.range(u[3]-1,0,-1));for(let e=0;e<u[3];e++)d[e]*=E[2];let M=0;for(let e=0;e<u[3];e++)for(let b=0;b<u[2];b++)for(let z=0;z<u[1];z++)p[U[z]+O[b]+d[e]]=C.img[M],M++;return p}function nice(C,u){const h=Math.floor(Math.log(C)/Math.log(10)),g=C/Math.pow(10,h);let p;return u?g<1.5?p=1:g<3?p=2:g<7?p=5:p=10:g<=1?p=1:g<=2?p=2:g<=5?p=5:p=10,p*Math.pow(10,h)}function loose_label(C,u,h=4){const g=nice(u-C,!1),p=nice(g/(h-1),!0),w=Math.floor(C/p)*p,T=Math.ceil(u/p)*p;return[p,w,T,w===C&&T===u]}function tickSpacing(C,u){let h=loose_label(C,u,3);return h[3]||(h=loose_label(C,u,5)),h[3]||(h=loose_label(C,u,4)),h[3]||(h=loose_label(C,u,3)),h[3]||(h=loose_label(C,u,5)),[h[0],h[1],h[2]]}function deg2rad(C){return C*(Math.PI/180)}function negMinMax(C,u,h,g){let p=-C,w=-u;return isFinite(h)&&isFinite(g)&&(p=h,w=g),p>w&&([p,w]=[w,p]),[p,w]}function swizzleVec3(C,u=[0,1,2]){const h=create$2();return h[0]=C[u[0]],h[1]=C[u[1]],h[2]=C[u[2]],h}function isRadiological(C){const u=fromValues$1(1,0,0,0),h=create$1();return transformMat4(h,u,C),h[0]}function unProject(C,u,h,g){const p=fromValues$1(C,u,h,1),w=clone$2(g);invert(w,w),p[0]=p[0]*2-1,p[1]=p[1]*2-1,p[2]=p[2]*2-1;const T=create$1();return transformMat4(T,p,w),T[3]===0||(T[0]/=T[3],T[1]/=T[3],T[2]/=T[3]),T}function unpackFloatFromVec4i(C){const u=[5960464477539063e-23,152587890625e-16,.00390625,1];return(C[0]*u[0]+C[1]*u[1]+C[2]*u[2]+C[3]*u[3])/255}function clamp(C,u,h){return Math.min(Math.max(C,u),h)}function encodeRLE(C){const u=C.length;let h=0;const g=new Uint8Array(u+Math.ceil(.01*u)),p=new Int8Array(g.buffer);let w=0;for(;h<u;){let T=C[h];h++;let E=1;for(;E<129&&h<u&&C[h]===T;)h++,E++;if(E>1){p[w]=-E+1,w++,g[w]=T,w++;continue}for(;h<u&&!(E>127||h+2<u&&T!==C[h]&&C[h+2]===C[h]&&C[h+1]===C[h]);)T=C[h],h++,E++;g[w]=E-1,w++;for(let L=0;L<E;L++)g[w]=C[h-E+L],w++}return log.info("PackBits "+u+" -> "+w+" bytes (x"+u/w+")"),g.slice(0,w)}function decodeRLE(C,u){const h=new Uint8Array(C.buffer),g=new Int8Array(h.buffer);let p=0;const w=new Uint8Array(u);let T=0;for(;p<h.length;){const E=g[p];if(p++,E<0){const L=g[p];p++;for(let U=0;U<1-E;U++)w[T]=L,T++}else for(let L=0;L<E+1;L++)w[T]=g[p],p++,T++}return w}function intensityRaw2Scaled(C,u){return C.scl_slope===0&&(C.scl_slope=1),u*C.scl_slope+C.scl_inter}var extendStatics=function(C,u){return extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(h,g){h.__proto__=g}||function(h,g){for(var p in g)Object.prototype.hasOwnProperty.call(g,p)&&(h[p]=g[p])},extendStatics(C,u)};function __extends(C,u){if(typeof u!="function"&&u!==null)throw new TypeError("Class extends value "+String(u)+" is not a constructor or null");extendStatics(C,u);function h(){this.constructor=C}C.prototype=u===null?Object.create(u):(h.prototype=u.prototype,new h)}var __assign=function(){return __assign=Object.assign||function(u){for(var h,g=1,p=arguments.length;g<p;g++){h=arguments[g];for(var w in h)Object.prototype.hasOwnProperty.call(h,w)&&(u[w]=h[w])}return u},__assign.apply(this,arguments)};function __values(C){var u=typeof Symbol=="function"&&Symbol.iterator,h=u&&C[u],g=0;if(h)return h.call(C);if(C&&typeof C.length=="number")return{next:function(){return C&&g>=C.length&&(C=void 0),{value:C&&C[g++],done:!C}}};throw new TypeError(u?"Object is not iterable.":"Symbol.iterator is not defined.")}function __read(C,u){var h=typeof Symbol=="function"&&C[Symbol.iterator];if(!h)return C;var g=h.call(C),p,w=[],T;try{for(;(u===void 0||u-- >0)&&!(p=g.next()).done;)w.push(p.value)}catch(E){T={error:E}}finally{try{p&&!p.done&&(h=g.return)&&h.call(g)}finally{if(T)throw T.error}}return w}function __spreadArray(C,u,h){if(h||arguments.length===2)for(var g=0,p=u.length,w;g<p;g++)(w||!(g in u))&&(w||(w=Array.prototype.slice.call(u,0,g)),w[g]=u[g]);return C.concat(w||Array.prototype.slice.call(u))}typeof SuppressedError=="function"&&SuppressedError;function isFunction(C){return typeof C=="function"}function createErrorClass(C){var u=function(g){Error.call(g),g.stack=new Error().stack},h=C(u);return h.prototype=Object.create(Error.prototype),h.prototype.constructor=h,h}var UnsubscriptionError=createErrorClass(function(C){return function(h){C(this),this.message=h?h.length+` errors occurred during unsubscription:
1738
1738
  `+h.map(function(g,p){return p+1+") "+g.toString()}).join(`
1739
- `):"",this.name="UnsubscriptionError",this.errors=h}});function arrRemove(C,u){if(C){var h=C.indexOf(u);0<=h&&C.splice(h,1)}}var Subscription=function(){function C(u){this.initialTeardown=u,this.closed=!1,this._parentage=null,this._finalizers=null}return C.prototype.unsubscribe=function(){var u,h,g,p,w;if(!this.closed){this.closed=!0;var T=this._parentage;if(T)if(this._parentage=null,Array.isArray(T))try{for(var E=__values(T),L=E.next();!L.done;L=E.next()){var U=L.value;U.remove(this)}}catch(z){u={error:z}}finally{try{L&&!L.done&&(h=E.return)&&h.call(E)}finally{if(u)throw u.error}}else T.remove(this);var O=this.initialTeardown;if(isFunction(O))try{O()}catch(z){w=z instanceof UnsubscriptionError?z.errors:[z]}var d=this._finalizers;if(d){this._finalizers=null;try{for(var M=__values(d),e=M.next();!e.done;e=M.next()){var b=e.value;try{execFinalizer(b)}catch(z){w=w??[],z instanceof UnsubscriptionError?w=__spreadArray(__spreadArray([],__read(w)),__read(z.errors)):w.push(z)}}}catch(z){g={error:z}}finally{try{e&&!e.done&&(p=M.return)&&p.call(M)}finally{if(g)throw g.error}}}if(w)throw new UnsubscriptionError(w)}},C.prototype.add=function(u){var h;if(u&&u!==this)if(this.closed)execFinalizer(u);else{if(u instanceof C){if(u.closed||u._hasParent(this))return;u._addParent(this)}(this._finalizers=(h=this._finalizers)!==null&&h!==void 0?h:[]).push(u)}},C.prototype._hasParent=function(u){var h=this._parentage;return h===u||Array.isArray(h)&&h.includes(u)},C.prototype._addParent=function(u){var h=this._parentage;this._parentage=Array.isArray(h)?(h.push(u),h):h?[h,u]:u},C.prototype._removeParent=function(u){var h=this._parentage;h===u?this._parentage=null:Array.isArray(h)&&arrRemove(h,u)},C.prototype.remove=function(u){var h=this._finalizers;h&&arrRemove(h,u),u instanceof C&&u._removeParent(this)},C.EMPTY=function(){var u=new C;return u.closed=!0,u}(),C}(),EMPTY_SUBSCRIPTION=Subscription.EMPTY;function isSubscription(C){return C instanceof Subscription||C&&"closed"in C&&isFunction(C.remove)&&isFunction(C.add)&&isFunction(C.unsubscribe)}function execFinalizer(C){isFunction(C)?C():C.unsubscribe()}var config={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},timeoutProvider={setTimeout:function(C,u){for(var h=[],g=2;g<arguments.length;g++)h[g-2]=arguments[g];return setTimeout.apply(void 0,__spreadArray([C,u],__read(h)))},clearTimeout:function(C){var u=timeoutProvider.delegate;return((u==null?void 0:u.clearTimeout)||clearTimeout)(C)},delegate:void 0};function reportUnhandledError(C){timeoutProvider.setTimeout(function(){throw C})}function noop(){}function errorContext(C){C()}var Subscriber=function(C){__extends(u,C);function u(h){var g=C.call(this)||this;return g.isStopped=!1,h?(g.destination=h,isSubscription(h)&&h.add(g)):g.destination=EMPTY_OBSERVER,g}return u.create=function(h,g,p){return new SafeSubscriber(h,g,p)},u.prototype.next=function(h){this.isStopped||this._next(h)},u.prototype.error=function(h){this.isStopped||(this.isStopped=!0,this._error(h))},u.prototype.complete=function(){this.isStopped||(this.isStopped=!0,this._complete())},u.prototype.unsubscribe=function(){this.closed||(this.isStopped=!0,C.prototype.unsubscribe.call(this),this.destination=null)},u.prototype._next=function(h){this.destination.next(h)},u.prototype._error=function(h){try{this.destination.error(h)}finally{this.unsubscribe()}},u.prototype._complete=function(){try{this.destination.complete()}finally{this.unsubscribe()}},u}(Subscription),_bind=Function.prototype.bind;function bind(C,u){return _bind.call(C,u)}var ConsumerObserver=function(){function C(u){this.partialObserver=u}return C.prototype.next=function(u){var h=this.partialObserver;if(h.next)try{h.next(u)}catch(g){handleUnhandledError(g)}},C.prototype.error=function(u){var h=this.partialObserver;if(h.error)try{h.error(u)}catch(g){handleUnhandledError(g)}else handleUnhandledError(u)},C.prototype.complete=function(){var u=this.partialObserver;if(u.complete)try{u.complete()}catch(h){handleUnhandledError(h)}},C}(),SafeSubscriber=function(C){__extends(u,C);function u(h,g,p){var w=C.call(this)||this,T;if(isFunction(h)||!h)T={next:h??void 0,error:g??void 0,complete:p??void 0};else{var E;w&&config.useDeprecatedNextContext?(E=Object.create(h),E.unsubscribe=function(){return w.unsubscribe()},T={next:h.next&&bind(h.next,E),error:h.error&&bind(h.error,E),complete:h.complete&&bind(h.complete,E)}):T=h}return w.destination=new ConsumerObserver(T),w}return u}(Subscriber);function handleUnhandledError(C){reportUnhandledError(C)}function defaultErrorHandler(C){throw C}var EMPTY_OBSERVER={closed:!0,next:noop,error:defaultErrorHandler,complete:noop},observable=function(){return typeof Symbol=="function"&&Symbol.observable||"@@observable"}();function identity(C){return C}function pipeFromArray(C){return C.length===0?identity:C.length===1?C[0]:function(h){return C.reduce(function(g,p){return p(g)},h)}}var Observable=function(){function C(u){u&&(this._subscribe=u)}return C.prototype.lift=function(u){var h=new C;return h.source=this,h.operator=u,h},C.prototype.subscribe=function(u,h,g){var p=this,w=isSubscriber(u)?u:new SafeSubscriber(u,h,g);return errorContext(function(){var T=p,E=T.operator,L=T.source;w.add(E?E.call(w,L):L?p._subscribe(w):p._trySubscribe(w))}),w},C.prototype._trySubscribe=function(u){try{return this._subscribe(u)}catch(h){u.error(h)}},C.prototype.forEach=function(u,h){var g=this;return h=getPromiseCtor(h),new h(function(p,w){var T=new SafeSubscriber({next:function(E){try{u(E)}catch(L){w(L),T.unsubscribe()}},error:w,complete:p});g.subscribe(T)})},C.prototype._subscribe=function(u){var h;return(h=this.source)===null||h===void 0?void 0:h.subscribe(u)},C.prototype[observable]=function(){return this},C.prototype.pipe=function(){for(var u=[],h=0;h<arguments.length;h++)u[h]=arguments[h];return pipeFromArray(u)(this)},C.prototype.toPromise=function(u){var h=this;return u=getPromiseCtor(u),new u(function(g,p){var w;h.subscribe(function(T){return w=T},function(T){return p(T)},function(){return g(w)})})},C.create=function(u){return new C(u)},C}();function getPromiseCtor(C){var u;return(u=C??config.Promise)!==null&&u!==void 0?u:Promise}function isObserver(C){return C&&isFunction(C.next)&&isFunction(C.error)&&isFunction(C.complete)}function isSubscriber(C){return C&&C instanceof Subscriber||isObserver(C)&&isSubscription(C)}var ObjectUnsubscribedError=createErrorClass(function(C){return function(){C(this),this.name="ObjectUnsubscribedError",this.message="object unsubscribed"}}),Subject=function(C){__extends(u,C);function u(){var h=C.call(this)||this;return h.closed=!1,h.currentObservers=null,h.observers=[],h.isStopped=!1,h.hasError=!1,h.thrownError=null,h}return u.prototype.lift=function(h){var g=new AnonymousSubject(this,this);return g.operator=h,g},u.prototype._throwIfClosed=function(){if(this.closed)throw new ObjectUnsubscribedError},u.prototype.next=function(h){var g=this;errorContext(function(){var p,w;if(g._throwIfClosed(),!g.isStopped){g.currentObservers||(g.currentObservers=Array.from(g.observers));try{for(var T=__values(g.currentObservers),E=T.next();!E.done;E=T.next()){var L=E.value;L.next(h)}}catch(U){p={error:U}}finally{try{E&&!E.done&&(w=T.return)&&w.call(T)}finally{if(p)throw p.error}}}})},u.prototype.error=function(h){var g=this;errorContext(function(){if(g._throwIfClosed(),!g.isStopped){g.hasError=g.isStopped=!0,g.thrownError=h;for(var p=g.observers;p.length;)p.shift().error(h)}})},u.prototype.complete=function(){var h=this;errorContext(function(){if(h._throwIfClosed(),!h.isStopped){h.isStopped=!0;for(var g=h.observers;g.length;)g.shift().complete()}})},u.prototype.unsubscribe=function(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null},Object.defineProperty(u.prototype,"observed",{get:function(){var h;return((h=this.observers)===null||h===void 0?void 0:h.length)>0},enumerable:!1,configurable:!0}),u.prototype._trySubscribe=function(h){return this._throwIfClosed(),C.prototype._trySubscribe.call(this,h)},u.prototype._subscribe=function(h){return this._throwIfClosed(),this._checkFinalizedStatuses(h),this._innerSubscribe(h)},u.prototype._innerSubscribe=function(h){var g=this,p=this,w=p.hasError,T=p.isStopped,E=p.observers;return w||T?EMPTY_SUBSCRIPTION:(this.currentObservers=null,E.push(h),new Subscription(function(){g.currentObservers=null,arrRemove(E,h)}))},u.prototype._checkFinalizedStatuses=function(h){var g=this,p=g.hasError,w=g.thrownError,T=g.isStopped;p?h.error(w):T&&h.complete()},u.prototype.asObservable=function(){var h=new Observable;return h.source=this,h},u.create=function(h,g){return new AnonymousSubject(h,g)},u}(Observable),AnonymousSubject=function(C){__extends(u,C);function u(h,g){var p=C.call(this)||this;return p.destination=h,p.source=g,p}return u.prototype.next=function(h){var g,p;(p=(g=this.destination)===null||g===void 0?void 0:g.next)===null||p===void 0||p.call(g,h)},u.prototype.error=function(h){var g,p;(p=(g=this.destination)===null||g===void 0?void 0:g.error)===null||p===void 0||p.call(g,h)},u.prototype.complete=function(){var h,g;(g=(h=this.destination)===null||h===void 0?void 0:h.complete)===null||g===void 0||g.call(h)},u.prototype._subscribe=function(h){var g,p;return(p=(g=this.source)===null||g===void 0?void 0:g.subscribe(h))!==null&&p!==void 0?p:EMPTY_SUBSCRIPTION},u}(Subject),dateTimestampProvider={now:function(){return(dateTimestampProvider.delegate||Date).now()},delegate:void 0},ReplaySubject=function(C){__extends(u,C);function u(h,g,p){h===void 0&&(h=1/0),g===void 0&&(g=1/0),p===void 0&&(p=dateTimestampProvider);var w=C.call(this)||this;return w._bufferSize=h,w._windowTime=g,w._timestampProvider=p,w._buffer=[],w._infiniteTimeWindow=!0,w._infiniteTimeWindow=g===1/0,w._bufferSize=Math.max(1,h),w._windowTime=Math.max(1,g),w}return u.prototype.next=function(h){var g=this,p=g.isStopped,w=g._buffer,T=g._infiniteTimeWindow,E=g._timestampProvider,L=g._windowTime;p||(w.push(h),!T&&w.push(E.now()+L)),this._trimBuffer(),C.prototype.next.call(this,h)},u.prototype._subscribe=function(h){this._throwIfClosed(),this._trimBuffer();for(var g=this._innerSubscribe(h),p=this,w=p._infiniteTimeWindow,T=p._buffer,E=T.slice(),L=0;L<E.length&&!h.closed;L+=w?1:2)h.next(E[L]);return this._checkFinalizedStatuses(h),g},u.prototype._trimBuffer=function(){var h=this,g=h._bufferSize,p=h._timestampProvider,w=h._buffer,T=h._infiniteTimeWindow,E=(T?1:2)*g;if(g<1/0&&E<w.length&&w.splice(0,w.length-E),!T){for(var L=p.now(),U=0,O=1;O<w.length&&w[O]<=L;O+=2)U=O;U&&w.splice(0,U+1)}},u}(Subject),DEFAULT_WEBSOCKET_CONFIG={url:"",deserializer:function(C){return JSON.parse(C.data)},serializer:function(C){return JSON.stringify(C)}},WEBSOCKETSUBJECT_INVALID_ERROR_OBJECT="WebSocketSubject.error must be called with an object with an error code, and an optional reason: { code: number, reason: string }",WebSocketSubject=function(C){__extends(u,C);function u(h,g){var p=C.call(this)||this;if(p._socket=null,h instanceof Observable)p.destination=g,p.source=h;else{var w=p._config=__assign({},DEFAULT_WEBSOCKET_CONFIG);if(p._output=new Subject,typeof h=="string")w.url=h;else for(var T in h)h.hasOwnProperty(T)&&(w[T]=h[T]);if(!w.WebSocketCtor&&WebSocket)w.WebSocketCtor=WebSocket;else if(!w.WebSocketCtor)throw new Error("no WebSocket constructor can be found");p.destination=new ReplaySubject}return p}return u.prototype.lift=function(h){var g=new u(this._config,this.destination);return g.operator=h,g.source=this,g},u.prototype._resetState=function(){this._socket=null,this.source||(this.destination=new ReplaySubject),this._output=new Subject},u.prototype.multiplex=function(h,g,p){var w=this;return new Observable(function(T){try{w.next(h())}catch(L){T.error(L)}var E=w.subscribe({next:function(L){try{p(L)&&T.next(L)}catch(U){T.error(U)}},error:function(L){return T.error(L)},complete:function(){return T.complete()}});return function(){try{w.next(g())}catch(L){T.error(L)}E.unsubscribe()}})},u.prototype._connectSocket=function(){var h=this,g=this._config,p=g.WebSocketCtor,w=g.protocol,T=g.url,E=g.binaryType,L=this._output,U=null;try{U=w?new p(T,w):new p(T),this._socket=U,E&&(this._socket.binaryType=E)}catch(d){L.error(d);return}var O=new Subscription(function(){h._socket=null,U&&U.readyState===1&&U.close()});U.onopen=function(d){var M=h._socket;if(!M){U.close(),h._resetState();return}var e=h._config.openObserver;e&&e.next(d);var b=h.destination;h.destination=Subscriber.create(function(z){if(U.readyState===1)try{var W=h._config.serializer;U.send(W(z))}catch(o0){h.destination.error(o0)}},function(z){var W=h._config.closingObserver;W&&W.next(void 0),z&&z.code?U.close(z.code,z.reason):L.error(new TypeError(WEBSOCKETSUBJECT_INVALID_ERROR_OBJECT)),h._resetState()},function(){var z=h._config.closingObserver;z&&z.next(void 0),U.close(),h._resetState()}),b&&b instanceof ReplaySubject&&O.add(b.subscribe(h.destination))},U.onerror=function(d){h._resetState(),L.error(d)},U.onclose=function(d){U===h._socket&&h._resetState();var M=h._config.closeObserver;M&&M.next(d),d.wasClean?L.complete():L.error(d)},U.onmessage=function(d){try{var M=h._config.deserializer;L.next(M(d))}catch(e){L.error(e)}}},u.prototype._subscribe=function(h){var g=this,p=this.source;return p?p.subscribe(h):(this._socket||this._connectSocket(),this._output.subscribe(h),h.add(function(){var w=g._socket;g._output.observers.length===0&&(w&&(w.readyState===1||w.readyState===0)&&w.close(),g._resetState())}),h)},u.prototype.unsubscribe=function(){var h=this._socket;h&&(h.readyState===1||h.readyState===0)&&h.close(),this._resetState(),C.prototype.unsubscribe.call(this)},u}(AnonymousSubject);function webSocket(C){return new WebSocketSubject(C)}var NVMESSAGE=(C=>(C[C.ZOOM=1]="ZOOM",C[C.CLIP_PLANE=2]="CLIP_PLANE",C[C.AZIMUTH_ELEVATION=3]="AZIMUTH_ELEVATION",C[C.FRAME_CHANGED=4]="FRAME_CHANGED",C[C.VOLUME_ADDED_FROM_URL=5]="VOLUME_ADDED_FROM_URL",C[C.VOLUME_WITH_URL_REMOVED=6]="VOLUME_WITH_URL_REMOVED",C[C.COLORMAP_CHANGED=7]="COLORMAP_CHANGED",C[C.OPACITY_CHANGED=8]="OPACITY_CHANGED",C[C.MESH_FROM_URL_ADDED=9]="MESH_FROM_URL_ADDED",C[C.MESH_WITH_URL_REMOVED=10]="MESH_WITH_URL_REMOVED",C[C.CUSTOM_SHADER_ADDED=11]="CUSTOM_SHADER_ADDED",C[C.SHADER_CHANGED=12]="SHADER_CHANGED",C[C.MESH_PROPERTY_CHANGED=13]="MESH_PROPERTY_CHANGED",C.USER_JOINED="user joined",C.CREATE="create",C.VOLUME_LOADED_FROM_URL="volume with url added",C))(NVMESSAGE||{});class SessionUser{constructor(u,h,g,p){d0(this,"id");d0(this,"displayName");d0(this,"key");d0(this,"properties");this.id=h||v4(),this.displayName=u||`user-${this.id}`,this.key=g||v4(),this.properties=p||new Map}}function storageAvailable(C){const u=window[C],h="test";try{return u.setItem(h,h),u.removeItem(h),!0}catch{return!1}}class SessionBus{constructor(u,h,g,p="",w=""){d0(this,"userList");d0(this,"user");d0(this,"userQueueName");d0(this,"userListName");d0(this,"onMessageCallback");d0(this,"isConnectedToServer",!1);d0(this,"isController",!1);d0(this,"sessionScene",{});d0(this,"sessionKey");d0(this,"sessionName");d0(this,"sessionSceneName");d0(this,"serverConnection$",null);if(this.userList=[],this.user=h||new SessionUser("anonymous"),this.onMessageCallback=g,this.sessionScene={},this.sessionKey=w||v4(),this.sessionName=u,this.sessionSceneName=`session-${u}-scene`,p)this.connectToServer(p,u),this.subscribeToServer(),this.isConnectedToServer=!0,this.serverConnection$!==null&&this.serverConnection$.next({op:NVMESSAGE.CREATE,key:this.sessionKey});else{if(!storageAvailable("localStorage"))throw new Error("Local storage unavailable");this.userQueueName=`user-${this.user.id}-q`,this.userListName=`${u}-user-list`,this.userList=JSON.parse(localStorage.getItem(this.userListName)||"[]"),this.userList.push(this.user),localStorage.setItem(this.userListName,JSON.stringify(this.userList)),localStorage.setItem(this.userQueueName,JSON.stringify([])),window.addEventListener("storage",this.localStorageEventListener.bind(this))}}sendSessionMessage(u){u.from=this.user.id,this.isConnectedToServer&&this.serverConnection$!==null?this.serverConnection$.next({...u,key:this.sessionKey,userKey:this.user.key}):this.sendLocalMessage(u)}connectToServer(u,h){const g=new URL(u);g.pathname="websockets",g.search="?session="+h,this.serverConnection$=webSocket(g.href),log.debug(g.href)}subscribeToServer(){this.serverConnection$!==null&&this.serverConnection$.subscribe({next:u=>{this.onMessageCallback(u)},error:u=>log.error(u),complete:()=>log.debug("complete")})}sendLocalMessage(u){for(const h of this.userList){if(h.id===this.user.id)continue;const g=`user-${h.id}-q`,p=localStorage.getItem(g),w=p?JSON.parse(p):[];w.push(u),localStorage.setItem(g,JSON.stringify(w))}}localStorageEventListener(u){if(u.newValue)switch(u.key){case this.userListName:{const h=JSON.parse(u.newValue),g=JSON.parse(u.oldValue??"[]");this.userList=h;const p=h.filter(w=>!g.map(T=>T.id).includes(w.id));for(const w of p)this.onMessageCallback({op:NVMESSAGE.USER_JOINED,user:w})}break;case this.userQueueName:{const h=JSON.parse(u.newValue);for(const g of h)this.onMessageCallback&&this.onMessageCallback(g);localStorage.setItem(this.userQueueName??"",JSON.stringify([]))}break}}}class NVController{constructor(u){d0(this,"niivue");d0(this,"mediaUrlMap");d0(this,"isInSession",!1);d0(this,"user");d0(this,"sessionBus");d0(this,"onFrameChange",(u,h)=>{});this.niivue=u,this.mediaUrlMap=new Map,this.niivue.onLocationChange=this.onLocationChangeHandler.bind(this),this.niivue.onZoom3DChange=this.onZoom3DChangeHandler.bind(this),this.niivue.scene.onAzimuthElevationChange=this.onAzimuthElevationChangeHandler.bind(this),this.niivue.onClipPlaneChange=this.onClipPlaneChangeHandler.bind(this),this.niivue.onVolumeAddedFromUrl=this.onVolumeAddedFromUrlHandler.bind(this),this.niivue.onVolumeWithUrlRemoved=this.onVolumeWithUrlRemovedHandler.bind(this),this.niivue.onMeshAddedFromUrl=this.onMeshAddedFromUrlHandler.bind(this),this.niivue.onMeshWithUrlRemoved=this.onMeshWithUrlRemovedHandler.bind(this),this.niivue.onCustomMeshShaderAdded=this.onCustomMeshShaderAddedHandler.bind(this),this.niivue.onMeshShaderChanged=this.onMeshShaderChanged.bind(this),this.niivue.onMeshPropertyChanged=this.onMeshPropertyChanged.bind(this),this.niivue.onFrameChange=this.onFrameChangeHandler.bind(this);for(const h of this.niivue.volumes)h.onColormapChange=this.onColormapChangeHandler.bind(this),h.onOpacityChange=this.onOpacityChangeHandler.bind(this)}onLocationChangeHandler(u){log.debug(u)}addVolume(u,h){this.niivue.volumes.push(u);const g=this.niivue.volumes.length===1?0:this.niivue.volumes.length-1;this.niivue.setVolume(u,g),this.niivue.mediaUrlMap.set(u,h)}addMesh(u,h){this.niivue.meshes.push(u);const g=this.niivue.meshes.length===1?0:this.niivue.meshes.length-1;this.niivue.setMesh(u,g),this.niivue.mediaUrlMap.set(u,h)}onNewMessage(u){switch(u.op){case NVMESSAGE.ZOOM:this.niivue.volScaleMultiplier=u.zoom;break;case NVMESSAGE.CLIP_PLANE:this.niivue.scene.clipPlane=u.clipPlane;break;case NVMESSAGE.AZIMUTH_ELEVATION:this.niivue.scene._elevation=u.elevation,this.niivue.scene._azimuth=u.azimuth;break;case NVMESSAGE.FRAME_CHANGED:{const h=this.niivue.getMediaByUrl(u.url);h&&(h.frame4D=u.index)}break;case NVMESSAGE.VOLUME_ADDED_FROM_URL:this.niivue.getMediaByUrl(u.imageOptions.url)||NVImage.loadFromUrl(u.imageOptions).then(h=>{h&&this.addVolume(h,u.imageOptions.url)}).catch(h=>{if(h)throw h});break;case NVMESSAGE.VOLUME_WITH_URL_REMOVED:{const h=this.niivue.getMediaByUrl(u.url);h&&(this.niivue.setVolume(h,-1),this.niivue.mediaUrlMap.delete(h))}break;case NVMESSAGE.COLORMAP_CHANGED:{const h=this.niivue.getMediaByUrl(u.url);h._colormap=u.colormap,this.niivue.updateGLVolume()}break;case NVMESSAGE.OPACITY_CHANGED:{const h=this.niivue.getMediaByUrl(u.url);h._opacity=u.opacity,this.niivue.updateGLVolume()}break;case NVMESSAGE.MESH_FROM_URL_ADDED:this.niivue.getMediaByUrl(u.meshOptions.url)||(u.meshOptions.gl=this.niivue.gl,NVMesh.loadFromUrl(u.meshOptions).then(h=>{this.addMesh(h,u.meshOptions.url)}).catch(h=>{if(h)throw h}));break;case NVMESSAGE.MESH_WITH_URL_REMOVED:{const h=this.niivue.getMediaByUrl(u.url);h&&(this.niivue.setMesh(h,-1),this.niivue.mediaUrlMap.delete(h))}break;case NVMESSAGE.CUSTOM_SHADER_ADDED:{const h=this.niivue.createCustomMeshShader(u.fragmentShaderText,u.name);this.niivue.meshShaders.push(h)}break;case NVMESSAGE.SHADER_CHANGED:this.niivue.meshes[u.meshIndex].meshShaderIndex=u.shaderIndex,this.niivue.updateGLVolume();break;case NVMESSAGE.MESH_PROPERTY_CHANGED:this.niivue.meshes[u.meshIndex].setProperty(u.key,u.val,this.niivue.gl);break}this.niivue.drawScene()}connectToSession(u,h,g,p){this.user=h||new SessionUser,this.sessionBus=new SessionBus(u,this.user,this.onNewMessage.bind(this),g,p),this.isInSession=!0}onZoom3DChangeHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.ZOOM,zoom:u})}onAzimuthElevationChangeHandler(u,h){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.AZIMUTH_ELEVATION,azimuth:u,elevation:h})}onClipPlaneChangeHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.CLIP_PLANE,clipPlane:u})}onVolumeAddedFromUrlHandler(u,h){this.isInSession&&this.sessionBus&&(log.debug(u),this.sessionBus.sendSessionMessage({op:NVMESSAGE.VOLUME_ADDED_FROM_URL,imageOptions:u})),h.onColormapChange=this.onColormapChangeHandler.bind(this),h.onOpacityChange=this.onOpacityChangeHandler.bind(this)}onImageLoadedHandler(u){if(u.onColormapChange=this.onColormapChangeHandler.bind(this),u.onOpacityChange=this.onOpacityChangeHandler.bind(this),this.isInSession&&this.sessionBus&&this.niivue.mediaUrlMap.has(u)){const h=this.niivue.mediaUrlMap.get(u);this.sessionBus.sendSessionMessage({op:NVMESSAGE.VOLUME_LOADED_FROM_URL,url:h})}}onVolumeWithUrlRemovedHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.VOLUME_WITH_URL_REMOVED,url:u})}onMeshAddedFromUrlHandler(u){log.debug("mesh loaded from url"),log.debug(u),this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.MESH_FROM_URL_ADDED,meshOptions:u})}onMeshLoadedHandler(u){log.debug("mesh has been added"),log.debug(u)}onMeshWithUrlRemovedHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.MESH_WITH_URL_REMOVED,url:u})}onColormapChangeHandler(u){if(this.isInSession&&this.sessionBus&&this.niivue.mediaUrlMap.has(u)){const h=this.niivue.mediaUrlMap.get(u),g=u.colormap;this.sessionBus.sendSessionMessage({op:NVMESSAGE.COLORMAP_CHANGED,url:h,colormap:g})}}onOpacityChangeHandler(u){if(this.isInSession&&this.sessionBus&&this.niivue.mediaUrlMap.has(u)){const h=this.niivue.mediaUrlMap.get(u),g=u.opacity;this.sessionBus.sendSessionMessage({op:NVMESSAGE.OPACITY_CHANGED,url:h,opacity:g})}}onFrameChangeHandler(u,h){if(log.debug("frame has changed to "+h),log.debug(u),this.niivue.mediaUrlMap.has(u)&&this.isInSession&&this.sessionBus){const g=this.niivue.mediaUrlMap.get(u);this.sessionBus.sendSessionMessage({op:NVMESSAGE.FRAME_CHANGED,url:g,index:h})}this.onFrameChange(u,h)}onCustomMeshShaderAddedHandler(u,h){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.CUSTOM_SHADER_ADDED,fragmentShaderText:u,name:h})}onMeshShaderChanged(u,h){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.SHADER_CHANGED,meshIndex:u,shaderIndex:h})}onMeshPropertyChanged(u,h,g){this.isInSession&&this.sessionBus&&(log.debug(NVMESSAGE.MESH_PROPERTY_CHANGED),this.sessionBus.sendSessionMessage({op:NVMESSAGE.MESH_PROPERTY_CHANGED,meshIndex:u,key:h,val:g}))}}const MESH_EXTENSIONS=["ASC","BYU","DFS","FSM","PIAL","ORIG","INFLATED","SMOOTHWM","SPHERE","WHITE","G","GEO","GII","ICO","MZ3","NV","OBJ","OFF","PLY","SRF","STL","TCK","TRACT","TRI","TRK","TT","TRX","VTK","X3D","JCON","JSON"],LEFT_MOUSE_BUTTON=0,CENTER_MOUSE_BUTTON=1,RIGHT_MOUSE_BUTTON=2,TEXTURE0_BACK_VOL=33984,TEXTURE1_COLORMAPS=33985,TEXTURE2_OVERLAY_VOL=33986,TEXTURE3_FONT=33987,TEXTURE4_THUMBNAIL=33988,TEXTURE5_MATCAP=33989,TEXTURE6_GRADIENT=33990,TEXTURE7_DRAW=33991,TEXTURE8_GRADIENT_TEMP=33992,TEXTURE9_ORIENT=33993,TEXTURE10_BLEND=33994,TEXTURE11_GC_BACK=33995,TEXTURE12_GC_STRENGTH0=33996,TEXTURE13_GC_STRENGTH1=33997,TEXTURE14_GC_LABEL0=33998,TEXTURE15_GC_LABEL1=33999,defaultSaveImageOptions={filename:"",isSaveDrawing:!1,volumeByIndex:0};class Niivue{constructor(u=DEFAULT_OPTIONS){d0(this,"loaders",{});d0(this,"canvas",null);d0(this,"_gl",null);d0(this,"isBusy",!1);d0(this,"needsRefresh",!1);d0(this,"colormapTexture",null);d0(this,"colormapLists",[]);d0(this,"volumeTexture",null);d0(this,"gradientTexture",null);d0(this,"gradientTextureAmount",0);d0(this,"drawTexture",null);d0(this,"drawUndoBitmaps",[]);d0(this,"drawLut",cmapper.makeDrawLut("$itksnap"));d0(this,"drawOpacity",.8);d0(this,"renderDrawAmbientOcclusion",.4);d0(this,"colorbarHeight",0);d0(this,"drawPenLocation",[NaN,NaN,NaN]);d0(this,"drawPenAxCorSag",-1);d0(this,"drawFillOverwrites",!0);d0(this,"drawPenFillPts",[]);d0(this,"overlayTexture",null);d0(this,"overlayTextureID",null);d0(this,"sliceMMShader");d0(this,"sliceV1Shader");d0(this,"orientCubeShader");d0(this,"orientCubeShaderVAO",null);d0(this,"rectShader");d0(this,"renderShader");d0(this,"lineShader");d0(this,"line3DShader");d0(this,"passThroughShader");d0(this,"renderGradientShader");d0(this,"renderSliceShader");d0(this,"renderVolumeShader");d0(this,"pickingMeshShader");d0(this,"pickingImageShader");d0(this,"colorbarShader");d0(this,"fontShader",null);d0(this,"fiberShader");d0(this,"fontTexture",null);d0(this,"circleShader");d0(this,"matCapTexture",null);d0(this,"bmpShader",null);d0(this,"bmpTexture",null);d0(this,"thumbnailVisible",!1);d0(this,"bmpTextureWH",1);d0(this,"growCutShader");d0(this,"orientShaderAtlasU",null);d0(this,"orientShaderAtlasI",null);d0(this,"orientShaderU",null);d0(this,"orientShaderI",null);d0(this,"orientShaderF",null);d0(this,"orientShaderRGBU",null);d0(this,"surfaceShader",null);d0(this,"blurShader",null);d0(this,"sobelShader",null);d0(this,"genericVAO",null);d0(this,"unusedVAO",null);d0(this,"crosshairs3D",null);d0(this,"DEFAULT_FONT_GLYPH_SHEET",defaultFontPNG);d0(this,"DEFAULT_FONT_METRICS",defaultFontMetrics);d0(this,"fontMetrics");d0(this,"fontMets",null);d0(this,"backgroundMasksOverlays",0);d0(this,"overlayOutlineWidth",0);d0(this,"overlayAlphaShader",1);d0(this,"isAlphaClipDark",!1);d0(this,"position");d0(this,"extentsMin");d0(this,"extentsMax");d0(this,"resizeObserver",null);d0(this,"syncOpts",{});d0(this,"readyForSync",!1);d0(this,"uiData",{mousedown:!1,touchdown:!1,mouseButtonLeftDown:!1,mouseButtonCenterDown:!1,mouseButtonRightDown:!1,mouseDepthPicker:!1,clickedTile:-1,pan2DxyzmmAtMouseDown:[0,0,0,1],prevX:0,prevY:0,currX:0,currY:0,currentTouchTime:0,lastTouchTime:0,touchTimer:null,doubleTouch:!1,isDragging:!1,dragStart:[0,0],dragEnd:[0,0],dragClipPlaneStartDepthAziElev:[0,0,0],lastTwoTouchDistance:0,multiTouchGesture:!1});d0(this,"back",null);d0(this,"overlays",[]);d0(this,"deferredVolumes",[]);d0(this,"deferredMeshes",[]);d0(this,"furthestVertexFromOrigin",100);d0(this,"volScale",[]);d0(this,"vox",[]);d0(this,"mousePos",[0,0]);d0(this,"screenSlices",[]);d0(this,"cuboidVertexBuffer");d0(this,"otherNV",null);d0(this,"volumeObject3D",null);d0(this,"pivot3D",[0,0,0]);d0(this,"furthestFromPivot",10);d0(this,"currentClipPlaneIndex",0);d0(this,"lastCalled",new Date().getTime());d0(this,"selectedObjectId",-1);d0(this,"CLIP_PLANE_ID",1);d0(this,"VOLUME_ID",254);d0(this,"DISTANCE_FROM_CAMERA",-.54);d0(this,"graph",{LTWH:[0,0,640,480],opacity:0,vols:[0],autoSizeMultiplanar:!1,normalizeValues:!1});d0(this,"meshShaders",[{Name:"Phong",Frag:fragMeshShader},{Name:"Matte",Frag:fragMeshMatteShader},{Name:"Harmonic",Frag:fragMeshShaderSHBlue},{Name:"Hemispheric",Frag:fragMeshHemiShader},{Name:"Crevice",Frag:fragMeshShaderCrevice},{Name:"Edge",Frag:fragMeshEdgeShader},{Name:"Diffuse",Frag:fragMeshDiffuseEdgeShader},{Name:"Outline",Frag:fragMeshOutlineShader},{Name:"Specular",Frag:fragMeshSpecularEdgeShader},{Name:"Toon",Frag:fragMeshToonShader},{Name:"Flat",Frag:fragFlatMeshShader},{Name:"Matcap",Frag:fragMeshMatcapShader}]);d0(this,"dragModes",{contrast:DRAG_MODE.contrast,measurement:DRAG_MODE.measurement,none:DRAG_MODE.none,pan:DRAG_MODE.pan,slicer3D:DRAG_MODE.slicer3D,callbackOnly:DRAG_MODE.callbackOnly});d0(this,"sliceTypeAxial",SLICE_TYPE.AXIAL);d0(this,"sliceTypeCoronal",SLICE_TYPE.CORONAL);d0(this,"sliceTypeSagittal",SLICE_TYPE.SAGITTAL);d0(this,"sliceTypeMultiplanar",SLICE_TYPE.MULTIPLANAR);d0(this,"sliceTypeRender",SLICE_TYPE.RENDER);d0(this,"onDragRelease",()=>{});d0(this,"onMouseUp",()=>{});d0(this,"onLocationChange",()=>{});d0(this,"onIntensityChange",()=>{});d0(this,"onClickToSegment",()=>{});d0(this,"onImageLoaded",()=>{});d0(this,"onMeshLoaded",()=>{});d0(this,"onFrameChange",()=>{});d0(this,"onError",()=>{});d0(this,"onColormapChange",()=>{});d0(this,"onInfo",()=>{});d0(this,"onWarn",()=>{});d0(this,"onDebug",()=>{});d0(this,"onVolumeAddedFromUrl",()=>{});d0(this,"onVolumeWithUrlRemoved",()=>{});d0(this,"onVolumeUpdated",()=>{});d0(this,"onMeshAddedFromUrl",()=>{});d0(this,"onMeshAdded",()=>{});d0(this,"onMeshWithUrlRemoved",()=>{});d0(this,"onZoom3DChange",()=>{});d0(this,"onAzimuthElevationChange",()=>{});d0(this,"onClipPlaneChange",()=>{});d0(this,"onCustomMeshShaderAdded",()=>{});d0(this,"onMeshShaderChanged",()=>{});d0(this,"onMeshPropertyChanged",()=>{});d0(this,"onDocumentLoaded",()=>{});d0(this,"document",new NVDocument);d0(this,"mediaUrlMap",new Map);d0(this,"initialized",!1);d0(this,"currentDrawUndoBitmap");d0(this,"loadingText");for(const h in u)typeof u[h]=="function"?this[h]=u[h]:this.opts[h]=DEFAULT_OPTIONS[h]===void 0?DEFAULT_OPTIONS[h]:u[h];this.opts.isHighResolutionCapable?this.uiData.dpr=window.devicePixelRatio||1:this.uiData.dpr=1,this.currentDrawUndoBitmap=this.opts.maxDrawUndoBitmaps,this.opts.drawingEnabled&&this.createEmptyDrawing(),this.opts.thumbnail.length>0&&(this.thumbnailVisible=!0),this.loadingText=this.opts.loadingText,log.setLogLevel(this.opts.logLevel)}get scene(){return this.document.scene}get opts(){return this.document.opts}get sliceMosaicString(){return this.document.opts.sliceMosaicString||""}set sliceMosaicString(u){this.document.opts.sliceMosaicString=u}get volumes(){return this.document.volumes}set volumes(u){this.document.volumes=u}get meshes(){return this.document.meshes}set meshes(u){this.document.meshes=u}get drawBitmap(){return this.document.drawBitmap}set drawBitmap(u){this.document.drawBitmap=u}get volScaleMultiplier(){return this.scene.volScaleMultiplier}set volScaleMultiplier(u){this.setScale(u)}async saveScene(u="niivue.png"){function h(p,w){const T=document.createElement("a");document.body.appendChild(T),T.style.display="none";const E=window.URL.createObjectURL(p);T.href=E,T.download=w,T.click(),T.remove()}const g=this.canvas;if(!g)throw new Error("canvas not defined");this.drawScene(),g.toBlob(p=>{p&&(u===""&&(u=`niivue-screenshot-${new Date().toString()}.png`,u=u.replace(/\s/g,"_")),h(p,u))})}async attachTo(u,h=null){return await this.attachToCanvas(document.getElementById(u),h),log.debug("attached to element with id: ",u),this}async attachToCanvas(u,h=null){return this.canvas=u,h===null&&(h=navigator.hardwareConcurrency>6,log.debug("AntiAlias ",h," Threads ",navigator.hardwareConcurrency)),this.gl=this.canvas.getContext("webgl2",{alpha:!0,antialias:h}),log.info("NIIVUE VERSION ",version),this.canvas.parentElement.style.backgroundColor="black",this.opts.isResizeCanvas&&(this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.display="block",this.canvas.width=this.canvas.offsetWidth,this.canvas.height=this.canvas.offsetHeight,window.addEventListener("resize",this.resizeListener.bind(this)),this.resizeObserver=new ResizeObserver(this.resizeListener.bind(this)),this.resizeObserver.observe(this.canvas.parentElement)),this.registerInteractions(),await this.init(),this.drawScene(),this}syncWith(u,h={"2d":!0,"3d":!0}){this.otherNV=u,this.syncOpts={...h}}broadcastTo(u,h={"2d":!0,"3d":!0}){this.otherNV=u,this.syncOpts=h}sync(){if(!this.gl||!this.otherNV||typeof this.otherNV>"u"||!this.gl.canvas.matches(":focus"))return;const u=this.frac2mm(this.scene.crosshairPos);if(this.otherNV instanceof Niivue){const h=this.scene.gamma,g=this.otherNV.scene.gamma;h!==g&&this.otherNV.setGamma(h),this.syncOpts["2d"]&&(this.otherNV.scene.crosshairPos=this.otherNV.mm2frac(u),this.otherNV.scene.pan2Dxyzmm=clone(this.scene.pan2Dxyzmm)),this.syncOpts["3d"]&&(this.otherNV.scene.renderAzimuth=this.scene.renderAzimuth,this.otherNV.scene.renderElevation=this.scene.renderElevation,this.otherNV.scene.volScaleMultiplier=this.scene.volScaleMultiplier),this.otherNV.drawScene(),this.otherNV.createOnLocationChange()}else if(Array.isArray(this.otherNV))for(let h=0;h<this.otherNV.length;h++){if(this.otherNV[h]===this)continue;const g=this.scene.gamma,p=this.otherNV[h].scene.gamma;g!==p&&this.otherNV[h].setGamma(g),this.syncOpts["2d"]&&(this.otherNV[h].scene.crosshairPos=this.otherNV[h].mm2frac(u),this.otherNV[h].scene.pan2Dxyzmm=clone(this.scene.pan2Dxyzmm)),this.syncOpts["3d"]&&(this.otherNV[h].scene.renderAzimuth=this.scene.renderAzimuth,this.otherNV[h].scene.renderElevation=this.scene.renderElevation,this.otherNV[h].scene.volScaleMultiplier=this.scene.volScaleMultiplier),this.otherNV[h].drawScene(),this.otherNV[h].createOnLocationChange()}}arrayEquals(u,h){return Array.isArray(u)&&Array.isArray(h)&&u.length===h.length&&u.every((g,p)=>g===h[p])}resizeListener(){if(!(!this.canvas||!this.gl)){if(!this.opts.isResizeCanvas){this.opts.isHighResolutionCapable&&(log.warn("isHighResolutionCapable requires isResizeCanvas"),this.opts.isHighResolutionCapable=!1),this.uiData.dpr=1,this.drawScene();return}this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.display="block",this.opts.isHighResolutionCapable?(this.uiData.dpr=window.devicePixelRatio||1,log.debug("devicePixelRatio: "+this.uiData.dpr)):this.uiData.dpr=1,"width"in this.canvas.parentElement?(this.canvas.width=this.canvas.parentElement.width*this.uiData.dpr,this.canvas.height=this.canvas.parentElement.height*this.uiData.dpr):(this.canvas.width=this.canvas.offsetWidth*this.uiData.dpr,this.canvas.height=this.canvas.offsetHeight*this.uiData.dpr),this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height),this.drawScene()}}getRelativeMousePosition(u,h){if(h=h||u.target,!h)return;const g=h.getBoundingClientRect();return{x:u.clientX-g.left,y:u.clientY-g.top}}getNoPaddingNoBorderCanvasRelativeMousePosition(u,h){return h=h||u.target,this.getRelativeMousePosition(u,h)}mouseContextMenuListener(u){u.preventDefault()}mouseDownListener(u){u.preventDefault(),this.drawPenLocation=[NaN,NaN,NaN],this.drawPenAxCorSag=-1,this.uiData.mousedown=!0,log.debug("mouse down"),log.debug(u);const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);if(!h)return;const[g,p]=[h.x*this.uiData.dpr,h.y*this.uiData.dpr],w=this.getLabelAtPoint([g,p]);if(w){if(w.onClick){w.onClick(w);return}for(const T of this.meshes)if(T.type===MeshType.CONNECTOME)for(const E of T.nodes)E.label===w&&(this.scene.crosshairPos=this.mm2frac([E.x,E.y,E.z]),this.updateGLVolume(),this.drawScene())}this.uiData.clickedTile=this.tileIndex(g,p),u.button===LEFT_MOUSE_BUTTON&&u.shiftKey?(this.uiData.mouseButtonCenterDown=!0,this.mouseCenterButtonHandler(u)):u.button===LEFT_MOUSE_BUTTON?(this.uiData.mouseButtonLeftDown=!0,this.mouseLeftButtonHandler(u)):u.button===RIGHT_MOUSE_BUTTON?(this.uiData.mouseButtonRightDown=!0,this.mouseRightButtonHandler(u)):u.button===CENTER_MOUSE_BUTTON&&(this.uiData.mouseButtonCenterDown=!0,this.mouseCenterButtonHandler(u))}mouseLeftButtonHandler(u){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);this.mouseDown(h.x,h.y),this.mouseClick(h.x,h.y)}mouseCenterButtonHandler(u){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);this.mousePos=[h.x*this.uiData.dpr,h.y*this.uiData.dpr],this.opts.dragMode!==DRAG_MODE.none&&(this.setDragStart(h.x,h.y),this.uiData.isDragging||(this.uiData.pan2DxyzmmAtMouseDown=clone(this.scene.pan2Dxyzmm)),this.uiData.isDragging=!0,this.uiData.dragClipPlaneStartDepthAziElev=this.scene.clipPlaneDepthAziElev)}mouseRightButtonHandler(u){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);this.mousePos=[h.x*this.uiData.dpr,h.y*this.uiData.dpr],this.opts.dragMode!==DRAG_MODE.none&&(this.setDragStart(h.x,h.y),this.uiData.isDragging||(this.uiData.pan2DxyzmmAtMouseDown=clone(this.scene.pan2Dxyzmm)),this.uiData.isDragging=!0,this.uiData.dragClipPlaneStartDepthAziElev=this.scene.clipPlaneDepthAziElev)}calculateMinMaxVoxIdx(u){if(u.length>2)throw new Error("array must not contain more than two values");return[Math.floor(Math.min(u[0],u[1])),Math.floor(Math.max(u[0],u[1]))]}calculateNewRange({volIdx:u=0}={}){if(this.opts.sliceType===SLICE_TYPE.RENDER&&this.sliceMosaicString.length<1||this.uiData.dragStart[0]===this.uiData.dragEnd[0]&&this.uiData.dragStart[1]===this.uiData.dragEnd[1])return;let h=this.canvasPos2frac([this.uiData.dragStart[0],this.uiData.dragStart[1]]);if(h[0]<0)return;const g=this.frac2vox(h,u);if(h=this.canvasPos2frac([this.uiData.dragEnd[0],this.uiData.dragEnd[1]]),h[0]<0)return;const p=this.frac2vox(h,u);let w=-Number.MAX_VALUE,T=Number.MAX_VALUE;const E=this.calculateMinMaxVoxIdx([g[0],p[0]]),L=this.calculateMinMaxVoxIdx([g[1],p[1]]),U=this.calculateMinMaxVoxIdx([g[2],p[2]]);g[0]-p[0]===0?E[1]=g[0]+1:g[1]-p[1]===0?L[1]=g[1]+1:g[2]-p[2]===0&&(U[1]=g[2]+1);const O=this.volumes[u].hdr,d=this.volumes[u].img;if(!O||!d)return;const M=O.dims[1],e=O.dims[2];for(let W=U[0];W<U[1];W++){const o0=W*M*e;for(let J=L[0];J<L[1];J++){const K=J*M;for(let y=E[0];y<E[1];y++){const Z=o0+K+y;T>d[Z]&&(T=d[Z]),w<d[Z]&&(w=d[Z])}}}if(T>=w)return;const b=intensityRaw2Scaled(O,T),z=intensityRaw2Scaled(O,w);this.volumes[u].cal_min=b,this.volumes[u].cal_max=z,this.onIntensityChange(this.volumes[u])}generateMouseUpCallback(u,h){const g=this.tileIndex(this.uiData.dragStart[0],this.uiData.dragStart[1]),p=this.tileIndex(this.uiData.dragEnd[0],this.uiData.dragEnd[1]);let w=-1;g===p&&(w=p);let T=-1;w>=0&&(T=this.screenSlices[w].axCorSag);const E=this.frac2mm(u),L=this.frac2mm(h),U=create$2();sub$1(U,fromValues$2(E[0],E[1],E[2]),fromValues$2(L[0],L[1],L[2]));const O=len(U),d=this.frac2vox(u),M=this.frac2vox(h);this.onDragRelease({fracStart:u,fracEnd:h,voxStart:d,voxEnd:M,mmStart:E,mmEnd:L,mmLength:O,tileIdx:w,axCorSag:T})}mouseUpListener(){function u(p){return Object.prototype.toString.call(p).indexOf("Function")>-1}const h={mouseButtonRightDown:this.uiData.mouseButtonRightDown,mouseButtonCenterDown:this.uiData.mouseButtonCenterDown,isDragging:this.uiData.isDragging,mousePos:this.mousePos,fracPos:this.canvasPos2frac(this.mousePos)};this.uiData.mousedown=!1,this.uiData.mouseButtonRightDown=!1;const g=this.uiData.mouseButtonCenterDown;if(this.uiData.mouseButtonCenterDown=!1,this.uiData.mouseButtonLeftDown=!1,this.drawPenFillPts.length>0?this.drawPenFilled():this.drawPenAxCorSag>=0&&this.drawAddUndoBitmap(),this.drawPenLocation=[NaN,NaN,NaN],this.drawPenAxCorSag=-1,u(this.onMouseUp)&&this.onMouseUp(h),this.uiData.isDragging){this.uiData.isDragging=!1,this.opts.dragMode===DRAG_MODE.callbackOnly&&this.drawScene();const p=this.canvasPos2frac([this.uiData.dragStart[0],this.uiData.dragStart[1]]),w=this.canvasPos2frac([this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);if(this.generateMouseUpCallback(p,w),this.opts.dragMode!==DRAG_MODE.contrast||g||this.uiData.dragStart[0]===this.uiData.dragEnd[0]&&this.uiData.dragStart[1]===this.uiData.dragEnd[1])return;this.calculateNewRange({volIdx:0}),this.refreshLayers(this.volumes[0],0)}this.drawScene()}checkMultitouch(u){if(this.uiData.touchdown&&!this.uiData.multiTouchGesture){const h=this.canvas.getBoundingClientRect();this.mouseDown(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top),this.mouseClick(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top)}}touchStartListener(u){u.preventDefault(),this.uiData.touchTimer||(this.uiData.touchTimer=setTimeout(()=>{this.resetBriCon(u)},this.opts.longTouchTimeout)),this.uiData.touchdown=!0,this.uiData.currentTouchTime=new Date().getTime();const h=this.uiData.currentTouchTime-this.uiData.lastTouchTime;if(h<this.opts.doubleTouchTimeout&&h>0){this.uiData.doubleTouch=!0,this.setDragStart(u.targetTouches[0].clientX-u.target.getBoundingClientRect().left,u.targetTouches[0].clientY-u.target.getBoundingClientRect().top),this.resetBriCon(u),this.uiData.lastTouchTime=this.uiData.currentTouchTime;return}else this.uiData.doubleTouch=!1,this.setDragStart(0,0),this.setDragEnd(0,0),this.uiData.lastTouchTime=this.uiData.currentTouchTime;this.uiData.touchdown&&u.touches.length<2?this.uiData.multiTouchGesture=!1:this.uiData.multiTouchGesture=!0,setTimeout(this.checkMultitouch.bind(this),1,u)}touchEndListener(u){if(u.preventDefault(),this.uiData.touchdown=!1,this.uiData.lastTwoTouchDistance=0,this.uiData.multiTouchGesture=!1,this.uiData.touchTimer&&(clearTimeout(this.uiData.touchTimer),this.uiData.touchTimer=null),this.uiData.isDragging){this.uiData.isDragging=!1,this.opts.dragMode===DRAG_MODE.contrast&&(this.calculateNewRange(),this.refreshLayers(this.volumes[0],0));const h=this.canvasPos2frac([this.uiData.dragStart[0],this.uiData.dragStart[1]]),g=this.canvasPos2frac([this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);this.generateMouseUpCallback(h,g)}this.mouseUpListener()}mouseMoveListener(u){if(this.uiData.mousedown){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);if(!h)return;const g=h.x*this.uiData.dpr,p=h.y*this.uiData.dpr;if(this.tileIndex(g,p)!==this.uiData.clickedTile)return;this.uiData.mouseButtonLeftDown?(this.mouseMove(h.x,h.y),this.mouseClick(h.x,h.y)):(this.uiData.mouseButtonRightDown||this.uiData.mouseButtonCenterDown)&&this.setDragEnd(h.x,h.y),this.drawScene(),this.uiData.prevX=this.uiData.currX,this.uiData.prevY=this.uiData.currY}else if(!this.uiData.mousedown&&this.opts.clickToSegment){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);if(!h)return;const g=h.x*this.uiData.dpr,p=h.y*this.uiData.dpr;this.mousePos=[g,p],this.drawScene()}}resetBriCon(u=null){if(this.uiData.isDragging)return;let h=!1;this.opts.sliceType===SLICE_TYPE.RENDER&&(h=!0);let g=0,p=0;if(u!==null&&("targetTouches"in u?(g=u.targetTouches[0].clientX-u.target.getBoundingClientRect().left,p=u.targetTouches[0].clientY-u.target.getBoundingClientRect().top):(g=u.offsetX,p=u.offsetY),g*=this.uiData.dpr,p*=this.uiData.dpr,this.inRenderTile(g,p)>=0&&(h=!0)),h){this.uiData.mouseDepthPicker=!0,this.drawScene(),this.drawScene();return}this.opts.dragMode!==DRAG_MODE.slicer3D&&(this.volumes.length<1||this.uiData.doubleTouch||(this.volumes[0].cal_min=this.volumes[0].robust_min,this.volumes[0].cal_max=this.volumes[0].robust_max,this.onIntensityChange(this.volumes[0]),this.refreshLayers(this.volumes[0],0),this.drawScene()))}setDragStart(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr,this.uiData.dragStart[0]=u,this.uiData.dragStart[1]=h}setDragEnd(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr,this.uiData.dragEnd[0]=u,this.uiData.dragEnd[1]=h}touchMoveListener(u){if(this.uiData.touchdown&&u.touches.length<2){const h=this.canvas.getBoundingClientRect();if(this.uiData.isDragging||(this.uiData.pan2DxyzmmAtMouseDown=clone(this.scene.pan2Dxyzmm)),this.uiData.isDragging=!0,this.uiData.doubleTouch&&this.uiData.isDragging){this.setDragEnd(u.targetTouches[0].clientX-u.target.getBoundingClientRect().left,u.targetTouches[0].clientY-u.target.getBoundingClientRect().top),this.drawScene();return}this.mouseClick(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top),this.mouseMove(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top)}else this.handlePinchZoom(u)}handlePinchZoom(u){if(u.targetTouches.length===2&&u.changedTouches.length===2){const h=Math.hypot(u.touches[0].pageX-u.touches[1].pageX,u.touches[0].pageY-u.touches[1].pageY),g=this.canvas.getBoundingClientRect();this.mousePos=[u.touches[0].clientX-g.left,u.touches[0].clientY-g.top],h<this.uiData.lastTwoTouchDistance?this.sliceScroll2D(-.01,u.touches[0].clientX-g.left,u.touches[0].clientY-g.top):this.sliceScroll2D(.01,u.touches[0].clientX-g.left,u.touches[0].clientY-g.top),this.uiData.lastTwoTouchDistance=h}}keyUpListener(u){if(u.code===this.opts.clipPlaneHotKey){const h=new Date().getTime();if(h-this.lastCalled>this.opts.keyDebounceTime){switch(this.currentClipPlaneIndex=(this.currentClipPlaneIndex+1)%7,this.currentClipPlaneIndex){case 0:this.scene.clipPlaneDepthAziElev=[2,0,0];break;case 1:this.scene.clipPlaneDepthAziElev=[0,270,0];break;case 2:this.scene.clipPlaneDepthAziElev=[0,90,0];break;case 3:this.scene.clipPlaneDepthAziElev=[0,0,0];break;case 4:this.scene.clipPlaneDepthAziElev=[0,180,0];break;case 5:this.scene.clipPlaneDepthAziElev=[0,0,-90];break;case 6:this.scene.clipPlaneDepthAziElev=[0,0,90];break}this.setClipPlane(this.scene.clipPlaneDepthAziElev)}this.lastCalled=h}else if(u.code===this.opts.viewModeHotKey){const h=new Date().getTime();h-this.lastCalled>this.opts.keyDebounceTime&&(this.setSliceType((this.opts.sliceType+1)%5),this.lastCalled=h)}}keyDownListener(u){u.code==="KeyH"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth-1,this.scene.renderElevation):u.code==="KeyL"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth+1,this.scene.renderElevation):u.code==="KeyJ"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth,this.scene.renderElevation+1):u.code==="KeyK"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth,this.scene.renderElevation-1):u.code==="KeyH"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(-1,0,0):u.code==="KeyL"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(1,0,0):u.code==="KeyU"&&this.opts.sliceType!==SLICE_TYPE.RENDER&&u.ctrlKey?this.moveCrosshairInVox(0,0,1):u.code==="KeyD"&&this.opts.sliceType!==SLICE_TYPE.RENDER&&u.ctrlKey?this.moveCrosshairInVox(0,0,-1):u.code==="KeyJ"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(0,-1,0):u.code==="KeyK"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(0,1,0):u.code==="KeyM"&&this.opts.sliceType!==SLICE_TYPE.RENDER?(this.opts.dragMode++,this.opts.dragMode>=DRAG_MODE.slicer3D&&(this.opts.dragMode=DRAG_MODE.none),log.info("drag mode changed to ",DRAG_MODE[this.opts.dragMode])):u.code==="ArrowLeft"?this.setFrame4D(this.volumes[0].id,this.volumes[0].frame4D-1):u.code==="ArrowRight"?this.setFrame4D(this.volumes[0].id,this.volumes[0].frame4D+1):u.code==="Slash"&&u.shiftKey&&alert(`NIIVUE VERSION: ${version}`)}wheelListener(u){if(u.preventDefault(),u.stopPropagation(),this.thumbnailVisible)return;const h=this.canvas.getBoundingClientRect();u.deltaY<0?this.sliceScroll2D(-.01,u.clientX-h.left,u.clientY-h.top):this.sliceScroll2D(.01,u.clientX-h.left,u.clientY-h.top)}registerInteractions(){if(!this.canvas)throw new Error("canvas undefined");this.canvas.addEventListener("mousedown",this.mouseDownListener.bind(this)),this.canvas.addEventListener("mouseup",this.mouseUpListener.bind(this)),this.canvas.addEventListener("mousemove",this.mouseMoveListener.bind(this)),this.canvas.addEventListener("touchstart",this.touchStartListener.bind(this)),this.canvas.addEventListener("touchend",this.touchEndListener.bind(this)),this.canvas.addEventListener("touchmove",this.touchMoveListener.bind(this)),this.canvas.addEventListener("wheel",this.wheelListener.bind(this)),this.canvas.addEventListener("contextmenu",this.mouseContextMenuListener.bind(this)),this.canvas.addEventListener("dblclick",this.resetBriCon.bind(this)),this.canvas.addEventListener("dragenter",this.dragEnterListener.bind(this),!1),this.canvas.addEventListener("dragover",this.dragOverListener.bind(this),!1),this.canvas.addEventListener("drop",this.dropListener.bind(this),!1),this.canvas.setAttribute("tabindex","0"),this.canvas.addEventListener("keyup",this.keyUpListener.bind(this),!1),this.canvas.addEventListener("keydown",this.keyDownListener.bind(this),!1)}dragEnterListener(u){u.stopPropagation(),u.preventDefault()}dragOverListener(u){u.stopPropagation(),u.preventDefault()}getFileExt(u,h=!0){log.debug("fullname: ",u);const g=/(?:\.([^.]+))?$/;let p=g.exec(u)[1];if(p=p.toUpperCase(),p==="GZ")p=g.exec(u.slice(0,-3))[1],p=p.toUpperCase();else if(p==="CBOR"){const w=p;p=g.exec(u.slice(0,-5))[1],p=p.toUpperCase(),p=`${p}.${w}`}return h?p:p.toLowerCase()}async addVolumeFromUrl(u){const h=await NVImage.loadFromUrl(u);return this.document.addImageOptions(h,u),h.onColormapChange=this.onColormapChange,this.mediaUrlMap.set(h,u.url),this.onVolumeAddedFromUrl&&this.onVolumeAddedFromUrl(u,h),this.addVolume(h),h}async addVolumesFromUrl(u){const h=u.map(async p=>{const w=this.getFileExt(p.url);if(this.loaders[w]){let L=p.url;const U=this.loaders[w].toExt;let O=p.name||p.url;if(O=O.split("/").pop(),typeof p.url=="string"){const M=p.url;try{const e=await fetch(M);if(!e.ok)throw new Error(`Failed to load file: ${e.statusText}`);L=await e.arrayBuffer()}catch(e){throw new Error(`Failed to load url ${M}: ${e}`)}}const d=await this.loaders[w].loader(L);p.url=d,p.name=`${O}.${U}`}const T={url:p.url,headers:p.headers,name:p.name,colormap:p.colormap?p.colormap:p.colorMap,colormapNegative:p.colormapNegative?p.colormapNegative:p.colorMapNegative,opacity:p.opacity,urlImgData:p.urlImgData,cal_min:p.cal_min,cal_max:p.cal_max,trustCalMinMax:this.opts.trustCalMinMax,isManifest:p.isManifest,frame4D:p.frame4D,limitFrames4D:p.limitFrames4D||this.opts.limitFrames4D,colorbarVisible:p.colorbarVisible},E=await NVImage.loadFromUrl(T);return this.document.addImageOptions(E,T),E.onColormapChange=this.onColormapChange,this.mediaUrlMap.set(E,T.url),this.onVolumeAddedFromUrl&&this.onVolumeAddedFromUrl(T,E),E}),g=await Promise.all(h);for(let p=0;p<g.length;p++)this.addVolume(g[p]);return g}getMediaByUrl(u){return[...this.mediaUrlMap.entries()].filter(h=>h[1]===u).map(h=>h[0]).pop()}removeVolumeByUrl(u){const h=this.getMediaByUrl(u);if(h)this.removeVolume(h);else throw new Error("No volume with URL present")}readDirectory(u){const h=u.createReader();let g=[];const p=async T=>{const E=[],L=async U=>new Promise((O,d)=>U.file(O,d));for(let U=0;U<T.length;U++)E.push(await L(T[U]));return E},w=()=>{h.readEntries(T=>{T.length?(g=g.concat(T),w()):p(g).then(E=>{NVImage.loadFromFile({file:E,name:u.name,urlImgData:null,imageType:NVIMAGE_TYPE.DCM_FOLDER}).then(L=>{this.addVolume(L),this.setDrawingEnabled(!0)}).catch(L=>{throw L})}).catch(E=>{throw E})})};return w(),g}isMeshExt(u){const h=this.getFileExt(u);return log.debug("dropped ext"),log.debug(h),MESH_EXTENSIONS.includes(h)}async loadFromArrayBuffer(u,h){const g=this.getFileExt(h);if(MESH_EXTENSIONS.includes(g)){await this.addMeshFromUrl({url:h,buffer:u});return}const p=NVImageFromUrlOptions(h);p.buffer=u,p.name=h,await this.addVolumeFromUrl(p)}async loadFromFile(u){const h=this.getFileExt(u.name);if(MESH_EXTENSIONS.includes(h)){await NVMesh.loadFromFile({file:u,gl:this.gl,name:u.name}).then(g=>{this.addMesh(g)});return}await NVImage.loadFromFile({file:u,name:u.name}).then(g=>{this.addVolume(g)})}useLoader(u,h,g){this.loaders={...this.loaders,[h.toUpperCase()]:{loader:u,toExt:g}}}dropListener(u){if(u.stopPropagation(),u.preventDefault(),!this.opts.dragAndDropEnabled)return;const h=u.dataTransfer;if(!h)return;const g=h.getData("text/uri-list");if(g){const p=NVImageFromUrlOptions(g),w=this.getFileExt(g);log.debug("dropped ext"),log.debug(w),MESH_EXTENSIONS.includes(w)?this.addMeshFromUrl({url:g}).catch(T=>{throw T}):w==="NVD"?this.loadDocumentFromUrl(g).catch(T=>{throw T}):this.addVolumeFromUrl(p).catch(T=>{throw T})}else{const p=h.items;if(p.length>0){!u.shiftKey&&!u.altKey&&(this.volumes=[],this.overlays=[],this.meshes=[]),this.closeDrawing();for(const w of Array.from(p)){const T=w.webkitGetAsEntry();if(log.debug(T),!T)throw new Error("could not get entry from file");if(T.isFile){const E=this.getFileExt(T.name);if(E==="PNG"){T.file(U=>{this.loadBmpTexture(U).catch(O=>{throw O})});continue}let L;if(T.name.lastIndexOf("HEAD")!==-1)for(const U of Array.from(p)){const O=U.webkitGetAsEntry();if(!O)throw new Error("could not get paired entry");const d=T.name.substring(0,T.name.lastIndexOf("HEAD")),M=O.name.substring(0,O.name.lastIndexOf("BRIK"));d===M&&(L=O)}if(T.name.lastIndexOf("BRIK")!==-1)continue;if(MESH_EXTENSIONS.includes(E)){T.file(U=>{NVMesh.loadFromFile({file:U,gl:this.gl,name:U.name}).then(O=>{this.addMesh(O)}).catch(O=>{throw O})});continue}else if(E==="NVD"){T.file(U=>{NVDocument.loadFromFile(U).then(O=>{this.loadDocument(O),log.debug("loaded document")}).catch(O=>{throw O})});break}T.file(U=>{L?L.file(O=>{NVImage.loadFromFile({file:U,urlImgData:O,limitFrames4D:this.opts.limitFrames4D}).then(d=>{this.addVolume(d)}).catch(d=>{throw d})}):NVImage.loadFromFile({file:U,urlImgData:L,limitFrames4D:this.opts.limitFrames4D}).then(O=>{u.altKey?(log.debug("alt key detected: assuming this is a drawing overlay"),this.drawClearAllUndoBitmaps(),this.loadDrawing(O)):this.addVolume(O)}).catch(O=>{throw O})})}else T.isDirectory&&this.readDirectory(T)}}}this.drawScene()}setMultiplanarPadPixels(u){this.opts.multiplanarPadPixels=u,this.drawScene()}setMultiplanarLayout(u){typeof u=="string"&&(u=parseInt(u)),this.opts.multiplanarLayout=u,this.drawScene()}setCornerOrientationText(u){this.opts.isCornerOrientationText=u,this.updateGLVolume()}setRadiologicalConvention(u){this.opts.isRadiologicalConvention=u,this.updateGLVolume()}setDefaults(u={},h=!1){this.document.opts={...DEFAULT_OPTIONS},this.scene.sceneData={...INITIAL_SCENE_DATA};for(const g in u)typeof u[g]=="function"?this[g]=u[g]:this.opts[g]=DEFAULT_OPTIONS[g]===void 0?DEFAULT_OPTIONS[g]:u[g];if(this.scene.pan2Dxyzmm=[0,0,0,1],h&&this.volumes&&this.volumes.length>0)for(let g=0;g<this.volumes.length;g++)this.volumes[g].cal_min=this.volumes[g].robust_min,this.volumes[g].cal_max=this.volumes[g].robust_max;this.updateGLVolume()}setMeshThicknessOn2D(u){this.opts.meshThicknessOn2D=u,this.updateGLVolume()}setSliceMosaicString(u){this.sliceMosaicString=u,this.updateGLVolume()}setSliceMM(u){this.opts.isSliceMM=u,this.updateGLVolume()}setAdditiveBlend(u){this.opts.isAdditiveBlend=u,this.updateGLVolume()}getRadiologicalConvention(){return this.opts.isRadiologicalConvention}setHighResolutionCapable(u){this.opts.isHighResolutionCapable=u,u&&!this.opts.isResizeCanvas&&(log.warn("isHighResolutionCapable requires isResizeCanvas"),this.opts.isHighResolutionCapable=!1),this.opts.isHighResolutionCapable||(this.uiData.dpr=1),this.resizeListener(),this.drawScene()}addVolume(u){this.volumes.push(u);const h=this.volumes.length===1?0:this.volumes.length-1;this.setVolume(u,h),this.onImageLoaded(u),log.debug("loaded volume",u.name),log.debug(u)}addMesh(u){this.meshes.push(u);const h=this.meshes.length===1?0:this.meshes.length-1;this.setMesh(u,h),this.onMeshLoaded(u)}getVolumeIndexByID(u){const h=this.volumes.length;for(let g=0;g<h;g++)if(this.volumes[g].id===u)return g;return-1}drawAddUndoBitmap(){if(!this.drawBitmap||this.drawBitmap.length<1){log.debug("drawAddUndoBitmap error: No drawing open");return}this.currentDrawUndoBitmap++,this.currentDrawUndoBitmap>=this.opts.maxDrawUndoBitmaps&&(this.currentDrawUndoBitmap=0),this.drawUndoBitmaps[this.currentDrawUndoBitmap]=encodeRLE(this.drawBitmap)}drawClearAllUndoBitmaps(){if(this.currentDrawUndoBitmap=this.opts.maxDrawUndoBitmaps,!(!this.drawUndoBitmaps||this.drawUndoBitmaps.length<1))for(let u=this.drawUndoBitmaps.length-1;u>=0;u--)this.drawUndoBitmaps[u]=new Uint8Array}drawUndo(){if(this.drawUndoBitmaps.length<1){log.debug("undo bitmaps not loaded");return}if(this.currentDrawUndoBitmap--,this.currentDrawUndoBitmap<0&&(this.currentDrawUndoBitmap=this.drawUndoBitmaps.length-1),this.currentDrawUndoBitmap>=this.drawUndoBitmaps.length&&(this.currentDrawUndoBitmap=0),this.drawUndoBitmaps[this.currentDrawUndoBitmap].length<2){log.debug("drawUndo is misbehaving");return}this.drawBitmap=decodeRLE(this.drawUndoBitmaps[this.currentDrawUndoBitmap],this.drawBitmap.length),this.refreshDrawing(!0)}loadDrawing(u){if(this.drawBitmap&&log.debug("Overwriting open drawing!"),!this.back)throw new Error("back undefined");this.drawClearAllUndoBitmaps();const h=u.hdr.dims;if(h[1]!==this.back.hdr.dims[1]||h[2]!==this.back.hdr.dims[2]||h[3]!==this.back.hdr.dims[3])return log.debug("drawing dimensions do not match background image"),!1;u.img.constructor!==Uint8Array&&log.debug("Drawings should be UINT8");const g=u.permRAS,p=h[1]*h[2]*h[3];this.drawBitmap=new Uint8Array(p),this.drawTexture=this.r8Tex(this.drawTexture,TEXTURE7_DRAW,this.back.dims,!0);const w=[0,0,0];for(let z=0;z<3;z++)for(let W=0;W<3;W++)Math.abs(g[z])-1===W&&(w[W]=z*Math.sign(g[z]));let T=1;const E=[1,1,1],L=[!1,!1,!1];for(let z=0;z<w.length;z++)for(let W=0;W<w.length;W++)Math.abs(w[W])===z&&(E[W]=T,(w[W]<0||Object.is(w[W],-0))&&(L[W]=!0),T*=h[W+1]);let U=NVUtilities.range(0,h[1]-1,1);L[0]&&(U=NVUtilities.range(h[1]-1,0,-1));for(let z=0;z<h[1];z++)U[z]*=E[0];let O=NVUtilities.range(0,h[2]-1,1);L[1]&&(O=NVUtilities.range(h[2]-1,0,-1));for(let z=0;z<h[2];z++)O[z]*=E[1];let d=NVUtilities.range(0,h[3]-1,1);L[2]&&(d=NVUtilities.range(h[3]-1,0,-1));for(let z=0;z<h[3];z++)d[z]*=E[2];const M=u.img,e=this.drawBitmap;let b=0;for(let z=0;z<h[3];z++)for(let W=0;W<h[2];W++)for(let o0=0;o0<h[1];o0++)e[U[o0]+O[W]+d[z]]=M[b],b++;return this.drawAddUndoBitmap(),this.refreshDrawing(!1),this.drawScene(),!0}binarize(u){const h=u.hdr.dims,g=h[1]*h[2]*h[3],p=new Uint8Array(g);for(let w=0;w<g;w++)u.img[w]!==0&&(p[w]=1);u.img=p,u.hdr.datatypeCode=NiiDataType.DT_UINT8,u.hdr.cal_min=0,u.hdr.cal_max=1}async loadDrawingFromUrl(u,h=!1){this.drawBitmap&&log.debug("Overwriting open drawing!"),this.drawClearAllUndoBitmaps();let g=!1;try{const p=await NVImage.loadFromUrl(NVImageFromUrlOptions(u));h&&await this.binarize(p),g=this.loadDrawing(p)}catch{log.error("loadDrawingFromUrl() failed to load "+u),this.drawClearAllUndoBitmaps()}return g}findOtsu(u=2){if(this.volumes.length<1)return[];const h=this.volumes[0].img,g=h.length;if(g<1)return[];const p=256,w=p-1,T=new Array(p).fill(0),E=this.volumes[0].cal_min,L=this.volumes[0].cal_max;if(L<=E)return[];const U=(L-E)/p;function O(J){return J*U+E}const d=(p-1)/Math.abs(L-E),M=this.volumes[0].hdr.scl_inter,e=this.volumes[0].hdr.scl_slope;for(let J=0;J<g;J++){let K=h[J]*e+M;K=Math.min(Math.max(K,E),L),K=Math.round((K-E)*d),T[K]++}const b=Array(p).fill(0).map(()=>Array(p).fill(0)),z=Array(p).fill(0).map(()=>Array(p).fill(0));for(let J=1;J<p;++J)b[J][J]=T[J],z[J][J]=J*T[J];for(let J=1;J<p-1;++J)b[1][J+1]=b[1][J]+T[J+1],z[1][J+1]=z[1][J]+(J+1)*T[J+1];for(let J=2;J<p;J++)for(let K=J+1;K<p;K++)b[J][K]=b[1][K]-b[1][J-1],z[J][K]=z[1][K]-z[1][J-1];for(let J=1;J<p;++J)for(let K=J+1;K<p;K++)b[J][K]!==0&&(b[J][K]=z[J][K]*z[J][K]/b[J][K]);let W=0;const o0=[1/0,1/0,1/0];if(u>3)for(let J=0;J<p-3;J++)for(let K=J+1;K<p-2;K++)for(let y=K+1;y<p-1;y++){const Z=b[0][J]+b[J+1][K]+b[K+1][y]+b[y+1][w];Z>W&&(o0[0]=J,o0[1]=K,o0[2]=y,W=Z)}else if(u===3)for(let J=0;J<p-2;J++)for(let K=J+1;K<p-1;K++){const y=b[0][J]+b[J+1][K]+b[K+1][w];y>W&&(o0[0]=J,o0[1]=K,W=y)}else for(let J=0;J<p-1;J++){const K=b[0][J]+b[J+1][w];K>W&&(o0[0]=J,W=K)}return[O(o0[0]),O(o0[1]),O(o0[2])]}drawOtsu(u=2){if(this.volumes.length===0)return;const h=this.volumes[0].img.length,g=this.findOtsu(u);if(g.length<3)return;this.drawBitmap||this.createEmptyDrawing();const p=this.drawBitmap,w=this.volumes[0].img;for(let T=0;T<h;T++){if(p[T]!==0)continue;const E=w[T];E>g[0]&&(p[T]=1),E>g[1]&&(p[T]=2),E>g[2]&&(p[T]=3)}this.drawAddUndoBitmap(),this.refreshDrawing(!0)}removeHaze(u=5,h=0){const g=this.volumes[h].img,p=this.volumes[h].hdr,w=g.length;let T=2;(u===5||u===1)&&(T=4),(u===4||u===2)&&(T=3);const E=this.findOtsu(T);if(E.length<3)return;let L=E[0];u===1&&(L=E[2]),u===2&&(L=E[1]);const U=p.scl_inter,O=p.scl_slope,d=this.volumes[h].global_min;for(let M=0;M<w;M++)g[M]*O+U<L&&(g[M]=d);this.refreshLayers(this.volumes[h],0),this.drawScene()}saveImage(u=defaultSaveImageOptions){var E;const h={...defaultSaveImageOptions,...u},{filename:g,isSaveDrawing:p,volumeByIndex:w}=h;if(log.debug("saveImage",g,p,w),((E=this.back)==null?void 0:E.dims)===void 0)return log.debug("No voxelwise image open"),!1;if(p){if(!this.drawBitmap)return log.debug("No drawing open"),!1;const L=this.volumes[0].permRAS;if(L[0]===1&&L[1]===2&&L[2]===3)return log.debug("saving drawing"),this.volumes[0].saveToDisk(g,this.drawBitmap);{log.debug("saving drawing");const U=this.volumes[0].hdr.dims,O=[0,0,0];for(let Z=0;Z<3;Z++)for(let X=0;X<3;X++)Math.abs(L[Z])-1===X&&(O[X]=Z*Math.sign(L[Z]));let d=1;const M=[1,1,1],e=[!1,!1,!1];for(let Z=0;Z<O.length;Z++)for(let X=0;X<O.length;X++)Math.abs(O[X])===Z&&(M[X]=d,(O[X]<0||Object.is(O[X],-0))&&(e[X]=!0),d*=U[X+1]);let b=NVUtilities.range(0,U[1]-1,1);e[0]&&(b=NVUtilities.range(U[1]-1,0,-1));for(let Z=0;Z<U[1];Z++)b[Z]*=M[0];let z=NVUtilities.range(0,U[2]-1,1);e[1]&&(z=NVUtilities.range(U[2]-1,0,-1));for(let Z=0;Z<U[2];Z++)z[Z]*=M[1];let W=NVUtilities.range(0,U[3]-1,1);e[2]&&(W=NVUtilities.range(U[3]-1,0,-1));for(let Z=0;Z<U[3];Z++)W[Z]*=M[2];const o0=new Uint8Array(this.drawBitmap),J=new Uint8Array(U[1]*U[2]*U[3]);let K=0;for(let Z=0;Z<U[3];Z++)for(let X=0;X<U[2];X++)for(let e0=0;e0<U[1];e0++)J[K]=o0[b[e0]+z[X]+W[Z]],K++;return log.debug("saving drawing"),this.volumes[0].saveToDisk(g,J)}}return log.debug("saving image"),this.volumes[w].saveToDisk(g)}getMeshIndexByID(u){if(typeof u=="number")return u>=this.meshes.length?-1:u;const h=this.meshes.length;for(let g=0;g<h;g++)if(this.meshes[g].id===u)return g;return-1}setMeshProperty(u,h,g){const p=this.getMeshIndexByID(u);if(p<0){log.warn("setMeshProperty() id not loaded",u);return}this.meshes[p].setProperty(h,g,this.gl),this.updateGLVolume(),this.onMeshPropertyChanged(p,h,g)}indexNearestXYZmm(u,h,g,p){const w=this.getMeshIndexByID(u);return w<0?(log.warn("indexNearestXYZmm() id not loaded",u),[NaN,NaN]):this.meshes[w].indexNearestXYZmm(h,g,p)}decimateHierarchicalMesh(u,h=3){const g=this.getMeshIndexByID(u);if(g<0){log.warn("reverseFaces() id not loaded",u);return}const p=this.meshes[g].decimateHierarchicalMesh(this.gl,h);return this.updateGLVolume(),p}reverseFaces(u){const h=this.getMeshIndexByID(u);if(h<0){log.warn("reverseFaces() id not loaded",u);return}this.meshes[h].reverseFaces(this.gl),this.updateGLVolume()}setMeshLayerProperty(u,h,g,p){const w=this.getMeshIndexByID(u);if(w<0){log.warn("setMeshLayerProperty() id not loaded",u);return}this.meshes[w].setLayerProperty(h,g,p,this.gl),this.updateGLVolume()}setPan2Dxyzmm(u){this.scene.pan2Dxyzmm=u,this.opts.yoke3Dto2DZoom&&(this.scene.volScaleMultiplier=u[3]),this.drawScene()}setRenderAzimuthElevation(u,h){this.scene.renderAzimuth=u,this.scene.renderElevation=h,this.onAzimuthElevationChange(u,h),this.drawScene()}getOverlayIndexByID(u){const h=this.overlays.length;for(let g=0;g<h;g++)if(this.overlays[g].id===u)return g;return-1}setVolume(u,h=0){const g=this.volumes.length;if(h>g)return;const p=this.getVolumeIndexByID(u.id);h===0?(this.volumes.splice(p,1),this.volumes.unshift(u),this.back=this.volumes[0],this.overlays=this.volumes.slice(1)):h<0?(this.volumes.splice(this.getVolumeIndexByID(u.id),1),this.back=this.volumes[0],this.volumes.length>1?this.overlays=this.volumes.slice(1):this.overlays=[]):(this.volumes.splice(p,1),this.volumes.splice(h,0,u),this.overlays=this.volumes.slice(1),this.back=this.volumes[0]),this.updateGLVolume()}setMesh(u,h=0){this.meshes.forEach(w=>{log.debug("MESH: ",w.name)});const g=this.meshes.length;if(h>g)return;const p=this.getMeshIndexByID(u.id);h===0?(this.meshes.splice(p,1),this.meshes.unshift(u)):h<0?this.meshes.splice(this.getMeshIndexByID(u.id),1):(this.meshes.splice(p,1),this.meshes.splice(h,0,u)),this.updateGLVolume(),this.meshes.forEach(w=>{log.debug(w.name)})}removeVolume(u){if(this.setVolume(u,-1),this.mediaUrlMap.has(u)){const h=this.mediaUrlMap.get(u);this.onVolumeWithUrlRemoved(h),this.mediaUrlMap.delete(u)}this.drawScene()}removeVolumeByIndex(u){if(u>=this.volumes.length)throw new Error("Index of volume out of bounds");this.removeVolume(this.volumes[u])}removeMesh(u){if(u.unloadMesh(this.gl),this.setMesh(u,-1),this.mediaUrlMap.has(u)){const h=this.mediaUrlMap.get(u);this.onMeshWithUrlRemoved(h),this.mediaUrlMap.delete(u)}}removeMeshByUrl(u){const h=this.getMediaByUrl(u);h&&(this.removeMesh(h),this.mediaUrlMap.delete(h),this.onMeshWithUrlRemoved(u))}moveVolumeToBottom(u){this.setVolume(u,0)}moveVolumeUp(u){const h=this.getVolumeIndexByID(u.id);this.setVolume(u,h+1)}moveVolumeDown(u){const h=this.getVolumeIndexByID(u.id);this.setVolume(u,h-1)}moveVolumeToTop(u){this.setVolume(u,this.volumes.length-1)}mouseDown(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr,this.mousePos=[u,h]}mouseMove(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr;const g=(u-this.mousePos[0])/this.uiData.dpr,p=(h-this.mousePos[1])/this.uiData.dpr;this.mousePos=[u,h],!(this.inRenderTile(u,h)<0)&&(Math.abs(g)<1&&Math.abs(p)<1||(this.scene.renderAzimuth+=g,this.scene.renderElevation+=p,this.drawScene()))}sph2cartDeg(u,h){const g=-h*(Math.PI/180),p=(u-90)%360*(Math.PI/180),w=[Math.cos(g)*Math.cos(p),Math.cos(g)*Math.sin(p),Math.sin(g)],T=Math.sqrt(w[0]*w[0]+w[1]*w[1]+w[2]*w[2]);return T<=0||(w[0]/=T,w[1]/=T,w[2]/=T),w}setClipPlane(u){const h=this.sph2cartDeg(u[1]+180,u[2]);this.scene.clipPlane=[h[0],h[1],h[2],u[0]],this.scene.clipPlaneDepthAziElev=u,this.onClipPlaneChange(this.scene.clipPlane),this.drawScene()}setCrosshairColor(u){this.opts.crosshairColor=u,this.drawScene()}setCrosshairWidth(u){this.opts.crosshairWidth=u,this.crosshairs3D&&(this.crosshairs3D.mm[0]=NaN),this.drawScene()}setDrawColormap(u){this.drawLut=cmapper.makeDrawLut(u),this.updateGLVolume()}setDrawingEnabled(u){this.opts.drawingEnabled=u,this.opts.drawingEnabled&&(this.drawBitmap||this.createEmptyDrawing()),this.drawScene()}setPenValue(u,h=!1){this.opts.penValue=u,this.opts.isFilledPen=h,this.drawScene()}setDrawOpacity(u){this.drawOpacity=u,this.drawScene()}setSelectionBoxColor(u){this.opts.selectionBoxColor=u}sliceScroll2D(u,h,g,p=!0){if(this.inGraphTile(h,g)){let w=this.volumes[0].frame4D;u>0&&w++,u<0&&w--,this.setFrame4D(this.volumes[0].id,w);return}if(u!==0&&this.opts.dragMode===DRAG_MODE.pan&&this.inRenderTile(this.uiData.dpr*h,this.uiData.dpr*g)===-1){let w=this.scene.pan2Dxyzmm[3]*(1+10*u);w=Math.round(w*10)/10;const T=this.scene.pan2Dxyzmm[3]-w;this.opts.yoke3Dto2DZoom&&(this.scene.volScaleMultiplier=w),this.scene.pan2Dxyzmm[3]=w;const E=this.frac2mm(this.scene.crosshairPos);this.scene.pan2Dxyzmm[0]+=T*E[0],this.scene.pan2Dxyzmm[1]+=T*E[1],this.scene.pan2Dxyzmm[2]+=T*E[2],this.drawScene(),this.canvas.focus(),this.sync();return}this.mouseClick(h,g,u,p)}setSliceType(u){return this.opts.sliceType=u,this.drawScene(),this}setOpacity(u,h){if(this.volumes[u].opacity=h,u===0){this.drawScene();return}this.updateGLVolume()}setScale(u){this.scene.volScaleMultiplier=u,this.drawScene()}setClipPlaneColor(u){this.opts.clipPlaneColor=u,this.renderShader.use(this.gl),this.gl.uniform4fv(this.renderShader.uniforms.clipPlaneColor,this.opts.clipPlaneColor),this.drawScene()}setClipPlaneThick(u){this.opts.clipThick=u,this.renderShader.use(this.gl),this.gl.uniform1f(this.renderShader.uniforms.clipThick,this.opts.clipThick),this.drawScene()}setClipVolume(u,h){this.opts.clipVolumeLow=[Math.min(u[0],h[0]),Math.min(u[1],h[1]),Math.min(u[2],h[2])],this.opts.clipVolumeHigh=[Math.max(u[0],h[0]),Math.max(u[1],h[1]),Math.max(u[2],h[2])],this.renderShader.use(this.gl),this.gl.uniform3fv(this.renderShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.renderShader.uniforms.clipHi,this.opts.clipVolumeHigh),this.pickingImageShader.use(this.gl),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi,this.opts.clipVolumeHigh),this.drawScene()}async setVolumeRenderIllumination(u=0){this.renderShader=this.renderVolumeShader,u>0&&(this.renderShader=this.renderGradientShader),u<0&&(this.renderShader=this.renderSliceShader),this.initRenderShader(this.renderShader,u),this.renderShader.use(this.gl),this.setClipPlaneColor(this.opts.clipPlaneColor),this.gradientTextureAmount=u,this.refreshLayers(this.volumes[0],0),this.drawScene()}overlayRGBA(u){const h=u.hdr,g=h.dims[1]*h.dims[2]*h.dims[3],p=new Uint8ClampedArray(g*4),w=.2*Math.min(Math.min(h.dims[1],h.dims[2]),h.dims[3]),T=.5*h.dims[1],E=.5*h.dims[2],L=.5*h.dims[3];let U=0;for(let O=0;O<h.dims[3];O++)for(let d=0;d<h.dims[2];d++)for(let M=0;M<h.dims[1];M++){const e=Math.abs(M-T),b=Math.abs(d-E),z=Math.abs(O-L),W=Math.sqrt(e*e+b*b+z*z);let o0=0;W<w&&(o0=255),p[U++]=0,p[U++]=o0,p[U++]=0,p[U++]=o0*.5}return p}vox2mm(u,h){return NVUtilities.vox2mm(u,h)}cloneVolume(u){return this.volumes[u].clone()}async loadDocumentFromUrl(u){const h=await NVDocument.loadFromUrl(u);this.loadDocument(h)}loadDocument(u){this.volumes=[],this.meshes=[],this.document=u,this.document.labels=this.document.labels?this.document.labels:[];const h={...DEFAULT_OPTIONS,...u.opts};this.scene.pan2Dxyzmm=u.scene.pan2Dxyzmm?u.scene.pan2Dxyzmm:[0,0,0,1],this.document.opts=h,this.setClipPlane(this.scene.clipPlaneDepthAziElev),log.debug("load document",u),this.mediaUrlMap.clear(),this.createEmptyDrawing();const g=u.encodedImageBlobs;for(let w=0;w<u.imageOptionsArray.length;w++){const T=u.imageOptionsArray[w],E=g[w];if(E){"colorMap"in T&&(T.colormap=T.colorMap);const L=NVImage.loadFromBase64({base64:E,...T});if(L){if(L.colormapLabel){const U=Object.keys(L.colormapLabel.lut).length,O=new Uint8ClampedArray(U);for(const d in L.colormapLabel.lut)O[d]=L.colormapLabel.lut[d];L.colormapLabel.lut=O}this.addVolume(L)}}}this.volumes.length>0&&(this.back=this.volumes[0]);const p=u.encodedDrawingBlob;if(p){const w=u.imageOptionsArray[0],T=NVImage.loadFromBase64({base64:p,...w});T&&this.loadDrawing(T)}for(const w of u.meshDataObjects??[]){const T={gl:this.gl,...w};w.offsetPt0&&(T.rgba255[3]=0,T.tris=new Uint32Array(w.offsetPt0)),log.debug(T);const E=new NVMesh(T.pts,T.tris,T.name,T.rgba255,T.opacity,T.visible,this.gl,T.connectome,T.dpg,T.dps,T.dpv);w.offsetPt0&&(E.fiberGroupColormap=w.fiberGroupColormap,E.fiberColor=w.fiberColor,E.fiberDither=w.fiberDither,E.fiberRadius=w.fiberRadius,E.colormap=w.colormap),E.meshShaderIndex=T.meshShaderIndex,E.layers=T.layers,E.updateMesh(this.gl),log.debug(E),this.addMesh(E)}if(u.data.connectomes)for(const w of u.data.connectomes){const T=JSON.parse(w),E=this.loadConnectomeAsMesh(T);E.updateMesh(this.gl),this.addMesh(E)}return this.updateGLVolume(),this.drawScene(),this.onDocumentLoaded(u),this}async generateLoadDocumentJavaScript(u,h){const g=this.json(),p=await NVUtilities.compressToBase64String(JSON.stringify(g));return`
1739
+ `):"",this.name="UnsubscriptionError",this.errors=h}});function arrRemove(C,u){if(C){var h=C.indexOf(u);0<=h&&C.splice(h,1)}}var Subscription=function(){function C(u){this.initialTeardown=u,this.closed=!1,this._parentage=null,this._finalizers=null}return C.prototype.unsubscribe=function(){var u,h,g,p,w;if(!this.closed){this.closed=!0;var T=this._parentage;if(T)if(this._parentage=null,Array.isArray(T))try{for(var E=__values(T),L=E.next();!L.done;L=E.next()){var U=L.value;U.remove(this)}}catch(z){u={error:z}}finally{try{L&&!L.done&&(h=E.return)&&h.call(E)}finally{if(u)throw u.error}}else T.remove(this);var O=this.initialTeardown;if(isFunction(O))try{O()}catch(z){w=z instanceof UnsubscriptionError?z.errors:[z]}var d=this._finalizers;if(d){this._finalizers=null;try{for(var M=__values(d),e=M.next();!e.done;e=M.next()){var b=e.value;try{execFinalizer(b)}catch(z){w=w??[],z instanceof UnsubscriptionError?w=__spreadArray(__spreadArray([],__read(w)),__read(z.errors)):w.push(z)}}}catch(z){g={error:z}}finally{try{e&&!e.done&&(p=M.return)&&p.call(M)}finally{if(g)throw g.error}}}if(w)throw new UnsubscriptionError(w)}},C.prototype.add=function(u){var h;if(u&&u!==this)if(this.closed)execFinalizer(u);else{if(u instanceof C){if(u.closed||u._hasParent(this))return;u._addParent(this)}(this._finalizers=(h=this._finalizers)!==null&&h!==void 0?h:[]).push(u)}},C.prototype._hasParent=function(u){var h=this._parentage;return h===u||Array.isArray(h)&&h.includes(u)},C.prototype._addParent=function(u){var h=this._parentage;this._parentage=Array.isArray(h)?(h.push(u),h):h?[h,u]:u},C.prototype._removeParent=function(u){var h=this._parentage;h===u?this._parentage=null:Array.isArray(h)&&arrRemove(h,u)},C.prototype.remove=function(u){var h=this._finalizers;h&&arrRemove(h,u),u instanceof C&&u._removeParent(this)},C.EMPTY=function(){var u=new C;return u.closed=!0,u}(),C}(),EMPTY_SUBSCRIPTION=Subscription.EMPTY;function isSubscription(C){return C instanceof Subscription||C&&"closed"in C&&isFunction(C.remove)&&isFunction(C.add)&&isFunction(C.unsubscribe)}function execFinalizer(C){isFunction(C)?C():C.unsubscribe()}var config={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},timeoutProvider={setTimeout:function(C,u){for(var h=[],g=2;g<arguments.length;g++)h[g-2]=arguments[g];return setTimeout.apply(void 0,__spreadArray([C,u],__read(h)))},clearTimeout:function(C){var u=timeoutProvider.delegate;return((u==null?void 0:u.clearTimeout)||clearTimeout)(C)},delegate:void 0};function reportUnhandledError(C){timeoutProvider.setTimeout(function(){throw C})}function noop(){}function errorContext(C){C()}var Subscriber=function(C){__extends(u,C);function u(h){var g=C.call(this)||this;return g.isStopped=!1,h?(g.destination=h,isSubscription(h)&&h.add(g)):g.destination=EMPTY_OBSERVER,g}return u.create=function(h,g,p){return new SafeSubscriber(h,g,p)},u.prototype.next=function(h){this.isStopped||this._next(h)},u.prototype.error=function(h){this.isStopped||(this.isStopped=!0,this._error(h))},u.prototype.complete=function(){this.isStopped||(this.isStopped=!0,this._complete())},u.prototype.unsubscribe=function(){this.closed||(this.isStopped=!0,C.prototype.unsubscribe.call(this),this.destination=null)},u.prototype._next=function(h){this.destination.next(h)},u.prototype._error=function(h){try{this.destination.error(h)}finally{this.unsubscribe()}},u.prototype._complete=function(){try{this.destination.complete()}finally{this.unsubscribe()}},u}(Subscription),_bind=Function.prototype.bind;function bind(C,u){return _bind.call(C,u)}var ConsumerObserver=function(){function C(u){this.partialObserver=u}return C.prototype.next=function(u){var h=this.partialObserver;if(h.next)try{h.next(u)}catch(g){handleUnhandledError(g)}},C.prototype.error=function(u){var h=this.partialObserver;if(h.error)try{h.error(u)}catch(g){handleUnhandledError(g)}else handleUnhandledError(u)},C.prototype.complete=function(){var u=this.partialObserver;if(u.complete)try{u.complete()}catch(h){handleUnhandledError(h)}},C}(),SafeSubscriber=function(C){__extends(u,C);function u(h,g,p){var w=C.call(this)||this,T;if(isFunction(h)||!h)T={next:h??void 0,error:g??void 0,complete:p??void 0};else{var E;w&&config.useDeprecatedNextContext?(E=Object.create(h),E.unsubscribe=function(){return w.unsubscribe()},T={next:h.next&&bind(h.next,E),error:h.error&&bind(h.error,E),complete:h.complete&&bind(h.complete,E)}):T=h}return w.destination=new ConsumerObserver(T),w}return u}(Subscriber);function handleUnhandledError(C){reportUnhandledError(C)}function defaultErrorHandler(C){throw C}var EMPTY_OBSERVER={closed:!0,next:noop,error:defaultErrorHandler,complete:noop},observable=function(){return typeof Symbol=="function"&&Symbol.observable||"@@observable"}();function identity(C){return C}function pipeFromArray(C){return C.length===0?identity:C.length===1?C[0]:function(h){return C.reduce(function(g,p){return p(g)},h)}}var Observable=function(){function C(u){u&&(this._subscribe=u)}return C.prototype.lift=function(u){var h=new C;return h.source=this,h.operator=u,h},C.prototype.subscribe=function(u,h,g){var p=this,w=isSubscriber(u)?u:new SafeSubscriber(u,h,g);return errorContext(function(){var T=p,E=T.operator,L=T.source;w.add(E?E.call(w,L):L?p._subscribe(w):p._trySubscribe(w))}),w},C.prototype._trySubscribe=function(u){try{return this._subscribe(u)}catch(h){u.error(h)}},C.prototype.forEach=function(u,h){var g=this;return h=getPromiseCtor(h),new h(function(p,w){var T=new SafeSubscriber({next:function(E){try{u(E)}catch(L){w(L),T.unsubscribe()}},error:w,complete:p});g.subscribe(T)})},C.prototype._subscribe=function(u){var h;return(h=this.source)===null||h===void 0?void 0:h.subscribe(u)},C.prototype[observable]=function(){return this},C.prototype.pipe=function(){for(var u=[],h=0;h<arguments.length;h++)u[h]=arguments[h];return pipeFromArray(u)(this)},C.prototype.toPromise=function(u){var h=this;return u=getPromiseCtor(u),new u(function(g,p){var w;h.subscribe(function(T){return w=T},function(T){return p(T)},function(){return g(w)})})},C.create=function(u){return new C(u)},C}();function getPromiseCtor(C){var u;return(u=C??config.Promise)!==null&&u!==void 0?u:Promise}function isObserver(C){return C&&isFunction(C.next)&&isFunction(C.error)&&isFunction(C.complete)}function isSubscriber(C){return C&&C instanceof Subscriber||isObserver(C)&&isSubscription(C)}var ObjectUnsubscribedError=createErrorClass(function(C){return function(){C(this),this.name="ObjectUnsubscribedError",this.message="object unsubscribed"}}),Subject=function(C){__extends(u,C);function u(){var h=C.call(this)||this;return h.closed=!1,h.currentObservers=null,h.observers=[],h.isStopped=!1,h.hasError=!1,h.thrownError=null,h}return u.prototype.lift=function(h){var g=new AnonymousSubject(this,this);return g.operator=h,g},u.prototype._throwIfClosed=function(){if(this.closed)throw new ObjectUnsubscribedError},u.prototype.next=function(h){var g=this;errorContext(function(){var p,w;if(g._throwIfClosed(),!g.isStopped){g.currentObservers||(g.currentObservers=Array.from(g.observers));try{for(var T=__values(g.currentObservers),E=T.next();!E.done;E=T.next()){var L=E.value;L.next(h)}}catch(U){p={error:U}}finally{try{E&&!E.done&&(w=T.return)&&w.call(T)}finally{if(p)throw p.error}}}})},u.prototype.error=function(h){var g=this;errorContext(function(){if(g._throwIfClosed(),!g.isStopped){g.hasError=g.isStopped=!0,g.thrownError=h;for(var p=g.observers;p.length;)p.shift().error(h)}})},u.prototype.complete=function(){var h=this;errorContext(function(){if(h._throwIfClosed(),!h.isStopped){h.isStopped=!0;for(var g=h.observers;g.length;)g.shift().complete()}})},u.prototype.unsubscribe=function(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null},Object.defineProperty(u.prototype,"observed",{get:function(){var h;return((h=this.observers)===null||h===void 0?void 0:h.length)>0},enumerable:!1,configurable:!0}),u.prototype._trySubscribe=function(h){return this._throwIfClosed(),C.prototype._trySubscribe.call(this,h)},u.prototype._subscribe=function(h){return this._throwIfClosed(),this._checkFinalizedStatuses(h),this._innerSubscribe(h)},u.prototype._innerSubscribe=function(h){var g=this,p=this,w=p.hasError,T=p.isStopped,E=p.observers;return w||T?EMPTY_SUBSCRIPTION:(this.currentObservers=null,E.push(h),new Subscription(function(){g.currentObservers=null,arrRemove(E,h)}))},u.prototype._checkFinalizedStatuses=function(h){var g=this,p=g.hasError,w=g.thrownError,T=g.isStopped;p?h.error(w):T&&h.complete()},u.prototype.asObservable=function(){var h=new Observable;return h.source=this,h},u.create=function(h,g){return new AnonymousSubject(h,g)},u}(Observable),AnonymousSubject=function(C){__extends(u,C);function u(h,g){var p=C.call(this)||this;return p.destination=h,p.source=g,p}return u.prototype.next=function(h){var g,p;(p=(g=this.destination)===null||g===void 0?void 0:g.next)===null||p===void 0||p.call(g,h)},u.prototype.error=function(h){var g,p;(p=(g=this.destination)===null||g===void 0?void 0:g.error)===null||p===void 0||p.call(g,h)},u.prototype.complete=function(){var h,g;(g=(h=this.destination)===null||h===void 0?void 0:h.complete)===null||g===void 0||g.call(h)},u.prototype._subscribe=function(h){var g,p;return(p=(g=this.source)===null||g===void 0?void 0:g.subscribe(h))!==null&&p!==void 0?p:EMPTY_SUBSCRIPTION},u}(Subject),dateTimestampProvider={now:function(){return(dateTimestampProvider.delegate||Date).now()},delegate:void 0},ReplaySubject=function(C){__extends(u,C);function u(h,g,p){h===void 0&&(h=1/0),g===void 0&&(g=1/0),p===void 0&&(p=dateTimestampProvider);var w=C.call(this)||this;return w._bufferSize=h,w._windowTime=g,w._timestampProvider=p,w._buffer=[],w._infiniteTimeWindow=!0,w._infiniteTimeWindow=g===1/0,w._bufferSize=Math.max(1,h),w._windowTime=Math.max(1,g),w}return u.prototype.next=function(h){var g=this,p=g.isStopped,w=g._buffer,T=g._infiniteTimeWindow,E=g._timestampProvider,L=g._windowTime;p||(w.push(h),!T&&w.push(E.now()+L)),this._trimBuffer(),C.prototype.next.call(this,h)},u.prototype._subscribe=function(h){this._throwIfClosed(),this._trimBuffer();for(var g=this._innerSubscribe(h),p=this,w=p._infiniteTimeWindow,T=p._buffer,E=T.slice(),L=0;L<E.length&&!h.closed;L+=w?1:2)h.next(E[L]);return this._checkFinalizedStatuses(h),g},u.prototype._trimBuffer=function(){var h=this,g=h._bufferSize,p=h._timestampProvider,w=h._buffer,T=h._infiniteTimeWindow,E=(T?1:2)*g;if(g<1/0&&E<w.length&&w.splice(0,w.length-E),!T){for(var L=p.now(),U=0,O=1;O<w.length&&w[O]<=L;O+=2)U=O;U&&w.splice(0,U+1)}},u}(Subject),DEFAULT_WEBSOCKET_CONFIG={url:"",deserializer:function(C){return JSON.parse(C.data)},serializer:function(C){return JSON.stringify(C)}},WEBSOCKETSUBJECT_INVALID_ERROR_OBJECT="WebSocketSubject.error must be called with an object with an error code, and an optional reason: { code: number, reason: string }",WebSocketSubject=function(C){__extends(u,C);function u(h,g){var p=C.call(this)||this;if(p._socket=null,h instanceof Observable)p.destination=g,p.source=h;else{var w=p._config=__assign({},DEFAULT_WEBSOCKET_CONFIG);if(p._output=new Subject,typeof h=="string")w.url=h;else for(var T in h)h.hasOwnProperty(T)&&(w[T]=h[T]);if(!w.WebSocketCtor&&WebSocket)w.WebSocketCtor=WebSocket;else if(!w.WebSocketCtor)throw new Error("no WebSocket constructor can be found");p.destination=new ReplaySubject}return p}return u.prototype.lift=function(h){var g=new u(this._config,this.destination);return g.operator=h,g.source=this,g},u.prototype._resetState=function(){this._socket=null,this.source||(this.destination=new ReplaySubject),this._output=new Subject},u.prototype.multiplex=function(h,g,p){var w=this;return new Observable(function(T){try{w.next(h())}catch(L){T.error(L)}var E=w.subscribe({next:function(L){try{p(L)&&T.next(L)}catch(U){T.error(U)}},error:function(L){return T.error(L)},complete:function(){return T.complete()}});return function(){try{w.next(g())}catch(L){T.error(L)}E.unsubscribe()}})},u.prototype._connectSocket=function(){var h=this,g=this._config,p=g.WebSocketCtor,w=g.protocol,T=g.url,E=g.binaryType,L=this._output,U=null;try{U=w?new p(T,w):new p(T),this._socket=U,E&&(this._socket.binaryType=E)}catch(d){L.error(d);return}var O=new Subscription(function(){h._socket=null,U&&U.readyState===1&&U.close()});U.onopen=function(d){var M=h._socket;if(!M){U.close(),h._resetState();return}var e=h._config.openObserver;e&&e.next(d);var b=h.destination;h.destination=Subscriber.create(function(z){if(U.readyState===1)try{var W=h._config.serializer;U.send(W(z))}catch(o0){h.destination.error(o0)}},function(z){var W=h._config.closingObserver;W&&W.next(void 0),z&&z.code?U.close(z.code,z.reason):L.error(new TypeError(WEBSOCKETSUBJECT_INVALID_ERROR_OBJECT)),h._resetState()},function(){var z=h._config.closingObserver;z&&z.next(void 0),U.close(),h._resetState()}),b&&b instanceof ReplaySubject&&O.add(b.subscribe(h.destination))},U.onerror=function(d){h._resetState(),L.error(d)},U.onclose=function(d){U===h._socket&&h._resetState();var M=h._config.closeObserver;M&&M.next(d),d.wasClean?L.complete():L.error(d)},U.onmessage=function(d){try{var M=h._config.deserializer;L.next(M(d))}catch(e){L.error(e)}}},u.prototype._subscribe=function(h){var g=this,p=this.source;return p?p.subscribe(h):(this._socket||this._connectSocket(),this._output.subscribe(h),h.add(function(){var w=g._socket;g._output.observers.length===0&&(w&&(w.readyState===1||w.readyState===0)&&w.close(),g._resetState())}),h)},u.prototype.unsubscribe=function(){var h=this._socket;h&&(h.readyState===1||h.readyState===0)&&h.close(),this._resetState(),C.prototype.unsubscribe.call(this)},u}(AnonymousSubject);function webSocket(C){return new WebSocketSubject(C)}var NVMESSAGE=(C=>(C[C.ZOOM=1]="ZOOM",C[C.CLIP_PLANE=2]="CLIP_PLANE",C[C.AZIMUTH_ELEVATION=3]="AZIMUTH_ELEVATION",C[C.FRAME_CHANGED=4]="FRAME_CHANGED",C[C.VOLUME_ADDED_FROM_URL=5]="VOLUME_ADDED_FROM_URL",C[C.VOLUME_WITH_URL_REMOVED=6]="VOLUME_WITH_URL_REMOVED",C[C.COLORMAP_CHANGED=7]="COLORMAP_CHANGED",C[C.OPACITY_CHANGED=8]="OPACITY_CHANGED",C[C.MESH_FROM_URL_ADDED=9]="MESH_FROM_URL_ADDED",C[C.MESH_WITH_URL_REMOVED=10]="MESH_WITH_URL_REMOVED",C[C.CUSTOM_SHADER_ADDED=11]="CUSTOM_SHADER_ADDED",C[C.SHADER_CHANGED=12]="SHADER_CHANGED",C[C.MESH_PROPERTY_CHANGED=13]="MESH_PROPERTY_CHANGED",C.USER_JOINED="user joined",C.CREATE="create",C.VOLUME_LOADED_FROM_URL="volume with url added",C))(NVMESSAGE||{});class SessionUser{constructor(u,h,g,p){d0(this,"id");d0(this,"displayName");d0(this,"key");d0(this,"properties");this.id=h||v4(),this.displayName=u||`user-${this.id}`,this.key=g||v4(),this.properties=p||new Map}}function storageAvailable(C){const u=window[C],h="test";try{return u.setItem(h,h),u.removeItem(h),!0}catch{return!1}}class SessionBus{constructor(u,h,g,p="",w=""){d0(this,"userList");d0(this,"user");d0(this,"userQueueName");d0(this,"userListName");d0(this,"onMessageCallback");d0(this,"isConnectedToServer",!1);d0(this,"isController",!1);d0(this,"sessionScene",{});d0(this,"sessionKey");d0(this,"sessionName");d0(this,"sessionSceneName");d0(this,"serverConnection$",null);if(this.userList=[],this.user=h||new SessionUser("anonymous"),this.onMessageCallback=g,this.sessionScene={},this.sessionKey=w||v4(),this.sessionName=u,this.sessionSceneName=`session-${u}-scene`,p)this.connectToServer(p,u),this.subscribeToServer(),this.isConnectedToServer=!0,this.serverConnection$!==null&&this.serverConnection$.next({op:NVMESSAGE.CREATE,key:this.sessionKey});else{if(!storageAvailable("localStorage"))throw new Error("Local storage unavailable");this.userQueueName=`user-${this.user.id}-q`,this.userListName=`${u}-user-list`,this.userList=JSON.parse(localStorage.getItem(this.userListName)||"[]"),this.userList.push(this.user),localStorage.setItem(this.userListName,JSON.stringify(this.userList)),localStorage.setItem(this.userQueueName,JSON.stringify([])),window.addEventListener("storage",this.localStorageEventListener.bind(this))}}sendSessionMessage(u){u.from=this.user.id,this.isConnectedToServer&&this.serverConnection$!==null?this.serverConnection$.next({...u,key:this.sessionKey,userKey:this.user.key}):this.sendLocalMessage(u)}connectToServer(u,h){const g=new URL(u);g.pathname="websockets",g.search="?session="+h,this.serverConnection$=webSocket(g.href),log.debug(g.href)}subscribeToServer(){this.serverConnection$!==null&&this.serverConnection$.subscribe({next:u=>{this.onMessageCallback(u)},error:u=>log.error(u),complete:()=>log.debug("complete")})}sendLocalMessage(u){for(const h of this.userList){if(h.id===this.user.id)continue;const g=`user-${h.id}-q`,p=localStorage.getItem(g),w=p?JSON.parse(p):[];w.push(u),localStorage.setItem(g,JSON.stringify(w))}}localStorageEventListener(u){if(u.newValue)switch(u.key){case this.userListName:{const h=JSON.parse(u.newValue),g=JSON.parse(u.oldValue??"[]");this.userList=h;const p=h.filter(w=>!g.map(T=>T.id).includes(w.id));for(const w of p)this.onMessageCallback({op:NVMESSAGE.USER_JOINED,user:w})}break;case this.userQueueName:{const h=JSON.parse(u.newValue);for(const g of h)this.onMessageCallback&&this.onMessageCallback(g);localStorage.setItem(this.userQueueName??"",JSON.stringify([]))}break}}}class NVController{constructor(u){d0(this,"niivue");d0(this,"mediaUrlMap");d0(this,"isInSession",!1);d0(this,"user");d0(this,"sessionBus");d0(this,"onFrameChange",(u,h)=>{});this.niivue=u,this.mediaUrlMap=new Map,this.niivue.onLocationChange=this.onLocationChangeHandler.bind(this),this.niivue.onZoom3DChange=this.onZoom3DChangeHandler.bind(this),this.niivue.scene.onAzimuthElevationChange=this.onAzimuthElevationChangeHandler.bind(this),this.niivue.onClipPlaneChange=this.onClipPlaneChangeHandler.bind(this),this.niivue.onVolumeAddedFromUrl=this.onVolumeAddedFromUrlHandler.bind(this),this.niivue.onVolumeWithUrlRemoved=this.onVolumeWithUrlRemovedHandler.bind(this),this.niivue.onMeshAddedFromUrl=this.onMeshAddedFromUrlHandler.bind(this),this.niivue.onMeshWithUrlRemoved=this.onMeshWithUrlRemovedHandler.bind(this),this.niivue.onCustomMeshShaderAdded=this.onCustomMeshShaderAddedHandler.bind(this),this.niivue.onMeshShaderChanged=this.onMeshShaderChanged.bind(this),this.niivue.onMeshPropertyChanged=this.onMeshPropertyChanged.bind(this),this.niivue.onFrameChange=this.onFrameChangeHandler.bind(this);for(const h of this.niivue.volumes)h.onColormapChange=this.onColormapChangeHandler.bind(this),h.onOpacityChange=this.onOpacityChangeHandler.bind(this)}onLocationChangeHandler(u){log.debug(u)}addVolume(u,h){this.niivue.volumes.push(u);const g=this.niivue.volumes.length===1?0:this.niivue.volumes.length-1;this.niivue.setVolume(u,g),this.niivue.mediaUrlMap.set(u,h)}addMesh(u,h){this.niivue.meshes.push(u);const g=this.niivue.meshes.length===1?0:this.niivue.meshes.length-1;this.niivue.setMesh(u,g),this.niivue.mediaUrlMap.set(u,h)}onNewMessage(u){switch(u.op){case NVMESSAGE.ZOOM:this.niivue.volScaleMultiplier=u.zoom;break;case NVMESSAGE.CLIP_PLANE:this.niivue.scene.clipPlane=u.clipPlane;break;case NVMESSAGE.AZIMUTH_ELEVATION:this.niivue.scene._elevation=u.elevation,this.niivue.scene._azimuth=u.azimuth;break;case NVMESSAGE.FRAME_CHANGED:{const h=this.niivue.getMediaByUrl(u.url);h&&(h.frame4D=u.index)}break;case NVMESSAGE.VOLUME_ADDED_FROM_URL:this.niivue.getMediaByUrl(u.imageOptions.url)||NVImage.loadFromUrl(u.imageOptions).then(h=>{h&&this.addVolume(h,u.imageOptions.url)}).catch(h=>{if(h)throw h});break;case NVMESSAGE.VOLUME_WITH_URL_REMOVED:{const h=this.niivue.getMediaByUrl(u.url);h&&(this.niivue.setVolume(h,-1),this.niivue.mediaUrlMap.delete(h))}break;case NVMESSAGE.COLORMAP_CHANGED:{const h=this.niivue.getMediaByUrl(u.url);h._colormap=u.colormap,this.niivue.updateGLVolume()}break;case NVMESSAGE.OPACITY_CHANGED:{const h=this.niivue.getMediaByUrl(u.url);h._opacity=u.opacity,this.niivue.updateGLVolume()}break;case NVMESSAGE.MESH_FROM_URL_ADDED:this.niivue.getMediaByUrl(u.meshOptions.url)||(u.meshOptions.gl=this.niivue.gl,NVMesh.loadFromUrl(u.meshOptions).then(h=>{this.addMesh(h,u.meshOptions.url)}).catch(h=>{if(h)throw h}));break;case NVMESSAGE.MESH_WITH_URL_REMOVED:{const h=this.niivue.getMediaByUrl(u.url);h&&(this.niivue.setMesh(h,-1),this.niivue.mediaUrlMap.delete(h))}break;case NVMESSAGE.CUSTOM_SHADER_ADDED:{const h=this.niivue.createCustomMeshShader(u.fragmentShaderText,u.name);this.niivue.meshShaders.push(h)}break;case NVMESSAGE.SHADER_CHANGED:this.niivue.meshes[u.meshIndex].meshShaderIndex=u.shaderIndex,this.niivue.updateGLVolume();break;case NVMESSAGE.MESH_PROPERTY_CHANGED:this.niivue.meshes[u.meshIndex].setProperty(u.key,u.val,this.niivue.gl);break}this.niivue.drawScene()}connectToSession(u,h,g,p){this.user=h||new SessionUser,this.sessionBus=new SessionBus(u,this.user,this.onNewMessage.bind(this),g,p),this.isInSession=!0}onZoom3DChangeHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.ZOOM,zoom:u})}onAzimuthElevationChangeHandler(u,h){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.AZIMUTH_ELEVATION,azimuth:u,elevation:h})}onClipPlaneChangeHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.CLIP_PLANE,clipPlane:u})}onVolumeAddedFromUrlHandler(u,h){this.isInSession&&this.sessionBus&&(log.debug(u),this.sessionBus.sendSessionMessage({op:NVMESSAGE.VOLUME_ADDED_FROM_URL,imageOptions:u})),h.onColormapChange=this.onColormapChangeHandler.bind(this),h.onOpacityChange=this.onOpacityChangeHandler.bind(this)}onImageLoadedHandler(u){if(u.onColormapChange=this.onColormapChangeHandler.bind(this),u.onOpacityChange=this.onOpacityChangeHandler.bind(this),this.isInSession&&this.sessionBus&&this.niivue.mediaUrlMap.has(u)){const h=this.niivue.mediaUrlMap.get(u);this.sessionBus.sendSessionMessage({op:NVMESSAGE.VOLUME_LOADED_FROM_URL,url:h})}}onVolumeWithUrlRemovedHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.VOLUME_WITH_URL_REMOVED,url:u})}onMeshAddedFromUrlHandler(u){log.debug("mesh loaded from url"),log.debug(u),this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.MESH_FROM_URL_ADDED,meshOptions:u})}onMeshLoadedHandler(u){log.debug("mesh has been added"),log.debug(u)}onMeshWithUrlRemovedHandler(u){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.MESH_WITH_URL_REMOVED,url:u})}onColormapChangeHandler(u){if(this.isInSession&&this.sessionBus&&this.niivue.mediaUrlMap.has(u)){const h=this.niivue.mediaUrlMap.get(u),g=u.colormap;this.sessionBus.sendSessionMessage({op:NVMESSAGE.COLORMAP_CHANGED,url:h,colormap:g})}}onOpacityChangeHandler(u){if(this.isInSession&&this.sessionBus&&this.niivue.mediaUrlMap.has(u)){const h=this.niivue.mediaUrlMap.get(u),g=u.opacity;this.sessionBus.sendSessionMessage({op:NVMESSAGE.OPACITY_CHANGED,url:h,opacity:g})}}onFrameChangeHandler(u,h){if(log.debug("frame has changed to "+h),log.debug(u),this.niivue.mediaUrlMap.has(u)&&this.isInSession&&this.sessionBus){const g=this.niivue.mediaUrlMap.get(u);this.sessionBus.sendSessionMessage({op:NVMESSAGE.FRAME_CHANGED,url:g,index:h})}this.onFrameChange(u,h)}onCustomMeshShaderAddedHandler(u,h){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.CUSTOM_SHADER_ADDED,fragmentShaderText:u,name:h})}onMeshShaderChanged(u,h){this.isInSession&&this.sessionBus&&this.sessionBus.sendSessionMessage({op:NVMESSAGE.SHADER_CHANGED,meshIndex:u,shaderIndex:h})}onMeshPropertyChanged(u,h,g){this.isInSession&&this.sessionBus&&(log.debug(NVMESSAGE.MESH_PROPERTY_CHANGED),this.sessionBus.sendSessionMessage({op:NVMESSAGE.MESH_PROPERTY_CHANGED,meshIndex:u,key:h,val:g}))}}const MESH_EXTENSIONS=["ASC","BYU","DFS","FSM","PIAL","ORIG","INFLATED","SMOOTHWM","SPHERE","WHITE","G","GEO","GII","ICO","MZ3","NV","OBJ","OFF","PLY","SRF","STL","TCK","TRACT","TRI","TRK","TT","TRX","VTK","X3D","JCON","JSON"],LEFT_MOUSE_BUTTON=0,CENTER_MOUSE_BUTTON=1,RIGHT_MOUSE_BUTTON=2,TEXTURE0_BACK_VOL=33984,TEXTURE1_COLORMAPS=33985,TEXTURE2_OVERLAY_VOL=33986,TEXTURE3_FONT=33987,TEXTURE4_THUMBNAIL=33988,TEXTURE5_MATCAP=33989,TEXTURE6_GRADIENT=33990,TEXTURE7_DRAW=33991,TEXTURE8_GRADIENT_TEMP=33992,TEXTURE9_ORIENT=33993,TEXTURE10_BLEND=33994,TEXTURE11_GC_BACK=33995,TEXTURE12_GC_STRENGTH0=33996,TEXTURE13_GC_STRENGTH1=33997,TEXTURE14_GC_LABEL0=33998,TEXTURE15_GC_LABEL1=33999,defaultSaveImageOptions={filename:"",isSaveDrawing:!1,volumeByIndex:0};class Niivue{constructor(u=DEFAULT_OPTIONS){d0(this,"loaders",{});d0(this,"canvas",null);d0(this,"_gl",null);d0(this,"isBusy",!1);d0(this,"needsRefresh",!1);d0(this,"colormapTexture",null);d0(this,"colormapLists",[]);d0(this,"volumeTexture",null);d0(this,"gradientTexture",null);d0(this,"gradientTextureAmount",0);d0(this,"drawTexture",null);d0(this,"drawUndoBitmaps",[]);d0(this,"drawLut",cmapper.makeDrawLut("$itksnap"));d0(this,"drawOpacity",.8);d0(this,"renderDrawAmbientOcclusion",.4);d0(this,"colorbarHeight",0);d0(this,"drawPenLocation",[NaN,NaN,NaN]);d0(this,"drawPenAxCorSag",-1);d0(this,"drawFillOverwrites",!0);d0(this,"drawPenFillPts",[]);d0(this,"overlayTexture",null);d0(this,"overlayTextureID",null);d0(this,"sliceMMShader");d0(this,"sliceV1Shader");d0(this,"orientCubeShader");d0(this,"orientCubeShaderVAO",null);d0(this,"rectShader");d0(this,"renderShader");d0(this,"lineShader");d0(this,"line3DShader");d0(this,"passThroughShader");d0(this,"renderGradientShader");d0(this,"renderSliceShader");d0(this,"renderVolumeShader");d0(this,"pickingMeshShader");d0(this,"pickingImageShader");d0(this,"colorbarShader");d0(this,"fontShader",null);d0(this,"fiberShader");d0(this,"fontTexture",null);d0(this,"circleShader");d0(this,"matCapTexture",null);d0(this,"bmpShader",null);d0(this,"bmpTexture",null);d0(this,"thumbnailVisible",!1);d0(this,"bmpTextureWH",1);d0(this,"growCutShader");d0(this,"orientShaderAtlasU",null);d0(this,"orientShaderAtlasI",null);d0(this,"orientShaderU",null);d0(this,"orientShaderI",null);d0(this,"orientShaderF",null);d0(this,"orientShaderRGBU",null);d0(this,"surfaceShader",null);d0(this,"blurShader",null);d0(this,"sobelShader",null);d0(this,"genericVAO",null);d0(this,"unusedVAO",null);d0(this,"crosshairs3D",null);d0(this,"DEFAULT_FONT_GLYPH_SHEET",defaultFontPNG);d0(this,"DEFAULT_FONT_METRICS",defaultFontMetrics);d0(this,"fontMetrics");d0(this,"fontMets",null);d0(this,"backgroundMasksOverlays",0);d0(this,"overlayOutlineWidth",0);d0(this,"overlayAlphaShader",1);d0(this,"isAlphaClipDark",!1);d0(this,"position");d0(this,"extentsMin");d0(this,"extentsMax");d0(this,"resizeObserver",null);d0(this,"syncOpts",{});d0(this,"readyForSync",!1);d0(this,"uiData",{mousedown:!1,touchdown:!1,mouseButtonLeftDown:!1,mouseButtonCenterDown:!1,mouseButtonRightDown:!1,mouseDepthPicker:!1,clickedTile:-1,pan2DxyzmmAtMouseDown:[0,0,0,1],prevX:0,prevY:0,currX:0,currY:0,currentTouchTime:0,lastTouchTime:0,touchTimer:null,doubleTouch:!1,isDragging:!1,dragStart:[0,0],dragEnd:[0,0],dragClipPlaneStartDepthAziElev:[0,0,0],lastTwoTouchDistance:0,multiTouchGesture:!1});d0(this,"back",null);d0(this,"overlays",[]);d0(this,"deferredVolumes",[]);d0(this,"deferredMeshes",[]);d0(this,"furthestVertexFromOrigin",100);d0(this,"volScale",[]);d0(this,"vox",[]);d0(this,"mousePos",[0,0]);d0(this,"screenSlices",[]);d0(this,"cuboidVertexBuffer");d0(this,"otherNV",null);d0(this,"volumeObject3D",null);d0(this,"pivot3D",[0,0,0]);d0(this,"furthestFromPivot",10);d0(this,"currentClipPlaneIndex",0);d0(this,"lastCalled",new Date().getTime());d0(this,"selectedObjectId",-1);d0(this,"CLIP_PLANE_ID",1);d0(this,"VOLUME_ID",254);d0(this,"DISTANCE_FROM_CAMERA",-.54);d0(this,"graph",{LTWH:[0,0,640,480],opacity:0,vols:[0],autoSizeMultiplanar:!1,normalizeValues:!1});d0(this,"meshShaders",[{Name:"Phong",Frag:fragMeshShader},{Name:"Matte",Frag:fragMeshMatteShader},{Name:"Harmonic",Frag:fragMeshShaderSHBlue},{Name:"Hemispheric",Frag:fragMeshHemiShader},{Name:"Crevice",Frag:fragMeshShaderCrevice},{Name:"Edge",Frag:fragMeshEdgeShader},{Name:"Diffuse",Frag:fragMeshDiffuseEdgeShader},{Name:"Outline",Frag:fragMeshOutlineShader},{Name:"Specular",Frag:fragMeshSpecularEdgeShader},{Name:"Toon",Frag:fragMeshToonShader},{Name:"Flat",Frag:fragFlatMeshShader},{Name:"Matcap",Frag:fragMeshMatcapShader}]);d0(this,"dragModes",{contrast:DRAG_MODE.contrast,measurement:DRAG_MODE.measurement,none:DRAG_MODE.none,pan:DRAG_MODE.pan,slicer3D:DRAG_MODE.slicer3D,callbackOnly:DRAG_MODE.callbackOnly});d0(this,"sliceTypeAxial",SLICE_TYPE.AXIAL);d0(this,"sliceTypeCoronal",SLICE_TYPE.CORONAL);d0(this,"sliceTypeSagittal",SLICE_TYPE.SAGITTAL);d0(this,"sliceTypeMultiplanar",SLICE_TYPE.MULTIPLANAR);d0(this,"sliceTypeRender",SLICE_TYPE.RENDER);d0(this,"onDragRelease",()=>{});d0(this,"onMouseUp",()=>{});d0(this,"onLocationChange",()=>{});d0(this,"onIntensityChange",()=>{});d0(this,"onClickToSegment",()=>{});d0(this,"onImageLoaded",()=>{});d0(this,"onMeshLoaded",()=>{});d0(this,"onFrameChange",()=>{});d0(this,"onError",()=>{});d0(this,"onColormapChange",()=>{});d0(this,"onInfo",()=>{});d0(this,"onWarn",()=>{});d0(this,"onDebug",()=>{});d0(this,"onVolumeAddedFromUrl",()=>{});d0(this,"onVolumeWithUrlRemoved",()=>{});d0(this,"onVolumeUpdated",()=>{});d0(this,"onMeshAddedFromUrl",()=>{});d0(this,"onMeshAdded",()=>{});d0(this,"onMeshWithUrlRemoved",()=>{});d0(this,"onZoom3DChange",()=>{});d0(this,"onAzimuthElevationChange",()=>{});d0(this,"onClipPlaneChange",()=>{});d0(this,"onCustomMeshShaderAdded",()=>{});d0(this,"onMeshShaderChanged",()=>{});d0(this,"onMeshPropertyChanged",()=>{});d0(this,"onDocumentLoaded",()=>{});d0(this,"document",new NVDocument);d0(this,"mediaUrlMap",new Map);d0(this,"initialized",!1);d0(this,"currentDrawUndoBitmap");d0(this,"loadingText");for(const h in u)typeof u[h]=="function"?this[h]=u[h]:this.opts[h]=DEFAULT_OPTIONS[h]===void 0?DEFAULT_OPTIONS[h]:u[h];this.opts.isHighResolutionCapable?this.uiData.dpr=window.devicePixelRatio||1:this.uiData.dpr=1,this.currentDrawUndoBitmap=this.opts.maxDrawUndoBitmaps,this.opts.drawingEnabled&&this.createEmptyDrawing(),this.opts.thumbnail.length>0&&(this.thumbnailVisible=!0),this.loadingText=this.opts.loadingText,log.setLogLevel(this.opts.logLevel)}get scene(){return this.document.scene}get opts(){return this.document.opts}get sliceMosaicString(){return this.document.opts.sliceMosaicString||""}set sliceMosaicString(u){this.document.opts.sliceMosaicString=u}get volumes(){return this.document.volumes}set volumes(u){this.document.volumes=u}get meshes(){return this.document.meshes}set meshes(u){this.document.meshes=u}get drawBitmap(){return this.document.drawBitmap}set drawBitmap(u){this.document.drawBitmap=u}get volScaleMultiplier(){return this.scene.volScaleMultiplier}set volScaleMultiplier(u){this.setScale(u)}async saveScene(u="niivue.png"){function h(p,w){const T=document.createElement("a");document.body.appendChild(T),T.style.display="none";const E=window.URL.createObjectURL(p);T.href=E,T.download=w,T.click(),T.remove()}const g=this.canvas;if(!g)throw new Error("canvas not defined");this.drawScene(),g.toBlob(p=>{p&&(u===""&&(u=`niivue-screenshot-${new Date().toString()}.png`,u=u.replace(/\s/g,"_")),h(p,u))})}async attachTo(u,h=null){return await this.attachToCanvas(document.getElementById(u),h),log.debug("attached to element with id: ",u),this}async attachToCanvas(u,h=null){return this.canvas=u,h===null&&(h=navigator.hardwareConcurrency>6,log.debug("AntiAlias ",h," Threads ",navigator.hardwareConcurrency)),this.gl=this.canvas.getContext("webgl2",{alpha:!0,antialias:h}),log.info("NIIVUE VERSION ",version),this.canvas.parentElement.style.backgroundColor="black",this.opts.isResizeCanvas&&(this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.display="block",this.canvas.width=this.canvas.offsetWidth,this.canvas.height=this.canvas.offsetHeight,window.addEventListener("resize",this.resizeListener.bind(this)),this.resizeObserver=new ResizeObserver(this.resizeListener.bind(this)),this.resizeObserver.observe(this.canvas.parentElement)),this.registerInteractions(),await this.init(),this.drawScene(),this}syncWith(u,h={"2d":!0,"3d":!0}){this.otherNV=u,this.syncOpts={...h}}broadcastTo(u,h={"2d":!0,"3d":!0}){this.otherNV=u,this.syncOpts=h}sync(){if(!this.gl||!this.otherNV||typeof this.otherNV>"u"||!this.gl.canvas.matches(":focus"))return;const u=this.frac2mm(this.scene.crosshairPos);if(this.otherNV instanceof Niivue){const h=this.scene.gamma,g=this.otherNV.scene.gamma;h!==g&&this.otherNV.setGamma(h),this.syncOpts["2d"]&&(this.otherNV.scene.crosshairPos=this.otherNV.mm2frac(u),this.otherNV.scene.pan2Dxyzmm=clone(this.scene.pan2Dxyzmm)),this.syncOpts["3d"]&&(this.otherNV.scene.renderAzimuth=this.scene.renderAzimuth,this.otherNV.scene.renderElevation=this.scene.renderElevation,this.otherNV.scene.volScaleMultiplier=this.scene.volScaleMultiplier),this.otherNV.drawScene(),this.otherNV.createOnLocationChange()}else if(Array.isArray(this.otherNV))for(let h=0;h<this.otherNV.length;h++){if(this.otherNV[h]===this)continue;const g=this.scene.gamma,p=this.otherNV[h].scene.gamma;g!==p&&this.otherNV[h].setGamma(g),this.syncOpts["2d"]&&(this.otherNV[h].scene.crosshairPos=this.otherNV[h].mm2frac(u),this.otherNV[h].scene.pan2Dxyzmm=clone(this.scene.pan2Dxyzmm)),this.syncOpts["3d"]&&(this.otherNV[h].scene.renderAzimuth=this.scene.renderAzimuth,this.otherNV[h].scene.renderElevation=this.scene.renderElevation,this.otherNV[h].scene.volScaleMultiplier=this.scene.volScaleMultiplier),this.otherNV[h].drawScene(),this.otherNV[h].createOnLocationChange()}}arrayEquals(u,h){return Array.isArray(u)&&Array.isArray(h)&&u.length===h.length&&u.every((g,p)=>g===h[p])}resizeListener(){if(!(!this.canvas||!this.gl)){if(!this.opts.isResizeCanvas){this.opts.isHighResolutionCapable&&(log.warn("isHighResolutionCapable requires isResizeCanvas"),this.opts.isHighResolutionCapable=!1),this.uiData.dpr=1,this.drawScene();return}this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.display="block",this.opts.isHighResolutionCapable?(this.uiData.dpr=window.devicePixelRatio||1,log.debug("devicePixelRatio: "+this.uiData.dpr)):this.uiData.dpr=1,"width"in this.canvas.parentElement?(this.canvas.width=this.canvas.parentElement.width*this.uiData.dpr,this.canvas.height=this.canvas.parentElement.height*this.uiData.dpr):(this.canvas.width=this.canvas.offsetWidth*this.uiData.dpr,this.canvas.height=this.canvas.offsetHeight*this.uiData.dpr),this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height),this.drawScene()}}getRelativeMousePosition(u,h){if(h=h||u.target,!h)return;const g=h.getBoundingClientRect();return{x:u.clientX-g.left,y:u.clientY-g.top}}getNoPaddingNoBorderCanvasRelativeMousePosition(u,h){return h=h||u.target,this.getRelativeMousePosition(u,h)}mouseContextMenuListener(u){u.preventDefault()}mouseDownListener(u){u.preventDefault(),this.drawPenLocation=[NaN,NaN,NaN],this.drawPenAxCorSag=-1,this.uiData.mousedown=!0,log.debug("mouse down"),log.debug(u);const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);if(!h)return;const[g,p]=[h.x*this.uiData.dpr,h.y*this.uiData.dpr],w=this.getLabelAtPoint([g,p]);if(w){if(w.onClick){w.onClick(w);return}for(const T of this.meshes)if(T.type===MeshType.CONNECTOME)for(const E of T.nodes)E.label===w&&(this.scene.crosshairPos=this.mm2frac([E.x,E.y,E.z]),this.updateGLVolume(),this.drawScene())}this.uiData.clickedTile=this.tileIndex(g,p),u.button===LEFT_MOUSE_BUTTON&&u.shiftKey?(this.uiData.mouseButtonCenterDown=!0,this.mouseCenterButtonHandler(u)):u.button===LEFT_MOUSE_BUTTON?(this.uiData.mouseButtonLeftDown=!0,this.mouseLeftButtonHandler(u)):u.button===RIGHT_MOUSE_BUTTON?(this.uiData.mouseButtonRightDown=!0,this.mouseRightButtonHandler(u)):u.button===CENTER_MOUSE_BUTTON&&(this.uiData.mouseButtonCenterDown=!0,this.mouseCenterButtonHandler(u))}mouseLeftButtonHandler(u){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);this.mouseDown(h.x,h.y),this.mouseClick(h.x,h.y)}mouseCenterButtonHandler(u){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);this.mousePos=[h.x*this.uiData.dpr,h.y*this.uiData.dpr],this.opts.dragMode!==DRAG_MODE.none&&(this.setDragStart(h.x,h.y),this.uiData.isDragging||(this.uiData.pan2DxyzmmAtMouseDown=clone(this.scene.pan2Dxyzmm)),this.uiData.isDragging=!0,this.uiData.dragClipPlaneStartDepthAziElev=this.scene.clipPlaneDepthAziElev)}mouseRightButtonHandler(u){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);this.mousePos=[h.x*this.uiData.dpr,h.y*this.uiData.dpr],this.opts.dragMode!==DRAG_MODE.none&&(this.setDragStart(h.x,h.y),this.uiData.isDragging||(this.uiData.pan2DxyzmmAtMouseDown=clone(this.scene.pan2Dxyzmm)),this.uiData.isDragging=!0,this.uiData.dragClipPlaneStartDepthAziElev=this.scene.clipPlaneDepthAziElev)}calculateMinMaxVoxIdx(u){if(u.length>2)throw new Error("array must not contain more than two values");return[Math.floor(Math.min(u[0],u[1])),Math.floor(Math.max(u[0],u[1]))]}calculateNewRange({volIdx:u=0}={}){if(this.opts.sliceType===SLICE_TYPE.RENDER&&this.sliceMosaicString.length<1||this.uiData.dragStart[0]===this.uiData.dragEnd[0]&&this.uiData.dragStart[1]===this.uiData.dragEnd[1])return;let h=this.canvasPos2frac([this.uiData.dragStart[0],this.uiData.dragStart[1]]);if(h[0]<0)return;const g=this.frac2vox(h,u);if(h=this.canvasPos2frac([this.uiData.dragEnd[0],this.uiData.dragEnd[1]]),h[0]<0)return;const p=this.frac2vox(h,u);let w=-Number.MAX_VALUE,T=Number.MAX_VALUE;const E=this.calculateMinMaxVoxIdx([g[0],p[0]]),L=this.calculateMinMaxVoxIdx([g[1],p[1]]),U=this.calculateMinMaxVoxIdx([g[2],p[2]]);g[0]-p[0]===0?E[1]=g[0]+1:g[1]-p[1]===0?L[1]=g[1]+1:g[2]-p[2]===0&&(U[1]=g[2]+1);const O=this.volumes[u].hdr,d=this.volumes[u].img;if(!O||!d)return;const M=O.dims[1],e=O.dims[2];for(let W=U[0];W<U[1];W++){const o0=W*M*e;for(let J=L[0];J<L[1];J++){const K=J*M;for(let y=E[0];y<E[1];y++){const Z=o0+K+y;T>d[Z]&&(T=d[Z]),w<d[Z]&&(w=d[Z])}}}if(T>=w)return;const b=intensityRaw2Scaled(O,T),z=intensityRaw2Scaled(O,w);this.volumes[u].cal_min=b,this.volumes[u].cal_max=z,this.onIntensityChange(this.volumes[u])}generateMouseUpCallback(u,h){const g=this.tileIndex(this.uiData.dragStart[0],this.uiData.dragStart[1]),p=this.tileIndex(this.uiData.dragEnd[0],this.uiData.dragEnd[1]);let w=-1;g===p&&(w=p);let T=-1;w>=0&&(T=this.screenSlices[w].axCorSag);const E=this.frac2mm(u),L=this.frac2mm(h),U=create$2();sub$1(U,fromValues$2(E[0],E[1],E[2]),fromValues$2(L[0],L[1],L[2]));const O=len(U),d=this.frac2vox(u),M=this.frac2vox(h);this.onDragRelease({fracStart:u,fracEnd:h,voxStart:d,voxEnd:M,mmStart:E,mmEnd:L,mmLength:O,tileIdx:w,axCorSag:T})}mouseUpListener(){function u(p){return Object.prototype.toString.call(p).indexOf("Function")>-1}const h={mouseButtonRightDown:this.uiData.mouseButtonRightDown,mouseButtonCenterDown:this.uiData.mouseButtonCenterDown,isDragging:this.uiData.isDragging,mousePos:this.mousePos,fracPos:this.canvasPos2frac(this.mousePos)};this.uiData.mousedown=!1,this.uiData.mouseButtonRightDown=!1;const g=this.uiData.mouseButtonCenterDown;if(this.uiData.mouseButtonCenterDown=!1,this.uiData.mouseButtonLeftDown=!1,this.drawPenFillPts.length>0?this.drawPenFilled():this.drawPenAxCorSag>=0&&this.drawAddUndoBitmap(),this.drawPenLocation=[NaN,NaN,NaN],this.drawPenAxCorSag=-1,u(this.onMouseUp)&&this.onMouseUp(h),this.uiData.isDragging){this.uiData.isDragging=!1,this.opts.dragMode===DRAG_MODE.callbackOnly&&this.drawScene();const p=this.canvasPos2frac([this.uiData.dragStart[0],this.uiData.dragStart[1]]),w=this.canvasPos2frac([this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);if(this.generateMouseUpCallback(p,w),this.opts.dragMode!==DRAG_MODE.contrast||g||this.uiData.dragStart[0]===this.uiData.dragEnd[0]&&this.uiData.dragStart[1]===this.uiData.dragEnd[1])return;this.calculateNewRange({volIdx:0}),this.refreshLayers(this.volumes[0],0)}this.drawScene()}checkMultitouch(u){if(this.uiData.touchdown&&!this.uiData.multiTouchGesture){const h=this.canvas.getBoundingClientRect();this.mouseDown(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top),this.mouseClick(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top)}}touchStartListener(u){u.preventDefault(),this.uiData.touchTimer||(this.uiData.touchTimer=setTimeout(()=>{this.resetBriCon(u)},this.opts.longTouchTimeout)),this.uiData.touchdown=!0,this.uiData.currentTouchTime=new Date().getTime();const h=this.uiData.currentTouchTime-this.uiData.lastTouchTime;if(h<this.opts.doubleTouchTimeout&&h>0){this.uiData.doubleTouch=!0,this.setDragStart(u.targetTouches[0].clientX-u.target.getBoundingClientRect().left,u.targetTouches[0].clientY-u.target.getBoundingClientRect().top),this.resetBriCon(u),this.uiData.lastTouchTime=this.uiData.currentTouchTime;return}else this.uiData.doubleTouch=!1,this.setDragStart(0,0),this.setDragEnd(0,0),this.uiData.lastTouchTime=this.uiData.currentTouchTime;this.uiData.touchdown&&u.touches.length<2?this.uiData.multiTouchGesture=!1:this.uiData.multiTouchGesture=!0,setTimeout(this.checkMultitouch.bind(this),1,u)}touchEndListener(u){if(u.preventDefault(),this.uiData.touchdown=!1,this.uiData.lastTwoTouchDistance=0,this.uiData.multiTouchGesture=!1,this.uiData.touchTimer&&(clearTimeout(this.uiData.touchTimer),this.uiData.touchTimer=null),this.uiData.isDragging){this.uiData.isDragging=!1,this.opts.dragMode===DRAG_MODE.contrast&&(this.calculateNewRange(),this.refreshLayers(this.volumes[0],0));const h=this.canvasPos2frac([this.uiData.dragStart[0],this.uiData.dragStart[1]]),g=this.canvasPos2frac([this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);this.generateMouseUpCallback(h,g)}this.mouseUpListener()}mouseMoveListener(u){if(this.uiData.mousedown){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);if(!h)return;const g=h.x*this.uiData.dpr,p=h.y*this.uiData.dpr;if(this.tileIndex(g,p)!==this.uiData.clickedTile)return;this.uiData.mouseButtonLeftDown?(this.mouseMove(h.x,h.y),this.mouseClick(h.x,h.y)):(this.uiData.mouseButtonRightDown||this.uiData.mouseButtonCenterDown)&&this.setDragEnd(h.x,h.y),this.drawScene(),this.uiData.prevX=this.uiData.currX,this.uiData.prevY=this.uiData.currY}else if(!this.uiData.mousedown&&this.opts.clickToSegment){const h=this.getNoPaddingNoBorderCanvasRelativeMousePosition(u,this.gl.canvas);if(!h)return;const g=h.x*this.uiData.dpr,p=h.y*this.uiData.dpr;this.mousePos=[g,p],this.drawScene()}}resetBriCon(u=null){if(this.uiData.isDragging)return;let h=!1;this.opts.sliceType===SLICE_TYPE.RENDER&&(h=!0);let g=0,p=0;if(u!==null&&("targetTouches"in u?(g=u.targetTouches[0].clientX-u.target.getBoundingClientRect().left,p=u.targetTouches[0].clientY-u.target.getBoundingClientRect().top):(g=u.offsetX,p=u.offsetY),g*=this.uiData.dpr,p*=this.uiData.dpr,this.inRenderTile(g,p)>=0&&(h=!0)),h){this.uiData.mouseDepthPicker=!0,this.drawScene(),this.drawScene();return}this.opts.dragMode!==DRAG_MODE.slicer3D&&(this.volumes.length<1||this.uiData.doubleTouch||(this.volumes[0].cal_min=this.volumes[0].robust_min,this.volumes[0].cal_max=this.volumes[0].robust_max,this.onIntensityChange(this.volumes[0]),this.refreshLayers(this.volumes[0],0),this.drawScene()))}setDragStart(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr,this.uiData.dragStart[0]=u,this.uiData.dragStart[1]=h}setDragEnd(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr,this.uiData.dragEnd[0]=u,this.uiData.dragEnd[1]=h}touchMoveListener(u){if(this.uiData.touchdown&&u.touches.length<2){const h=this.canvas.getBoundingClientRect();if(this.uiData.isDragging||(this.uiData.pan2DxyzmmAtMouseDown=clone(this.scene.pan2Dxyzmm)),this.uiData.isDragging=!0,this.uiData.doubleTouch&&this.uiData.isDragging){this.setDragEnd(u.targetTouches[0].clientX-u.target.getBoundingClientRect().left,u.targetTouches[0].clientY-u.target.getBoundingClientRect().top),this.drawScene();return}this.mouseClick(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top),this.mouseMove(u.touches[0].clientX-h.left,u.touches[0].clientY-h.top)}else this.handlePinchZoom(u)}handlePinchZoom(u){if(u.targetTouches.length===2&&u.changedTouches.length===2){const h=Math.hypot(u.touches[0].pageX-u.touches[1].pageX,u.touches[0].pageY-u.touches[1].pageY),g=this.canvas.getBoundingClientRect();this.mousePos=[u.touches[0].clientX-g.left,u.touches[0].clientY-g.top],h<this.uiData.lastTwoTouchDistance?this.sliceScroll2D(-.01,u.touches[0].clientX-g.left,u.touches[0].clientY-g.top):this.sliceScroll2D(.01,u.touches[0].clientX-g.left,u.touches[0].clientY-g.top),this.uiData.lastTwoTouchDistance=h}}keyUpListener(u){if(u.code===this.opts.clipPlaneHotKey){const h=new Date().getTime();if(h-this.lastCalled>this.opts.keyDebounceTime){switch(this.currentClipPlaneIndex=(this.currentClipPlaneIndex+1)%7,this.currentClipPlaneIndex){case 0:this.scene.clipPlaneDepthAziElev=[2,0,0];break;case 1:this.scene.clipPlaneDepthAziElev=[0,270,0];break;case 2:this.scene.clipPlaneDepthAziElev=[0,90,0];break;case 3:this.scene.clipPlaneDepthAziElev=[0,0,0];break;case 4:this.scene.clipPlaneDepthAziElev=[0,180,0];break;case 5:this.scene.clipPlaneDepthAziElev=[0,0,-90];break;case 6:this.scene.clipPlaneDepthAziElev=[0,0,90];break}this.setClipPlane(this.scene.clipPlaneDepthAziElev)}this.lastCalled=h}else if(u.code===this.opts.viewModeHotKey){const h=new Date().getTime();h-this.lastCalled>this.opts.keyDebounceTime&&(this.setSliceType((this.opts.sliceType+1)%5),this.lastCalled=h)}}keyDownListener(u){u.code==="KeyH"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth-1,this.scene.renderElevation):u.code==="KeyL"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth+1,this.scene.renderElevation):u.code==="KeyJ"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth,this.scene.renderElevation+1):u.code==="KeyK"&&this.opts.sliceType===SLICE_TYPE.RENDER?this.setRenderAzimuthElevation(this.scene.renderAzimuth,this.scene.renderElevation-1):u.code==="KeyH"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(-1,0,0):u.code==="KeyL"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(1,0,0):u.code==="KeyU"&&this.opts.sliceType!==SLICE_TYPE.RENDER&&u.ctrlKey?this.moveCrosshairInVox(0,0,1):u.code==="KeyD"&&this.opts.sliceType!==SLICE_TYPE.RENDER&&u.ctrlKey?this.moveCrosshairInVox(0,0,-1):u.code==="KeyJ"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(0,-1,0):u.code==="KeyK"&&this.opts.sliceType!==SLICE_TYPE.RENDER?this.moveCrosshairInVox(0,1,0):u.code==="KeyM"&&this.opts.sliceType!==SLICE_TYPE.RENDER?(this.opts.dragMode++,this.opts.dragMode>=DRAG_MODE.slicer3D&&(this.opts.dragMode=DRAG_MODE.none),log.info("drag mode changed to ",DRAG_MODE[this.opts.dragMode])):u.code==="ArrowLeft"?this.setFrame4D(this.volumes[0].id,this.volumes[0].frame4D-1):u.code==="ArrowRight"?this.setFrame4D(this.volumes[0].id,this.volumes[0].frame4D+1):u.code==="Slash"&&u.shiftKey&&alert(`NIIVUE VERSION: ${version}`)}wheelListener(u){if(u.preventDefault(),u.stopPropagation(),this.thumbnailVisible)return;const h=this.canvas.getBoundingClientRect();u.deltaY<0?this.sliceScroll2D(-.01,u.clientX-h.left,u.clientY-h.top):this.sliceScroll2D(.01,u.clientX-h.left,u.clientY-h.top)}registerInteractions(){if(!this.canvas)throw new Error("canvas undefined");this.canvas.addEventListener("mousedown",this.mouseDownListener.bind(this)),this.canvas.addEventListener("mouseup",this.mouseUpListener.bind(this)),this.canvas.addEventListener("mousemove",this.mouseMoveListener.bind(this)),this.canvas.addEventListener("touchstart",this.touchStartListener.bind(this)),this.canvas.addEventListener("touchend",this.touchEndListener.bind(this)),this.canvas.addEventListener("touchmove",this.touchMoveListener.bind(this)),this.canvas.addEventListener("wheel",this.wheelListener.bind(this)),this.canvas.addEventListener("contextmenu",this.mouseContextMenuListener.bind(this)),this.canvas.addEventListener("dblclick",this.resetBriCon.bind(this)),this.canvas.addEventListener("dragenter",this.dragEnterListener.bind(this),!1),this.canvas.addEventListener("dragover",this.dragOverListener.bind(this),!1),this.canvas.addEventListener("drop",this.dropListener.bind(this),!1),this.canvas.setAttribute("tabindex","0"),this.canvas.addEventListener("keyup",this.keyUpListener.bind(this),!1),this.canvas.addEventListener("keydown",this.keyDownListener.bind(this),!1)}dragEnterListener(u){u.stopPropagation(),u.preventDefault()}dragOverListener(u){u.stopPropagation(),u.preventDefault()}getFileExt(u,h=!0){log.debug("fullname: ",u);const g=/(?:\.([^.]+))?$/;let p=g.exec(u)[1];if(p=p.toUpperCase(),p==="GZ")p=g.exec(u.slice(0,-3))[1],p=p.toUpperCase();else if(p==="CBOR"){const w=p;p=g.exec(u.slice(0,-5))[1],p=p.toUpperCase(),p=`${p}.${w}`}return h?p:p.toLowerCase()}async addVolumeFromUrl(u){const h=await NVImage.loadFromUrl(u);return this.document.addImageOptions(h,u),h.onColormapChange=this.onColormapChange,this.mediaUrlMap.set(h,u.url),this.onVolumeAddedFromUrl&&this.onVolumeAddedFromUrl(u,h),this.addVolume(h),h}async addVolumesFromUrl(u){const h=u.map(async p=>{const w=this.getFileExt(p.name||p.url);if(this.loaders[w]){let L=p.url;const U=this.loaders[w].toExt;let O=p.name||p.url;if(O=O.split("/").pop(),typeof p.url=="string"){const M=p.url;try{const e=await fetch(M);if(!e.ok)throw new Error(`Failed to load file: ${e.statusText}`);L=await e.arrayBuffer()}catch(e){throw new Error(`Failed to load url ${M}: ${e}`)}}const d=await this.loaders[w].loader(L);p.url=d,p.name=`${O}.${U}`}const T={url:p.url,headers:p.headers,name:p.name,colormap:p.colormap?p.colormap:p.colorMap,colormapNegative:p.colormapNegative?p.colormapNegative:p.colorMapNegative,opacity:p.opacity,urlImgData:p.urlImgData,cal_min:p.cal_min,cal_max:p.cal_max,trustCalMinMax:this.opts.trustCalMinMax,isManifest:p.isManifest,frame4D:p.frame4D,limitFrames4D:p.limitFrames4D||this.opts.limitFrames4D,colorbarVisible:p.colorbarVisible},E=await NVImage.loadFromUrl(T);return this.document.addImageOptions(E,T),E.onColormapChange=this.onColormapChange,this.mediaUrlMap.set(E,T.url),this.onVolumeAddedFromUrl&&this.onVolumeAddedFromUrl(T,E),E}),g=await Promise.all(h);for(let p=0;p<g.length;p++)this.addVolume(g[p]);return g}getMediaByUrl(u){return[...this.mediaUrlMap.entries()].filter(h=>h[1]===u).map(h=>h[0]).pop()}removeVolumeByUrl(u){const h=this.getMediaByUrl(u);if(h)this.removeVolume(h);else throw new Error("No volume with URL present")}readDirectory(u){const h=u.createReader();let g=[];const p=async T=>{const E=[],L=async U=>new Promise((O,d)=>U.file(O,d));for(let U=0;U<T.length;U++)E.push(await L(T[U]));return E},w=()=>{h.readEntries(T=>{T.length?(g=g.concat(T),w()):p(g).then(E=>{NVImage.loadFromFile({file:E,name:u.name,urlImgData:null,imageType:NVIMAGE_TYPE.DCM_FOLDER}).then(L=>{this.addVolume(L),this.setDrawingEnabled(!0)}).catch(L=>{throw L})}).catch(E=>{throw E})})};return w(),g}isMeshExt(u){const h=this.getFileExt(u);return log.debug("dropped ext"),log.debug(h),MESH_EXTENSIONS.includes(h)}async loadFromArrayBuffer(u,h){const g=this.getFileExt(h);if(MESH_EXTENSIONS.includes(g)){await this.addMeshFromUrl({url:h,buffer:u});return}const p=NVImageFromUrlOptions(h);p.buffer=u,p.name=h,await this.addVolumeFromUrl(p)}async loadFromFile(u){const h=this.getFileExt(u.name);if(MESH_EXTENSIONS.includes(h)){await NVMesh.loadFromFile({file:u,gl:this.gl,name:u.name}).then(g=>{this.addMesh(g)});return}await NVImage.loadFromFile({file:u,name:u.name}).then(g=>{this.addVolume(g)})}useLoader(u,h,g){this.loaders={...this.loaders,[h.toUpperCase()]:{loader:u,toExt:g}}}dropListener(u){if(u.stopPropagation(),u.preventDefault(),!this.opts.dragAndDropEnabled)return;const h=u.dataTransfer;if(!h)return;const g=h.getData("text/uri-list");if(g){const p=NVImageFromUrlOptions(g),w=this.getFileExt(g);log.debug("dropped ext"),log.debug(w),MESH_EXTENSIONS.includes(w)?this.addMeshFromUrl({url:g}).catch(T=>{throw T}):w==="NVD"?this.loadDocumentFromUrl(g).catch(T=>{throw T}):this.addVolumeFromUrl(p).catch(T=>{throw T})}else{const p=h.items;if(p.length>0){!u.shiftKey&&!u.altKey&&(this.volumes=[],this.overlays=[],this.meshes=[]),this.closeDrawing();for(const w of Array.from(p)){const T=w.webkitGetAsEntry();if(log.debug(T),!T)throw new Error("could not get entry from file");if(T.isFile){const E=this.getFileExt(T.name);if(E==="PNG"){T.file(U=>{this.loadBmpTexture(U).catch(O=>{throw O})});continue}let L;if(T.name.lastIndexOf("HEAD")!==-1)for(const U of Array.from(p)){const O=U.webkitGetAsEntry();if(!O)throw new Error("could not get paired entry");const d=T.name.substring(0,T.name.lastIndexOf("HEAD")),M=O.name.substring(0,O.name.lastIndexOf("BRIK"));d===M&&(L=O)}if(T.name.lastIndexOf("BRIK")!==-1)continue;if(MESH_EXTENSIONS.includes(E)){T.file(U=>{NVMesh.loadFromFile({file:U,gl:this.gl,name:U.name}).then(O=>{this.addMesh(O)}).catch(O=>{throw O})});continue}else if(E==="NVD"){T.file(U=>{NVDocument.loadFromFile(U).then(O=>{this.loadDocument(O),log.debug("loaded document")}).catch(O=>{throw O})});break}T.file(U=>{L?L.file(O=>{NVImage.loadFromFile({file:U,urlImgData:O,limitFrames4D:this.opts.limitFrames4D}).then(d=>{this.addVolume(d)}).catch(d=>{throw d})}):NVImage.loadFromFile({file:U,urlImgData:L,limitFrames4D:this.opts.limitFrames4D}).then(O=>{u.altKey?(log.debug("alt key detected: assuming this is a drawing overlay"),this.drawClearAllUndoBitmaps(),this.loadDrawing(O)):this.addVolume(O)}).catch(O=>{throw O})})}else T.isDirectory&&this.readDirectory(T)}}}this.drawScene()}setMultiplanarPadPixels(u){this.opts.multiplanarPadPixels=u,this.drawScene()}setMultiplanarLayout(u){typeof u=="string"&&(u=parseInt(u)),this.opts.multiplanarLayout=u,this.drawScene()}setCornerOrientationText(u){this.opts.isCornerOrientationText=u,this.updateGLVolume()}setRadiologicalConvention(u){this.opts.isRadiologicalConvention=u,this.updateGLVolume()}setDefaults(u={},h=!1){this.document.opts={...DEFAULT_OPTIONS},this.scene.sceneData={...INITIAL_SCENE_DATA};for(const g in u)typeof u[g]=="function"?this[g]=u[g]:this.opts[g]=DEFAULT_OPTIONS[g]===void 0?DEFAULT_OPTIONS[g]:u[g];if(this.scene.pan2Dxyzmm=[0,0,0,1],h&&this.volumes&&this.volumes.length>0)for(let g=0;g<this.volumes.length;g++)this.volumes[g].cal_min=this.volumes[g].robust_min,this.volumes[g].cal_max=this.volumes[g].robust_max;this.updateGLVolume()}setMeshThicknessOn2D(u){this.opts.meshThicknessOn2D=u,this.updateGLVolume()}setSliceMosaicString(u){this.sliceMosaicString=u,this.updateGLVolume()}setSliceMM(u){this.opts.isSliceMM=u,this.updateGLVolume()}setAdditiveBlend(u){this.opts.isAdditiveBlend=u,this.updateGLVolume()}getRadiologicalConvention(){return this.opts.isRadiologicalConvention}setHighResolutionCapable(u){this.opts.isHighResolutionCapable=u,u&&!this.opts.isResizeCanvas&&(log.warn("isHighResolutionCapable requires isResizeCanvas"),this.opts.isHighResolutionCapable=!1),this.opts.isHighResolutionCapable||(this.uiData.dpr=1),this.resizeListener(),this.drawScene()}addVolume(u){this.volumes.push(u);const h=this.volumes.length===1?0:this.volumes.length-1;this.setVolume(u,h),this.onImageLoaded(u),log.debug("loaded volume",u.name),log.debug(u)}addMesh(u){this.meshes.push(u);const h=this.meshes.length===1?0:this.meshes.length-1;this.setMesh(u,h),this.onMeshLoaded(u)}getVolumeIndexByID(u){const h=this.volumes.length;for(let g=0;g<h;g++)if(this.volumes[g].id===u)return g;return-1}drawAddUndoBitmap(){if(!this.drawBitmap||this.drawBitmap.length<1){log.debug("drawAddUndoBitmap error: No drawing open");return}this.currentDrawUndoBitmap++,this.currentDrawUndoBitmap>=this.opts.maxDrawUndoBitmaps&&(this.currentDrawUndoBitmap=0),this.drawUndoBitmaps[this.currentDrawUndoBitmap]=encodeRLE(this.drawBitmap)}drawClearAllUndoBitmaps(){if(this.currentDrawUndoBitmap=this.opts.maxDrawUndoBitmaps,!(!this.drawUndoBitmaps||this.drawUndoBitmaps.length<1))for(let u=this.drawUndoBitmaps.length-1;u>=0;u--)this.drawUndoBitmaps[u]=new Uint8Array}drawUndo(){if(this.drawUndoBitmaps.length<1){log.debug("undo bitmaps not loaded");return}if(this.currentDrawUndoBitmap--,this.currentDrawUndoBitmap<0&&(this.currentDrawUndoBitmap=this.drawUndoBitmaps.length-1),this.currentDrawUndoBitmap>=this.drawUndoBitmaps.length&&(this.currentDrawUndoBitmap=0),this.drawUndoBitmaps[this.currentDrawUndoBitmap].length<2){log.debug("drawUndo is misbehaving");return}this.drawBitmap=decodeRLE(this.drawUndoBitmaps[this.currentDrawUndoBitmap],this.drawBitmap.length),this.refreshDrawing(!0)}loadDrawing(u){if(this.drawBitmap&&log.debug("Overwriting open drawing!"),!this.back)throw new Error("back undefined");this.drawClearAllUndoBitmaps();const h=u.hdr.dims;if(h[1]!==this.back.hdr.dims[1]||h[2]!==this.back.hdr.dims[2]||h[3]!==this.back.hdr.dims[3])return log.debug("drawing dimensions do not match background image"),!1;u.img.constructor!==Uint8Array&&log.debug("Drawings should be UINT8");const g=u.permRAS,p=h[1]*h[2]*h[3];this.drawBitmap=new Uint8Array(p),this.drawTexture=this.r8Tex(this.drawTexture,TEXTURE7_DRAW,this.back.dims,!0);const w=[0,0,0];for(let z=0;z<3;z++)for(let W=0;W<3;W++)Math.abs(g[z])-1===W&&(w[W]=z*Math.sign(g[z]));let T=1;const E=[1,1,1],L=[!1,!1,!1];for(let z=0;z<w.length;z++)for(let W=0;W<w.length;W++)Math.abs(w[W])===z&&(E[W]=T,(w[W]<0||Object.is(w[W],-0))&&(L[W]=!0),T*=h[W+1]);let U=NVUtilities.range(0,h[1]-1,1);L[0]&&(U=NVUtilities.range(h[1]-1,0,-1));for(let z=0;z<h[1];z++)U[z]*=E[0];let O=NVUtilities.range(0,h[2]-1,1);L[1]&&(O=NVUtilities.range(h[2]-1,0,-1));for(let z=0;z<h[2];z++)O[z]*=E[1];let d=NVUtilities.range(0,h[3]-1,1);L[2]&&(d=NVUtilities.range(h[3]-1,0,-1));for(let z=0;z<h[3];z++)d[z]*=E[2];const M=u.img,e=this.drawBitmap;let b=0;for(let z=0;z<h[3];z++)for(let W=0;W<h[2];W++)for(let o0=0;o0<h[1];o0++)e[U[o0]+O[W]+d[z]]=M[b],b++;return this.drawAddUndoBitmap(),this.refreshDrawing(!1),this.drawScene(),!0}binarize(u){const h=u.hdr.dims,g=h[1]*h[2]*h[3],p=new Uint8Array(g);for(let w=0;w<g;w++)u.img[w]!==0&&(p[w]=1);u.img=p,u.hdr.datatypeCode=NiiDataType.DT_UINT8,u.hdr.cal_min=0,u.hdr.cal_max=1}async loadDrawingFromUrl(u,h=!1){this.drawBitmap&&log.debug("Overwriting open drawing!"),this.drawClearAllUndoBitmaps();let g=!1;try{const p=await NVImage.loadFromUrl(NVImageFromUrlOptions(u));h&&await this.binarize(p),g=this.loadDrawing(p)}catch{log.error("loadDrawingFromUrl() failed to load "+u),this.drawClearAllUndoBitmaps()}return g}findOtsu(u=2){if(this.volumes.length<1)return[];const h=this.volumes[0].img,g=h.length;if(g<1)return[];const p=256,w=p-1,T=new Array(p).fill(0),E=this.volumes[0].cal_min,L=this.volumes[0].cal_max;if(L<=E)return[];const U=(L-E)/p;function O(J){return J*U+E}const d=(p-1)/Math.abs(L-E),M=this.volumes[0].hdr.scl_inter,e=this.volumes[0].hdr.scl_slope;for(let J=0;J<g;J++){let K=h[J]*e+M;K=Math.min(Math.max(K,E),L),K=Math.round((K-E)*d),T[K]++}const b=Array(p).fill(0).map(()=>Array(p).fill(0)),z=Array(p).fill(0).map(()=>Array(p).fill(0));for(let J=1;J<p;++J)b[J][J]=T[J],z[J][J]=J*T[J];for(let J=1;J<p-1;++J)b[1][J+1]=b[1][J]+T[J+1],z[1][J+1]=z[1][J]+(J+1)*T[J+1];for(let J=2;J<p;J++)for(let K=J+1;K<p;K++)b[J][K]=b[1][K]-b[1][J-1],z[J][K]=z[1][K]-z[1][J-1];for(let J=1;J<p;++J)for(let K=J+1;K<p;K++)b[J][K]!==0&&(b[J][K]=z[J][K]*z[J][K]/b[J][K]);let W=0;const o0=[1/0,1/0,1/0];if(u>3)for(let J=0;J<p-3;J++)for(let K=J+1;K<p-2;K++)for(let y=K+1;y<p-1;y++){const Z=b[0][J]+b[J+1][K]+b[K+1][y]+b[y+1][w];Z>W&&(o0[0]=J,o0[1]=K,o0[2]=y,W=Z)}else if(u===3)for(let J=0;J<p-2;J++)for(let K=J+1;K<p-1;K++){const y=b[0][J]+b[J+1][K]+b[K+1][w];y>W&&(o0[0]=J,o0[1]=K,W=y)}else for(let J=0;J<p-1;J++){const K=b[0][J]+b[J+1][w];K>W&&(o0[0]=J,W=K)}return[O(o0[0]),O(o0[1]),O(o0[2])]}drawOtsu(u=2){if(this.volumes.length===0)return;const h=this.volumes[0].img.length,g=this.findOtsu(u);if(g.length<3)return;this.drawBitmap||this.createEmptyDrawing();const p=this.drawBitmap,w=this.volumes[0].img;for(let T=0;T<h;T++){if(p[T]!==0)continue;const E=w[T];E>g[0]&&(p[T]=1),E>g[1]&&(p[T]=2),E>g[2]&&(p[T]=3)}this.drawAddUndoBitmap(),this.refreshDrawing(!0)}removeHaze(u=5,h=0){const g=this.volumes[h].img,p=this.volumes[h].hdr,w=g.length;let T=2;(u===5||u===1)&&(T=4),(u===4||u===2)&&(T=3);const E=this.findOtsu(T);if(E.length<3)return;let L=E[0];u===1&&(L=E[2]),u===2&&(L=E[1]);const U=p.scl_inter,O=p.scl_slope,d=this.volumes[h].global_min;for(let M=0;M<w;M++)g[M]*O+U<L&&(g[M]=d);this.refreshLayers(this.volumes[h],0),this.drawScene()}saveImage(u=defaultSaveImageOptions){var E;const h={...defaultSaveImageOptions,...u},{filename:g,isSaveDrawing:p,volumeByIndex:w}=h;if(log.debug("saveImage",g,p,w),((E=this.back)==null?void 0:E.dims)===void 0)return log.debug("No voxelwise image open"),!1;if(p){if(!this.drawBitmap)return log.debug("No drawing open"),!1;const L=this.volumes[0].permRAS;if(L[0]===1&&L[1]===2&&L[2]===3)return log.debug("saving drawing"),this.volumes[0].saveToDisk(g,this.drawBitmap);{log.debug("saving drawing");const U=this.volumes[0].hdr.dims,O=[0,0,0];for(let Z=0;Z<3;Z++)for(let X=0;X<3;X++)Math.abs(L[Z])-1===X&&(O[X]=Z*Math.sign(L[Z]));let d=1;const M=[1,1,1],e=[!1,!1,!1];for(let Z=0;Z<O.length;Z++)for(let X=0;X<O.length;X++)Math.abs(O[X])===Z&&(M[X]=d,(O[X]<0||Object.is(O[X],-0))&&(e[X]=!0),d*=U[X+1]);let b=NVUtilities.range(0,U[1]-1,1);e[0]&&(b=NVUtilities.range(U[1]-1,0,-1));for(let Z=0;Z<U[1];Z++)b[Z]*=M[0];let z=NVUtilities.range(0,U[2]-1,1);e[1]&&(z=NVUtilities.range(U[2]-1,0,-1));for(let Z=0;Z<U[2];Z++)z[Z]*=M[1];let W=NVUtilities.range(0,U[3]-1,1);e[2]&&(W=NVUtilities.range(U[3]-1,0,-1));for(let Z=0;Z<U[3];Z++)W[Z]*=M[2];const o0=new Uint8Array(this.drawBitmap),J=new Uint8Array(U[1]*U[2]*U[3]);let K=0;for(let Z=0;Z<U[3];Z++)for(let X=0;X<U[2];X++)for(let e0=0;e0<U[1];e0++)J[K]=o0[b[e0]+z[X]+W[Z]],K++;return log.debug("saving drawing"),this.volumes[0].saveToDisk(g,J)}}return log.debug("saving image"),this.volumes[w].saveToDisk(g)}getMeshIndexByID(u){if(typeof u=="number")return u>=this.meshes.length?-1:u;const h=this.meshes.length;for(let g=0;g<h;g++)if(this.meshes[g].id===u)return g;return-1}setMeshProperty(u,h,g){const p=this.getMeshIndexByID(u);if(p<0){log.warn("setMeshProperty() id not loaded",u);return}this.meshes[p].setProperty(h,g,this.gl),this.updateGLVolume(),this.onMeshPropertyChanged(p,h,g)}indexNearestXYZmm(u,h,g,p){const w=this.getMeshIndexByID(u);return w<0?(log.warn("indexNearestXYZmm() id not loaded",u),[NaN,NaN]):this.meshes[w].indexNearestXYZmm(h,g,p)}decimateHierarchicalMesh(u,h=3){const g=this.getMeshIndexByID(u);if(g<0){log.warn("reverseFaces() id not loaded",u);return}const p=this.meshes[g].decimateHierarchicalMesh(this.gl,h);return this.updateGLVolume(),p}reverseFaces(u){const h=this.getMeshIndexByID(u);if(h<0){log.warn("reverseFaces() id not loaded",u);return}this.meshes[h].reverseFaces(this.gl),this.updateGLVolume()}setMeshLayerProperty(u,h,g,p){const w=this.getMeshIndexByID(u);if(w<0){log.warn("setMeshLayerProperty() id not loaded",u);return}this.meshes[w].setLayerProperty(h,g,p,this.gl),this.updateGLVolume()}setPan2Dxyzmm(u){this.scene.pan2Dxyzmm=u,this.opts.yoke3Dto2DZoom&&(this.scene.volScaleMultiplier=u[3]),this.drawScene()}setRenderAzimuthElevation(u,h){this.scene.renderAzimuth=u,this.scene.renderElevation=h,this.onAzimuthElevationChange(u,h),this.drawScene()}getOverlayIndexByID(u){const h=this.overlays.length;for(let g=0;g<h;g++)if(this.overlays[g].id===u)return g;return-1}setVolume(u,h=0){const g=this.volumes.length;if(h>g)return;const p=this.getVolumeIndexByID(u.id);h===0?(this.volumes.splice(p,1),this.volumes.unshift(u),this.back=this.volumes[0],this.overlays=this.volumes.slice(1)):h<0?(this.volumes.splice(this.getVolumeIndexByID(u.id),1),this.back=this.volumes[0],this.volumes.length>1?this.overlays=this.volumes.slice(1):this.overlays=[]):(this.volumes.splice(p,1),this.volumes.splice(h,0,u),this.overlays=this.volumes.slice(1),this.back=this.volumes[0]),this.updateGLVolume()}setMesh(u,h=0){this.meshes.forEach(w=>{log.debug("MESH: ",w.name)});const g=this.meshes.length;if(h>g)return;const p=this.getMeshIndexByID(u.id);h===0?(this.meshes.splice(p,1),this.meshes.unshift(u)):h<0?this.meshes.splice(this.getMeshIndexByID(u.id),1):(this.meshes.splice(p,1),this.meshes.splice(h,0,u)),this.updateGLVolume(),this.meshes.forEach(w=>{log.debug(w.name)})}removeVolume(u){if(this.setVolume(u,-1),this.mediaUrlMap.has(u)){const h=this.mediaUrlMap.get(u);this.onVolumeWithUrlRemoved(h),this.mediaUrlMap.delete(u)}this.drawScene()}removeVolumeByIndex(u){if(u>=this.volumes.length)throw new Error("Index of volume out of bounds");this.removeVolume(this.volumes[u])}removeMesh(u){if(u.unloadMesh(this.gl),this.setMesh(u,-1),this.mediaUrlMap.has(u)){const h=this.mediaUrlMap.get(u);this.onMeshWithUrlRemoved(h),this.mediaUrlMap.delete(u)}}removeMeshByUrl(u){const h=this.getMediaByUrl(u);h&&(this.removeMesh(h),this.mediaUrlMap.delete(h),this.onMeshWithUrlRemoved(u))}moveVolumeToBottom(u){this.setVolume(u,0)}moveVolumeUp(u){const h=this.getVolumeIndexByID(u.id);this.setVolume(u,h+1)}moveVolumeDown(u){const h=this.getVolumeIndexByID(u.id);this.setVolume(u,h-1)}moveVolumeToTop(u){this.setVolume(u,this.volumes.length-1)}mouseDown(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr,this.mousePos=[u,h]}mouseMove(u,h){u*=this.uiData.dpr,h*=this.uiData.dpr;const g=(u-this.mousePos[0])/this.uiData.dpr,p=(h-this.mousePos[1])/this.uiData.dpr;this.mousePos=[u,h],!(this.inRenderTile(u,h)<0)&&(Math.abs(g)<1&&Math.abs(p)<1||(this.scene.renderAzimuth+=g,this.scene.renderElevation+=p,this.drawScene()))}sph2cartDeg(u,h){const g=-h*(Math.PI/180),p=(u-90)%360*(Math.PI/180),w=[Math.cos(g)*Math.cos(p),Math.cos(g)*Math.sin(p),Math.sin(g)],T=Math.sqrt(w[0]*w[0]+w[1]*w[1]+w[2]*w[2]);return T<=0||(w[0]/=T,w[1]/=T,w[2]/=T),w}setClipPlane(u){const h=this.sph2cartDeg(u[1]+180,u[2]);this.scene.clipPlane=[h[0],h[1],h[2],u[0]],this.scene.clipPlaneDepthAziElev=u,this.onClipPlaneChange(this.scene.clipPlane),this.drawScene()}setCrosshairColor(u){this.opts.crosshairColor=u,this.drawScene()}setCrosshairWidth(u){this.opts.crosshairWidth=u,this.crosshairs3D&&(this.crosshairs3D.mm[0]=NaN),this.drawScene()}setDrawColormap(u){this.drawLut=cmapper.makeDrawLut(u),this.updateGLVolume()}setDrawingEnabled(u){this.opts.drawingEnabled=u,this.opts.drawingEnabled&&(this.drawBitmap||this.createEmptyDrawing()),this.drawScene()}setPenValue(u,h=!1){this.opts.penValue=u,this.opts.isFilledPen=h,this.drawScene()}setDrawOpacity(u){this.drawOpacity=u,this.drawScene()}setSelectionBoxColor(u){this.opts.selectionBoxColor=u}sliceScroll2D(u,h,g,p=!0){if(this.inGraphTile(h,g)){let w=this.volumes[0].frame4D;u>0&&w++,u<0&&w--,this.setFrame4D(this.volumes[0].id,w);return}if(u!==0&&this.opts.dragMode===DRAG_MODE.pan&&this.inRenderTile(this.uiData.dpr*h,this.uiData.dpr*g)===-1){let w=this.scene.pan2Dxyzmm[3]*(1+10*u);w=Math.round(w*10)/10;const T=this.scene.pan2Dxyzmm[3]-w;this.opts.yoke3Dto2DZoom&&(this.scene.volScaleMultiplier=w),this.scene.pan2Dxyzmm[3]=w;const E=this.frac2mm(this.scene.crosshairPos);this.scene.pan2Dxyzmm[0]+=T*E[0],this.scene.pan2Dxyzmm[1]+=T*E[1],this.scene.pan2Dxyzmm[2]+=T*E[2],this.drawScene(),this.canvas.focus(),this.sync();return}this.mouseClick(h,g,u,p)}setSliceType(u){return this.opts.sliceType=u,this.drawScene(),this}setOpacity(u,h){if(this.volumes[u].opacity=h,u===0){this.drawScene();return}this.updateGLVolume()}setScale(u){this.scene.volScaleMultiplier=u,this.drawScene()}setClipPlaneColor(u){this.opts.clipPlaneColor=u,this.renderShader.use(this.gl),this.gl.uniform4fv(this.renderShader.uniforms.clipPlaneColor,this.opts.clipPlaneColor),this.drawScene()}setClipPlaneThick(u){this.opts.clipThick=u,this.renderShader.use(this.gl),this.gl.uniform1f(this.renderShader.uniforms.clipThick,this.opts.clipThick),this.drawScene()}setClipVolume(u,h){this.opts.clipVolumeLow=[Math.min(u[0],h[0]),Math.min(u[1],h[1]),Math.min(u[2],h[2])],this.opts.clipVolumeHigh=[Math.max(u[0],h[0]),Math.max(u[1],h[1]),Math.max(u[2],h[2])],this.renderShader.use(this.gl),this.gl.uniform3fv(this.renderShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.renderShader.uniforms.clipHi,this.opts.clipVolumeHigh),this.pickingImageShader.use(this.gl),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi,this.opts.clipVolumeHigh),this.drawScene()}async setVolumeRenderIllumination(u=0){this.renderShader=this.renderVolumeShader,u>0&&(this.renderShader=this.renderGradientShader),u<0&&(this.renderShader=this.renderSliceShader),this.initRenderShader(this.renderShader,u),this.renderShader.use(this.gl),this.setClipPlaneColor(this.opts.clipPlaneColor),this.gradientTextureAmount=u,this.refreshLayers(this.volumes[0],0),this.drawScene()}overlayRGBA(u){const h=u.hdr,g=h.dims[1]*h.dims[2]*h.dims[3],p=new Uint8ClampedArray(g*4),w=.2*Math.min(Math.min(h.dims[1],h.dims[2]),h.dims[3]),T=.5*h.dims[1],E=.5*h.dims[2],L=.5*h.dims[3];let U=0;for(let O=0;O<h.dims[3];O++)for(let d=0;d<h.dims[2];d++)for(let M=0;M<h.dims[1];M++){const e=Math.abs(M-T),b=Math.abs(d-E),z=Math.abs(O-L),W=Math.sqrt(e*e+b*b+z*z);let o0=0;W<w&&(o0=255),p[U++]=0,p[U++]=o0,p[U++]=0,p[U++]=o0*.5}return p}vox2mm(u,h){return NVUtilities.vox2mm(u,h)}cloneVolume(u){return this.volumes[u].clone()}async loadDocumentFromUrl(u){const h=await NVDocument.loadFromUrl(u);this.loadDocument(h)}loadDocument(u){this.volumes=[],this.meshes=[],this.document=u,this.document.labels=this.document.labels?this.document.labels:[];const h={...DEFAULT_OPTIONS,...u.opts};this.scene.pan2Dxyzmm=u.scene.pan2Dxyzmm?u.scene.pan2Dxyzmm:[0,0,0,1],this.document.opts=h,this.setClipPlane(this.scene.clipPlaneDepthAziElev),log.debug("load document",u),this.mediaUrlMap.clear(),this.createEmptyDrawing();const g=u.encodedImageBlobs;for(let w=0;w<u.imageOptionsArray.length;w++){const T=u.imageOptionsArray[w],E=g[w];if(E){"colorMap"in T&&(T.colormap=T.colorMap);const L=NVImage.loadFromBase64({base64:E,...T});if(L){if(L.colormapLabel){const U=Object.keys(L.colormapLabel.lut).length,O=new Uint8ClampedArray(U);for(const d in L.colormapLabel.lut)O[d]=L.colormapLabel.lut[d];L.colormapLabel.lut=O}this.addVolume(L)}}}this.volumes.length>0&&(this.back=this.volumes[0]);const p=u.encodedDrawingBlob;if(p){const w=u.imageOptionsArray[0],T=NVImage.loadFromBase64({base64:p,...w});T&&this.loadDrawing(T)}for(const w of u.meshDataObjects??[]){const T={gl:this.gl,...w};w.offsetPt0&&(T.rgba255[3]=0,T.tris=new Uint32Array(w.offsetPt0)),log.debug(T);const E=new NVMesh(T.pts,T.tris,T.name,T.rgba255,T.opacity,T.visible,this.gl,T.connectome,T.dpg,T.dps,T.dpv);w.offsetPt0&&(E.fiberGroupColormap=w.fiberGroupColormap,E.fiberColor=w.fiberColor,E.fiberDither=w.fiberDither,E.fiberRadius=w.fiberRadius,E.colormap=w.colormap),E.meshShaderIndex=T.meshShaderIndex,E.layers=T.layers,E.updateMesh(this.gl),log.debug(E),this.addMesh(E)}if(u.data.connectomes)for(const w of u.data.connectomes){const T=JSON.parse(w),E=this.loadConnectomeAsMesh(T);E.updateMesh(this.gl),this.addMesh(E)}return this.updateGLVolume(),this.drawScene(),this.onDocumentLoaded(u),this}async generateLoadDocumentJavaScript(u,h){const g=this.json(),p=await NVUtilities.compressToBase64String(JSON.stringify(g));return`
1740
1740
  ${h}
1741
1741
 
1742
1742
  async function saveNiivueAsHtml(pageName) {
@@ -1826,5 +1826,5 @@ If this abort() is unexpected, build with -s ASSERTIONS=1 which can give more in
1826
1826
  button.onclick = saveAsHtml;
1827
1827
  <\/script>
1828
1828
  </body>
1829
- </html>`}async saveHTML(u="untitled.html",h="gl1",g){const p=await this.generateHTML(h,g);return NVUtilities.download(p,u,"application/html")}json(){return this.document.opts=this.opts,this.document.scene=this.scene,this.document.volumes=this.volumes,this.document.meshes=this.meshes,this.drawScene(),this.document.previewImageDataURL=this.canvas.toDataURL(),this.document.json()}async saveDocument(u="untitled.nvd",h=!0){return this.document.title=u,log.debug("saveDocument",this.volumes[0]),this.drawScene(),this.document.previewImageDataURL=this.canvas.toDataURL(),this.document.volumes=this.volumes,this.document.meshes=this.meshes,this.document.download(u,h)}async loadImages(u){const h=[],g=[];for(const p of u)if("url"in p){const w=this.getFileExt(p.url);if(this.loaders[w]){const T=this.loaders[w].toExt.toUpperCase();MESH_EXTENSIONS.includes(T)?g.push(p):h.push(p);continue}MESH_EXTENSIONS.includes(w.toUpperCase())?g.push(p):h.push(p)}return h.length>0&&await this.loadVolumes(h),g.length>0&&await this.loadMeshes(g),this}async loadVolumes(u){return this.loadingText="loading...",this.drawScene(),this.thumbnailVisible?(this.deferredVolumes=u,this):(this.volumes=[],this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT),await this.addVolumesFromUrl(u),this)}async addMeshFromUrl(u){const h=this.getFileExt(u.url);if(h==="JCON"||h==="JSON"){const w=await(await fetch(u.url,{})).json(),T=this.loadConnectomeAsMesh(w);return this.mediaUrlMap.set(T,u.url),this.onMeshAddedFromUrl(u,T),this.addMesh(T),T}const g=await NVMesh.loadFromUrl({...u,gl:this.gl});return this.mediaUrlMap.set(g,u.url),this.onMeshAddedFromUrl(u,g),this.addMesh(g),g}async addMeshesFromUrl(u){const h=u.map(async p=>{const w=this.getFileExt(p.url);if(this.loaders[w]){let E=p.url;const L=this.loaders[w].toExt;let U=p.name||p.url;if(U=U.split("/").pop(),typeof p.url=="string"){const e=p.url;try{const b=await fetch(e);if(!b.ok)throw new Error(`Failed to load file: ${b.statusText}`);E=await b.arrayBuffer()}catch(b){throw new Error(`Failed to load url ${e}: ${b}`)}}const{positions:O,indices:d}=await this.loaders[w].loader(E);p.name=`${U}.${L}`;const M=NVMeshUtilities.createMZ3(O,d,!1);p.buffer=M}if(w==="JCON"||w==="JSON"){const L=await(await fetch(p.url,{})).json(),U=this.loadConnectomeAsMesh(L);return this.mediaUrlMap.set(U,p.url),this.onMeshAddedFromUrl(p,U),U}const T=await NVMesh.loadFromUrl({...p,gl:this.gl});return this.mediaUrlMap.set(T,p.url),this.onMeshAddedFromUrl(p,T),T}),g=await Promise.all(h);for(let p=0;p<g.length;p++)this.addMesh(g[p]);return g}async loadMeshes(u){return this.loadingText="loading...",this.drawScene(),this.thumbnailVisible?(this.deferredMeshes=u,this):(this.initialized,this.meshes=[],this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT),await this.addMeshesFromUrl(u),this.updateGLVolume(),this.drawScene(),this)}async loadConnectomeFromUrl(u,h={}){const p=await(await fetch(u,{headers:h})).json();return this.loadConnectome(p)}async loadFreeSurferConnectomeFromUrl(u,h={}){const p=await(await fetch(u,{headers:h})).json();return this.loadFreeSurferConnectome(p)}async loadFreeSurferConnectome(u){const h=NVConnectome.convertFreeSurferConnectome(u);return this.loadConnectome(h)}handleNodeAdded(u){const h=u.detail.node,g=[1,1,1,1];this.addLabel(h.name,{textColor:g,bulletScale:1,bulletColor:g,lineWidth:0,lineColor:g,lineTerminator:LabelLineTerminator.NONE,textScale:1},[h.x,h.y,h.z]),this.drawScene()}loadConnectomeAsMesh(u){let h=u;if("data_type"in u&&u.data_type==="fs_pointset")h=NVConnectome.convertFreeSurferConnectome(u),log.warn("converted FreeSurfer connectome",h);else if("nodes"in u){const g=u.nodes;"names"in g&&"X"in g&&"Y"in g&&"Z"in g&&"Color"in g&&"Size"in g&&(h=NVConnectome.convertLegacyConnectome(u),log.warn("converted legacy connectome",h))}else throw new Error("not a known connectome format");return new NVConnectome(this.gl,h)}loadConnectome(u){this.loadingText="loading...",this.drawScene(),this.meshes=[],this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT);const h=this.loadConnectomeAsMesh(u);return this.addMesh(h),this.drawScene(),this}createEmptyDrawing(){if(this.back===null||!this.back.dims||Math.min(Math.min(this.back.dims[1],this.back.dims[2]),this.back.dims[3])<1)return;const h=this.back.dims[1]*this.back.dims[2]*this.back.dims[3];this.drawBitmap=new Uint8Array(h),this.drawClearAllUndoBitmaps(),this.drawAddUndoBitmap(),this.drawTexture=this.r8Tex(this.drawTexture,TEXTURE7_DRAW,this.back.dims,!0),this.refreshDrawing(!1)}r16Tex(u,h,g,p){u&&this.gl.deleteTexture(u),u=this.gl.createTexture(),this.gl.activeTexture(h),this.gl.bindTexture(this.gl.TEXTURE_3D,u),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R16I,g[1],g[2],g[3]);const w=g[1]*g[2]*g[3];return p.length!==w&&(p=new Int16Array(w)),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g[1],g[2],g[3],this.gl.RED_INTEGER,this.gl.SHORT,p),u}drawGrowCut(){if(!this.back||!this.back.dims)throw new Error("back not defined");const u=this.back.hdr,h=this.gl,g=u.dims[1]*u.dims[2]*u.dims[3];if(!this.drawBitmap||this.drawBitmap.length!==g){log.debug("bitmap dims are wrong");return}const p=h.createFramebuffer();h.bindFramebuffer(h.FRAMEBUFFER,p),h.disable(h.CULL_FACE),h.viewport(0,0,this.back.dims[1],this.back.dims[2]),h.disable(h.BLEND);let w=img2ras16(this.back);const T=this.r16Tex(null,TEXTURE11_GC_BACK,this.back.dims,w);for(let Z=1;Z<g;Z++)w[Z]=this.drawBitmap[Z];const E=this.r16Tex(null,TEXTURE14_GC_LABEL0,this.back.dims,w),L=this.r16Tex(null,TEXTURE15_GC_LABEL1,this.back.dims,w),U=1e4;for(let Z=1;Z<g;Z++)w[Z]>0&&(w[Z]=U);const O=this.r16Tex(null,TEXTURE12_GC_STRENGTH0,this.back.dims,w),d=this.r16Tex(null,TEXTURE13_GC_STRENGTH1,this.back.dims,w);h.bindVertexArray(this.genericVAO);const M=this.growCutShader;M.use(h);const e=128;h.uniform1i(M.uniforms.finalPass,0),h.uniform1i(M.uniforms.backTex,11);for(let Z=0;Z<e;Z++){h.uniform1i(M.uniforms.labelTex,14),h.uniform1i(M.uniforms.strengthTex,12);for(let X=0;X<this.back.dims[3];X++){const e0=1/this.back.dims[3]*(X+.5);h.uniform1f(M.uniforms.coordZ,e0),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,L,0,X),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT1,d,0,X),h.drawBuffers([h.COLOR_ATTACHMENT0,h.COLOR_ATTACHMENT1]),h.checkFramebufferStatus(h.FRAMEBUFFER)!==h.FRAMEBUFFER_COMPLETE&&log.error("Incomplete framebuffer"),h.drawArrays(h.TRIANGLE_STRIP,0,4)}Z===e-1&&h.uniform1i(M.uniforms.finalPass,1),h.uniform1i(M.uniforms.labelTex,15),h.uniform1i(M.uniforms.strengthTex,13);for(let X=0;X<this.back.dims[3];X++){const e0=1/this.back.dims[3]*(X+.5);h.uniform1f(M.uniforms.coordZ,e0),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,E,0,X),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT1,O,0,X),h.drawBuffers([h.COLOR_ATTACHMENT0,h.COLOR_ATTACHMENT1]),h.checkFramebufferStatus(h.FRAMEBUFFER)!==h.FRAMEBUFFER_COMPLETE&&log.error("Incomplete framebuffer"),h.drawArrays(h.TRIANGLE_STRIP,0,4)}}h.drawBuffers([h.COLOR_ATTACHMENT0]);const b=h.COLOR_ATTACHMENT1,z=E;h.readBuffer(b);const W=h.getParameter(h.IMPLEMENTATION_COLOR_READ_FORMAT),o0=h.getParameter(h.IMPLEMENTATION_COLOR_READ_TYPE);(W!==h.RED_INTEGER||o0!==h.SHORT)&&log.debug("readPixels will fail."),w=new Int16Array;const J=this.back.dims[1]*this.back.dims[2],K=new Int16Array(J);for(let Z=0;Z<this.back.dims[3];Z++)h.framebufferTextureLayer(h.FRAMEBUFFER,b,z,0,Z),h.readPixels(0,0,this.back.dims[1],this.back.dims[2],W,o0,K),w=Int16Array.from([...w,...K]);let y=w[0];for(let Z=0;Z<w.length;Z++)y=Math.max(y,w[Z]);for(let Z=1;Z<g;Z++)this.drawBitmap[Z]=w[Z];h.deleteTexture(T),h.deleteTexture(O),h.deleteTexture(d),h.deleteTexture(E),h.deleteTexture(L),h.bindVertexArray(this.unusedVAO),h.viewport(0,0,h.canvas.width,h.canvas.height),h.bindFramebuffer(h.FRAMEBUFFER,null),h.deleteFramebuffer(p),this.drawAddUndoBitmap(),this.refreshDrawing(!0)}drawPt(u,h,g,p){var L;if(!((L=this.back)!=null&&L.dims))throw new Error("back.dims not set");const w=this.back.dims[1],T=this.back.dims[2],E=this.back.dims[3];u=Math.min(Math.max(u,0),w-1),h=Math.min(Math.max(h,0),T-1),g=Math.min(Math.max(g,0),E-1),this.drawBitmap[u+h*w+g*w*T]=p}drawPenLine(u,h,g){const p=Math.abs(u[0]-h[0]),w=Math.abs(u[1]-h[1]),T=Math.abs(u[2]-h[2]);let E=-1,L=-1,U=-1;h[0]>u[0]&&(E=1),h[1]>u[1]&&(L=1),h[2]>u[2]&&(U=1);let O=u[0],d=u[1],M=u[2];const e=h[0],b=h[1],z=h[2];if(p>=w&&p>=T){let W=2*w-p,o0=2*T-p;for(;O!==e;)O+=E,W>=0&&(d+=L,W-=2*p),o0>=0&&(M+=U,o0-=2*p),W+=2*w,o0+=2*T,this.drawPt(O,d,M,g)}else if(w>=p&&w>=T){let W=2*p-w,o0=2*T-w;for(;d!==b;)d+=L,W>=0&&(O+=E,W-=2*w),o0>=0&&(M+=U,o0-=2*w),W+=2*p,o0+=2*T,this.drawPt(O,d,M,g)}else{let W=2*w-T,o0=2*p-T;for(;M!==z;)M+=U,W>=0&&(d+=L,W-=2*T),o0>=0&&(O+=E,o0-=2*T),W+=2*w,o0+=2*p,this.drawPt(O,d,M,g)}}drawFloodFillCore(u,h,g=6){var O;if(!((O=this.back)!=null&&O.dims))throw new Error("back.dims undefined");const p=[this.back.dims[1],this.back.dims[2],this.back.dims[3]],w=p[0],T=w*p[1];function E(d){return d[0]+d[1]*w+d[2]*T}function L(d){const M=Math.floor(d/T),e=Math.floor((d-M*T)/w);return[Math.floor(d%w),e,M]}const U=[];for(U.push(h),u[h]=2;U.length>0;){let d=function(b){const z=e.slice();if(z[0]+=b[0],z[1]+=b[1],z[2]+=b[2],z[0]<0||z[1]<0||z[2]<0||z[0]>=p[0]||z[1]>=p[1]||z[2]>=p[2])return;const W=E(z);u[W]===1&&(u[W]=2,U.push(W))};const M=U[0];U.shift();const e=L(M);d([0,0,-1]),d([0,0,1]),d([0,-1,0]),d([0,1,0]),d([-1,0,0]),d([1,0,0]),!(g<=6)&&(d([-1,-1,0]),d([1,1,0]),d([-1,1,0]),d([1,1,0]),d([0,-1,-1]),d([0,1,-1]),d([-1,0,-1]),d([1,0,-1]),d([0,-1,1]),d([0,1,1]),d([-1,0,1]),d([1,0,1]),!(g<=18)&&(d([-1,-1,-1]),d([1,-1,-1]),d([-1,1,-1]),d([1,1,-1]),d([-1,-1,1]),d([1,-1,1]),d([-1,1,1]),d([1,1,1])))}}drawFloodFill(u,h=0,g=0,p=NaN,w=NaN,T=6){var z;if(!this.drawBitmap)throw new Error("drawBitmap undefined");if(!((z=this.back)!=null&&z.dims))throw new Error("back.dims undefined");h=Math.abs(h);const E=[this.back.dims[1],this.back.dims[2],this.back.dims[3]];if(u[0]<0||u[1]<0||u[2]<0||u[0]>=E[0]||u[1]>=E[1]||u[2]>=E[2])return;const L=E[0],U=L*E[1],O=U*E[2],d=this.drawBitmap.slice();if(d.length!==U*E[2])return;function M(W){return W[0]+W[1]*L+W[2]*U}const e=M(u),b=d[e];if(b===h){g!==0?log.debug("drawFloodFill selected voxel is not part of a drawing"):log.debug("drawFloodFill selected voxel is already desired color");return}for(let W=1;W<O;W++)d[W]=0,this.drawBitmap[W]===b&&(d[W]=1);if(this.drawFloodFillCore(d,e,T),g!==0){const W=this.volumes[0].img2RAS();let o0=W[e],J=o0;if(isFinite(w)&&isFinite(p))o0=w,J=p;else{for(let K=1;K<O;K++)d[K]===2&&(o0=Math.max(o0,W[K]),J=Math.min(J,W[K]));g===Number.POSITIVE_INFINITY&&(o0=g),g===Number.NEGATIVE_INFINITY&&(J=g)}log.debug("Intensity range of selected cluster :",J,o0);for(let K=1;K<O;K++)d[K]=0,W[K]>=J&&W[K]<=o0&&(d[K]=1);this.drawFloodFillCore(d,e,T),h=b}for(let W=1;W<O;W++)d[W]===2&&(this.drawBitmap[W]=h);this.drawAddUndoBitmap(),this.refreshDrawing(!1)}drawPenFilled(){var b;const u=this.drawPenFillPts.length;if(u<2){this.drawPenFillPts=[];return}const h=this.drawPenAxCorSag;let g=0,p=1;if(h===1&&(p=2),h===2&&(g=1,p=2),!((b=this.back)!=null&&b.dims))throw new Error("back.dims undefined");const w=[this.back.dims[g+1],this.back.dims[p+1]],T=new Uint8Array(w[0]*w[1]);let E=1;function L(z,W){const o0=Math.abs(z[0]-W[0]),J=Math.abs(z[1]-W[1]);T[z[0]+z[1]*w[0]]=E,T[W[0]+W[1]*w[0]]=E;let K=-1,y=-1;W[0]>z[0]&&(K=1),W[1]>z[1]&&(y=1);let Z=z[0],X=z[1];const e0=W[0],s0=W[1];if(o0>=J){let j=2*J-o0;for(;Z!==e0;)Z+=K,j>=0&&(X+=y,j-=2*o0),j+=2*J,T[Z+X*w[0]]=E}else{let j=2*o0-J;for(;X!==s0;)X+=y,j>=0&&(Z+=K,j-=2*J),j+=2*o0,T[Z+X*w[0]]=E}}const U=[this.drawPenFillPts[0][g],this.drawPenFillPts[0][p]];let O=U;for(let z=1;z<u;z++){const W=[this.drawPenFillPts[z][g],this.drawPenFillPts[z][p]];L(O,W),O=W}L(U,O);const d=[];function M(z){if(z[0]<0||z[1]<0||z[0]>=w[0]||z[1]>=w[1])return;const W=z[0]+z[1]*w[0];T[W]===0&&(d.push(z),T[W]=2)}for(let z=0;z<w[0];z++)M([z,0]);for(let z=0;z<w[0];z++)M([z,w[1]-1]);for(let z=0;z<w[1];z++)M([0,z]);for(let z=0;z<w[1];z++)M([w[0]-1,z]);for(;d.length>0;){const z=d.shift();M([z[0]-1,z[1]]),M([z[0]+1,z[1]]),M([z[0],z[1]-1]),M([z[0],z[1]+1])}E=this.opts.penValue;const e=this.drawPenFillPts[0][3-(g+p)];if(!this.drawBitmap)throw new Error("drawBitmap undefined");if(h===0){const z=e*w[0]*w[1];for(let W=0;W<w[0]*w[1];W++)T[W]!==2&&(this.drawBitmap[W+z]=E)}else{let z=1;const W=this.back.dims[1]*this.back.dims[2];let o0=e*this.back.dims[1];h===2&&(z=this.back.dims[1],o0=e);let J=0;for(let K=0;K<w[1];K++)for(let y=0;y<w[0];y++)T[J]!==2&&(this.drawBitmap[y*z+K*W+o0]=E),J++}if(!this.drawFillOverwrites&&this.drawUndoBitmaps[this.currentDrawUndoBitmap].length>0){const z=this.drawBitmap.length,W=decodeRLE(this.drawUndoBitmaps[this.currentDrawUndoBitmap],z);for(let o0=0;o0<z;o0++)W[o0]!==0&&(this.drawBitmap[o0]=W[o0])}this.drawPenFillPts=[],this.drawAddUndoBitmap(),this.refreshDrawing(!1)}closeDrawing(){this.drawClearAllUndoBitmaps(),this.rgbaTex(this.drawTexture,TEXTURE7_DRAW,[2,2,2,2],!0),this.drawBitmap=null,this.drawScene()}refreshDrawing(u=!0){var p;if(!((p=this.back)!=null&&p.dims))throw new Error("back.dims undefined");if(!this.drawBitmap)throw new Error("drawBitmap undefined");const h=this.back.dims.slice(),g=this.back.dims[1]*this.back.dims[2]*this.back.dims[3];this.drawBitmap.length===8?(h[1]=2,h[2]=2,h[3]=2):g!==this.drawBitmap.length&&log.warn("Drawing bitmap must match the background image"),this.gl.activeTexture(TEXTURE7_DRAW),this.gl.bindTexture(this.gl.TEXTURE_3D,this.drawTexture),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,h[1],h[2],h[3],this.gl.RED,this.gl.UNSIGNED_BYTE,this.drawBitmap),u&&this.drawScene()}r8Tex(u,h,g,p=!1){if(u&&this.gl.deleteTexture(u),u=this.gl.createTexture(),this.gl.activeTexture(h),this.gl.bindTexture(this.gl.TEXTURE_3D,u),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R8,g[1],g[2],g[3]),p){const w=new Uint8Array(g[1]*g[2]*g[3]);this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g[1],g[2],g[3],this.gl.RED,this.gl.UNSIGNED_BYTE,w)}return u}rgbaTex(u,h,g,p=!1){if(u&&this.gl.deleteTexture(u),u=this.gl.createTexture(),this.gl.activeTexture(h),this.gl.bindTexture(this.gl.TEXTURE_3D,u),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.RGBA8,g[1],g[2],g[3]),p){const w=new Uint8Array(g[1]*g[2]*g[3]*4);this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g[1],g[2],g[3],this.gl.RGBA,this.gl.UNSIGNED_BYTE,w)}return u}requestCORSIfNotSameOrigin(u,h){new URL(h,window.location.href).origin!==window.location.origin&&(u.crossOrigin="")}async loadPngAsTexture(u,h){return new Promise((g,p)=>{const w=new Image;w.onload=()=>{if(!this.bmpShader)return;let T;h===4?(this.bmpTexture!==null&&this.gl.deleteTexture(this.bmpTexture),this.bmpTexture=this.gl.createTexture(),T=this.bmpTexture,this.bmpTextureWH=w.width/w.height,this.gl.activeTexture(TEXTURE4_THUMBNAIL),this.bmpShader.use(this.gl),this.gl.uniform1i(this.bmpShader.uniforms.bmpTexture,4)):h===5?(this.gl.activeTexture(TEXTURE5_MATCAP),this.matCapTexture!==null&&this.gl.deleteTexture(this.matCapTexture),this.matCapTexture=this.gl.createTexture(),T=this.matCapTexture):(this.fontShader.use(this.gl),this.gl.activeTexture(TEXTURE3_FONT),this.gl.uniform1i(this.fontShader.uniforms.fontTexture,3),this.fontTexture!==null&&this.gl.deleteTexture(this.fontTexture),this.fontTexture=this.gl.createTexture(),T=this.fontTexture),this.gl.bindTexture(this.gl.TEXTURE_2D,T),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,w),g(T),h!==4&&this.drawScene()},w.onerror=p,this.requestCORSIfNotSameOrigin(w,u),w.src=u})}async loadFontTexture(u){return this.loadPngAsTexture(u,3)}async loadBmpTexture(u){return this.loadPngAsTexture(u,4)}async loadMatCapTexture(u){return this.loadPngAsTexture(u,5)}initFontMets(){if(!this.fontMetrics)throw new Error("fontMetrics undefined");this.fontMets={distanceRange:this.fontMetrics.atlas.distanceRange,size:this.fontMetrics.atlas.size,mets:{}};for(let g=0;g<256;g++)this.fontMets.mets[g]={xadv:0,uv_lbwh:[0,0,0,0],lbwh:[0,0,0,0]};const u=this.fontMetrics.atlas.width,h=this.fontMetrics.atlas.height;for(let g=0;g<this.fontMetrics.glyphs.length;g++){const p=this.fontMetrics.glyphs[g],w=p.unicode;if(this.fontMets.mets[w].xadv=p.advance,p.planeBounds===void 0)continue;let T=p.atlasBounds.left/u,E=(h-p.atlasBounds.top)/h,L=(p.atlasBounds.right-p.atlasBounds.left)/u,U=(p.atlasBounds.top-p.atlasBounds.bottom)/h;this.fontMets.mets[w].uv_lbwh=[T,E,L,U],T=p.planeBounds.left,E=p.planeBounds.bottom,L=p.planeBounds.right-p.planeBounds.left,U=p.planeBounds.top-p.planeBounds.bottom,this.fontMets.mets[w].lbwh=[T,E,L,U]}}async loadFont(u=defaultFontPNG,h=defaultFontMetrics){await this.loadFontTexture(u);const g=await fetch(h);if(!g.ok)throw Error(g.statusText);const p=await g.text();this.fontMetrics=JSON.parse(p),this.initFontMets(),this.fontShader.use(this.gl),this.drawScene()}async loadDefaultMatCap(){return this.loadMatCapTexture(defaultMatCap)}async loadDefaultFont(){await this.loadFontTexture(this.DEFAULT_FONT_GLYPH_SHEET),this.fontMetrics=this.DEFAULT_FONT_METRICS,this.initFontMets()}async initText(){this.fontShader=new Shader(this.gl,vertFontShader,fragFontShader),this.fontShader.use(this.gl),await this.loadDefaultFont(),await this.loadDefaultMatCap(),this.drawLoadingText(this.loadingText)}meshShaderNameToNumber(u="Phong"){const h=u.toLowerCase();for(let g=0;g<this.meshShaders.length;g++)if(this.meshShaders[g].Name.toLowerCase()===h)return g}setMeshShader(u,h=2){let g=0;if(typeof h=="number"?g=h:g=this.meshShaderNameToNumber(h),g===void 0)throw new Error("shaderIndex undefined");g=Math.min(g,this.meshShaders.length-1),g=Math.max(g,0);const p=this.getMeshIndexByID(u);if(p>=this.meshes.length){log.debug("Unable to change shader until mesh is loaded (maybe you need async)");return}this.meshes[p].meshShaderIndex=g,this.updateGLVolume(),this.onMeshShaderChanged(p,g)}createCustomMeshShader(u,h="Custom"){if(!u)throw new Error("Need fragment shader");const g=this.meshShaderNameToNumber(h);g>=0&&(this.gl.deleteProgram(this.meshShaders[g].shader.program),this.meshShaders.splice(g,1));const p=new Shader(this.gl,vertMeshShader,u);return p.use(this.gl),{Name:h,Frag:u,shader:p}}setCustomMeshShader(u="",h="Custom"){const g=this.createCustomMeshShader(u,h);return this.meshShaders.push(g),this.onCustomMeshShaderAdded(u,h),this.meshShaders.length-1}meshShaderNames(u=!0){const h=[];for(let g=0;g<this.meshShaders.length;g++)h.push(this.meshShaders[g].Name);return u===!0?h.sort():h}initRenderShader(u,h=0){u.use(this.gl),this.gl.uniform1i(u.uniforms.volume,0),this.gl.uniform1i(u.uniforms.colormap,1),this.gl.uniform1i(u.uniforms.overlay,2),this.gl.uniform1i(u.uniforms.drawing,7),this.gl.uniform1fv(u.uniforms.renderDrawAmbientOcclusion,[this.renderDrawAmbientOcclusion,1]),this.gl.uniform1f(u.uniforms.gradientAmount,h)}async init(){const u=this.gl.getExtension("WEBGL_debug_renderer_info");if(u){const T=this.gl.getParameter(u.UNMASKED_VENDOR_WEBGL),E=this.gl.getParameter(u.UNMASKED_RENDERER_WEBGL);log.info("renderer vendor: ",T),log.info("renderer: ",E)}else log.info("debug_renderer_info unavailable");const h=this.gl.getParameter(this.gl.RENDERER);log.info("firefox renderer: ",h),this.gl.clearDepth(0),this.gl.enable(this.gl.CULL_FACE),this.gl.cullFace(this.gl.FRONT),this.gl.enable(this.gl.BLEND),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.volumeTexture=this.rgbaTex(this.volumeTexture,TEXTURE0_BACK_VOL,[2,2,2,2],!0),this.overlayTexture=this.rgbaTex(this.overlayTexture,TEXTURE2_OVERLAY_VOL,[2,2,2,2],!0),this.drawTexture=this.r8Tex(this.drawTexture,TEXTURE7_DRAW,[2,2,2,2],!0);const g=[1,1,0,1,0,0,0,1,0,0,0,0],p=this.gl;this.cuboidVertexBuffer=p.createBuffer(),p.bindBuffer(p.ARRAY_BUFFER,this.cuboidVertexBuffer),p.bufferData(p.ARRAY_BUFFER,new Float32Array(g),p.STATIC_DRAW),this.genericVAO=p.createVertexArray(),p.bindVertexArray(this.genericVAO),p.bindBuffer(p.ARRAY_BUFFER,this.cuboidVertexBuffer),p.enableVertexAttribArray(0),p.vertexAttribPointer(0,3,p.FLOAT,!1,0,0),p.bindVertexArray(this.unusedVAO),this.pickingMeshShader=new Shader(p,vertMeshShader,fragMeshDepthShader),this.pickingMeshShader.use(p),this.pickingImageShader=new Shader(p,vertRenderShader,fragVolumePickingShader),this.pickingImageShader.use(p),p.uniform1i(this.pickingImageShader.uniforms.volume,0),p.uniform1i(this.pickingImageShader.uniforms.colormap,1),p.uniform1i(this.pickingImageShader.uniforms.overlay,2),p.uniform1i(this.pickingImageShader.uniforms.drawing,7),this.sliceMMShader=new Shader(p,vertSliceMMShader,fragSliceMMShader),this.sliceMMShader.use(p),p.uniform1i(this.sliceMMShader.uniforms.volume,0),p.uniform1i(this.sliceMMShader.uniforms.colormap,1),p.uniform1i(this.sliceMMShader.uniforms.overlay,2),p.uniform1i(this.sliceMMShader.uniforms.drawing,7),p.uniform1f(this.sliceMMShader.uniforms.drawOpacity,this.drawOpacity),this.sliceV1Shader=new Shader(p,vertSliceMMShader,fragSliceV1Shader),this.sliceV1Shader.use(p),p.uniform1i(this.sliceV1Shader.uniforms.volume,0),p.uniform1i(this.sliceV1Shader.uniforms.colormap,1),p.uniform1i(this.sliceV1Shader.uniforms.overlay,2),p.uniform1i(this.sliceV1Shader.uniforms.drawing,7),p.uniform1f(this.sliceV1Shader.uniforms.drawOpacity,this.drawOpacity),this.orientCubeShader=new Shader(p,vertOrientCubeShader,fragOrientCubeShader),this.orientCubeShaderVAO=p.createVertexArray(),p.bindVertexArray(this.orientCubeShaderVAO);const w=p.createBuffer();p.enableVertexAttribArray(0),p.enableVertexAttribArray(1),p.bindBuffer(p.ARRAY_BUFFER,w),p.bufferData(p.ARRAY_BUFFER,orientCube,p.STATIC_DRAW),p.vertexAttribPointer(0,3,p.FLOAT,!1,24,0),p.enableVertexAttribArray(1),p.vertexAttribPointer(1,3,p.FLOAT,!1,24,12),p.bindVertexArray(this.unusedVAO),this.rectShader=new Shader(p,vertRectShader,fragRectShader),this.rectShader.use(p),this.lineShader=new Shader(p,vertLineShader,fragRectShader),this.lineShader.use(p),this.line3DShader=new Shader(p,vertLine3DShader,fragRectShader),this.line3DShader.use(p),this.circleShader=new Shader(p,vertCircleShader,fragCircleShader),this.circleShader.use(p),this.renderVolumeShader=new Shader(p,vertRenderShader,fragRenderShader),this.initRenderShader(this.renderVolumeShader),this.renderSliceShader=new Shader(p,vertRenderShader,fragRenderSliceShader),this.initRenderShader(this.renderSliceShader),this.renderGradientShader=new Shader(p,vertRenderShader,fragRenderGradientShader),this.initRenderShader(this.renderGradientShader,.3),p.uniform1i(this.renderGradientShader.uniforms.matCap,5),p.uniform1i(this.renderGradientShader.uniforms.gradient,6),this.renderShader=this.renderVolumeShader,this.colorbarShader=new Shader(p,vertColorbarShader,fragColorbarShader),this.colorbarShader.use(p),p.uniform1i(this.colorbarShader.uniforms.colormap,1),this.blurShader=new Shader(p,blurVertShader,blurFragShader),this.sobelShader=new Shader(p,blurVertShader,sobelFragShader),this.growCutShader=new Shader(p,vertGrowCutShader,fragGrowCutShader),this.passThroughShader=new Shader(p,vertPassThroughShader,fragPassThroughShader),this.orientShaderAtlasU=new Shader(p,vertOrientShader,fragOrientShaderU.concat(fragOrientShaderAtlas)),this.orientShaderAtlasI=new Shader(p,vertOrientShader,fragOrientShaderI.concat(fragOrientShaderAtlas)),this.orientShaderU=new Shader(p,vertOrientShader,fragOrientShaderU.concat(fragOrientShader)),this.orientShaderI=new Shader(p,vertOrientShader,fragOrientShaderI.concat(fragOrientShader)),this.orientShaderF=new Shader(p,vertOrientShader,fragOrientShaderF.concat(fragOrientShader)),this.orientShaderRGBU=new Shader(p,vertOrientShader,fragOrientShaderU.concat(fragRGBOrientShader)),this.surfaceShader=new Shader(p,vertSurfaceShader,fragSurfaceShader),this.surfaceShader.use(p),this.fiberShader=new Shader(p,vertFiberShader,fragFiberShader),this.pickingImageShader.use(p);for(let T=0;T<this.meshShaders.length;T++){const E=this.meshShaders[T];E.Name==="Flat"?E.shader=new Shader(p,vertFlatMeshShader,fragFlatMeshShader):E.shader=new Shader(p,vertMeshShader,E.Frag),E.shader.use(p),E.shader.isMatcap=E.Name==="Matcap",E.shader.isMatcap&&p.uniform1i(E.shader.uniforms.matCap,5)}return this.bmpShader=new Shader(p,vertBmpShader,fragBmpShader),await this.initText(),this.opts.thumbnail.length>0&&(await this.loadBmpTexture(this.opts.thumbnail),this.thumbnailVisible=!0),this.updateGLVolume(),this.initialized=!0,this.resizeListener(),this.drawScene(),this}gradientGL(u){const h=this.gl,g=[0,0,0,0,1,0,1,0,0,1,1,0],p=h.createVertexArray();h.bindVertexArray(p);const w=h.createBuffer();h.bindBuffer(h.ARRAY_BUFFER,w),h.bufferData(h.ARRAY_BUFFER,new Float32Array(g),h.STATIC_DRAW),h.enableVertexAttribArray(0),h.vertexAttribPointer(0,3,h.FLOAT,!1,0,0);const T=h.createFramebuffer();h.bindFramebuffer(h.FRAMEBUFFER,T),h.disable(h.CULL_FACE),h.viewport(0,0,u.dims[1],u.dims[2]),h.disable(h.BLEND);const E=this.rgbaTex(null,TEXTURE8_GRADIENT_TEMP,u.dims),L=this.blurShader;L.use(h),h.activeTexture(TEXTURE0_BACK_VOL),h.bindTexture(h.TEXTURE_3D,this.volumeTexture);const U=.7;h.uniform1i(L.uniforms.intensityVol,0),h.uniform1f(L.uniforms.dX,U/u.dims[1]),h.uniform1f(L.uniforms.dY,U/u.dims[2]),h.uniform1f(L.uniforms.dZ,U/u.dims[3]),h.bindVertexArray(p);for(let M=0;M<u.dims[3]-1;M++){const e=1/u.dims[3]*(M+.5);h.uniform1f(L.uniforms.coordZ,e),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,E,0,M),h.clear(h.DEPTH_BUFFER_BIT),h.drawArrays(h.TRIANGLE_STRIP,0,g.length/3)}const O=this.sobelShader;O.use(h),h.activeTexture(TEXTURE8_GRADIENT_TEMP),h.bindTexture(h.TEXTURE_3D,E),h.uniform1i(O.uniforms.intensityVol,8);const d=.7;h.uniform1f(O.uniforms.dX,d/u.dims[1]),h.uniform1f(O.uniforms.dY,d/u.dims[2]),h.uniform1f(O.uniforms.dZ,d/u.dims[3]),h.uniform1f(O.uniforms.coordZ,.5),h.bindVertexArray(p),h.activeTexture(TEXTURE0_BACK_VOL),this.gradientTexture!==null&&h.deleteTexture(this.gradientTexture),this.gradientTexture=this.rgbaTex(this.gradientTexture,TEXTURE6_GRADIENT,u.dims);for(let M=0;M<u.dims[3]-1;M++){const e=1/u.dims[3]*(M+.5);h.uniform1f(O.uniforms.coordZ,e),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,this.gradientTexture,0,M),h.clear(h.DEPTH_BUFFER_BIT),h.drawArrays(h.TRIANGLE_STRIP,0,g.length/3)}h.deleteFramebuffer(T),h.deleteTexture(E),h.deleteBuffer(w),h.bindFramebuffer(h.FRAMEBUFFER,null)}updateGLVolume(){let u=0;const h=this.volumes.length;this.refreshColormaps();for(let g=0;g<h;g++)this.volumes[g].toRAS&&(this.refreshLayers(this.volumes[g],u),u++);if(this.furthestVertexFromOrigin=0,h>0&&(this.furthestVertexFromOrigin=this.volumeObject3D.furthestVertexFromOrigin),this.meshes)for(let g=0;g<this.meshes.length;g++)this.furthestVertexFromOrigin=Math.max(this.furthestVertexFromOrigin,this.meshes[g].furthestVertexFromOrigin);this.onVolumeUpdated&&this.onVolumeUpdated(),this.drawScene()}getDescriptives(u=0,h=[],g=!1){const p=this.volumes[u].hdr;let w=p.scl_slope;isNaN(w)&&(w=1);let T=p.scl_inter;isNaN(T)&&(T=1);const E=this.volumes[u].img,L=E.length,U=new Float32Array(L);for(let e0=0;e0<L;e0++)U[e0]=E[e0]*w+T;const O=new Uint8Array(L);for(let e0=0;e0<L;e0++)O[e0]=1;if(h.length>0)for(let e0=0;e0<h.length;e0++){const s0=this.volumes[h[e0]].img;if(s0.length!==L){log.debug("Mask resolution does not match image. Skipping masking layer "+h[e0]);continue}for(let j=0;j<L;j++)(s0[j]===0||isNaN(s0[j]))&&(O[j]=0)}else if(h.length<1&&g)for(let e0=0;e0<L;e0++)(this.drawBitmap[e0]===0||isNaN(this.drawBitmap[e0]))&&(O[e0]=0);let d=0,M=0,e=0,b=Number.NEGATIVE_INFINITY,z=Number.POSITIVE_INFINITY,W=0,o0=0,J=0;for(let e0=0;e0<L;e0++){if(O[e0]<1)continue;const s0=U[e0];d++;let j=M+(s0-M)/d;e=e+(s0-M)*(s0-j),M=j,s0!==0&&(W++,j=o0+(s0-o0)/W,J=J+(s0-o0)*(s0-j),o0=j,z=Math.min(s0,b),b=Math.max(s0,b))}const K=Math.sqrt(e/(d-1)),y=Math.sqrt(J/(W-1)),Z=z,X=b;return d!==W&&(z=Math.min(0,b),b=Math.max(0,b)),{mean:M,stdev:K,nvox:d,volumeMM3:d*p.pixDims[1]*p.pixDims[2]*p.pixDims[3],volumeML:d*p.pixDims[1]*p.pixDims[2]*p.pixDims[3]*.001,min:z,max:b,meanNot0:o0,stdevNot0:y,nvoxNot0:W,minNot0:Z,maxNot0:X,cal_min:this.volumes[u].cal_min,cal_max:this.volumes[u].cal_max,robust_min:this.volumes[u].robust_min,robust_max:this.volumes[u].robust_max}}refreshLayers(u,h){var Z;if(this.refreshColormaps(),this.volumes.length<1)return;const g=u.hdr;let p=u.img;u.frame4D>0&&u.frame4D<u.nFrame4D&&(p=u.img.slice(u.frame4D*u.nVox3D,(u.frame4D+1)*u.nVox3D));const w=u.opacity;if(h>1&&w===0)return;let T=null;if(!this.back)throw new Error("back undefined");this.gl.bindVertexArray(this.unusedVAO),this.crosshairs3D&&(this.crosshairs3D.mm[0]=NaN);let E=clone$2(u.toRAS);if(h===0){this.volumeObject3D=u.toNiivueObject3D(this.VOLUME_ID,this.gl),invert(E,E),this.back.matRAS=u.matRAS,this.back.dims=u.dimsRAS,this.back.pixDims=u.pixDimsRAS,T=this.rgbaTex(this.volumeTexture,TEXTURE0_BACK_VOL,u.dimsRAS);const{volScale:X,vox:e0}=this.sliceScale(!0);if(this.volScale=X,this.vox=e0,this.volumeObject3D.scale=X,!this.renderShader)throw new Error("renderShader undefined");this.renderShader.use(this.gl),this.gl.uniform3fv(this.renderShader.uniforms.texVox,e0),this.gl.uniform3fv(this.renderShader.uniforms.volScale,X);const s0=this.pickingImageShader;s0.use(this.gl),this.gl.uniform1i(s0.uniforms.volume,0),this.gl.uniform1i(s0.uniforms.colormap,1),this.gl.uniform1i(s0.uniforms.overlay,2),this.gl.uniform3fv(s0.uniforms.volScale,X),log.debug(this.volumeObject3D)}else{((Z=this.back)==null?void 0:Z.dims)===void 0&&log.error("Fatal error: Unable to render overlay: background dimensions not defined!");const X=this.mm2frac(u.mm000,0,!0);let e0=this.mm2frac(u.mm100,0,!0),s0=this.mm2frac(u.mm010,0,!0),j=this.mm2frac(u.mm001,0,!0);e0=subtract$1(e0,e0,X),s0=subtract$1(s0,s0,X),j=subtract$1(j,j,X),E=fromValues$3(e0[0],s0[0],j[0],X[0],e0[1],s0[1],j[1],X[1],e0[2],s0[2],j[2],X[2],0,0,0,1),invert(E,E),h===1?(T=this.rgbaTex(this.overlayTexture,TEXTURE2_OVERLAY_VOL,this.back.dims),this.overlayTexture=T,this.overlayTextureID=T):T=this.overlayTextureID}const L=this.gl.createFramebuffer();this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,L),this.gl.disable(this.gl.CULL_FACE),this.gl.viewport(0,0,this.back.dims[1],this.back.dims[2]),this.gl.disable(this.gl.BLEND);const U=this.gl.createTexture();this.gl.activeTexture(TEXTURE9_ORIENT),this.gl.bindTexture(this.gl.TEXTURE_3D,U),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1);let O=this.orientShaderU;if(!g)throw new Error("hdr undefined");if(!p)throw new Error("img undefined");if(g.datatypeCode===NiiDataType.DT_UINT8)g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(O=this.orientShaderAtlasU),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R8UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED_INTEGER,this.gl.UNSIGNED_BYTE,p);else if(g.datatypeCode===NiiDataType.DT_INT16)O=this.orientShaderI,g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(O=this.orientShaderAtlasI),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R16I,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED_INTEGER,this.gl.SHORT,p);else if(g.datatypeCode===NiiDataType.DT_FLOAT32)this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R32F,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED,this.gl.FLOAT,p),O=this.orientShaderF;else if(g.datatypeCode===NiiDataType.DT_FLOAT64){let X=new Float32Array;X=Float32Array.from(p),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R32F,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED,this.gl.FLOAT,X),O=this.orientShaderF}else g.datatypeCode===NiiDataType.DT_RGB24?(O=this.orientShaderRGBU,O.use(this.gl),this.gl.uniform1i(O.uniforms.hasAlpha,0),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.RGB8UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RGB_INTEGER,this.gl.UNSIGNED_BYTE,p)):g.datatypeCode===NiiDataType.DT_UINT16?(g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(O=this.orientShaderAtlasU),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R16UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED_INTEGER,this.gl.UNSIGNED_SHORT,p)):g.datatypeCode===NiiDataType.DT_RGBA32&&(O=this.orientShaderRGBU,O.use(this.gl),this.gl.uniform1i(O.uniforms.hasAlpha,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.RGBA8UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RGBA_INTEGER,this.gl.UNSIGNED_BYTE,p));u.global_min===void 0&&u.calMinMax();let d=null;if(this.gl.bindVertexArray(this.genericVAO),h>1){if(!this.back.dims)throw new Error("back.dims undefined");d=this.rgbaTex(d,TEXTURE10_BLEND,this.back.dims),this.gl.bindTexture(this.gl.TEXTURE_3D,d);const X=this.passThroughShader;X.use(this.gl),this.gl.uniform1i(X.uniforms.in3D,2);for(let e0=0;e0<this.back.dims[3];e0++){const s0=1/this.back.dims[3]*(e0+.5);this.gl.uniform1f(X.uniforms.coordZ,s0),this.gl.framebufferTextureLayer(this.gl.FRAMEBUFFER,this.gl.COLOR_ATTACHMENT0,d,0,e0),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}}else d=this.rgbaTex(d,TEXTURE10_BLEND,[2,2,2,2]);O.use(this.gl),this.gl.activeTexture(TEXTURE1_COLORMAPS);let M=null;if(u.colormapLabel!==null&&u.colormapLabel.lut.length>7){const X=u.colormapLabel.max-u.colormapLabel.min+1;M=this.createColormapTexture(M,1,X),this.gl.texSubImage2D(this.gl.TEXTURE_2D,0,0,0,X,1,this.gl.RGBA,this.gl.UNSIGNED_BYTE,u.colormapLabel.lut),this.gl.uniform1f(O.uniforms.cal_min,u.colormapLabel.min-.5),this.gl.uniform1f(O.uniforms.cal_max,u.colormapLabel.max+.5),this.gl.bindTexture(this.gl.TEXTURE_2D,M)}else this.gl.bindTexture(this.gl.TEXTURE_2D,this.colormapTexture),this.gl.uniform1f(O.uniforms.cal_min,u.cal_min),this.gl.uniform1f(O.uniforms.cal_max,u.cal_max);this.gl.uniform1i(O.uniforms.isAlphaThreshold,u.alphaThreshold),this.gl.uniform1i(O.uniforms.isAdditiveBlend,this.opts.isAdditiveBlend?1:0);let e=Number.POSITIVE_INFINITY,b=Number.NEGATIVE_INFINITY;if(u.colormapNegative.length>0&&(e=Math.min(-u.cal_min,-u.cal_max),b=Math.max(-u.cal_min,-u.cal_max),isFinite(u.cal_minNeg)&&isFinite(u.cal_maxNeg)&&(e=Math.min(u.cal_minNeg,u.cal_maxNeg),b=Math.max(u.cal_minNeg,u.cal_maxNeg))),!O)throw new Error("orientShader undefined");this.gl.uniform1f(O.uniforms.layer??null,h),this.gl.uniform1f(O.uniforms.cal_minNeg??null,e),this.gl.uniform1f(O.uniforms.cal_maxNeg??null,b),this.gl.bindTexture(this.gl.TEXTURE_3D,U),this.gl.uniform1i(O.uniforms.intensityVol??null,9),this.gl.uniform1i(O.uniforms.blend3D??null,10),this.gl.uniform1i(O.uniforms.colormap??null,1),this.gl.uniform1f(O.uniforms.scl_inter??null,g.scl_inter),this.gl.uniform1f(O.uniforms.scl_slope??null,g.scl_slope),this.gl.uniform1f(O.uniforms.opacity??null,w),this.gl.uniform1i(O.uniforms.modulationVol??null,7);let z=null;if(u.modulationImage!==null&&u.modulationImage>=0&&u.modulationImage<this.volumes.length){log.debug("modulating",this.volumes);const X=this.volumes[u.modulationImage].hdr;if(X.dims[1]===g.dims[1]&&X.dims[2]===g.dims[2]&&X.dims[3]===g.dims[3]){u.modulateAlpha?(this.gl.uniform1i(O.uniforms.modulation,2),this.gl.uniform1f(O.uniforms.opacity,1)):this.gl.uniform1i(O.uniforms.modulation,1),z=this.r8Tex(z,this.gl.TEXTURE7,g.dims,!0),this.gl.activeTexture(this.gl.TEXTURE7),this.gl.bindTexture(this.gl.TEXTURE_3D,z);const e0=g.dims[1]*g.dims[2]*g.dims[3],s0=new Uint8Array(e0),j=this.volumes[u.modulationImage].cal_min,f=1/(this.volumes[u.modulationImage].cal_max-j),g0=this.volumes[u.modulationImage].img.buffer;let c0=new Uint8Array(g0);switch(X.datatypeCode){case NiiDataType.DT_INT16:c0=new Int16Array(g0);break;case NiiDataType.DT_FLOAT32:c0=new Float32Array(g0);break;case NiiDataType.DT_FLOAT64:c0=new Float64Array(g0);break;case NiiDataType.DT_RGB24:c0=new Uint8Array(g0);break;case NiiDataType.DT_UINT16:c0=new Uint16Array(g0);break}log.debug(this.volumes[u.modulationImage]);const A0=this.volumes[u.modulationImage].colormapNegative.length>0;let v0=this.volumes[u.modulationImage].cal_min,I0=this.volumes[u.modulationImage].cal_max;isFinite(this.volumes[u.modulationImage].cal_minNeg)&&isFinite(this.volumes[u.modulationImage].cal_maxNeg)&&(v0=this.volumes[u.modulationImage].cal_minNeg,I0=this.volumes[u.modulationImage].cal_minNeg),v0=Math.abs(v0),I0=Math.abs(I0),v0>I0&&([v0,I0]=[I0,v0]);const T0=1/(I0-v0);let E0=Math.abs(u.modulateAlpha);E0=Math.max(E0,1);const M0=this.volumes[u.modulationImage].frame4D*e0;for(let U0=0;U0<e0;U0++){const oe=c0[U0+M0]*X.scl_slope+X.scl_inter;let ae=(oe-j)*f;A0&&oe<0&&(ae=(Math.abs(oe)-v0)*T0),ae=Math.min(Math.max(ae,0),1),ae=Math.pow(ae,E0)*255,s0[U0]=ae}this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED,this.gl.UNSIGNED_BYTE,s0)}else log.debug("Modulation image dimensions do not match target")}else this.gl.uniform1i(O.uniforms.modulation,0);if(this.gl.uniformMatrix4fv(O.uniforms.mtx,!1,E),!this.back.dims)throw new Error("back.dims undefined");let W=0;g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(W=this.opts.atlasOutline),this.gl.uniform4fv(O.uniforms.xyzaFrac,[1/this.back.dims[1],1/this.back.dims[2],1/this.back.dims[3],W]),log.debug("back dims: ",this.back.dims);for(let X=0;X<this.back.dims[3];X++){const e0=1/this.back.dims[3]*(X+.5);this.gl.uniform1f(O.uniforms.coordZ,e0),this.gl.framebufferTextureLayer(this.gl.FRAMEBUFFER,this.gl.COLOR_ATTACHMENT0,T,0,X),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}if(this.gl.bindVertexArray(this.unusedVAO),this.gl.deleteTexture(U),this.gl.deleteTexture(z),this.gl.deleteTexture(d),this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height),this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,null),this.gl.deleteFramebuffer(L),h===0&&(this.volumeTexture=T,this.gradientTextureAmount>0?this.gradientGL(g):(this.gradientTexture!==null&&this.gl.deleteTexture(this.gradientTexture),this.gradientTexture=null)),!this.renderShader)throw new Error("renderShader undefined");this.renderShader.use(this.gl);const o0=this.sliceScale(!0),J=o0.vox,K=o0.volScale;if(this.gl.uniform1f(this.renderShader.uniforms.overlays,this.overlays),this.gl.uniform4fv(this.renderShader.uniforms.clipPlaneColor,this.opts.clipPlaneColor),this.gl.uniform1f(this.renderShader.uniforms.clipThick,this.opts.clipThick),this.gl.uniform3fv(this.renderShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.renderShader.uniforms.clipHi,this.opts.clipVolumeHigh),this.gl.uniform1f(this.renderShader.uniforms.backOpacity,this.volumes[0].opacity),this.gl.uniform1f(this.renderShader.uniforms.renderOverlayBlend,this.opts.renderOverlayBlend),this.gl.uniform4fv(this.renderShader.uniforms.clipPlane,this.scene.clipPlane),this.gl.uniform3fv(this.renderShader.uniforms.texVox,J),this.gl.uniform3fv(this.renderShader.uniforms.volScale,K),!this.pickingImageShader)throw new Error("pickingImageShader undefined");this.pickingImageShader.use(this.gl),this.gl.uniform1f(this.pickingImageShader.uniforms.overlays,this.overlays.length),this.gl.uniform3fv(this.pickingImageShader.uniforms.texVox,J),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi,this.opts.clipVolumeHigh);let y=this.sliceMMShader;if(this.opts.isV1SliceShader&&(y=this.sliceV1Shader),!y)throw new Error("slice shader undefined");y.use(this.gl),this.gl.uniform1f(y.uniforms.overlays,this.overlays.length),this.gl.uniform1f(y.uniforms.drawOpacity,this.drawOpacity),M!==null&&(this.gl.deleteTexture(M),this.gl.activeTexture(TEXTURE1_COLORMAPS),this.gl.bindTexture(this.gl.TEXTURE_2D,this.colormapTexture)),this.gl.uniform1i(y.uniforms.drawing,7),this.gl.activeTexture(TEXTURE7_DRAW),this.gl.bindTexture(this.gl.TEXTURE_3D,this.drawTexture),this.updateInterpolation(h)}colormaps(){return cmapper.colormaps()}addColormap(u,h){cmapper.addColormap(u,h)}setColormap(u,h){const g=this.getVolumeIndexByID(u);this.volumes[g].colormap=h,this.updateGLVolume()}idx(u,h,g,p){return g*p[0]*p[1]+h*p[0]+u}check_previous_slice(u,h,g,p,w,T,E,L){const U=new Uint32Array(27);let O=0;if(!w)return 0;const d=u[this.idx(g,p,w,T)];if(E>=6){const M=this.idx(g,p,w-1,T);d===u[M]&&(U[O++]=h[M])}if(E>=18){if(g){const M=this.idx(g-1,p,w-1,T);d===u[M]&&(U[O++]=h[M])}if(p){const M=this.idx(g,p-1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g<T[0]-1){const M=this.idx(g+1,p,w-1,T);d===u[M]&&(U[O++]=h[M])}if(p<T[1]-1){const M=this.idx(g,p+1,w-1,T);d===u[M]&&(U[O++]=h[M])}}if(E===26){if(g&&p){const M=this.idx(g-1,p-1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g<T[0]-1&&p){const M=this.idx(g+1,p-1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g&&p<T[1]-1){const M=this.idx(g-1,p+1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g<T[0]-1&&p<T[1]-1){const M=this.idx(g+1,p+1,w-1,T);d===u[M]&&(U[O++]=h[M])}}return O?(this.fill_tratab(L,U,O),U[0]):0}do_initial_labelling(u,h,g){let p=1;const w=8192;let T=w,E=new Uint32Array(T).fill(0);const L=new Uint32Array(h[0]*h[1]*h[2]).fill(0),U=new Uint32Array(27);for(let O=0;O<h[2];O++)for(let d=0;d<h[1];d++)for(let M=0;M<h[0];M++){let e=0;const b=u[this.idx(M,d,O,h)];if(b!==0){if(U[0]=this.check_previous_slice(u,L,M,d,O,h,g,E),U[0]&&(e+=1),g>=6){if(M){const z=this.idx(M-1,d,O,h);b===u[z]&&(U[e++]=L[z])}if(d){const z=this.idx(M,d-1,O,h);b===u[z]&&(U[e++]=L[z])}}if(g>=18){if(d&&M){const z=this.idx(M-1,d-1,O,h);b===u[z]&&(U[e++]=L[z])}if(d&&M<h[0]-1){const z=this.idx(M+1,d-1,O,h);b===u[z]&&(U[e++]=L[z])}}if(e)L[this.idx(M,d,O,h)]=U[0],this.fill_tratab(E,U,e);else{if(L[this.idx(M,d,O,h)]=p,p>=T){T+=w;const z=new Uint32Array(T);z.set(E),E=z}E[p-1]=p,p++}}}for(let O=0;O<p-1;O++){let d=O;for(;E[d]!==d+1;)d=E[d]-1;E[O]=d+1}return[p-1,E,L]}fill_tratab(u,h,g){let p=0;const w=new Uint32Array(g+5).fill(0);let E=2147483647;for(let L=0;L<g;L++){let U=h[L];for(p=0;u[U-1]!==U;)if(U=u[U-1],p++,p>100){log.info(`
1829
+ </html>`}async saveHTML(u="untitled.html",h="gl1",g){const p=await this.generateHTML(h,g);return NVUtilities.download(p,u,"application/html")}json(){return this.document.opts=this.opts,this.document.scene=this.scene,this.document.volumes=this.volumes,this.document.meshes=this.meshes,this.drawScene(),this.document.previewImageDataURL=this.canvas.toDataURL(),this.document.json()}async saveDocument(u="untitled.nvd",h=!0){return this.document.title=u,log.debug("saveDocument",this.volumes[0]),this.drawScene(),this.document.previewImageDataURL=this.canvas.toDataURL(),this.document.volumes=this.volumes,this.document.meshes=this.meshes,this.document.download(u,h)}async loadImages(u){const h=[],g=[];for(const p of u)if("url"in p){const w=this.getFileExt(p.url);if(this.loaders[w]){const T=this.loaders[w].toExt.toUpperCase();MESH_EXTENSIONS.includes(T)?g.push(p):h.push(p);continue}MESH_EXTENSIONS.includes(w.toUpperCase())?g.push(p):h.push(p)}return h.length>0&&await this.loadVolumes(h),g.length>0&&await this.loadMeshes(g),this}async loadVolumes(u){return this.loadingText="loading...",this.drawScene(),this.thumbnailVisible?(this.deferredVolumes=u,this):(this.volumes=[],this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT),await this.addVolumesFromUrl(u),this)}async addMeshFromUrl(u){const h=this.getFileExt(u.url);if(h==="JCON"||h==="JSON"){const w=await(await fetch(u.url,{})).json(),T=this.loadConnectomeAsMesh(w);return this.mediaUrlMap.set(T,u.url),this.onMeshAddedFromUrl(u,T),this.addMesh(T),T}const g=await NVMesh.loadFromUrl({...u,gl:this.gl});return this.mediaUrlMap.set(g,u.url),this.onMeshAddedFromUrl(u,g),this.addMesh(g),g}async addMeshesFromUrl(u){const h=u.map(async p=>{const w=this.getFileExt(p.name||p.url);if(this.loaders[w]){let E=p.url;const L=this.loaders[w].toExt;let U=p.name||p.url;if(U=U.split("/").pop(),typeof p.url=="string"){const e=p.url;try{const b=await fetch(e);if(!b.ok)throw new Error(`Failed to load file: ${b.statusText}`);E=await b.arrayBuffer()}catch(b){throw new Error(`Failed to load url ${e}: ${b}`)}}const{positions:O,indices:d}=await this.loaders[w].loader(E);p.name=`${U}.${L}`;const M=NVMeshUtilities.createMZ3(O,d,!1);p.buffer=M}if(w==="JCON"||w==="JSON"){const L=await(await fetch(p.url,{})).json(),U=this.loadConnectomeAsMesh(L);return this.mediaUrlMap.set(U,p.url),this.onMeshAddedFromUrl(p,U),U}const T=await NVMesh.loadFromUrl({...p,gl:this.gl});return this.mediaUrlMap.set(T,p.url),this.onMeshAddedFromUrl(p,T),T}),g=await Promise.all(h);for(let p=0;p<g.length;p++)this.addMesh(g[p]);return g}async loadMeshes(u){return this.loadingText="loading...",this.drawScene(),this.thumbnailVisible?(this.deferredMeshes=u,this):(this.initialized,this.meshes=[],this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT),await this.addMeshesFromUrl(u),this.updateGLVolume(),this.drawScene(),this)}async loadConnectomeFromUrl(u,h={}){const p=await(await fetch(u,{headers:h})).json();return this.loadConnectome(p)}async loadFreeSurferConnectomeFromUrl(u,h={}){const p=await(await fetch(u,{headers:h})).json();return this.loadFreeSurferConnectome(p)}async loadFreeSurferConnectome(u){const h=NVConnectome.convertFreeSurferConnectome(u);return this.loadConnectome(h)}handleNodeAdded(u){const h=u.detail.node,g=[1,1,1,1];this.addLabel(h.name,{textColor:g,bulletScale:1,bulletColor:g,lineWidth:0,lineColor:g,lineTerminator:LabelLineTerminator.NONE,textScale:1},[h.x,h.y,h.z]),this.drawScene()}loadConnectomeAsMesh(u){let h=u;if("data_type"in u&&u.data_type==="fs_pointset")h=NVConnectome.convertFreeSurferConnectome(u),log.warn("converted FreeSurfer connectome",h);else if("nodes"in u){const g=u.nodes;"names"in g&&"X"in g&&"Y"in g&&"Z"in g&&"Color"in g&&"Size"in g&&(h=NVConnectome.convertLegacyConnectome(u),log.warn("converted legacy connectome",h))}else throw new Error("not a known connectome format");return new NVConnectome(this.gl,h)}loadConnectome(u){this.loadingText="loading...",this.drawScene(),this.meshes=[],this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT);const h=this.loadConnectomeAsMesh(u);return this.addMesh(h),this.drawScene(),this}createEmptyDrawing(){if(this.back===null||!this.back.dims||Math.min(Math.min(this.back.dims[1],this.back.dims[2]),this.back.dims[3])<1)return;const h=this.back.dims[1]*this.back.dims[2]*this.back.dims[3];this.drawBitmap=new Uint8Array(h),this.drawClearAllUndoBitmaps(),this.drawAddUndoBitmap(),this.drawTexture=this.r8Tex(this.drawTexture,TEXTURE7_DRAW,this.back.dims,!0),this.refreshDrawing(!1)}r16Tex(u,h,g,p){u&&this.gl.deleteTexture(u),u=this.gl.createTexture(),this.gl.activeTexture(h),this.gl.bindTexture(this.gl.TEXTURE_3D,u),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R16I,g[1],g[2],g[3]);const w=g[1]*g[2]*g[3];return p.length!==w&&(p=new Int16Array(w)),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g[1],g[2],g[3],this.gl.RED_INTEGER,this.gl.SHORT,p),u}drawGrowCut(){if(!this.back||!this.back.dims)throw new Error("back not defined");const u=this.back.hdr,h=this.gl,g=u.dims[1]*u.dims[2]*u.dims[3];if(!this.drawBitmap||this.drawBitmap.length!==g){log.debug("bitmap dims are wrong");return}const p=h.createFramebuffer();h.bindFramebuffer(h.FRAMEBUFFER,p),h.disable(h.CULL_FACE),h.viewport(0,0,this.back.dims[1],this.back.dims[2]),h.disable(h.BLEND);let w=img2ras16(this.back);const T=this.r16Tex(null,TEXTURE11_GC_BACK,this.back.dims,w);for(let Z=1;Z<g;Z++)w[Z]=this.drawBitmap[Z];const E=this.r16Tex(null,TEXTURE14_GC_LABEL0,this.back.dims,w),L=this.r16Tex(null,TEXTURE15_GC_LABEL1,this.back.dims,w),U=1e4;for(let Z=1;Z<g;Z++)w[Z]>0&&(w[Z]=U);const O=this.r16Tex(null,TEXTURE12_GC_STRENGTH0,this.back.dims,w),d=this.r16Tex(null,TEXTURE13_GC_STRENGTH1,this.back.dims,w);h.bindVertexArray(this.genericVAO);const M=this.growCutShader;M.use(h);const e=128;h.uniform1i(M.uniforms.finalPass,0),h.uniform1i(M.uniforms.backTex,11);for(let Z=0;Z<e;Z++){h.uniform1i(M.uniforms.labelTex,14),h.uniform1i(M.uniforms.strengthTex,12);for(let X=0;X<this.back.dims[3];X++){const e0=1/this.back.dims[3]*(X+.5);h.uniform1f(M.uniforms.coordZ,e0),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,L,0,X),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT1,d,0,X),h.drawBuffers([h.COLOR_ATTACHMENT0,h.COLOR_ATTACHMENT1]),h.checkFramebufferStatus(h.FRAMEBUFFER)!==h.FRAMEBUFFER_COMPLETE&&log.error("Incomplete framebuffer"),h.drawArrays(h.TRIANGLE_STRIP,0,4)}Z===e-1&&h.uniform1i(M.uniforms.finalPass,1),h.uniform1i(M.uniforms.labelTex,15),h.uniform1i(M.uniforms.strengthTex,13);for(let X=0;X<this.back.dims[3];X++){const e0=1/this.back.dims[3]*(X+.5);h.uniform1f(M.uniforms.coordZ,e0),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,E,0,X),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT1,O,0,X),h.drawBuffers([h.COLOR_ATTACHMENT0,h.COLOR_ATTACHMENT1]),h.checkFramebufferStatus(h.FRAMEBUFFER)!==h.FRAMEBUFFER_COMPLETE&&log.error("Incomplete framebuffer"),h.drawArrays(h.TRIANGLE_STRIP,0,4)}}h.drawBuffers([h.COLOR_ATTACHMENT0]);const b=h.COLOR_ATTACHMENT1,z=E;h.readBuffer(b);const W=h.getParameter(h.IMPLEMENTATION_COLOR_READ_FORMAT),o0=h.getParameter(h.IMPLEMENTATION_COLOR_READ_TYPE);(W!==h.RED_INTEGER||o0!==h.SHORT)&&log.debug("readPixels will fail."),w=new Int16Array;const J=this.back.dims[1]*this.back.dims[2],K=new Int16Array(J);for(let Z=0;Z<this.back.dims[3];Z++)h.framebufferTextureLayer(h.FRAMEBUFFER,b,z,0,Z),h.readPixels(0,0,this.back.dims[1],this.back.dims[2],W,o0,K),w=Int16Array.from([...w,...K]);let y=w[0];for(let Z=0;Z<w.length;Z++)y=Math.max(y,w[Z]);for(let Z=1;Z<g;Z++)this.drawBitmap[Z]=w[Z];h.deleteTexture(T),h.deleteTexture(O),h.deleteTexture(d),h.deleteTexture(E),h.deleteTexture(L),h.bindVertexArray(this.unusedVAO),h.viewport(0,0,h.canvas.width,h.canvas.height),h.bindFramebuffer(h.FRAMEBUFFER,null),h.deleteFramebuffer(p),this.drawAddUndoBitmap(),this.refreshDrawing(!0)}drawPt(u,h,g,p){var L;if(!((L=this.back)!=null&&L.dims))throw new Error("back.dims not set");const w=this.back.dims[1],T=this.back.dims[2],E=this.back.dims[3];u=Math.min(Math.max(u,0),w-1),h=Math.min(Math.max(h,0),T-1),g=Math.min(Math.max(g,0),E-1),this.drawBitmap[u+h*w+g*w*T]=p}drawPenLine(u,h,g){const p=Math.abs(u[0]-h[0]),w=Math.abs(u[1]-h[1]),T=Math.abs(u[2]-h[2]);let E=-1,L=-1,U=-1;h[0]>u[0]&&(E=1),h[1]>u[1]&&(L=1),h[2]>u[2]&&(U=1);let O=u[0],d=u[1],M=u[2];const e=h[0],b=h[1],z=h[2];if(p>=w&&p>=T){let W=2*w-p,o0=2*T-p;for(;O!==e;)O+=E,W>=0&&(d+=L,W-=2*p),o0>=0&&(M+=U,o0-=2*p),W+=2*w,o0+=2*T,this.drawPt(O,d,M,g)}else if(w>=p&&w>=T){let W=2*p-w,o0=2*T-w;for(;d!==b;)d+=L,W>=0&&(O+=E,W-=2*w),o0>=0&&(M+=U,o0-=2*w),W+=2*p,o0+=2*T,this.drawPt(O,d,M,g)}else{let W=2*w-T,o0=2*p-T;for(;M!==z;)M+=U,W>=0&&(d+=L,W-=2*T),o0>=0&&(O+=E,o0-=2*T),W+=2*w,o0+=2*p,this.drawPt(O,d,M,g)}}drawFloodFillCore(u,h,g=6){var O;if(!((O=this.back)!=null&&O.dims))throw new Error("back.dims undefined");const p=[this.back.dims[1],this.back.dims[2],this.back.dims[3]],w=p[0],T=w*p[1];function E(d){return d[0]+d[1]*w+d[2]*T}function L(d){const M=Math.floor(d/T),e=Math.floor((d-M*T)/w);return[Math.floor(d%w),e,M]}const U=[];for(U.push(h),u[h]=2;U.length>0;){let d=function(b){const z=e.slice();if(z[0]+=b[0],z[1]+=b[1],z[2]+=b[2],z[0]<0||z[1]<0||z[2]<0||z[0]>=p[0]||z[1]>=p[1]||z[2]>=p[2])return;const W=E(z);u[W]===1&&(u[W]=2,U.push(W))};const M=U[0];U.shift();const e=L(M);d([0,0,-1]),d([0,0,1]),d([0,-1,0]),d([0,1,0]),d([-1,0,0]),d([1,0,0]),!(g<=6)&&(d([-1,-1,0]),d([1,1,0]),d([-1,1,0]),d([1,1,0]),d([0,-1,-1]),d([0,1,-1]),d([-1,0,-1]),d([1,0,-1]),d([0,-1,1]),d([0,1,1]),d([-1,0,1]),d([1,0,1]),!(g<=18)&&(d([-1,-1,-1]),d([1,-1,-1]),d([-1,1,-1]),d([1,1,-1]),d([-1,-1,1]),d([1,-1,1]),d([-1,1,1]),d([1,1,1])))}}drawFloodFill(u,h=0,g=0,p=NaN,w=NaN,T=6){var z;if(!this.drawBitmap)throw new Error("drawBitmap undefined");if(!((z=this.back)!=null&&z.dims))throw new Error("back.dims undefined");h=Math.abs(h);const E=[this.back.dims[1],this.back.dims[2],this.back.dims[3]];if(u[0]<0||u[1]<0||u[2]<0||u[0]>=E[0]||u[1]>=E[1]||u[2]>=E[2])return;const L=E[0],U=L*E[1],O=U*E[2],d=this.drawBitmap.slice();if(d.length!==U*E[2])return;function M(W){return W[0]+W[1]*L+W[2]*U}const e=M(u),b=d[e];if(b===h){g!==0?log.debug("drawFloodFill selected voxel is not part of a drawing"):log.debug("drawFloodFill selected voxel is already desired color");return}for(let W=1;W<O;W++)d[W]=0,this.drawBitmap[W]===b&&(d[W]=1);if(this.drawFloodFillCore(d,e,T),g!==0){const W=this.volumes[0].img2RAS();let o0=W[e],J=o0;if(isFinite(w)&&isFinite(p))o0=w,J=p;else{for(let K=1;K<O;K++)d[K]===2&&(o0=Math.max(o0,W[K]),J=Math.min(J,W[K]));g===Number.POSITIVE_INFINITY&&(o0=g),g===Number.NEGATIVE_INFINITY&&(J=g)}log.debug("Intensity range of selected cluster :",J,o0);for(let K=1;K<O;K++)d[K]=0,W[K]>=J&&W[K]<=o0&&(d[K]=1);this.drawFloodFillCore(d,e,T),h=b}for(let W=1;W<O;W++)d[W]===2&&(this.drawBitmap[W]=h);this.drawAddUndoBitmap(),this.refreshDrawing(!1)}drawPenFilled(){var b;const u=this.drawPenFillPts.length;if(u<2){this.drawPenFillPts=[];return}const h=this.drawPenAxCorSag;let g=0,p=1;if(h===1&&(p=2),h===2&&(g=1,p=2),!((b=this.back)!=null&&b.dims))throw new Error("back.dims undefined");const w=[this.back.dims[g+1],this.back.dims[p+1]],T=new Uint8Array(w[0]*w[1]);let E=1;function L(z,W){const o0=Math.abs(z[0]-W[0]),J=Math.abs(z[1]-W[1]);T[z[0]+z[1]*w[0]]=E,T[W[0]+W[1]*w[0]]=E;let K=-1,y=-1;W[0]>z[0]&&(K=1),W[1]>z[1]&&(y=1);let Z=z[0],X=z[1];const e0=W[0],s0=W[1];if(o0>=J){let j=2*J-o0;for(;Z!==e0;)Z+=K,j>=0&&(X+=y,j-=2*o0),j+=2*J,T[Z+X*w[0]]=E}else{let j=2*o0-J;for(;X!==s0;)X+=y,j>=0&&(Z+=K,j-=2*J),j+=2*o0,T[Z+X*w[0]]=E}}const U=[this.drawPenFillPts[0][g],this.drawPenFillPts[0][p]];let O=U;for(let z=1;z<u;z++){const W=[this.drawPenFillPts[z][g],this.drawPenFillPts[z][p]];L(O,W),O=W}L(U,O);const d=[];function M(z){if(z[0]<0||z[1]<0||z[0]>=w[0]||z[1]>=w[1])return;const W=z[0]+z[1]*w[0];T[W]===0&&(d.push(z),T[W]=2)}for(let z=0;z<w[0];z++)M([z,0]);for(let z=0;z<w[0];z++)M([z,w[1]-1]);for(let z=0;z<w[1];z++)M([0,z]);for(let z=0;z<w[1];z++)M([w[0]-1,z]);for(;d.length>0;){const z=d.shift();M([z[0]-1,z[1]]),M([z[0]+1,z[1]]),M([z[0],z[1]-1]),M([z[0],z[1]+1])}E=this.opts.penValue;const e=this.drawPenFillPts[0][3-(g+p)];if(!this.drawBitmap)throw new Error("drawBitmap undefined");if(h===0){const z=e*w[0]*w[1];for(let W=0;W<w[0]*w[1];W++)T[W]!==2&&(this.drawBitmap[W+z]=E)}else{let z=1;const W=this.back.dims[1]*this.back.dims[2];let o0=e*this.back.dims[1];h===2&&(z=this.back.dims[1],o0=e);let J=0;for(let K=0;K<w[1];K++)for(let y=0;y<w[0];y++)T[J]!==2&&(this.drawBitmap[y*z+K*W+o0]=E),J++}if(!this.drawFillOverwrites&&this.drawUndoBitmaps[this.currentDrawUndoBitmap].length>0){const z=this.drawBitmap.length,W=decodeRLE(this.drawUndoBitmaps[this.currentDrawUndoBitmap],z);for(let o0=0;o0<z;o0++)W[o0]!==0&&(this.drawBitmap[o0]=W[o0])}this.drawPenFillPts=[],this.drawAddUndoBitmap(),this.refreshDrawing(!1)}closeDrawing(){this.drawClearAllUndoBitmaps(),this.rgbaTex(this.drawTexture,TEXTURE7_DRAW,[2,2,2,2],!0),this.drawBitmap=null,this.drawScene()}refreshDrawing(u=!0){var p;if(!((p=this.back)!=null&&p.dims))throw new Error("back.dims undefined");if(!this.drawBitmap)throw new Error("drawBitmap undefined");const h=this.back.dims.slice(),g=this.back.dims[1]*this.back.dims[2]*this.back.dims[3];this.drawBitmap.length===8?(h[1]=2,h[2]=2,h[3]=2):g!==this.drawBitmap.length&&log.warn("Drawing bitmap must match the background image"),this.gl.activeTexture(TEXTURE7_DRAW),this.gl.bindTexture(this.gl.TEXTURE_3D,this.drawTexture),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,h[1],h[2],h[3],this.gl.RED,this.gl.UNSIGNED_BYTE,this.drawBitmap),u&&this.drawScene()}r8Tex(u,h,g,p=!1){if(u&&this.gl.deleteTexture(u),u=this.gl.createTexture(),this.gl.activeTexture(h),this.gl.bindTexture(this.gl.TEXTURE_3D,u),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R8,g[1],g[2],g[3]),p){const w=new Uint8Array(g[1]*g[2]*g[3]);this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g[1],g[2],g[3],this.gl.RED,this.gl.UNSIGNED_BYTE,w)}return u}rgbaTex(u,h,g,p=!1){if(u&&this.gl.deleteTexture(u),u=this.gl.createTexture(),this.gl.activeTexture(h),this.gl.bindTexture(this.gl.TEXTURE_3D,u),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.RGBA8,g[1],g[2],g[3]),p){const w=new Uint8Array(g[1]*g[2]*g[3]*4);this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g[1],g[2],g[3],this.gl.RGBA,this.gl.UNSIGNED_BYTE,w)}return u}requestCORSIfNotSameOrigin(u,h){new URL(h,window.location.href).origin!==window.location.origin&&(u.crossOrigin="")}async loadPngAsTexture(u,h){return new Promise((g,p)=>{const w=new Image;w.onload=()=>{if(!this.bmpShader)return;let T;h===4?(this.bmpTexture!==null&&this.gl.deleteTexture(this.bmpTexture),this.bmpTexture=this.gl.createTexture(),T=this.bmpTexture,this.bmpTextureWH=w.width/w.height,this.gl.activeTexture(TEXTURE4_THUMBNAIL),this.bmpShader.use(this.gl),this.gl.uniform1i(this.bmpShader.uniforms.bmpTexture,4)):h===5?(this.gl.activeTexture(TEXTURE5_MATCAP),this.matCapTexture!==null&&this.gl.deleteTexture(this.matCapTexture),this.matCapTexture=this.gl.createTexture(),T=this.matCapTexture):(this.fontShader.use(this.gl),this.gl.activeTexture(TEXTURE3_FONT),this.gl.uniform1i(this.fontShader.uniforms.fontTexture,3),this.fontTexture!==null&&this.gl.deleteTexture(this.fontTexture),this.fontTexture=this.gl.createTexture(),T=this.fontTexture),this.gl.bindTexture(this.gl.TEXTURE_2D,T),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,w),g(T),h!==4&&this.drawScene()},w.onerror=p,this.requestCORSIfNotSameOrigin(w,u),w.src=u})}async loadFontTexture(u){return this.loadPngAsTexture(u,3)}async loadBmpTexture(u){return this.loadPngAsTexture(u,4)}async loadMatCapTexture(u){return this.loadPngAsTexture(u,5)}initFontMets(){if(!this.fontMetrics)throw new Error("fontMetrics undefined");this.fontMets={distanceRange:this.fontMetrics.atlas.distanceRange,size:this.fontMetrics.atlas.size,mets:{}};for(let g=0;g<256;g++)this.fontMets.mets[g]={xadv:0,uv_lbwh:[0,0,0,0],lbwh:[0,0,0,0]};const u=this.fontMetrics.atlas.width,h=this.fontMetrics.atlas.height;for(let g=0;g<this.fontMetrics.glyphs.length;g++){const p=this.fontMetrics.glyphs[g],w=p.unicode;if(this.fontMets.mets[w].xadv=p.advance,p.planeBounds===void 0)continue;let T=p.atlasBounds.left/u,E=(h-p.atlasBounds.top)/h,L=(p.atlasBounds.right-p.atlasBounds.left)/u,U=(p.atlasBounds.top-p.atlasBounds.bottom)/h;this.fontMets.mets[w].uv_lbwh=[T,E,L,U],T=p.planeBounds.left,E=p.planeBounds.bottom,L=p.planeBounds.right-p.planeBounds.left,U=p.planeBounds.top-p.planeBounds.bottom,this.fontMets.mets[w].lbwh=[T,E,L,U]}}async loadFont(u=defaultFontPNG,h=defaultFontMetrics){await this.loadFontTexture(u);const g=await fetch(h);if(!g.ok)throw Error(g.statusText);const p=await g.text();this.fontMetrics=JSON.parse(p),this.initFontMets(),this.fontShader.use(this.gl),this.drawScene()}async loadDefaultMatCap(){return this.loadMatCapTexture(defaultMatCap)}async loadDefaultFont(){await this.loadFontTexture(this.DEFAULT_FONT_GLYPH_SHEET),this.fontMetrics=this.DEFAULT_FONT_METRICS,this.initFontMets()}async initText(){this.fontShader=new Shader(this.gl,vertFontShader,fragFontShader),this.fontShader.use(this.gl),await this.loadDefaultFont(),await this.loadDefaultMatCap(),this.drawLoadingText(this.loadingText)}meshShaderNameToNumber(u="Phong"){const h=u.toLowerCase();for(let g=0;g<this.meshShaders.length;g++)if(this.meshShaders[g].Name.toLowerCase()===h)return g}setMeshShader(u,h=2){let g=0;if(typeof h=="number"?g=h:g=this.meshShaderNameToNumber(h),g===void 0)throw new Error("shaderIndex undefined");g=Math.min(g,this.meshShaders.length-1),g=Math.max(g,0);const p=this.getMeshIndexByID(u);if(p>=this.meshes.length){log.debug("Unable to change shader until mesh is loaded (maybe you need async)");return}this.meshes[p].meshShaderIndex=g,this.updateGLVolume(),this.onMeshShaderChanged(p,g)}createCustomMeshShader(u,h="Custom"){if(!u)throw new Error("Need fragment shader");const g=this.meshShaderNameToNumber(h);g>=0&&(this.gl.deleteProgram(this.meshShaders[g].shader.program),this.meshShaders.splice(g,1));const p=new Shader(this.gl,vertMeshShader,u);return p.use(this.gl),{Name:h,Frag:u,shader:p}}setCustomMeshShader(u="",h="Custom"){const g=this.createCustomMeshShader(u,h);return this.meshShaders.push(g),this.onCustomMeshShaderAdded(u,h),this.meshShaders.length-1}meshShaderNames(u=!0){const h=[];for(let g=0;g<this.meshShaders.length;g++)h.push(this.meshShaders[g].Name);return u===!0?h.sort():h}initRenderShader(u,h=0){u.use(this.gl),this.gl.uniform1i(u.uniforms.volume,0),this.gl.uniform1i(u.uniforms.colormap,1),this.gl.uniform1i(u.uniforms.overlay,2),this.gl.uniform1i(u.uniforms.drawing,7),this.gl.uniform1fv(u.uniforms.renderDrawAmbientOcclusion,[this.renderDrawAmbientOcclusion,1]),this.gl.uniform1f(u.uniforms.gradientAmount,h)}async init(){const u=this.gl.getExtension("WEBGL_debug_renderer_info");if(u){const T=this.gl.getParameter(u.UNMASKED_VENDOR_WEBGL),E=this.gl.getParameter(u.UNMASKED_RENDERER_WEBGL);log.info("renderer vendor: ",T),log.info("renderer: ",E)}else log.info("debug_renderer_info unavailable");const h=this.gl.getParameter(this.gl.RENDERER);log.info("firefox renderer: ",h),this.gl.clearDepth(0),this.gl.enable(this.gl.CULL_FACE),this.gl.cullFace(this.gl.FRONT),this.gl.enable(this.gl.BLEND),this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA),this.volumeTexture=this.rgbaTex(this.volumeTexture,TEXTURE0_BACK_VOL,[2,2,2,2],!0),this.overlayTexture=this.rgbaTex(this.overlayTexture,TEXTURE2_OVERLAY_VOL,[2,2,2,2],!0),this.drawTexture=this.r8Tex(this.drawTexture,TEXTURE7_DRAW,[2,2,2,2],!0);const g=[1,1,0,1,0,0,0,1,0,0,0,0],p=this.gl;this.cuboidVertexBuffer=p.createBuffer(),p.bindBuffer(p.ARRAY_BUFFER,this.cuboidVertexBuffer),p.bufferData(p.ARRAY_BUFFER,new Float32Array(g),p.STATIC_DRAW),this.genericVAO=p.createVertexArray(),p.bindVertexArray(this.genericVAO),p.bindBuffer(p.ARRAY_BUFFER,this.cuboidVertexBuffer),p.enableVertexAttribArray(0),p.vertexAttribPointer(0,3,p.FLOAT,!1,0,0),p.bindVertexArray(this.unusedVAO),this.pickingMeshShader=new Shader(p,vertMeshShader,fragMeshDepthShader),this.pickingMeshShader.use(p),this.pickingImageShader=new Shader(p,vertRenderShader,fragVolumePickingShader),this.pickingImageShader.use(p),p.uniform1i(this.pickingImageShader.uniforms.volume,0),p.uniform1i(this.pickingImageShader.uniforms.colormap,1),p.uniform1i(this.pickingImageShader.uniforms.overlay,2),p.uniform1i(this.pickingImageShader.uniforms.drawing,7),this.sliceMMShader=new Shader(p,vertSliceMMShader,fragSliceMMShader),this.sliceMMShader.use(p),p.uniform1i(this.sliceMMShader.uniforms.volume,0),p.uniform1i(this.sliceMMShader.uniforms.colormap,1),p.uniform1i(this.sliceMMShader.uniforms.overlay,2),p.uniform1i(this.sliceMMShader.uniforms.drawing,7),p.uniform1f(this.sliceMMShader.uniforms.drawOpacity,this.drawOpacity),this.sliceV1Shader=new Shader(p,vertSliceMMShader,fragSliceV1Shader),this.sliceV1Shader.use(p),p.uniform1i(this.sliceV1Shader.uniforms.volume,0),p.uniform1i(this.sliceV1Shader.uniforms.colormap,1),p.uniform1i(this.sliceV1Shader.uniforms.overlay,2),p.uniform1i(this.sliceV1Shader.uniforms.drawing,7),p.uniform1f(this.sliceV1Shader.uniforms.drawOpacity,this.drawOpacity),this.orientCubeShader=new Shader(p,vertOrientCubeShader,fragOrientCubeShader),this.orientCubeShaderVAO=p.createVertexArray(),p.bindVertexArray(this.orientCubeShaderVAO);const w=p.createBuffer();p.enableVertexAttribArray(0),p.enableVertexAttribArray(1),p.bindBuffer(p.ARRAY_BUFFER,w),p.bufferData(p.ARRAY_BUFFER,orientCube,p.STATIC_DRAW),p.vertexAttribPointer(0,3,p.FLOAT,!1,24,0),p.enableVertexAttribArray(1),p.vertexAttribPointer(1,3,p.FLOAT,!1,24,12),p.bindVertexArray(this.unusedVAO),this.rectShader=new Shader(p,vertRectShader,fragRectShader),this.rectShader.use(p),this.lineShader=new Shader(p,vertLineShader,fragRectShader),this.lineShader.use(p),this.line3DShader=new Shader(p,vertLine3DShader,fragRectShader),this.line3DShader.use(p),this.circleShader=new Shader(p,vertCircleShader,fragCircleShader),this.circleShader.use(p),this.renderVolumeShader=new Shader(p,vertRenderShader,fragRenderShader),this.initRenderShader(this.renderVolumeShader),this.renderSliceShader=new Shader(p,vertRenderShader,fragRenderSliceShader),this.initRenderShader(this.renderSliceShader),this.renderGradientShader=new Shader(p,vertRenderShader,fragRenderGradientShader),this.initRenderShader(this.renderGradientShader,.3),p.uniform1i(this.renderGradientShader.uniforms.matCap,5),p.uniform1i(this.renderGradientShader.uniforms.gradient,6),this.renderShader=this.renderVolumeShader,this.colorbarShader=new Shader(p,vertColorbarShader,fragColorbarShader),this.colorbarShader.use(p),p.uniform1i(this.colorbarShader.uniforms.colormap,1),this.blurShader=new Shader(p,blurVertShader,blurFragShader),this.sobelShader=new Shader(p,blurVertShader,sobelFragShader),this.growCutShader=new Shader(p,vertGrowCutShader,fragGrowCutShader),this.passThroughShader=new Shader(p,vertPassThroughShader,fragPassThroughShader),this.orientShaderAtlasU=new Shader(p,vertOrientShader,fragOrientShaderU.concat(fragOrientShaderAtlas)),this.orientShaderAtlasI=new Shader(p,vertOrientShader,fragOrientShaderI.concat(fragOrientShaderAtlas)),this.orientShaderU=new Shader(p,vertOrientShader,fragOrientShaderU.concat(fragOrientShader)),this.orientShaderI=new Shader(p,vertOrientShader,fragOrientShaderI.concat(fragOrientShader)),this.orientShaderF=new Shader(p,vertOrientShader,fragOrientShaderF.concat(fragOrientShader)),this.orientShaderRGBU=new Shader(p,vertOrientShader,fragOrientShaderU.concat(fragRGBOrientShader)),this.surfaceShader=new Shader(p,vertSurfaceShader,fragSurfaceShader),this.surfaceShader.use(p),this.fiberShader=new Shader(p,vertFiberShader,fragFiberShader),this.pickingImageShader.use(p);for(let T=0;T<this.meshShaders.length;T++){const E=this.meshShaders[T];E.Name==="Flat"?E.shader=new Shader(p,vertFlatMeshShader,fragFlatMeshShader):E.shader=new Shader(p,vertMeshShader,E.Frag),E.shader.use(p),E.shader.isMatcap=E.Name==="Matcap",E.shader.isMatcap&&p.uniform1i(E.shader.uniforms.matCap,5)}return this.bmpShader=new Shader(p,vertBmpShader,fragBmpShader),await this.initText(),this.opts.thumbnail.length>0&&(await this.loadBmpTexture(this.opts.thumbnail),this.thumbnailVisible=!0),this.updateGLVolume(),this.initialized=!0,this.resizeListener(),this.drawScene(),this}gradientGL(u){const h=this.gl,g=[0,0,0,0,1,0,1,0,0,1,1,0],p=h.createVertexArray();h.bindVertexArray(p);const w=h.createBuffer();h.bindBuffer(h.ARRAY_BUFFER,w),h.bufferData(h.ARRAY_BUFFER,new Float32Array(g),h.STATIC_DRAW),h.enableVertexAttribArray(0),h.vertexAttribPointer(0,3,h.FLOAT,!1,0,0);const T=h.createFramebuffer();h.bindFramebuffer(h.FRAMEBUFFER,T),h.disable(h.CULL_FACE),h.viewport(0,0,u.dims[1],u.dims[2]),h.disable(h.BLEND);const E=this.rgbaTex(null,TEXTURE8_GRADIENT_TEMP,u.dims),L=this.blurShader;L.use(h),h.activeTexture(TEXTURE0_BACK_VOL),h.bindTexture(h.TEXTURE_3D,this.volumeTexture);const U=.7;h.uniform1i(L.uniforms.intensityVol,0),h.uniform1f(L.uniforms.dX,U/u.dims[1]),h.uniform1f(L.uniforms.dY,U/u.dims[2]),h.uniform1f(L.uniforms.dZ,U/u.dims[3]),h.bindVertexArray(p);for(let M=0;M<u.dims[3]-1;M++){const e=1/u.dims[3]*(M+.5);h.uniform1f(L.uniforms.coordZ,e),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,E,0,M),h.clear(h.DEPTH_BUFFER_BIT),h.drawArrays(h.TRIANGLE_STRIP,0,g.length/3)}const O=this.sobelShader;O.use(h),h.activeTexture(TEXTURE8_GRADIENT_TEMP),h.bindTexture(h.TEXTURE_3D,E),h.uniform1i(O.uniforms.intensityVol,8);const d=.7;h.uniform1f(O.uniforms.dX,d/u.dims[1]),h.uniform1f(O.uniforms.dY,d/u.dims[2]),h.uniform1f(O.uniforms.dZ,d/u.dims[3]),h.uniform1f(O.uniforms.coordZ,.5),h.bindVertexArray(p),h.activeTexture(TEXTURE0_BACK_VOL),this.gradientTexture!==null&&h.deleteTexture(this.gradientTexture),this.gradientTexture=this.rgbaTex(this.gradientTexture,TEXTURE6_GRADIENT,u.dims);for(let M=0;M<u.dims[3]-1;M++){const e=1/u.dims[3]*(M+.5);h.uniform1f(O.uniforms.coordZ,e),h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,this.gradientTexture,0,M),h.clear(h.DEPTH_BUFFER_BIT),h.drawArrays(h.TRIANGLE_STRIP,0,g.length/3)}h.deleteFramebuffer(T),h.deleteTexture(E),h.deleteBuffer(w),h.bindFramebuffer(h.FRAMEBUFFER,null)}updateGLVolume(){let u=0;const h=this.volumes.length;this.refreshColormaps();for(let g=0;g<h;g++)this.volumes[g].toRAS&&(this.refreshLayers(this.volumes[g],u),u++);if(this.furthestVertexFromOrigin=0,h>0&&(this.furthestVertexFromOrigin=this.volumeObject3D.furthestVertexFromOrigin),this.meshes)for(let g=0;g<this.meshes.length;g++)this.furthestVertexFromOrigin=Math.max(this.furthestVertexFromOrigin,this.meshes[g].furthestVertexFromOrigin);this.onVolumeUpdated&&this.onVolumeUpdated(),this.drawScene()}getDescriptives(u=0,h=[],g=!1){const p=this.volumes[u].hdr;let w=p.scl_slope;isNaN(w)&&(w=1);let T=p.scl_inter;isNaN(T)&&(T=1);const E=this.volumes[u].img,L=E.length,U=new Float32Array(L);for(let e0=0;e0<L;e0++)U[e0]=E[e0]*w+T;const O=new Uint8Array(L);for(let e0=0;e0<L;e0++)O[e0]=1;if(h.length>0)for(let e0=0;e0<h.length;e0++){const s0=this.volumes[h[e0]].img;if(s0.length!==L){log.debug("Mask resolution does not match image. Skipping masking layer "+h[e0]);continue}for(let j=0;j<L;j++)(s0[j]===0||isNaN(s0[j]))&&(O[j]=0)}else if(h.length<1&&g)for(let e0=0;e0<L;e0++)(this.drawBitmap[e0]===0||isNaN(this.drawBitmap[e0]))&&(O[e0]=0);let d=0,M=0,e=0,b=Number.NEGATIVE_INFINITY,z=Number.POSITIVE_INFINITY,W=0,o0=0,J=0;for(let e0=0;e0<L;e0++){if(O[e0]<1)continue;const s0=U[e0];d++;let j=M+(s0-M)/d;e=e+(s0-M)*(s0-j),M=j,s0!==0&&(W++,j=o0+(s0-o0)/W,J=J+(s0-o0)*(s0-j),o0=j,z=Math.min(s0,b),b=Math.max(s0,b))}const K=Math.sqrt(e/(d-1)),y=Math.sqrt(J/(W-1)),Z=z,X=b;return d!==W&&(z=Math.min(0,b),b=Math.max(0,b)),{mean:M,stdev:K,nvox:d,volumeMM3:d*p.pixDims[1]*p.pixDims[2]*p.pixDims[3],volumeML:d*p.pixDims[1]*p.pixDims[2]*p.pixDims[3]*.001,min:z,max:b,meanNot0:o0,stdevNot0:y,nvoxNot0:W,minNot0:Z,maxNot0:X,cal_min:this.volumes[u].cal_min,cal_max:this.volumes[u].cal_max,robust_min:this.volumes[u].robust_min,robust_max:this.volumes[u].robust_max}}refreshLayers(u,h){var Z;if(this.refreshColormaps(),this.volumes.length<1)return;const g=u.hdr;let p=u.img;u.frame4D>0&&u.frame4D<u.nFrame4D&&(p=u.img.slice(u.frame4D*u.nVox3D,(u.frame4D+1)*u.nVox3D));const w=u.opacity;if(h>1&&w===0)return;let T=null;if(!this.back)throw new Error("back undefined");this.gl.bindVertexArray(this.unusedVAO),this.crosshairs3D&&(this.crosshairs3D.mm[0]=NaN);let E=clone$2(u.toRAS);if(h===0){this.volumeObject3D=u.toNiivueObject3D(this.VOLUME_ID,this.gl),invert(E,E),this.back.matRAS=u.matRAS,this.back.dims=u.dimsRAS,this.back.pixDims=u.pixDimsRAS,T=this.rgbaTex(this.volumeTexture,TEXTURE0_BACK_VOL,u.dimsRAS);const{volScale:X,vox:e0}=this.sliceScale(!0);if(this.volScale=X,this.vox=e0,this.volumeObject3D.scale=X,!this.renderShader)throw new Error("renderShader undefined");this.renderShader.use(this.gl),this.gl.uniform3fv(this.renderShader.uniforms.texVox,e0),this.gl.uniform3fv(this.renderShader.uniforms.volScale,X);const s0=this.pickingImageShader;s0.use(this.gl),this.gl.uniform1i(s0.uniforms.volume,0),this.gl.uniform1i(s0.uniforms.colormap,1),this.gl.uniform1i(s0.uniforms.overlay,2),this.gl.uniform3fv(s0.uniforms.volScale,X),log.debug(this.volumeObject3D)}else{((Z=this.back)==null?void 0:Z.dims)===void 0&&log.error("Fatal error: Unable to render overlay: background dimensions not defined!");const X=this.mm2frac(u.mm000,0,!0);let e0=this.mm2frac(u.mm100,0,!0),s0=this.mm2frac(u.mm010,0,!0),j=this.mm2frac(u.mm001,0,!0);e0=subtract$1(e0,e0,X),s0=subtract$1(s0,s0,X),j=subtract$1(j,j,X),E=fromValues$3(e0[0],s0[0],j[0],X[0],e0[1],s0[1],j[1],X[1],e0[2],s0[2],j[2],X[2],0,0,0,1),invert(E,E),h===1?(T=this.rgbaTex(this.overlayTexture,TEXTURE2_OVERLAY_VOL,this.back.dims),this.overlayTexture=T,this.overlayTextureID=T):T=this.overlayTextureID}const L=this.gl.createFramebuffer();this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,L),this.gl.disable(this.gl.CULL_FACE),this.gl.viewport(0,0,this.back.dims[1],this.back.dims[2]),this.gl.disable(this.gl.BLEND);const U=this.gl.createTexture();this.gl.activeTexture(TEXTURE9_ORIENT),this.gl.bindTexture(this.gl.TEXTURE_3D,U),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1);let O=this.orientShaderU;if(!g)throw new Error("hdr undefined");if(!p)throw new Error("img undefined");if(g.datatypeCode===NiiDataType.DT_UINT8)g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(O=this.orientShaderAtlasU),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R8UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED_INTEGER,this.gl.UNSIGNED_BYTE,p);else if(g.datatypeCode===NiiDataType.DT_INT16)O=this.orientShaderI,g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(O=this.orientShaderAtlasI),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R16I,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED_INTEGER,this.gl.SHORT,p);else if(g.datatypeCode===NiiDataType.DT_FLOAT32)this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R32F,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED,this.gl.FLOAT,p),O=this.orientShaderF;else if(g.datatypeCode===NiiDataType.DT_FLOAT64){let X=new Float32Array;X=Float32Array.from(p),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R32F,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED,this.gl.FLOAT,X),O=this.orientShaderF}else g.datatypeCode===NiiDataType.DT_RGB24?(O=this.orientShaderRGBU,O.use(this.gl),this.gl.uniform1i(O.uniforms.hasAlpha,0),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.RGB8UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RGB_INTEGER,this.gl.UNSIGNED_BYTE,p)):g.datatypeCode===NiiDataType.DT_UINT16?(g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(O=this.orientShaderAtlasU),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.R16UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED_INTEGER,this.gl.UNSIGNED_SHORT,p)):g.datatypeCode===NiiDataType.DT_RGBA32&&(O=this.orientShaderRGBU,O.use(this.gl),this.gl.uniform1i(O.uniforms.hasAlpha,1),this.gl.texStorage3D(this.gl.TEXTURE_3D,1,this.gl.RGBA8UI,g.dims[1],g.dims[2],g.dims[3]),this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RGBA_INTEGER,this.gl.UNSIGNED_BYTE,p));u.global_min===void 0&&u.calMinMax();let d=null;if(this.gl.bindVertexArray(this.genericVAO),h>1){if(!this.back.dims)throw new Error("back.dims undefined");d=this.rgbaTex(d,TEXTURE10_BLEND,this.back.dims),this.gl.bindTexture(this.gl.TEXTURE_3D,d);const X=this.passThroughShader;X.use(this.gl),this.gl.uniform1i(X.uniforms.in3D,2);for(let e0=0;e0<this.back.dims[3];e0++){const s0=1/this.back.dims[3]*(e0+.5);this.gl.uniform1f(X.uniforms.coordZ,s0),this.gl.framebufferTextureLayer(this.gl.FRAMEBUFFER,this.gl.COLOR_ATTACHMENT0,d,0,e0),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}}else d=this.rgbaTex(d,TEXTURE10_BLEND,[2,2,2,2]);O.use(this.gl),this.gl.activeTexture(TEXTURE1_COLORMAPS);let M=null;if(u.colormapLabel!==null&&u.colormapLabel.lut.length>7){const X=u.colormapLabel.max-u.colormapLabel.min+1;M=this.createColormapTexture(M,1,X),this.gl.texSubImage2D(this.gl.TEXTURE_2D,0,0,0,X,1,this.gl.RGBA,this.gl.UNSIGNED_BYTE,u.colormapLabel.lut),this.gl.uniform1f(O.uniforms.cal_min,u.colormapLabel.min-.5),this.gl.uniform1f(O.uniforms.cal_max,u.colormapLabel.max+.5),this.gl.bindTexture(this.gl.TEXTURE_2D,M)}else this.gl.bindTexture(this.gl.TEXTURE_2D,this.colormapTexture),this.gl.uniform1f(O.uniforms.cal_min,u.cal_min),this.gl.uniform1f(O.uniforms.cal_max,u.cal_max);this.gl.uniform1i(O.uniforms.isAlphaThreshold,u.alphaThreshold),this.gl.uniform1i(O.uniforms.isAdditiveBlend,this.opts.isAdditiveBlend?1:0);let e=Number.POSITIVE_INFINITY,b=Number.NEGATIVE_INFINITY;if(u.colormapNegative.length>0&&(e=Math.min(-u.cal_min,-u.cal_max),b=Math.max(-u.cal_min,-u.cal_max),isFinite(u.cal_minNeg)&&isFinite(u.cal_maxNeg)&&(e=Math.min(u.cal_minNeg,u.cal_maxNeg),b=Math.max(u.cal_minNeg,u.cal_maxNeg))),!O)throw new Error("orientShader undefined");this.gl.uniform1f(O.uniforms.layer??null,h),this.gl.uniform1f(O.uniforms.cal_minNeg??null,e),this.gl.uniform1f(O.uniforms.cal_maxNeg??null,b),this.gl.bindTexture(this.gl.TEXTURE_3D,U),this.gl.uniform1i(O.uniforms.intensityVol??null,9),this.gl.uniform1i(O.uniforms.blend3D??null,10),this.gl.uniform1i(O.uniforms.colormap??null,1),this.gl.uniform1f(O.uniforms.scl_inter??null,g.scl_inter),this.gl.uniform1f(O.uniforms.scl_slope??null,g.scl_slope),this.gl.uniform1f(O.uniforms.opacity??null,w),this.gl.uniform1i(O.uniforms.modulationVol??null,7);let z=null;if(u.modulationImage!==null&&u.modulationImage>=0&&u.modulationImage<this.volumes.length){log.debug("modulating",this.volumes);const X=this.volumes[u.modulationImage].hdr;if(X.dims[1]===g.dims[1]&&X.dims[2]===g.dims[2]&&X.dims[3]===g.dims[3]){u.modulateAlpha?(this.gl.uniform1i(O.uniforms.modulation,2),this.gl.uniform1f(O.uniforms.opacity,1)):this.gl.uniform1i(O.uniforms.modulation,1),z=this.r8Tex(z,this.gl.TEXTURE7,g.dims,!0),this.gl.activeTexture(this.gl.TEXTURE7),this.gl.bindTexture(this.gl.TEXTURE_3D,z);const e0=g.dims[1]*g.dims[2]*g.dims[3],s0=new Uint8Array(e0),j=this.volumes[u.modulationImage].cal_min,f=1/(this.volumes[u.modulationImage].cal_max-j),g0=this.volumes[u.modulationImage].img.buffer;let c0=new Uint8Array(g0);switch(X.datatypeCode){case NiiDataType.DT_INT16:c0=new Int16Array(g0);break;case NiiDataType.DT_FLOAT32:c0=new Float32Array(g0);break;case NiiDataType.DT_FLOAT64:c0=new Float64Array(g0);break;case NiiDataType.DT_RGB24:c0=new Uint8Array(g0);break;case NiiDataType.DT_UINT16:c0=new Uint16Array(g0);break}log.debug(this.volumes[u.modulationImage]);const A0=this.volumes[u.modulationImage].colormapNegative.length>0;let v0=this.volumes[u.modulationImage].cal_min,I0=this.volumes[u.modulationImage].cal_max;isFinite(this.volumes[u.modulationImage].cal_minNeg)&&isFinite(this.volumes[u.modulationImage].cal_maxNeg)&&(v0=this.volumes[u.modulationImage].cal_minNeg,I0=this.volumes[u.modulationImage].cal_minNeg),v0=Math.abs(v0),I0=Math.abs(I0),v0>I0&&([v0,I0]=[I0,v0]);const T0=1/(I0-v0);let E0=Math.abs(u.modulateAlpha);E0=Math.max(E0,1);const M0=this.volumes[u.modulationImage].frame4D*e0;for(let U0=0;U0<e0;U0++){const oe=c0[U0+M0]*X.scl_slope+X.scl_inter;let ae=(oe-j)*f;A0&&oe<0&&(ae=(Math.abs(oe)-v0)*T0),ae=Math.min(Math.max(ae,0),1),ae=Math.pow(ae,E0)*255,s0[U0]=ae}this.gl.texSubImage3D(this.gl.TEXTURE_3D,0,0,0,0,g.dims[1],g.dims[2],g.dims[3],this.gl.RED,this.gl.UNSIGNED_BYTE,s0)}else log.debug("Modulation image dimensions do not match target")}else this.gl.uniform1i(O.uniforms.modulation,0);if(this.gl.uniformMatrix4fv(O.uniforms.mtx,!1,E),!this.back.dims)throw new Error("back.dims undefined");let W=0;g.intent_code===NiiIntentCode.NIFTI_INTENT_LABEL&&(W=this.opts.atlasOutline),this.gl.uniform4fv(O.uniforms.xyzaFrac,[1/this.back.dims[1],1/this.back.dims[2],1/this.back.dims[3],W]),log.debug("back dims: ",this.back.dims);for(let X=0;X<this.back.dims[3];X++){const e0=1/this.back.dims[3]*(X+.5);this.gl.uniform1f(O.uniforms.coordZ,e0),this.gl.framebufferTextureLayer(this.gl.FRAMEBUFFER,this.gl.COLOR_ATTACHMENT0,T,0,X),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}if(this.gl.bindVertexArray(this.unusedVAO),this.gl.deleteTexture(U),this.gl.deleteTexture(z),this.gl.deleteTexture(d),this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height),this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,null),this.gl.deleteFramebuffer(L),h===0&&(this.volumeTexture=T,this.gradientTextureAmount>0?this.gradientGL(g):(this.gradientTexture!==null&&this.gl.deleteTexture(this.gradientTexture),this.gradientTexture=null)),!this.renderShader)throw new Error("renderShader undefined");this.renderShader.use(this.gl);const o0=this.sliceScale(!0),J=o0.vox,K=o0.volScale;if(this.gl.uniform1f(this.renderShader.uniforms.overlays,this.overlays),this.gl.uniform4fv(this.renderShader.uniforms.clipPlaneColor,this.opts.clipPlaneColor),this.gl.uniform1f(this.renderShader.uniforms.clipThick,this.opts.clipThick),this.gl.uniform3fv(this.renderShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.renderShader.uniforms.clipHi,this.opts.clipVolumeHigh),this.gl.uniform1f(this.renderShader.uniforms.backOpacity,this.volumes[0].opacity),this.gl.uniform1f(this.renderShader.uniforms.renderOverlayBlend,this.opts.renderOverlayBlend),this.gl.uniform4fv(this.renderShader.uniforms.clipPlane,this.scene.clipPlane),this.gl.uniform3fv(this.renderShader.uniforms.texVox,J),this.gl.uniform3fv(this.renderShader.uniforms.volScale,K),!this.pickingImageShader)throw new Error("pickingImageShader undefined");this.pickingImageShader.use(this.gl),this.gl.uniform1f(this.pickingImageShader.uniforms.overlays,this.overlays.length),this.gl.uniform3fv(this.pickingImageShader.uniforms.texVox,J),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo,this.opts.clipVolumeLow),this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi,this.opts.clipVolumeHigh);let y=this.sliceMMShader;if(this.opts.isV1SliceShader&&(y=this.sliceV1Shader),!y)throw new Error("slice shader undefined");y.use(this.gl),this.gl.uniform1f(y.uniforms.overlays,this.overlays.length),this.gl.uniform1f(y.uniforms.drawOpacity,this.drawOpacity),M!==null&&(this.gl.deleteTexture(M),this.gl.activeTexture(TEXTURE1_COLORMAPS),this.gl.bindTexture(this.gl.TEXTURE_2D,this.colormapTexture)),this.gl.uniform1i(y.uniforms.drawing,7),this.gl.activeTexture(TEXTURE7_DRAW),this.gl.bindTexture(this.gl.TEXTURE_3D,this.drawTexture),this.updateInterpolation(h)}colormaps(){return cmapper.colormaps()}addColormap(u,h){cmapper.addColormap(u,h)}setColormap(u,h){const g=this.getVolumeIndexByID(u);this.volumes[g].colormap=h,this.updateGLVolume()}idx(u,h,g,p){return g*p[0]*p[1]+h*p[0]+u}check_previous_slice(u,h,g,p,w,T,E,L){const U=new Uint32Array(27);let O=0;if(!w)return 0;const d=u[this.idx(g,p,w,T)];if(E>=6){const M=this.idx(g,p,w-1,T);d===u[M]&&(U[O++]=h[M])}if(E>=18){if(g){const M=this.idx(g-1,p,w-1,T);d===u[M]&&(U[O++]=h[M])}if(p){const M=this.idx(g,p-1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g<T[0]-1){const M=this.idx(g+1,p,w-1,T);d===u[M]&&(U[O++]=h[M])}if(p<T[1]-1){const M=this.idx(g,p+1,w-1,T);d===u[M]&&(U[O++]=h[M])}}if(E===26){if(g&&p){const M=this.idx(g-1,p-1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g<T[0]-1&&p){const M=this.idx(g+1,p-1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g&&p<T[1]-1){const M=this.idx(g-1,p+1,w-1,T);d===u[M]&&(U[O++]=h[M])}if(g<T[0]-1&&p<T[1]-1){const M=this.idx(g+1,p+1,w-1,T);d===u[M]&&(U[O++]=h[M])}}return O?(this.fill_tratab(L,U,O),U[0]):0}do_initial_labelling(u,h,g){let p=1;const w=8192;let T=w,E=new Uint32Array(T).fill(0);const L=new Uint32Array(h[0]*h[1]*h[2]).fill(0),U=new Uint32Array(27);for(let O=0;O<h[2];O++)for(let d=0;d<h[1];d++)for(let M=0;M<h[0];M++){let e=0;const b=u[this.idx(M,d,O,h)];if(b!==0){if(U[0]=this.check_previous_slice(u,L,M,d,O,h,g,E),U[0]&&(e+=1),g>=6){if(M){const z=this.idx(M-1,d,O,h);b===u[z]&&(U[e++]=L[z])}if(d){const z=this.idx(M,d-1,O,h);b===u[z]&&(U[e++]=L[z])}}if(g>=18){if(d&&M){const z=this.idx(M-1,d-1,O,h);b===u[z]&&(U[e++]=L[z])}if(d&&M<h[0]-1){const z=this.idx(M+1,d-1,O,h);b===u[z]&&(U[e++]=L[z])}}if(e)L[this.idx(M,d,O,h)]=U[0],this.fill_tratab(E,U,e);else{if(L[this.idx(M,d,O,h)]=p,p>=T){T+=w;const z=new Uint32Array(T);z.set(E),E=z}E[p-1]=p,p++}}}for(let O=0;O<p-1;O++){let d=O;for(;E[d]!==d+1;)d=E[d]-1;E[O]=d+1}return[p-1,E,L]}fill_tratab(u,h,g){let p=0;const w=new Uint32Array(g+5).fill(0);let E=2147483647;for(let L=0;L<g;L++){let U=h[L];for(p=0;u[U-1]!==U;)if(U=u[U-1],p++,p>100){log.info(`
1830
1830
  Ooh no!!`);break}w[L]=U,E=Math.min(E,U)}for(let L=0;L<g;L++)u[w[L]-1]=E}translate_labels(u,h,g,p){const w=h[0]*h[1]*h[2];let T=0;const E=new Uint32Array(w).fill(0);for(let O=0;O<p;O++)T=Math.max(T,g[O]);const L=new Uint32Array(T).fill(0);let U=0;for(let O=0;O<w;O++)u[O]&&(L[g[u[O]-1]-1]||(U+=1,L[g[u[O]-1]-1]=U),E[O]=L[g[u[O]-1]-1]);return[U,E]}largest_original_cluster_labels(u,h,g){const p=u.length,w=new Uint32Array(h+1).fill(0),T=new Uint32Array(h+1).fill(0);for(let U=0;U<p;U++){const O=u[U],d=g[U];w[d]=O,T[d]++}let E=0;for(let U=0;U<h+1;U++){const O=w[U];E=Math.max(E,O);for(let d=0;d<h+1;d++)d!==U&&O===w[d]&&(T[U]<T[d]||T[U]===T[d]&&U<d)&&(w[U]=0)}const L=new Uint32Array(p).fill(0);for(let U=0;U<p;U++)L[U]=w[g[U]];return[E,L]}bwlabel(u,h,g=26,p=!1,w=!1){const T=Date.now(),E=h[0]*h[1]*h[2],L=new Uint32Array(E).fill(0);if(![6,18,26].includes(g))return log.info("bwlabel: conn must be 6, 18 or 26."),[0,L];if(h[0]<2||h[1]<2||h[2]<1)return log.info("bwlabel: img must be 2 or 3-dimensional"),[0,L];if(p)for(let b=0;b<E;b++)u[b]!==0&&(L[b]=1);else L.set(u);let[U,O,d]=this.do_initial_labelling(L,h,g);O===void 0&&(O=new Uint32Array);const[M,e]=this.translate_labels(d,h,O,U);if(log.info(g+" neighbor clustering into "+M+" regions in "+(Date.now()-T)+"ms"),w){const[b,z]=this.largest_original_cluster_labels(L,M,e);return[b,z]}return[M,e]}async createConnectedLabelImage(u,h=26,g=!1,p=!1){var d,M;const w=this.getVolumeIndexByID(u),T=Uint32Array.from(((d=this.volumes[w].dims)==null?void 0:d.slice(1,4))??[]),E=Uint32Array.from(((M=this.volumes[w].img)==null?void 0:M.slice())??[]),[L,U]=this.bwlabel(E,T,h,g,p),O=this.volumes[w].clone();O.opacity=.5,O.colormap="random";for(let e=0;e<O.img.length;e++)O.img[e]=U[e];return O.cal_min=0,O.cal_max=L,O}async scalecropUint8(u,h=0,g=255,p,w){const T=u.length,E=new Uint8Array(T);for(let L=0;L<T;L++){let U=u[L];U=h+w*(U-p),U=Math.max(U,h),U=Math.min(U,g),E[L]=U}return E}async scalecropFloat32(u,h=0,g=1,p,w){const T=u.length,E=new Float32Array(T);for(let L=0;L<T;L++){let U=u[L];U=h+w*(U-p),U=Math.max(U,h),U=Math.min(U,g),E[L]=U}return E}getScale(u,h=0,g=255,p=0,w=.999){let T=u.global_min,E=u.global_max;if(u.hdr.datatypeCode===NiiDataType.DT_UINT8)return[T,1];if((!isFinite(p)||!isFinite(w))&&isFinite(u.cal_min)&&isFinite(u.cal_max)&&u.cal_max>u.cal_min){T=u.cal_min,E=u.cal_max;const K=(g-h)/(E-T);return log.info(" Robust Rescale: min: "+T+" max: "+E+" scale: "+K),console.log("Robust Rescale: min: "+T+" max: "+E+" scale: "+K),[T,K]}const L=u.img,U=u.hdr.dims[1]*u.hdr.dims[2]*u.hdr.dims[3];if(u.hdr.scl_slope!==1||u.hdr.scl_inter!==0){const K=u.img,y=new Float32Array(u.img.length);for(let Z=0;Z<U;Z++)y[Z]=K[Z]*u.hdr.scl_slope+u.hdr.scl_inter}if(T<0&&log.warn("WARNING: Input image has value(s) below 0.0 !"),log.info(" Input: min: "+T+" max: "+E),p===0&&w===1)return[T,1];let O=0;for(let K=0;K<U;K++)Math.abs(L[K])>=1e-15&&O++;const d=1e3,M=(E-T)/d,e=new Array(d).fill(0);for(let K=0;K<U;K++){const y=L[K];let Z=Math.floor((y-T)/M);Z=Math.min(Z,d-1),e[Z]++}const b=new Array(d).fill(0);b[0]=e[0];for(let K=1;K<d;K++)b[K]=b[K-1]+e[K];let z=Math.floor(p*U),W=0;for(;W<d&&!(b[W]>=z);)W++;const o0=T;for(T=W*M+o0,z=U-Math.floor((1-w)*O),W=0;W<d-1&&!(b[W+1]>=z);)W++;E=W*M+o0;let J=1;return T!==E&&(J=(g-h)/(E-T)),log.info(" Rescale: min: "+T+" max: "+E+" scale: "+J),[T,J]}conformVox2Vox(u,h,g=256,p=1,w=!1){const T=h.flat(),E=fromValues$3(T[0],T[1],T[2],T[3],T[4],T[5],T[6],T[7],T[8],T[9],T[10],T[11],T[12],T[13],T[14],T[15]),L=fromValues$1(u[1]/2,u[2]/2,u[3]/2,1),U=create$1(),O=create$3();transpose(O,E),transformMat4(U,L,O);const d=fromValues$2(U[0],U[1],U[2]),M=fromValues$2(p,p,p);let e=fromValues$3(-1,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,1);w&&(e=fromValues$3(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)),transpose(e,e);const b=fromValues$1(g,g,g,1),z=create$3();scale$3(z,e,M);const W=fromValues$1(b[0],b[1],b[2],1);transformMat4(W,W,z),scale$1(W,W,.5);const o0=create$2();subtract$1(o0,d,fromValues$2(W[0],W[1],W[2]));const J=create$3();transpose(J,z),J[3]=o0[0],J[7]=o0[1],J[11]=o0[2];const K=create$3();invert(K,J);const y=create$3();mul(y,E,K);const Z=create$3();return invert(Z,y),[J,y,Z]}async createNiftiArray(u=[256,256,256],h=[1,1,1],g=[1,0,0,-128,0,1,0,-128,0,0,1,-128,0,0,0,1],p=NiiDataType.DT_UINT8,w=new Uint8Array){return await NVImage.createNiftiArray(u,h,g,p,w)}async niftiArray2NVImage(u=new Uint8Array){return await NVImage.loadFromUrl({url:u})}async loadFromUrl(u){return await NVImage.loadFromUrl({url:u})}async conform(u,h=!1,g=!0,p=!1,w=!1){const L=this.conformVox2Vox(u.hdr.dims,u.hdr.affine.flat(),256,1,h),U=L[0],O=L[2],d=256*256*256,M=new Float32Array(d),e=new Float32Array(u.img),b=u.hdr.dims[1]*u.hdr.dims[2]*u.hdr.dims[3];if(u.hdr.scl_slope!==1||u.hdr.scl_inter!==0)for(let g0=0;g0<b;g0++)e[g0]=e[g0]*u.hdr.scl_slope+u.hdr.scl_inter;const z=u.hdr.dims[1],W=u.hdr.dims[2],o0=u.hdr.dims[3],J=z*W;let K=-1;function y(g0,c0,A0){return g0+c0*z+A0*J}const Z=O[0],X=O[4],e0=O[8];if(g)for(let g0=0;g0<256;g0++)for(let c0=0;c0<256;c0++){const A0=c0*O[1]+g0*O[2]+O[3],v0=c0*O[5]+g0*O[6]+O[7],I0=c0*O[9]+g0*O[10]+O[11];for(let T0=0;T0<256;T0++){const E0=T0*Z+A0,M0=T0*X+v0,U0=T0*e0+I0,oe=Math.floor(E0),ae=Math.floor(M0),_e=Math.floor(U0);if(K++,oe<0||ae<0||_e<0)continue;const Ge=Math.ceil(E0),Pe=Math.ceil(M0),R0=Math.ceil(U0);if(Ge>=z||Pe>=W||R0>=o0)continue;const ze=E0-oe,Xe=M0-ae,ft=U0-_e,pt=1-ze,ut=1-Xe,He=1-ft,Ye=y(oe,ae,_e);let se=0;se+=e[Ye]*pt*ut*He,se+=e[Ye+J]*pt*ut*ft,se+=e[Ye+z]*pt*Xe*He,se+=e[Ye+z+J]*pt*Xe*ft,se+=e[Ye+1]*ze*ut*He,se+=e[Ye+1+J]*ze*ut*ft,se+=e[Ye+1+z]*ze*Xe*He,se+=e[Ye+1+z+J]*ze*Xe*ft,M[K]=se}}else for(let g0=0;g0<256;g0++)for(let c0=0;c0<256;c0++){const A0=c0*O[1]+g0*O[2]+O[3],v0=c0*O[5]+g0*O[6]+O[7],I0=c0*O[9]+g0*O[10]+O[11];for(let T0=0;T0<256;T0++){const E0=Math.round(T0*Z+A0),M0=Math.round(T0*X+v0),U0=Math.round(T0*e0+I0);K++,!(E0<0||M0<0||U0<0)&&(E0>=z||M0>=W||U0>=o0||(M[K]=e[y(E0,M0,U0)]))}}let s0=0;w&&(s0=NaN);let j=new Uint8Array;if(p){const g0=await this.getScale(u,0,1,s0),c0=await this.scalecropFloat32(M,0,1,g0[0],g0[1]);j=await this.createNiftiArray([256,256,256],[1,1,1],Array.from(U),NiiDataType.DT_FLOAT32,new Uint8Array(c0.buffer))}else{const g0=await this.getScale(u,0,255,s0),c0=await this.scalecropUint8(M,0,255,g0[0],g0[1]);j=await this.createNiftiArray([256,256,256],[1,1,1],Array.from(U),2,c0)}return await this.niftiArray2NVImage(j)}setRenderDrawAmbientOcclusion(u){if(!this.renderShader)throw new Error("renderShader undefined");this.renderDrawAmbientOcclusion=u,this.renderShader.use(this.gl),this.gl.uniform1fv(this.renderShader.uniforms.renderDrawAmbientOcclusion,[this.renderDrawAmbientOcclusion,1]),this.drawScene()}setColorMap(u,h){this.setColormap(u,h)}setColormapNegative(u,h){const g=this.getVolumeIndexByID(u);this.volumes[g].colormapNegative=h,this.updateGLVolume()}setModulationImage(u,h,g=0){const p=this.getVolumeIndexByID(u);let w=null;h.length>0&&(w=this.getVolumeIndexByID(h)),this.volumes[p].modulationImage=w,this.volumes[p].modulateAlpha=g,this.updateGLVolume()}setGamma(u=1){this.scene.gamma=u,cmapper.gamma=u,this.updateGLVolume()}async loadDeferred4DVolumes(u){const h=this.getVolumeIndexByID(u),g=this.volumes[h];if(g.nTotalFrame4D<=g.nFrame4D)return;let p;g.fileObject?p=await NVImage.loadFromFile({file:g.fileObject}):p=await NVImage.loadFromUrl({url:g.url}),p&&(g.img=p.img.slice(),g.nTotalFrame4D=p.nTotalFrame4D,g.nFrame4D=p.nFrame4D,this.updateGLVolume())}setFrame4D(u,h){const g=this.getVolumeIndexByID(u),p=this.volumes[g];h>p.nFrame4D-1&&(h=p.nFrame4D-1),h<0&&(h=0),h!==p.frame4D&&(p.frame4D=h,this.updateGLVolume(),this.onFrameChange(p,h),this.createOnLocationChange())}getFrame4D(u){const h=this.getVolumeIndexByID(u);return this.volumes[h].frame4D}colormapFromKey(u){return cmapper.colormapFromKey(u)}colormap(u="",h=!1){return cmapper.colormap(u,h)}createColormapTexture(u=null,h=0,g=256){return u!==null&&this.gl.deleteTexture(u),h<1||g<1?null:(u=this.gl.createTexture(),this.gl.activeTexture(TEXTURE1_COLORMAPS),this.gl.bindTexture(this.gl.TEXTURE_2D,u),this.gl.texStorage2D(this.gl.TEXTURE_2D,1,this.gl.RGBA8,g,h),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_R,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT,1),u)}addColormapList(u="",h=NaN,g=NaN,p=!1,w=!1,T=!0,E=!1){u.length<1&&(T=!1),this.colormapLists.push({name:u,min:h,max:g,alphaThreshold:p,negative:w,visible:T,invert:E})}refreshColormaps(){if(this.colormapLists=[],this.volumes.length<1&&this.meshes.length<1)return;const u=this.volumes.length;if(u>0)for(let T=0;T<u;T++){const E=this.volumes[T],L=negMinMax(E.cal_min,E.cal_max,E.cal_minNeg,E.cal_maxNeg);this.addColormapList(E.colormapNegative,L[0],L[1],E.alphaThreshold!==void 0,!0,E.colorbarVisible,E.colormapInvert),this.addColormapList(E.colormap,E.cal_min,E.cal_max,E.alphaThreshold!==void 0,!1,E.colorbarVisible,E.colormapInvert)}const h=this.meshes.length;if(h>0)for(let T=0;T<h;T++){const E=this.meshes[T];if(!E.colorbarVisible)continue;const L=E.layers.length;if("edgeColormap"in E&&"edges"in E&&E.edges!==void 0){const U=negMinMax(E.edgeMin,E.edgeMax,NaN,NaN);this.addColormapList(E.edgeColormapNegative,U[0],U[1],!1,!0,!0,E.colormapInvert),this.addColormapList(E.edgeColormap,E.edgeMin,E.edgeMax,!1,!1,!0,E.colormapInvert)}if(!(L<1))for(let U=0;U<L;U++){const O=this.meshes[T].layers[U];if(!O.colorbarVisible||O.colormap.length<1)continue;const d=negMinMax(O.cal_min,O.cal_max,O.cal_minNeg,O.cal_maxNeg);this.addColormapList(O.colormapNegative,d[0],d[1],O.alphaThreshold,!0,!0,O.colormapInvert),this.addColormapList(O.colormap,O.cal_min,O.cal_max,O.alphaThreshold,!1,!0,O.colormapInvert)}}const g=this.colormapLists.length;if(g<1)return;this.colormapTexture=this.createColormapTexture(this.colormapTexture,g+1);let p=new Uint8ClampedArray;function w(T){const E=new Uint8ClampedArray(p.length+T.length);E.set(p),E.set(T,p.length),p=E}for(let T=0;T<g;T++)w(Array.from(this.colormap(this.colormapLists[T].name,this.colormapLists[T].invert)));return w(Array.from(this.drawLut.lut)),this.gl.texSubImage2D(this.gl.TEXTURE_2D,0,0,0,256,g+1,this.gl.RGBA,this.gl.UNSIGNED_BYTE,p),this}sliceScale(u=!1){var T;let h=this.screenFieldOfViewMM(SLICE_TYPE.AXIAL);u&&(h=this.screenFieldOfViewVox(SLICE_TYPE.AXIAL));const g=Math.max(h[0],Math.max(h[1],h[2])),p=[h[0]/g,h[1]/g,h[2]/g];if(!((T=this.back)!=null&&T.dims))throw new Error("back.dims undefined");const w=[this.back.dims[1],this.back.dims[2],this.back.dims[3]];return{volScale:p,vox:w,longestAxis:g,dimsMM:h}}tileIndex(u,h){for(let g=0;g<this.screenSlices.length;g++){const p=this.screenSlices[g].leftTopWidthHeight;if(u>p[0]&&h>p[1]&&u<p[0]+p[2]&&h<p[1]+p[3])return g}return-1}inRenderTile(u,h){const g=this.tileIndex(u,h);return g>=0&&this.screenSlices[g].axCorSag===SLICE_TYPE.RENDER?g:-1}sliceScroll3D(u=0){if(u!==0){if(this.volumes.length>0&&this.scene.clipPlaneDepthAziElev[0]<1.8){const h=this.scene.clipPlaneDepthAziElev.slice();return u>0&&(h[0]=Math.min(1.5,h[0]+.025)),u<0&&(h[0]=Math.max(-1.5,h[0]-.025)),h[0]!==this.scene.clipPlaneDepthAziElev[0]?(this.scene.clipPlaneDepthAziElev=h,this.setClipPlane(this.scene.clipPlaneDepthAziElev)):void 0}u>0&&(this.scene.volScaleMultiplier=Math.min(2,this.scene.volScaleMultiplier*1.1)),u<0&&(this.scene.volScaleMultiplier=Math.max(.5,this.scene.volScaleMultiplier*.9)),this.drawScene()}}deleteThumbnail(){this.bmpTexture&&(this.gl.deleteTexture(this.bmpTexture),this.bmpTexture=null,this.thumbnailVisible=!1)}inGraphTile(u,h){if(this.graph.opacity<=0||this.volumes.length<1||this.volumes[0].nFrame4D<1||!this.graph.plotLTWH||this.graph.plotLTWH[2]<1||this.graph.plotLTWH[3]<1)return!1;const g=[(u-this.graph.LTWH[0])/this.graph.LTWH[2],(h-this.graph.LTWH[1])/this.graph.LTWH[3]];return g[0]>0&&g[1]>0&&g[0]<=1&&g[1]<=1}mouseClick(u,h,g=0,p=!0){if(u*=this.uiData.dpr,h*=this.uiData.dpr,this.canvas.focus(),this.thumbnailVisible){this.thumbnailVisible=!1,Promise.all([this.loadVolumes(this.deferredVolumes),this.loadMeshes(this.deferredMeshes)]).catch(w=>{throw w});return}if(this.inGraphTile(u,h)){if(!this.graph.plotLTWH)throw new Error("plotLTWH undefined");const w=[(u-this.graph.plotLTWH[0])/this.graph.plotLTWH[2],(h-this.graph.plotLTWH[1])/this.graph.plotLTWH[3]];if(w[0]>0&&w[1]>0&&w[0]<=1&&w[1]<=1){const T=Math.round(w[0]*(this.volumes[0].nFrame4D-1));this.setFrame4D(this.volumes[0].id,T);return}w[0]>.5&&w[1]>1&&this.loadDeferred4DVolumes(this.volumes[0].id).catch(T=>{throw T});return}if(this.inRenderTile(u,h)>=0){this.sliceScroll3D(g),this.drawScene();return}if(!(this.screenSlices.length<1||this.gl.canvas.height<1||this.gl.canvas.width<1))for(let w=0;w<this.screenSlices.length;w++){const T=this.screenSlices[w].axCorSag;if(this.drawPenAxCorSag>=0&&this.drawPenAxCorSag!==T||T>SLICE_TYPE.SAGITTAL)continue;const E=this.screenXY2TextureFrac(u,h,w,!1);if(!(E[0]<0)){if(!p){this.scene.crosshairPos[2-T]=g,this.drawScene();return}if(g!==0){let L=1;g<0&&(L=-1);const U=[0,0,0];U[2-T]=L,this.moveCrosshairInVox(U[0],U[1],U[2]),this.drawScene(),this.createOnLocationChange(T);return}if(this.opts.isForceMouseClickToVoxelCenters?this.scene.crosshairPos=clone$1(this.vox2frac(this.frac2vox(E))):this.scene.crosshairPos=clone$1(E),this.opts.drawingEnabled){const L=this.frac2vox(this.scene.crosshairPos),U=this.frac2mm(this.scene.crosshairPos);if(this.opts.clickToSegment){const O=this.opts.clickToSegmentRadius,d=this.opts.clickToSegmentSteps,M=this.opts.clickToSegmentBright?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY;this.drawPenFillPts=[],this.drawPenAxCorSag=T;for(let b=1;b<=d;b++){const z=b/d*2*Math.PI,W=U[0]+O*Math.cos(z),o0=U[1]+O*Math.sin(z),J=U[2],K=this.back.mm2vox([W,o0,J])[0],y=this.back.mm2vox([W,o0,J])[1],Z=this.back.mm2vox([W,o0,J])[2];this.drawPt(K,y,Z,this.opts.penValue),this.drawPenFillPts.push([K,y,L[2]]),b===d&&this.drawFloodFill([K,y,L[2]],0,M,NaN,NaN,this.opts.floodFillNeighbors)}this.drawScene(),this.createOnLocationChange(T);const e=this.getDescriptives(0,[],!0);this.onClickToSegment({mL:e.volumeML,mm3:e.volumeMM3});return}if(!isFinite(this.opts.penValue)||this.opts.penValue<0||Object.is(this.opts.penValue,-0)){isFinite(this.opts.penValue)?this.drawFloodFill(L,Math.abs(this.opts.penValue),this.opts.penValue,NaN,NaN,this.opts.floodFillNeighbors):this.drawFloodFill(L,0,this.opts.penValue,NaN,NaN,this.opts.floodFillNeighbors);return}if(isNaN(this.drawPenLocation[0]))this.drawPenAxCorSag=T,this.drawPenFillPts=[],this.drawPt(...L,this.opts.penValue);else{if(L[0]===this.drawPenLocation[0]&&L[1]===this.drawPenLocation[1]&&L[2]===this.drawPenLocation[2])return;this.drawPenLine(L,this.drawPenLocation,this.opts.penValue)}this.drawPenLocation=L,this.opts.isFilledPen&&this.drawPenFillPts.push(L),this.refreshDrawing(!1)}this.drawScene(),this.createOnLocationChange(T);return}}}drawRuler(){let u=[],h=[];for(let L=0;L<this.screenSlices.length;L++)if(this.screenSlices[L].axCorSag!==SLICE_TYPE.RENDER&&this.screenSlices[L].fovMM.length>1){h=this.screenSlices[L].leftTopWidthHeight,u=this.screenSlices[L].fovMM;break}if(h.length<4)return;const p=100/u[0]*h[2],w=h[0]+.5*h[2]-.5*p,T=h[1]+h[3]-2*this.opts.rulerWidth,E=[w,T,w+p,T];this.drawRuler10cm(E)}drawRuler10cm(u){if(!this.lineShader)throw new Error("lineShader undefined");this.gl.bindVertexArray(this.genericVAO),this.lineShader.use(this.gl),this.gl.uniform4fv(this.lineShader.uniforms.lineColor,this.opts.rulerColor),this.gl.uniform2fv(this.lineShader.uniforms.canvasWidthHeight,[this.gl.canvas.width,this.gl.canvas.height]),this.gl.uniform1f(this.lineShader.uniforms.thickness,this.opts.rulerWidth),this.gl.uniform4fv(this.lineShader.uniforms.startXYendXY,u),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4);const h=-.1*(u[0]-u[2]),g=u[1],p=g-2*this.opts.rulerWidth,w=g-4*this.opts.rulerWidth;for(let T=0;T<11;T++){const E=u[0]+T*h,L=[E,g,E,p];T%5===0&&(L[3]=w),this.gl.uniform4fv(this.lineShader.uniforms.startXYendXY,L),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4)}this.gl.bindVertexArray(this.unusedVAO)}screenXY2mm(u,h,g=-1){let p;for(let w=0;w<this.screenSlices.length;w++){let T=w;if(g>=0&&(T=g),this.screenSlices[T].axCorSag>SLICE_TYPE.SAGITTAL)continue;const L=this.screenSlices[T].leftTopWidthHeight;if(u<L[0]||h<L[1]||u>L[0]+L[2]||h>L[1]+L[3]||(p=this.screenXY2TextureFrac(u,h,T,!1),p[0]<0))continue;const U=this.frac2mm(p);return fromValues$1(U[0],U[1],U[2],T)}return fromValues$1(NaN,NaN,NaN,NaN)}dragForPanZoom(u){const h=this.screenXY2mm(u[2],u[3]);if(isNaN(h[0]))return;const g=this.screenXY2mm(u[0],u[1],h[3]);if(isNaN(g[0])||isNaN(h[0])||isNaN(h[3]))return;const p=create$1(),w=this.uiData.pan2DxyzmmAtMouseDown[3];sub(p,h,g),this.scene.pan2Dxyzmm[0]=this.uiData.pan2DxyzmmAtMouseDown[0]+w*p[0],this.scene.pan2Dxyzmm[1]=this.uiData.pan2DxyzmmAtMouseDown[1]+w*p[1],this.scene.pan2Dxyzmm[2]=this.uiData.pan2DxyzmmAtMouseDown[2]+w*p[2],this.canvas.focus()}dragForCenterButton(u){this.dragForPanZoom(u)}dragForSlicer3D(u){let h=this.uiData.pan2DxyzmmAtMouseDown[3];const g=u[3]-u[1];h+=g*.01,h=Math.max(h,.1),h=Math.min(h,10);const w=this.scene.pan2Dxyzmm[3]-h;this.opts.yoke3Dto2DZoom&&(this.scene.volScaleMultiplier=h),this.scene.pan2Dxyzmm[3]=h;const T=this.frac2mm(this.scene.crosshairPos);this.scene.pan2Dxyzmm[0]+=w*T[0],this.scene.pan2Dxyzmm[1]+=w*T[1],this.scene.pan2Dxyzmm[2]+=w*T[2]}drawMeasurementTool(u){const h=this.gl;if(h.bindVertexArray(this.genericVAO),h.depthFunc(h.ALWAYS),h.enable(h.BLEND),h.blendFunc(h.SRC_ALPHA,h.ONE_MINUS_SRC_ALPHA),!this.lineShader)throw new Error("lineShader undefined");this.lineShader.use(this.gl),h.uniform4fv(this.lineShader.uniforms.lineColor,this.opts.rulerColor),h.uniform2fv(this.lineShader.uniforms.canvasWidthHeight,[h.canvas.width,h.canvas.height]),h.uniform1f(this.lineShader.uniforms.thickness,this.opts.rulerWidth),h.uniform4fv(this.lineShader.uniforms.startXYendXY,u),h.drawArrays(h.TRIANGLE_STRIP,0,4);const g=this.opts.rulerColor;g[3]=1,h.uniform4fv(this.lineShader.uniforms.lineColor,g);const p=this.opts.rulerWidth;h.uniform1f(this.lineShader.uniforms.thickness,p*2);let w=[u[0],u[1]-p,u[0],u[1]+p];h.uniform4fv(this.lineShader.uniforms.startXYendXY,w),h.drawArrays(h.TRIANGLE_STRIP,0,4),w=[u[2],u[3]-p,u[2],u[3]+p],h.uniform4fv(this.lineShader.uniforms.startXYendXY,w),h.drawArrays(h.TRIANGLE_STRIP,0,4);let T=this.canvasPos2frac([u[0],u[1]]),E=this.canvasPos2frac([u[2],u[3]]);if(T[0]>=0&&E[0]>=0){const L=this.frac2mm(T);T=fromValues$2(L[0],L[1],L[2]);const U=this.frac2mm(E);E=fromValues$2(U[0],U[1],U[2]);const O=create$2();sub$1(O,T,E);const d=len(O);let M=2;d>9&&(M=1),d>99&&(M=0);const e=d.toFixed(M);this.drawTextBetween(u,e,1,g)}h.bindVertexArray(this.unusedVAO)}drawRect(u,h=[1,0,0,-1]){if(h[3]<0&&(h=this.opts.crosshairColor),!this.rectShader)throw new Error("rectShader undefined");this.rectShader.use(this.gl),this.gl.enable(this.gl.BLEND),this.gl.uniform4fv(this.rectShader.uniforms.lineColor,h),this.gl.uniform2fv(this.rectShader.uniforms.canvasWidthHeight,[this.gl.canvas.width,this.gl.canvas.height]),this.gl.uniform4f(this.rectShader.uniforms.leftTopWidthHeight,u[0],u[1],u[2],u[3]),this.gl.bindVertexArray(this.genericVAO),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),this.gl.bindVertexArray(this.unusedVAO)}drawCircle(u,h=this.opts.fontColor,g=1){if(!this.circleShader)throw new Error("circleShader undefined");this.circleShader.use(this.gl),this.gl.enable(this.gl.BLEND),this.gl.uniform4fv(this.circleShader.uniforms.circleColor,h),this.gl.uniform2fv(this.circleShader.uniforms.canvasWidthHeight,[this.gl.canvas.width,this.gl.canvas.height]),this.gl.uniform4f(this.circleShader.uniforms.leftTopWidthHeight,u[0],u[1],u[2],u[3]),this.gl.uniform1f(this.circleShader.uniforms.fillPercent,g),this.gl.uniform4fv(this.circleShader.uniforms.circleColor,h),this.gl.bindVertexArray(this.genericVAO),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),this.gl.bindVertexArray(this.unusedVAO)}drawSelectionBox(u){this.drawRect(u,this.opts.selectionBoxColor)}effectiveCanvasHeight(){return this.gl.canvas.height-this.colorbarHeight}effectiveCanvasWidth(){return this.gl.canvas.width-this.getLegendPanelWidth()}getAllLabels(){const p=this.meshes.filter(T=>T.type===MeshType.CONNECTOME).flatMap(T=>T.nodes).map(T=>T.label).filter(T=>T!==void 0);return[...this.document.labels,...p]}getConnectomeLabels(){const p=this.meshes.filter(E=>E.type===MeshType.CONNECTOME).flatMap(E=>E.nodes).map(E=>E.label).filter(E=>E!==void 0),w=this.document.labels.filter(E=>E.anchor==null||E.anchor===LabelAnchorPoint.NONE),T=new Set(p);for(const E of w)T.add(E);return Array.from(T)}getBulletMarginWidth(){let u=0;const h=this.getConnectomeLabels();if(h.length===0)return 0;const g=h.length===1?h[0].style.bulletScale:h.reduce((T,E)=>T.style.bulletScale>E.style.bulletScale?T:E).style.bulletScale,p=h.length===1?h[0]:h.reduce((T,E)=>{const L=this.opts.textHeight*this.gl.canvas.height*T.style.textScale,U=this.opts.textHeight*this.gl.canvas.height*E.style.textScale;return this.textHeight(L,T.text)>this.textHeight(U,E.text)?T:E}),w=this.opts.textHeight*this.gl.canvas.height*p.style.textScale;return u=this.textHeight(w,p.text)*g,u+=w,u}getLegendPanelWidth(){const u=this.getConnectomeLabels();if(!this.opts.showLegend||u.length===0)return 0;const g=this.opts.textHeight*this.gl.canvas.height*1;let p=0;const w=u.reduce((U,O)=>{const d=this.opts.textHeight*this.gl.canvas.height*U.style.textScale,M=this.opts.textHeight*this.gl.canvas.height*O.style.textScale;return this.textWidth(d,U.text)>this.textWidth(M,O.text)?U:O}),T=this.opts.textHeight*this.gl.canvas.height*w.style.textScale,E=this.textWidth(T,w.text),L=this.getBulletMarginWidth();return E&&(p=L+E,p+=g*2),p>=this.gl.canvas.width?0:p}getLegendPanelHeight(){const u=this.getConnectomeLabels();let h=0;const p=this.opts.textHeight*this.gl.canvas.height*1;for(const w of u){const T=this.opts.textHeight*this.gl.canvas.height*w.style.textScale,E=this.textHeight(T,w.text);h+=E}return h&&(h+=p/2*(u.length+1)),h}reserveColorbarPanel(){let u=Math.max(this.opts.textHeight,.01);u=u*Math.min(this.gl.canvas.height,this.gl.canvas.width);const h=3*u,g=[0,this.gl.canvas.height-h,this.gl.canvas.width,h];return this.colorbarHeight=g[3]+1,g}drawColorbarCore(u=0,h=[0,0,0,0],g=!1,p=0,w=1,T){if(h[2]<=0||h[3]<=0)return;let E=Math.max(this.opts.textHeight,.01);E=E*Math.min(this.gl.canvas.height,this.gl.canvas.width);let L=E;const U=3*E;let O=E;if(h[3]<U){if(h[3]<3)return;L=1,O=h[3]-2}this.gl.disable(this.gl.DEPTH_TEST),this.colorbarHeight=h[3]+1;const d=[h[0]+L,h[1],h[2]-2*L,O],M=[d[0]-1,d[1]-1,d[2]+2,d[3]+2];if(this.drawRect(M,this.opts.crosshairColor),!this.colorbarShader)throw new Error("colorbarShader undefined");this.colorbarShader.use(this.gl),this.gl.activeTexture(TEXTURE1_COLORMAPS),this.gl.bindTexture(this.gl.TEXTURE_2D,this.colormapTexture),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.NEAREST),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.NEAREST);const e=u;if(this.gl.uniform1f(this.colorbarShader.uniforms.layer,e),this.gl.uniform2fv(this.colorbarShader.uniforms.canvasWidthHeight,[this.gl.canvas.width,this.gl.canvas.height]),this.gl.disable(this.gl.CULL_FACE),g){const e0=[d[0]+d[2],d[1],-d[2],d[3]];this.gl.uniform4fv(this.colorbarShader.uniforms.leftTopWidthHeight,e0)}else this.gl.uniform4fv(this.colorbarShader.uniforms.leftTopWidthHeight,d);this.gl.bindVertexArray(this.genericVAO),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),this.gl.bindVertexArray(this.unusedVAO),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR);let b=0;if(T&&w<0&&g?(b=w,w=0):T&&p>0&&(b=p,p=0),p===w||E<1)return;const z=Math.abs(w-p);let[W,o0]=tickSpacing(p,w);o0<p&&(o0+=W);function J(e0){return e0.toFixed(6).replace(/\.?0*$/,"")}let K=o0;const y=[0,d[1]+d[3]-E*.5,2,E*.75],Z=y[1]+y[3],X=1;for(;K<=w;){y[0]=d[0]+(K-p)/z*d[2],this.drawRect(y);const e0=J(X*K);this.drawTextBelow([y[0],Z],e0),K+=W}if(b!==0){const e0=[d[0]+(b-p)/z*d[2],d[1]-d[3]*.25,2,d[3]*1.5];this.drawRect(e0)}}drawColorbar(){const u=this.colormapLists,h=u.length;if(h<1)return;let g=0;for(let L=0;L<h;L++)u[L].visible&&g++;if(g<1)return;let p=this.reserveColorbarPanel(),w=Math.max(this.opts.textHeight,.01);w=w*Math.min(this.gl.canvas.height,this.gl.canvas.width);const T=3*w;let E=p[2]/g;(p[2]<=0||p[3]<=0)&&(E=this.gl.canvas.width/g,p=[0,this.gl.canvas.height-T,E,T]),p[2]=E;for(let L=0;L<h;L++)u[L].visible&&(this.drawColorbarCore(L,p,u[L].negative,u[L].min,u[L].max,u[L].alphaThreshold),p[0]+=E)}textWidth(u,h){if(!h)return 0;let g=0;const p=new TextEncoder().encode(h);for(let w=0;w<h.length;w++)g+=u*this.fontMets.mets[p[w]].xadv;return g}textHeight(u,h){if(!h)return 0;const g=new Set(Array.from(h)),p=new TextEncoder().encode(Array.from(g).join("")),T=Object.values(this.fontMets.mets).filter((E,L)=>p.includes(L)).reduce((E,L)=>E.lbwh[3]>L.lbwh[3]?E:L).lbwh[3];return u*T}drawChar(u,h,g){if(!this.fontShader)throw new Error("fontShader undefined");const p=this.fontMets.mets[g],w=u[0]+h*p.lbwh[0],T=-(h*p.lbwh[1]),E=h*p.lbwh[2],L=h*p.lbwh[3],U=u[1]+(T-L)+h;return this.gl.uniform4f(this.fontShader.uniforms.leftTopWidthHeight,w,U,E,L),this.gl.uniform4fv(this.fontShader.uniforms.uvLeftTopWidthHeight,p.uv_lbwh),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),h*p.xadv}drawLoadingText(u){if(!this.canvas)throw new Error("canvas undefined");this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height),this.gl.enable(this.gl.CULL_FACE),this.gl.enable(this.gl.BLEND),this.drawTextBelow([this.canvas.width/2,this.canvas.height/2],u,3)}drawText(u,h,g=1,p=null){if(this.opts.textHeight<=0)return;if(!this.fontShader)throw new Error("fontShader undefined");this.fontShader.use(this.gl);const w=this.opts.textHeight*Math.min(this.gl.canvas.height,this.gl.canvas.width)*g;this.gl.enable(this.gl.BLEND),this.gl.uniform2f(this.fontShader.uniforms.canvasWidthHeight,this.gl.canvas.width,this.gl.canvas.height),p===null&&(p=this.opts.fontColor),this.gl.uniform4fv(this.fontShader.uniforms.fontColor,p);let T=w/this.fontMets.size*this.fontMets.distanceRange;T=Math.max(T,1),this.gl.uniform1f(this.fontShader.uniforms.screenPxRange,T);const E=new TextEncoder().encode(h);this.gl.bindVertexArray(this.genericVAO);for(let L=0;L<h.length;L++)u[0]+=this.drawChar(u,w,E[L]);this.gl.bindVertexArray(this.unusedVAO)}drawTextRight(u,h,g=1,p=null){this.opts.textHeight<=0||(u[1]-=.5*this.opts.textHeight*this.gl.canvas.height,this.drawText(u,h,g,p))}drawTextLeft(u,h,g=1,p=null){if(this.opts.textHeight<=0)return;const w=this.opts.textHeight*this.gl.canvas.height*g;u[0]-=this.textWidth(w,h),u[1]-=.5*w,this.drawText(u,h,g,p)}drawTextRightBelow(u,h,g=1,p=null){this.opts.textHeight<=0||this.drawText(u,h,g,p)}drawTextBetween(u,h,g=1,p=null){if(this.opts.textHeight<=0)return;const w=[(u[0]+u[2])*.5,(u[1]+u[3])*.5],T=this.opts.textHeight*this.gl.canvas.height*g,E=this.textWidth(T,h);w[0]-=.5*E,w[1]-=.5*T;const L=[w[0]-1,w[1]-1,E+2,T+2];let U=p;U===null&&(U=this.opts.crosshairColor),U&&U[0]+U[1]+U[2]>.8?U=[0,0,0,.5]:U=[1,1,1,.5],this.drawRect(L,U),this.drawText(w,h,g,p)}drawTextBelow(u,h,g=1,p=null){if(this.opts.textHeight<=0)return;if(!this.canvas)throw new Error("canvas undefined");let w=this.opts.textHeight*this.gl.canvas.height*g,T=this.textWidth(w,h);T>this.canvas.width&&(g*=(this.canvas.width-2)/T,w=this.opts.textHeight*this.gl.canvas.height*g,T=this.textWidth(w,h)),u[0]-=.5*this.textWidth(w,h),u[0]=Math.max(u[0],1),u[0]=Math.min(u[0],this.canvas.width-T-1),this.drawText(u,h,g,p)}updateInterpolation(u,h=!1){let g=this.gl.LINEAR;!h&&this.opts.isNearestInterpolation&&(g=this.gl.NEAREST),u===0?this.gl.activeTexture(TEXTURE0_BACK_VOL):this.gl.activeTexture(TEXTURE2_OVERLAY_VOL),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MIN_FILTER,g),this.gl.texParameteri(this.gl.TEXTURE_3D,this.gl.TEXTURE_MAG_FILTER,g)}setAtlasOutline(u){this.opts.atlasOutline=u,this.updateGLVolume(),this.drawScene()}setInterpolation(u){this.opts.isNearestInterpolation=u;const h=this.volumes.length;if(!(h<1)){for(let g=0;g<h;g++)this.updateInterpolation(g);this.drawScene()}}calculateMvpMatrix2D(u,h,g,p=1/0,w=0,T=0,E=0,L){this.gl.viewport(u[0],this.gl.canvas.height-(u[1]+u[3]),u[2],u[3]);let O=h[0],d=g[0],M=[O,h[1]],e=[d-O,g[1]-h[1]];L&&(M=[g[0],h[1]],e=[h[0]-g[0],g[1]-h[1]],O=-g[0],d=-h[0]);const b=2*Math.max(Math.abs(h[2]),Math.abs(g[2])),z=create$3();let W=.01,o0=b*8;if(p!==1/0){let e0=L;E===0&&(T===0||T===180)&&(e0=!e0);let s0=b*1.8-w;e0||(s0=b*1.8+w),W=s0-p,o0=s0+p}ortho(z,O,d,h[1],g[1],W,o0);const J=create$3();J[0]=-1;const K=fromValues$2(0,0,-b*1.8);translate(J,J,K),rotateX(J,J,deg2rad(270-E)),rotateZ(J,J,deg2rad(T-180));const y=create$3();invert(y,J);const Z=create$3();transpose(Z,y);const X=create$3();return multiply(X,z,J),{modelViewProjectionMatrix:X,modelMatrix:J,normalMatrix:Z,leftTopMM:M,fovMM:e}}swizzleVec3MM(u,h){return h===SLICE_TYPE.CORONAL?u=swizzleVec3(u,[0,2,1]):h===SLICE_TYPE.SAGITTAL&&(u=swizzleVec3(u,[1,2,0])),u}screenFieldOfViewVox(u=0){const h=clone$1(this.volumeObject3D.fieldOfViewDeObliqueMM);return this.swizzleVec3MM(h,u)}screenFieldOfViewMM(u=0,h=!1){if(!h&&!this.opts.isSliceMM)return this.screenFieldOfViewVox(u);const g=this.volumeObject3D.extentsMin,p=this.volumeObject3D.extentsMax;let w=fromValues$2(g[0],g[1],g[2]),T=fromValues$2(p[0],p[1],p[2]);w=this.swizzleVec3MM(w,u),T=this.swizzleVec3MM(T,u);const E=create$2();return subtract$1(E,T,w),E}screenFieldOfViewExtendedVox(u=0){const h=this.volumes[0].extentsMinOrtho,g=this.volumes[0].extentsMaxOrtho;let p=fromValues$2(h[0],h[1],h[2]),w=fromValues$2(g[0],g[1],g[2]);const T=create$3();p=this.swizzleVec3MM(p,u),w=this.swizzleVec3MM(w,u);const E=create$2();return subtract$1(E,w,p),{mnMM:p,mxMM:w,rotation:T,fovMM:E}}screenFieldOfViewExtendedMM(u=0){if(!this.volumeObject3D)throw new Error("volumeObject3D undefined");const h=this.volumeObject3D.extentsMin,g=this.volumeObject3D.extentsMax;let p=fromValues$2(h[0],h[1],h[2]),w=fromValues$2(g[0],g[1],g[2]);const T=create$3();p=this.swizzleVec3MM(p,u),w=this.swizzleVec3MM(w,u);const E=create$2();return subtract$1(E,w,p),{mnMM:p,mxMM:w,rotation:T,fovMM:E}}drawSliceOrientationText(u,h,g=[NaN,NaN]){this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height);let p="S";h===SLICE_TYPE.AXIAL&&(p="A");let w=this.opts.isRadiologicalConvention?"R":"L";if(h===SLICE_TYPE.SAGITTAL&&(w=this.opts.sagittalNoseLeft?"A":"P"),this.opts.isCornerOrientationText){this.drawTextRightBelow([u[0],u[1]],w+p);return}let T=!0,E=!0;if(!isNaN(g[0])){const L=this.opts.textHeight*this.gl.canvas.height+2;g[1]>L&&(this.drawTextBelow([u[0]+u[2]*.5,u[1]+g[1]-L],p),T=!1);const U=this.textWidth(L,w)+2;g[0]>U&&(this.drawTextRight([u[0]+g[0]-U,u[1]+u[3]*.5],w),E=!1)}T&&this.drawTextBelow([u[0]+u[2]*.5,u[1]],p),E&&this.drawTextRight([u[0],u[1]+u[3]*.5],w)}xyMM2xyzMM(u,h){let g=2;u===SLICE_TYPE.CORONAL&&(g=1),u===SLICE_TYPE.SAGITTAL&&(g=0);let p=[0,0,0],w=[1,1,0],T=[1,0,1];p[g]=h,w[g]=h,T[g]=h,p=this.frac2mm(p),w=this.frac2mm(w),T=this.frac2mm(T),p=this.swizzleVec3MM(fromValues$2(p[0],p[1],p[2]),u),w=this.swizzleVec3MM(fromValues$2(w[0],w[1],w[2]),u),T=this.swizzleVec3MM(fromValues$2(T[0],T[1],T[2]),u);const E=(w[0]-p[0])*(T[1]-p[1])-(T[0]-p[0])*(w[1]-p[1]);let L=(w[0]-p[0])*(T[2]-p[2])-(T[0]-p[0])*(w[2]-p[2]);L/=E;let U=(w[1]-p[1])*(T[2]-p[2])-(T[1]-p[1])*(w[2]-p[2]);U/=E;const O=[0,0,0,0,0];return O[0]=p[0],O[1]=p[1],O[2]=p[2],O[3]=U,O[4]=L,O}draw2DMain(u,h,g=NaN){let p=this.volumes[0].frac2mm.slice(),w=this.screenFieldOfViewExtendedMM(h),T=create$3();this.opts.isSliceMM||(p=this.volumes[0].frac2mmOrtho.slice(),T=clone$2(this.volumes[0].mm2ortho),w=this.screenFieldOfViewExtendedVox(h));let E=this.opts.isRadiologicalConvention&&h<SLICE_TYPE.SAGITTAL;g===1/0||g===-1/0?(E=g!==1/0,h===SLICE_TYPE.CORONAL&&(E=!E)):this.opts.sagittalNoseLeft&&h===SLICE_TYPE.SAGITTAL&&(E=!E);let L=0,U=0;h===SLICE_TYPE.SAGITTAL?U=E?90:-90:h===SLICE_TYPE.CORONAL?U=E?180:0:(U=E?180:0,L=E?-90:90);const O=this.gl;let d=!1;if(u[2]===0||u[3]===0){d=!0;const J=O.canvas.width/w.fovMM[0],K=O.canvas.height/w.fovMM[1],y=Math.min(J,K),Z=J/y,X=K/y;w.fovMM[0]*=Z,w.fovMM[1]*=X;let e0=(w.mnMM[0]+w.mxMM[0])*.5;w.mnMM[0]=e0-w.fovMM[0]*.5,w.mxMM[0]=e0+w.fovMM[0]*.5,e0=(w.mnMM[1]+w.mxMM[1])*.5,w.mnMM[1]=e0-w.fovMM[1]*.5,w.mxMM[1]=e0+w.fovMM[1]*.5,u=[0,0,O.canvas.width,O.canvas.height]}if(isNaN(g)){const J=this.scene.pan2Dxyzmm,K=this.swizzleVec3MM(fromValues$2(J[0],J[1],J[2]),h),y=this.scene.pan2Dxyzmm[3];w.mnMM[0]-=K[0],w.mxMM[0]-=K[0],w.mnMM[1]-=K[1],w.mxMM[1]-=K[1],w.mnMM[0]/=y,w.mxMM[0]/=y,w.mnMM[1]/=y,w.mxMM[1]/=y}let M=2;h===SLICE_TYPE.CORONAL&&(M=1),h===SLICE_TYPE.SAGITTAL&&(M=0);let e=this.scene.crosshairPos[M],b=this.frac2mm(this.scene.crosshairPos);!isNaN(g)&&g!==1/0&&g!==-1/0&&(b=this.frac2mm([.5,.5,.5]),b[M]=g,e=this.mm2frac(b)[M]);const z=b[M];O.clear(O.DEPTH_BUFFER_BIT);let W=this.calculateMvpMatrix2D(u,w.mnMM,w.mxMM,1/0,0,U,L,E);if(g===1/0||g===-1/0){const J=u.slice();this.draw3D(u,W.modelViewProjectionMatrix,W.modelMatrix,W.normalMatrix,U,L);const K=this.screenSlices[this.screenSlices.length-1];K.leftTopWidthHeight=J,K.axCorSag=h,K.sliceFrac=1/0,K.AxyzMxy=this.xyMM2xyzMM(h,e),K.leftTopMM=W.leftTopMM,K.fovMM=W.fovMM;return}O.enable(O.DEPTH_TEST),O.blendFunc(O.SRC_ALPHA,O.ONE_MINUS_SRC_ALPHA),O.disable(O.BLEND),O.depthFunc(O.GREATER),O.disable(O.CULL_FACE);let o0=this.sliceMMShader;if(this.opts.isV1SliceShader&&(o0=this.sliceV1Shader),!o0)throw new Error("slice Shader undefined");if(o0.use(this.gl),O.uniform1f(o0.uniforms.overlayOutlineWidth,this.overlayOutlineWidth),O.uniform1f(o0.uniforms.overlayAlphaShader,this.overlayAlphaShader),O.uniform1i(o0.uniforms.isAlphaClipDark,this.isAlphaClipDark?1:0),O.uniform1i(o0.uniforms.backgroundMasksOverlays,this.backgroundMasksOverlays),O.uniform1f(o0.uniforms.drawOpacity,this.drawOpacity),O.enable(O.BLEND),O.blendFunc(O.SRC_ALPHA,O.ONE_MINUS_SRC_ALPHA),O.uniform1f(o0.uniforms.opacity,this.volumes[0].opacity),O.uniform1i(o0.uniforms.axCorSag,h),O.uniform1f(o0.uniforms.slice,e),O.uniformMatrix4fv(o0.uniforms.frac2mm,!1,p),O.uniformMatrix4fv(o0.uniforms.mvpMtx,!1,W.modelViewProjectionMatrix.slice()),O.bindVertexArray(this.genericVAO),O.drawArrays(O.TRIANGLE_STRIP,0,4),O.bindVertexArray(this.unusedVAO),this.screenSlices.push({leftTopWidthHeight:u,axCorSag:h,sliceFrac:e,AxyzMxy:this.xyMM2xyzMM(h,e),leftTopMM:W.leftTopMM,screen2frac:[],fovMM:W.fovMM}),isNaN(g)&&this.drawCrosshairs3D(!0,1,W.modelViewProjectionMatrix,!0,this.opts.isSliceMM),this.opts.meshThicknessOn2D>0){this.opts.meshThicknessOn2D!==1/0&&(W=this.calculateMvpMatrix2D(u,w.mnMM,w.mxMM,this.opts.meshThicknessOn2D,z,U,L,E));const J=clone$2(W.modelViewProjectionMatrix);multiply(J,J,T),this.drawMesh3D(!0,1,J,W.modelMatrix,W.normalMatrix)}isNaN(g)&&this.drawCrosshairs3D(!1,.15,W.modelViewProjectionMatrix,!0,this.opts.isSliceMM),d&&this.drawSliceOrientationText(u,h),this.readyForSync=!0}draw2D(u,h,g=NaN,p=[NaN,NaN]){const w=[NaN,NaN];if(isNaN(p[0]))this.draw2DMain(u,h,g);else{const T=u.slice();w[0]=Math.floor(.5*(T[2]-p[0])),w[1]=Math.floor(.5*(T[3]-p[1])),T[0]+=w[0],T[1]+=w[1],T[2]=p[0],T[3]=p[1],this.draw2DMain(T,h,g)}g===1/0||g===-1/0||h===SLICE_TYPE.RENDER||u[2]!==0&&u[3]!==0&&this.drawSliceOrientationText(u,h,w)}calculateMvpMatrix(u,h=[0,0,0,0],g,p){(h[2]===0||h[3]===0)&&(h=[0,0,this.gl.canvas.width,this.gl.canvas.height]);const w=h[2]/h[3];let T=this.furthestFromPivot;const E=this.pivot3D,L=create$3();T=.8*T/this.scene.volScaleMultiplier,w<1?ortho(L,-T,T,-T/w,T/w,T*.01,T*8):ortho(L,-T*w,T*w,-T,T,T*.01,T*8);const U=create$3();U[0]=-1;const O=fromValues$2(0,0,-T*1.8);translate(U,U,O),this.position&&translate(U,U,this.position),rotateX(U,U,deg2rad(270-p)),rotateZ(U,U,deg2rad(g-180)),translate(U,U,[-E[0],-E[1],-E[2]]);const d=create$3();invert(d,U);const M=create$3();transpose(M,d);const e=create$3();return multiply(e,L,U),[e,U,M]}calculateModelMatrix(u,h){if(!this.back)throw new Error("back undefined");const g=create$3();if(g[0]=-1,rotateX(g,g,deg2rad(270-h)),rotateZ(g,g,deg2rad(u-180)),this.back.obliqueRAS){const p=clone$2(this.back.obliqueRAS);multiply(g,g,p)}return g}calculateRayDirection(u,h){const g=this.calculateModelMatrix(u,h),p=fromValues$3(1,0,0,0,0,-1,0,0,0,0,-1,0,0,0,0,1),w=create$3();multiply(w,p,g);const T=create$3();invert(T,w);const E=fromValues$1(0,0,-1,1);transformMat4(E,E,T);const L=fromValues$2(E[0],E[1],E[2]);normalize$1(L,L);const U=5e-5;return Math.abs(L[0])<U&&(L[0]=U),Math.abs(L[1])<U&&(L[1]=U),Math.abs(L[2])<U&&(L[2]=U),L}sceneExtentsMinMax(u=!0){let h=fromValues$2(0,0,0),g=fromValues$2(0,0,0);if(this.volumes.length>0){if(!this.volumeObject3D)throw new Error("volumeObject3D undefined");h=fromValues$2(this.volumeObject3D.extentsMin[0],this.volumeObject3D.extentsMin[1],this.volumeObject3D.extentsMin[2]),g=fromValues$2(this.volumeObject3D.extentsMax[0],this.volumeObject3D.extentsMax[1],this.volumeObject3D.extentsMax[2]),u||(h=fromValues$2(this.volumes[0].extentsMinOrtho[0],this.volumes[0].extentsMinOrtho[1],this.volumes[0].extentsMinOrtho[2]),g=fromValues$2(this.volumes[0].extentsMaxOrtho[0],this.volumes[0].extentsMaxOrtho[1],this.volumes[0].extentsMaxOrtho[2]))}if(this.meshes.length>0){if(this.volumes.length<1){const w=this.meshes[0].extentsMin,T=this.meshes[0].extentsMax;h=fromValues$2(w[0],w[1],w[2]),g=fromValues$2(T[0],T[1],T[2])}for(let w=0;w<this.meshes.length;w++){const T=this.meshes[w].extentsMin,E=this.meshes[w].extentsMax,L=fromValues$2(T[0],T[1],T[2]);min$j(h,h,L);const U=fromValues$2(E[0],E[1],E[2]);max$l(g,g,U)}}const p=create$2();return subtract$1(p,g,h),[h,g,p]}setPivot3D(){const[u,h]=this.sceneExtentsMinMax(),g=create$2();add$1(g,u,h),scale$2(g,g,.5),this.pivot3D=[g[0],g[1],g[2]],subtract$1(g,h,u),this.extentsMin=u,this.extentsMax=h,this.furthestFromPivot=length$1(g)*.5}getMaxVols(){if(this.volumes.length<1)return 0;let u=0;for(let h=0;h<this.volumes.length;h++)u=Math.max(u,this.volumes[h].nFrame4D);return u}detectPartialllyLoaded4D(){if(this.volumes.length<1)return!1;for(let u=0;u<this.volumes.length;u++)if(this.volumes[u].nFrame4D<this.volumes[u].hdr.dims[4])return!0;return!1}drawGraph(){if(this.getMaxVols()<2)return;const u=this.graph;let h=0;if(this.graph.autoSizeMultiplanar&&this.opts.sliceType===SLICE_TYPE.MULTIPLANAR)for(let c0=0;c0<this.screenSlices.length;c0++){const A0=this.screenSlices[c0].axCorSag;if(A0===SLICE_TYPE.AXIAL&&(h=this.screenSlices[c0].leftTopWidthHeight[1]),A0!==SLICE_TYPE.SAGITTAL)continue;const v0=this.screenSlices[c0].leftTopWidthHeight.slice();v0[1]===h?(u.LTWH[0]=v0[0]+v0[2],u.LTWH[1]=v0[1]):(u.LTWH[0]=v0[0],u.LTWH[1]=v0[1]+v0[3]),u.LTWH[2]=v0[2],u.LTWH[3]=v0[2]}if(u.opacity<=0||u.LTWH[2]<=5||u.LTWH[3]<=5||u.LTWH[0]+u.LTWH[2]>this.gl.canvas.width||u.LTWH[1]+u.LTWH[3]>this.gl.canvas.height)return;u.backColor=[.15,.15,.15,u.opacity],u.lineColor=[1,1,1,1],this.opts.backColor[0]+this.opts.backColor[1]+this.opts.backColor[2]>1.5&&(u.backColor=[.95,.95,.95,u.opacity],u.lineColor=[0,0,0,1]),u.textColor=u.lineColor.slice(),u.lineThickness=4,u.lineAlpha=1,u.lines=[];const g=[];if(u.vols.length<1)this.volumes[0]!=null&&g.push(0);else for(let c0=0;c0<u.vols.length;c0++){const A0=u.vols[c0];this.volumes[A0]==null||this.volumes[A0].nFrame4D<2||g.push(A0)}if(g.length<1)return;const p=this.volumes[g[0]].nFrame4D;if(this.graph.selectedColumn=this.volumes[g[0]].frame4D,p<2){log.debug("Unable to generate a graph: Selected volume is 3D not 4D");return}for(let c0=0;c0<g.length;c0++){u.lines[c0]=[];const A0=this.frac2vox(this.scene.crosshairPos),v0=this.volumes[g[c0]];let I0=v0.nFrame4D;I0=Math.min(I0,p);for(let T0=0;T0<I0;T0++){const E0=v0.getValue(A0[0],A0[1],A0[2],T0);u.lines[c0].push(E0)}}u.lineRGB=[[1,0,0],[0,.7,0],[0,0,1],[1,1,0],[1,0,1],[0,1,1],[1,1,1],[0,0,0]];let w=u.lines[0][0],T=u.lines[0][0];for(let c0=0;c0<u.lines.length;c0++)for(let A0=0;A0<u.lines[c0].length;A0++){const v0=u.lines[c0][A0];w=Math.min(v0,w),T=Math.max(v0,T)}if(u.normalizeValues&&T>w){const c0=T-w;for(let A0=0;A0<u.lines.length;A0++)for(let v0=0;v0<u.lines[A0].length;v0++){const I0=u.lines[A0][v0];u.lines[A0][v0]=(I0-w)/c0}w=0,T=1}w>=T&&(T=w+1),this.drawRect(u.LTWH,u.backColor);const[E,L,U]=tickSpacing(w,T),O=Math.max(0,-1*Math.floor(Math.log(E)/Math.log(10)));w=Math.min(L,w),T=Math.max(U,T);function d(c0){return c0.toFixed(6).replace(/\.?0*$/,"")}const e=.07*(Math.min(u.LTWH[2],u.LTWH[3])/(this.fontMets.size*this.uiData.dpr));let b=this.opts.textHeight*this.gl.canvas.height*e;b<16&&(b=0);let z=0,W=L;if(b>0)for(;W<=T;){const c0=W.toFixed(O),A0=this.textWidth(b,c0);z=Math.max(A0,z),W+=E}const o0=.05,J=Math.abs(u.LTWH[2]),K=Math.abs(u.LTWH[3]),y=[u.LTWH[0]+o0*J+z,u.LTWH[1]+o0*K,u.LTWH[2]-z-2*o0*J,u.LTWH[3]-b-2*o0*K];this.graph.LTWH=u.LTWH,this.graph.plotLTWH=y,this.drawRect(y,this.opts.backColor);const Z=T-w,X=y[3]/Z,e0=y[2]/(u.lines[0].length-1),s0=y[1]+y[3];W=L+.5*E;const j=u.lineColor.slice();for(j[3]=.25*u.lineColor[3];W<=T;){const c0=s0-(W-w)*X;this.drawLine([y[0],c0,y[0]+y[2],c0],.5*u.lineThickness,j),W+=E}W=L;const f=.5*u.lineThickness;for(;W<=T;){const c0=s0-(W-w)*X;this.drawLine([y[0]-f,c0,y[0]+y[2]+u.lineThickness,c0],u.lineThickness,u.lineColor);const A0=W.toFixed(O);b>0&&this.drawTextLeft([y[0]-6,c0],A0,e,u.textColor),W+=E}let g0=1;for(;u.lines[0].length/g0>20;)g0*=5;for(let c0=0;c0<u.lines[0].length;c0+=g0){const A0=c0*e0+y[0];let v0=u.lineThickness;if(c0%2===1)v0*=.5,this.drawLine([A0,y[1],A0,y[1]+y[3]],v0,j);else{const I0=d(c0);b>0&&this.drawTextBelow([A0,2+y[1]+y[3]],I0,e,u.textColor),this.drawLine([A0,y[1],A0,y[1]+y[3]],v0,u.lineColor)}}for(let c0=0;c0<u.lines.length;c0++){let A0=[1,0,0,u.lineAlpha];c0<u.lineRGB.length&&(A0=[u.lineRGB[c0][0],u.lineRGB[c0][1],u.lineRGB[c0][2],u.lineAlpha]);for(let v0=1;v0<u.lines[c0].length;v0++){const I0=(v0-1)*e0,T0=v0*e0,E0=(u.lines[c0][v0-1]-w)*X,M0=(u.lines[c0][v0]-w)*X,U0=[y[0]+I0,y[1]+y[3]-E0,y[0]+T0,y[1]+y[3]-M0];this.drawLine(U0,u.lineThickness,A0)}}if(u.selectedColumn>=0&&u.selectedColumn<u.lines[0].length){const c0=u.selectedColumn*e0+y[0];this.drawLine([c0,y[1],c0,y[1]+y[3]],u.lineThickness,[u.lineRGB[3][0],u.lineRGB[3][1],u.lineRGB[3][2],1])}this.detectPartialllyLoaded4D()&&this.drawTextBelow([y[0]+y[2],y[1]+y[3]+b*.5],"...",e,u.textColor)}depthPicker(u,h){if(!this.uiData.mouseDepthPicker)return;this.uiData.mouseDepthPicker=!1;const g=this.gl,p=this.mousePos[0]*u[2]/u[2],w=g.canvas.height-this.mousePos[1]*u[3]/u[3]-1,T=new Uint8Array(4);if(g.readPixels(p,w,1,1,g.RGBA,g.UNSIGNED_BYTE,T),this.selectedObjectId=T[3],this.selectedObjectId===this.VOLUME_ID){this.scene.crosshairPos=new Float32Array(T.slice(0,3)).map(M=>M/255);return}const E=unpackFloatFromVec4i(T);if(E>1)return;const L=(this.mousePos[0]-u[0])/u[2],U=(g.canvas.height-this.mousePos[1]-u[1])/u[3],O=unProject(L,U,E,h),d=this.mm2frac(O,0,!0);d[0]<0||d[0]>1||d[1]<0||d[1]>1||d[2]<0||d[2]>1||(this.scene.crosshairPos=this.mm2frac(O,0,!0))}drawImage3D(u,h,g){if(this.volumes.length===0)return;const p=this.gl,w=this.calculateRayDirection(h,g),T=this.volumeObject3D;if(T){p.enable(p.BLEND),p.blendFunc(p.SRC_ALPHA,p.ONE_MINUS_SRC_ALPHA),p.enable(p.CULL_FACE),p.cullFace(p.FRONT);let E=this.renderShader;if(this.uiData.mouseDepthPicker&&(E=this.pickingImageShader),E.use(this.gl),p.uniform1i(E.uniforms.backgroundMasksOverlays,this.backgroundMasksOverlays),this.gradientTextureAmount>0){p.activeTexture(TEXTURE6_GRADIENT),p.bindTexture(p.TEXTURE_3D,this.gradientTexture);const L=this.calculateModelMatrix(h,g),U=create$3();invert(U,L);const O=create$3();transpose(O,U),p.uniformMatrix4fv(E.uniforms.normMtx,!1,O)}this.drawBitmap&&this.drawBitmap.length>8?p.uniform2f(E.uniforms.renderDrawAmbientOcclusionXY,this.renderDrawAmbientOcclusion,this.drawOpacity):p.uniform2f(E.uniforms.renderDrawAmbientOcclusionXY,this.renderDrawAmbientOcclusion,0),p.uniformMatrix4fv(E.uniforms.mvpMtx,!1,u),p.uniformMatrix4fv(E.uniforms.matRAS,!1,this.back.matRAS),p.uniform3fv(E.uniforms.rayDir,w),this.gradientTextureAmount<0?p.uniform4fv(E.uniforms.clipPlane,[this.scene.crosshairPos[0],this.scene.crosshairPos[1],this.scene.crosshairPos[2],30]):p.uniform4fv(E.uniforms.clipPlane,this.scene.clipPlane),p.uniform1f(E.uniforms.drawOpacity,1),p.bindVertexArray(T.vao),p.drawElements(T.mode,T.indexCount,p.UNSIGNED_SHORT,0),p.bindVertexArray(this.unusedVAO)}}drawOrientationCube(u,h=0,g=0){if(!this.opts.isOrientCube)return;const p=.05*Math.min(u[2],u[3]);if(p<5)return;const w=this.gl;w.enable(w.CULL_FACE),w.cullFace(w.BACK),this.orientCubeShader.use(this.gl),w.bindVertexArray(this.orientCubeShaderVAO);const T=create$3(),E=create$3();ortho(E,0,w.canvas.width,0,w.canvas.height,-10*p,10*p);let L=0;u[1]===0&&(L=w.canvas.height-this.effectiveCanvasHeight()),translate(T,T,[1.8*p+u[0],L+1.8*p+u[1],0]),scale$3(T,T,[p,p,p]),rotateX(T,T,deg2rad(270-g)),rotateZ(T,T,deg2rad(-h));const U=create$3();multiply(U,E,T),w.uniformMatrix4fv(this.orientCubeShader.uniforms.u_matrix,!1,U),w.drawArrays(w.TRIANGLE_STRIP,0,168),w.bindVertexArray(this.unusedVAO),this.gl.disable(this.gl.CULL_FACE)}createOnLocationChange(u=NaN){const[h,g,p]=this.sceneExtentsMinMax(!0),w=Math.max(Math.max(p[0],p[1]),p[2]);function T(M){return Math.max(0,-Math.ceil(Math.log10(Math.abs(M))))}let E=T(w*.001);const L=this.frac2mm(this.scene.crosshairPos,0,!0);function U(M,e=0){return parseFloat(M.toFixed(e))}let O=U(L[0],E)+"×"+U(L[1],E)+"×"+U(L[2],E);if(this.volumes.length>0&&this.volumes[0].nFrame4D>0&&(O+="×"+U(this.volumes[0].frame4D)),this.volumes.length>0){let M=" = ";for(let z=0;z<this.volumes.length;z++){const W=this.volumes[z].mm2vox(L);let o0=this.volumes[z].getValue(W[0],W[1],W[2],this.volumes[z].frame4D);if(E=3,this.volumes[z].colormapLabel!==null){const J=Math.round(o0);J>=0&&J<this.volumes[z].colormapLabel.labels.length?M+=this.volumes[z].colormapLabel.labels[J]:M+="undefined("+U(o0,E)+")"}else M+=U(o0,E);this.volumes[z].imaginary&&(o0=this.volumes[z].getValue(W[0],W[1],W[2],this.volumes[z].frame4D,!0),o0>=0&&(M+="+"),M+=U(o0,E)),M+=" "}O+=M;const e=this.back.dimsRAS,b=e[1]*e[2]*e[3];if(this.drawBitmap&&this.drawBitmap.length===b){const z=this.frac2vox(this.scene.crosshairPos),W=z[0]+z[1]*e[1]+z[2]*e[1]*e[2];O+=" "+this.drawLut.labels[this.drawBitmap[W]]}}const d={mm:this.frac2mm(this.scene.crosshairPos,0,!0),axCorSag:u,vox:this.frac2vox(this.scene.crosshairPos),frac:this.scene.crosshairPos,xy:[this.mousePos[0],this.mousePos[1]],values:this.volumes.map(M=>{const e=this.frac2mm(this.scene.crosshairPos,0,!0),b=M.mm2vox(e),z=M.getValue(b[0],b[1],b[2],M.frame4D);return{name:M.name,value:z,id:M.id,mm:e,vox:b}}),string:O};this.onLocationChange(d)}addLabel(u,h,g,p,w){const T={textColor:this.opts.legendTextColor,textScale:1,textAlignment:LabelTextAlignment.LEFT,lineWidth:0,lineColor:this.opts.legendTextColor,lineTerminator:LabelLineTerminator.NONE,bulletScale:0,bulletColor:this.opts.legendTextColor},E=h?{...T,...h}:{...T},L=new NVLabel3D(u,{...E},g,p,w);return this.document.labels.push(L),L}calculateScreenPoint(u,h,g){const p=create$1();return transformMat4(p,[...u,1],h),p[3]!==0&&(p[0]=(p[0]/p[3]+1)*.5*g[2],p[1]=(1-p[1]/p[3])*.5*g[3],p[2]/=p[3],p[0]+=g[0],p[1]+=g[1]),p}getLabelAtPoint(u){const g=this.opts.textHeight*Math.min(this.gl.canvas.height,this.gl.canvas.width)*1,p=this.opts.textHeight*this.gl.canvas.height*1;for(const O of this.document.labels){if(O.anchor==null||O.anchor===LabelAnchorPoint.NONE)continue;const d=this.opts.textHeight*this.gl.canvas.height*O.style.textScale,M=this.textHeight(d,O.text),e=this.textWidth(d,O.text);if(!(O.anchor&LabelAnchorFlag.LEFT&&u[0]>e)&&!(O.anchor&LabelAnchorFlag.CENTER&&(u[0]<(this.gl.canvas.width-e)/2||u[0]>(this.gl.canvas.width+e)/2))&&!(O.anchor&LabelAnchorFlag.RIGHT&&u[0]<this.gl.canvas.width-e)&&!(O.anchor&LabelAnchorFlag.TOP&&(u[1]<p/2||u[1]>M+p/2))&&!(O.anchor&LabelAnchorFlag.MIDDLE&&(u[1]<(this.gl.canvas.height-M-p)/2||u[1]>(this.gl.canvas.height+M-p/2)/2))&&!(O.anchor&LabelAnchorFlag.BOTTOM&&(u[1]<this.gl.canvas.height-M-p||u[1]>this.gl.canvas.height-p/2)))return O}log.debug("screenPoint",u);const w=this.getLegendPanelHeight(),T=this.getLegendPanelWidth(),E=this.gl.canvas.width-T;let L=(this.canvas.height-w)/2;if(log.debug("panelrect",E,L,E+T,L+w),u[0]<E||u[1]<L||u[0]>E+T||u[1]>L+w)return null;const U=this.getConnectomeLabels();for(const O of U){const d=this.opts.textHeight*this.gl.canvas.height*O.style.textScale,M=this.textHeight(d,O.text);if(u[1]>=L&&u[1]<=L+M+g/2)return O;L+=M,L+=g/2}return null}drawLabelLine(u,h,g,p,w=!1){const T=Array.isArray(u.points)&&Array.isArray(u.points[0])?u.points:[u.points];for(const E of T){const L=this.calculateScreenPoint(E,g,p);w?this.drawDottedLine([...h,L[0],L[1]],u.style.lineWidth,u.style.lineColor):this.draw3DLine(h,[L[0],L[1],L[2]],u.style.lineWidth,u.style.lineColor)}}draw3DLabel(u,h,g,p,w,T,E){const L=u.text,U=h[0],O=h[1],d=this.opts.textHeight*Math.min(this.gl.canvas.height,this.gl.canvas.width)*1,M=this.textHeight(u.style.textScale,L)*d;if(u.style.lineWidth>0&&Array.isArray(u.points)&&this.drawLabelLine(u,[U,O+M],g,p,E),u.style.bulletScale){const b=u.style.bulletScale*M,z=M-b,W=O+z/2+b/2,o0=U+(w-b)/2;this.drawCircle([o0,W,b,b],u.style.bulletColor)}let e=U;if(T)if(u.style.textAlignment!==LabelTextAlignment.LEFT){const b=this.textWidth(u.style.textScale,u.text)*d;if(u.style.textAlignment===LabelTextAlignment.RIGHT)e=U+T-d*1.5-b;else{const z=T-(w||d);e+=(z-b)/2}}else e+=w;this.drawText([e,O],L,u.style.textScale,u.style.textColor)}draw3DLabels(u,h,g=!1){const p=this.getConnectomeLabels();if(!this.opts.showLegend||p.length===0)return;if(!this.canvas)throw new Error("canvas undefined");const w=this.gl;w.disable(w.CULL_FACE),w.viewport(0,0,this.canvas.width,this.canvas.height);const E=this.opts.textHeight*Math.min(this.gl.canvas.height,this.gl.canvas.width)*1,L=this.getBulletMarginWidth(),U=this.getLegendPanelHeight(),O=this.getLegendPanelWidth(),d=w.canvas.width-O;let M=(this.canvas.height-U)/2;this.drawRect([w.canvas.width-O,M,O-E,U],this.opts.legendBackgroundColor);const e=w.getParameter(w.BLEND),b=w.getParameter(w.DEPTH_FUNC);g||(w.disable(w.BLEND),w.depthFunc(w.GREATER));for(const z of p){this.draw3DLabel(z,[d,M],u,h,L,O,g);const W=this.opts.textHeight*this.gl.canvas.height*z.style.textScale,o0=this.textHeight(W,z.text);M+=o0,M+=E/2}g||(w.depthFunc(b),e&&w.enable(w.BLEND))}drawAnchoredLabels(){const u=this.opts.textHeight*Math.min(this.gl.canvas.height,this.gl.canvas.width)*1,h=this.document.labels.filter(g=>g.anchor!=null&&g.anchor!==LabelAnchorPoint.NONE);for(const g of h){const p=g.text,w=this.textHeight(g.style.textScale,p)*u,T=this.textWidth(g.style.textScale,p)*u;let E,L;const O=this.opts.textHeight*this.gl.canvas.height*1,d=O;let M=O/4,e=0,b=0;g.anchor&LabelAnchorFlag.LEFT&&(E=0),g.anchor&LabelAnchorFlag.RIGHT&&(E=this.canvas.width-T,e-=O/4),g.anchor&LabelAnchorFlag.CENTER&&(E=(this.canvas.width-T)/2,e-=O/4,M+=O/4),g.anchor&LabelAnchorFlag.TOP&&(L=0),g.anchor&LabelAnchorFlag.MIDDLE&&(L=(this.canvas.height-w-O)/2,b-=O/4),g.anchor&LabelAnchorFlag.BOTTOM&&(L=this.canvas.height-w-O,b-=O/4),this.drawRect([E+e,L+b,T+M,w+d],g.style.backgroundColor),this.draw3DLabel(g,[E,L])}}draw3D(u=[0,0,0,0],h=null,g=null,p=null,w=null,T=0){const E=w!==null;this.setPivot3D(),E||(w=this.scene.renderAzimuth,T=this.scene.renderElevation);const L=this.gl;h===null&&([h,g,p]=this.calculateMvpMatrix(null,u,w,T));let U=[...u];if(u[2]===0||u[3]===0?(u=[0,0,L.canvas.width,L.canvas.height],U=[...u],this.screenSlices.push({leftTopWidthHeight:u,axCorSag:SLICE_TYPE.RENDER,sliceFrac:0,AxyzMxy:[],leftTopMM:[],fovMM:[isRadiological(g),0]})):(this.screenSlices.push({leftTopWidthHeight:u.slice(),axCorSag:SLICE_TYPE.RENDER,sliceFrac:0,AxyzMxy:[],leftTopMM:[],fovMM:[isRadiological(g),0]}),u[1]=L.canvas.height-u[3]-u[1]),L.enable(L.DEPTH_TEST),L.depthFunc(L.ALWAYS),L.depthMask(!0),L.clearDepth(0),this.draw3DLabels(h,U,!1),L.viewport(u[0],u[1],u[2],u[3]),this.volumes.length>0&&(this.updateInterpolation(0,!0),this.updateInterpolation(1,!0),this.drawImage3D(h,w,T)),this.updateInterpolation(0),this.updateInterpolation(1),E||this.drawCrosshairs3D(!0,1,h),this.drawMesh3D(!0,1,h,g,p),this.uiData.mouseDepthPicker){this.depthPicker(u,h),this.createOnLocationChange(),this.draw3D(u,h,g,p,w,T);return}this.opts.meshXRay>0&&this.drawMesh3D(!1,this.opts.meshXRay,h,g,p),this.draw3DLabels(h,U,!1),L.viewport(u[0],u[1],u[2],u[3]),E||this.drawCrosshairs3D(!1,.15,h),L.viewport(0,0,L.canvas.width,L.canvas.height),this.drawOrientationCube(u,w,T);const O="azimuth: "+this.scene.renderAzimuth.toFixed(0)+" elevation: "+this.scene.renderElevation.toFixed(0);return this.readyForSync=!0,this.sync(),this.draw3DLabels(h,U,!0),O}drawMesh3D(u=!0,h=1,g,p,w){if(this.meshes.length<1)return;const T=this.gl;g||([g,p,w]=this.calculateMvpMatrix(this.volumeObject3D,void 0,this.scene.renderAzimuth,this.scene.renderElevation)),T.enable(T.DEPTH_TEST),T.blendFunc(T.SRC_ALPHA,T.ONE_MINUS_SRC_ALPHA),T.disable(T.BLEND),T.depthFunc(T.GREATER),T.disable(T.CULL_FACE),u?(T.disable(T.BLEND),T.depthFunc(T.GREATER)):(T.enable(T.BLEND),T.depthFunc(T.ALWAYS),T.enable(T.CULL_FACE)),T.cullFace(T.BACK);let E=this.meshShaders[0].shader,L=!1;for(let U=0;U<this.meshes.length;U++)if(this.meshes[U].visible!==!1&&(E=this.meshShaders[this.meshes[U].meshShaderIndex].shader,this.uiData.mouseDepthPicker&&(E=this.pickingMeshShader),E.use(this.gl),T.uniformMatrix4fv(E.uniforms.mvpMtx,!1,g),T.uniformMatrix4fv(E.uniforms.normMtx,!1,w),T.uniform1f(E.uniforms.opacity,h),!(this.meshes[U].indexCount<3))){if(this.meshes[U].offsetPt0&&(this.meshes[U].fiberSides<3||this.meshes[U].fiberRadius<=0)){L=!0;continue}E.isMatcap&&(T.activeTexture(TEXTURE5_MATCAP),T.bindTexture(T.TEXTURE_2D,this.matCapTexture)),T.bindVertexArray(this.meshes[U].vao),T.drawElements(T.TRIANGLES,this.meshes[U].indexCount,T.UNSIGNED_INT,0),T.bindVertexArray(this.unusedVAO)}if(!L){T.enable(T.BLEND),T.depthFunc(T.ALWAYS);return}E=this.fiberShader,E.use(this.gl),T.uniformMatrix4fv(E.uniforms.mvpMtx,!1,g),T.uniform1f(E.uniforms.opacity,h);for(let U=0;U<this.meshes.length;U++)this.meshes[U].indexCount<3||this.meshes[U].offsetPt0&&(this.meshes[U].fiberSides>=3&&this.meshes[U].fiberRadius>0||(T.bindVertexArray(this.meshes[U].vaoFiber),T.drawElements(T.LINE_STRIP,this.meshes[U].indexCount,T.UNSIGNED_INT,0),T.bindVertexArray(this.unusedVAO)));T.enable(T.BLEND),T.depthFunc(T.ALWAYS),this.readyForSync=!0}drawCrosshairs3D(u=!0,h=1,g=null,p=!1,w=!0){if(!this.opts.show3Dcrosshair&&!p||this.opts.crosshairWidth<=0&&p)return;const T=this.gl,E=this.frac2mm(this.scene.crosshairPos,0,w);if(this.crosshairs3D===null||this.crosshairs3D.mm[0]!==E[0]||this.crosshairs3D.mm[1]!==E[1]||this.crosshairs3D.mm[2]!==E[2]){this.crosshairs3D!==null&&(T.deleteBuffer(this.crosshairs3D.indexBuffer),T.deleteBuffer(this.crosshairs3D.vertexBuffer));const[O,d,M]=this.sceneExtentsMinMax(w);let e=1;if(this.volumes.length>0){if(!this.back)throw new Error("back undefined");e=.5*Math.min(Math.min(this.back.pixDims[1],this.back.pixDims[2]),this.back.pixDims[3])}else(M[0]<50||M[0]>1e3)&&(e=M[0]*.02);e*=this.opts.crosshairWidth,this.crosshairs3D=NiivueObject3D.generateCrosshairs(this.gl,1,E,O,d,e,20,this.opts.crosshairGap),this.crosshairs3D.mm=E}if(!this.surfaceShader)throw new Error("surfaceShader undefined");const L=this.surfaceShader;L.use(this.gl),g==null&&([g]=this.calculateMvpMatrix(this.crosshairs3D,void 0,this.scene.renderAzimuth,this.scene.renderElevation)),T.uniformMatrix4fv(L.uniforms.mvpMtx,!1,g),T.bindBuffer(T.ELEMENT_ARRAY_BUFFER,this.crosshairs3D.indexBuffer),T.enable(T.DEPTH_TEST);const U=[...this.opts.crosshairColor];u?(T.disable(T.BLEND),T.depthFunc(T.GREATER)):(T.enable(T.BLEND),T.blendFunc(T.SRC_ALPHA,T.ONE_MINUS_SRC_ALPHA),T.depthFunc(T.ALWAYS)),U[3]=h,T.uniform4fv(L.uniforms.surfaceColor,U),T.bindVertexArray(this.crosshairs3D.vao),T.drawElements(T.TRIANGLES,this.crosshairs3D.indexCount,T.UNSIGNED_INT,0),T.bindVertexArray(this.unusedVAO)}mm2frac(u,h=0,g=!1){if(this.volumes.length<1){const p=fromValues$2(.1,.5,.5),[w,T,E]=this.sceneExtentsMinMax();return p[0]=(u[0]-w[0])/E[0],p[1]=(u[1]-w[1])/E[1],p[2]=(u[2]-w[2])/E[2],isFinite(p)||(isFinite(p[0])||(p[0]=.5),isFinite(p[1])||(p[1]=.5),isFinite(p[2])||(p[2]=.5),this.meshes.length<1&&log.error("mm2frac() not finite: objects not (yet) loaded.")),p}return this.volumes[h].convertMM2Frac(u,g||this.opts.isSliceMM)}vox2frac(u,h=0){return this.volumes[h].convertVox2Frac(u)}frac2vox(u,h=0){return this.volumes.length<=h?[0,0,0]:this.volumes[h].convertFrac2Vox(u)}moveCrosshairInVox(u,h,g){const p=this.frac2vox(this.scene.crosshairPos);p[0]+=u,p[1]+=h,p[2]+=g,p[0]=clamp(p[0],0,this.volumes[0].dimsRAS[1]-1),p[1]=clamp(p[1],0,this.volumes[0].dimsRAS[2]-1),p[2]=clamp(p[2],0,this.volumes[0].dimsRAS[3]-1),this.scene.crosshairPos=this.vox2frac(p),this.createOnLocationChange(),this.drawScene()}frac2mm(u,h=0,g=!1){const p=fromValues$1(u[0],u[1],u[2],1);if(this.volumes.length>0)return this.volumes[h].convertFrac2MM(u,g||this.opts.isSliceMM);{const[w,T]=this.sceneExtentsMinMax(),E=(L,U,O)=>L*(1-O)+U*O;p[0]=E(w[0],T[0],u[0]),p[1]=E(w[1],T[1],u[1]),p[2]=E(w[2],T[2],u[2])}return p}screenXY2TextureFrac(u,h,g,p=!0){const w=fromValues$2(-1,-1,-1),T=this.screenSlices[g].axCorSag;if(T>SLICE_TYPE.SAGITTAL)return w;const E=this.screenSlices[g].leftTopWidthHeight.slice();let L=!1;E[2]<0&&(L=!0,E[0]+=E[2],E[2]=-E[2]);let U=(u-E[0])/E[2];L&&(U=1-U);const O=1-(h-E[1])/E[3];if(U<0||U>1||O<0||O>1||this.screenSlices[g].AxyzMxy.length<4)return w;let d=fromValues$2(0,0,0);d[0]=this.screenSlices[g].leftTopMM[0]+U*this.screenSlices[g].fovMM[0],d[1]=this.screenSlices[g].leftTopMM[1]+O*this.screenSlices[g].fovMM[1];const M=this.screenSlices[g].AxyzMxy;d[2]=M[2]+M[4]*(d[1]-M[1])-M[3]*(d[0]-M[0]),T===SLICE_TYPE.CORONAL&&(d=swizzleVec3(d,[0,2,1])),T===SLICE_TYPE.SAGITTAL&&(d=swizzleVec3(d,[2,0,1]));const e=this.mm2frac(d);return p&&(e[0]<0||e[0]>1||e[1]<0||e[1]>1||e[2]<0||e[2]>1)?w:e}canvasPos2frac(u){for(let h=0;h<this.screenSlices.length;h++){const g=this.screenXY2TextureFrac(u[0],u[1],h);if(g[0]>=0)return g}return[-1,-1,-1]}scaleSlice(u,h,g=0,p=0){const w=this.effectiveCanvasWidth()-g,T=this.effectiveCanvasHeight()-p;let E=w/u;h*E>T&&(E=T/h);const L=u*E,U=h*E;return[(w-L)*.5,(T-U)*.5,L,U,E]}drawThumbnail(){if(!this.bmpShader)throw new Error("bmpShader undefined");this.bmpShader.use(this.gl),this.gl.uniform2f(this.bmpShader.uniforms.canvasWidthHeight,this.gl.canvas.width,this.gl.canvas.height);let u=this.gl.canvas.height,h=this.gl.canvas.height*this.bmpTextureWH;h>this.gl.canvas.width&&(u=this.gl.canvas.width/this.bmpTextureWH,h=this.gl.canvas.width),this.gl.uniform4f(this.bmpShader.uniforms.leftTopWidthHeight,0,0,h,u),this.gl.bindVertexArray(this.genericVAO),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),this.gl.bindVertexArray(this.unusedVAO)}drawLine(u,h=1,g=[1,0,0,-1]){if(this.gl.bindVertexArray(this.genericVAO),!this.lineShader)throw new Error("lineShader undefined");this.lineShader.use(this.gl),g[3]<0&&(g=this.opts.crosshairColor),this.gl.uniform4fv(this.lineShader.uniforms.lineColor,g),this.gl.uniform2fv(this.lineShader.uniforms.canvasWidthHeight,[this.gl.canvas.width,this.gl.canvas.height]),this.gl.uniform1f(this.lineShader.uniforms.thickness,h),this.gl.uniform4fv(this.lineShader.uniforms.startXYendXY,u),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),this.gl.bindVertexArray(this.unusedVAO)}draw3DLine(u,h,g=1,p=[1,0,0,-1]){if(this.gl.bindVertexArray(this.genericVAO),!this.line3DShader)throw new Error("line3DShader undefined");this.line3DShader.use(this.gl),p[3]<0&&(p=this.opts.crosshairColor),this.gl.uniform4fv(this.line3DShader.uniforms.lineColor,p),this.gl.uniform2fv(this.line3DShader.uniforms.canvasWidthHeight,[this.gl.canvas.width,this.gl.canvas.height]),this.gl.uniform1f(this.line3DShader.uniforms.thickness,g),this.gl.uniform2fv(this.line3DShader.uniforms.startXY,u),this.gl.uniform3fv(this.line3DShader.uniforms.endXYZ,h),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),this.gl.bindVertexArray(this.unusedVAO)}drawDottedLine(u,h=1,g=[1,0,0,-1]){if(this.gl.bindVertexArray(this.genericVAO),!this.lineShader)throw new Error("lineShader undefined");this.lineShader.use(this.gl);const p=g[3]<0?[...this.opts.crosshairColor]:[...g];p[3]=.3;const w=fromValues(u[2]-u[0],u[3]-u[1]),T=length(w);normalize(w,w);const L=this.opts.textHeight*Math.min(this.gl.canvas.height,this.gl.canvas.width)*1;scale(w,w,L/2);const U=length(w);let O=Math.floor(T/U);T%U&&O++;const d=[u[0],u[1]];this.gl.uniform4fv(this.lineShader.uniforms.lineColor,p),this.gl.uniform2fv(this.lineShader.uniforms.canvasWidthHeight,[this.gl.canvas.width,this.gl.canvas.height]),this.gl.uniform1f(this.lineShader.uniforms.thickness,h);for(let M=0;M<O-1;M++){if(M%2){d[0]+=w[0],d[1]+=w[1];continue}const e=[d[0],d[1],d[0]+w[0],d[1]+w[1]];this.gl.uniform4fv(this.lineShader.uniforms.startXYendXY,e),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4),d[0]+=w[0],d[1]+=w[1]}this.gl.bindVertexArray(this.unusedVAO)}drawGraphLine(u,h=[1,0,0,.5],g=2){this.drawLine(u,g,h)}drawCrossLinesMM(u,h,g,p,w){if(u<0||this.screenSlices.length<=u)return;const T=this.screenSlices[u];let E=T.sliceFrac;E===1/0&&log.warn("Rendering approximate cross lines in world view mode"),E===1/0&&(E=.5);let U=p.slice(),O=w.slice();const d=Math.max(1,this.opts.crosshairWidth);h===SLICE_TYPE.CORONAL&&(U=g.slice()),h===SLICE_TYPE.SAGITTAL&&(U=g.slice(),O=p.slice());function M(e){const b=fromValues(0,0);return b[0]=T.leftTopWidthHeight[0]+(e[0]-T.leftTopMM[0])/T.fovMM[0]*T.leftTopWidthHeight[2],b[1]=T.leftTopWidthHeight[1]+T.leftTopWidthHeight[3]-(e[1]-T.leftTopMM[1])/T.fovMM[1]*T.leftTopWidthHeight[3],b}if(U.length>0&&h===0){const e=E,b=1;for(let z=0;z<U.length;z++){const W=this.frac2mm([.5,.5,.5]);W[b]=U[z];let o0=this.mm2frac(W);o0=o0[b];let J=this.frac2mm([0,o0,e]);J=swizzleVec3(J,[0,1,2]);let K=this.frac2mm([1,o0,e]);K=swizzleVec3(K,[0,1,2]),J=M(J),K=M(K),this.drawLine([J[0],J[1],K[0],K[1]],d)}}if(U.length>0&&h===1){const e=E,b=2;for(let z=0;z<U.length;z++){const W=this.frac2mm([.5,.5,.5]);W[b]=U[z];let o0=this.mm2frac(W);o0=o0[b];let J=this.frac2mm([0,e,o0]);J=swizzleVec3(J,[0,2,1]);let K=this.frac2mm([1,e,o0]);K=swizzleVec3(K,[0,2,1]),J=M(J),K=M(K),this.drawLine([J[0],J[1],K[0],K[1]],d)}}if(U.length>0&&h===2){const e=E,b=2;for(let z=0;z<U.length;z++){const W=this.frac2mm([.5,.5,.5]);W[b]=U[z];let o0=this.mm2frac(W);o0=o0[b];let J=this.frac2mm([e,0,o0]);J=swizzleVec3(J,[1,2,0]);let K=this.frac2mm([e,1,o0]);K=swizzleVec3(K,[1,2,0]),J=M(J),K=M(K),this.drawLine([J[0],J[1],K[0],K[1]],d)}}if(O.length>0&&h===0){const e=E,b=0;for(let z=0;z<O.length;z++){const W=this.frac2mm([.5,.5,.5]);W[b]=O[z];let o0=this.mm2frac(W);o0=o0[b];let J=this.frac2mm([o0,0,e]);J=swizzleVec3(J,[0,1,2]);let K=this.frac2mm([o0,1,e]);K=swizzleVec3(K,[0,1,2]),J=M(J),K=M(K),this.drawLine([J[0],J[1],K[0],K[1]],d)}}if(O.length>0&&h===1){const e=E,b=0;for(let z=0;z<O.length;z++){const W=this.frac2mm([.5,.5,.5]);W[b]=O[z];let o0=this.mm2frac(W);o0=o0[b];let J=this.frac2mm([o0,e,0]);J=swizzleVec3(J,[0,2,1]);let K=this.frac2mm([o0,e,1]);K=swizzleVec3(K,[0,2,1]),J=M(J),K=M(K),this.drawLine([J[0],J[1],K[0],K[1]],d)}}if(O.length>0&&h===2){const e=E,b=1;for(let z=0;z<O.length;z++){const W=this.frac2mm([.5,.5,.5]);W[b]=O[z];let o0=this.mm2frac(W);o0=o0[b];let J=this.frac2mm([e,o0,0]);J=swizzleVec3(J,[1,2,0]);let K=this.frac2mm([e,o0,1]);K=swizzleVec3(K,[1,2,0]),J=M(J),K=M(K),this.drawLine([J[0],J[1],K[0],K[1]],d)}}}drawCrossLines(u,h,g,p,w){if(u<0||this.screenSlices.length<=u)return;if(this.opts.isSliceMM)return this.drawCrossLinesMM(u,h,g,p,w);if(this.screenSlices[u].sliceFrac===1/0)return this.drawCrossLinesMM(u,h,g,p,w);const T=this.screenSlices[u];let E=p.slice(),L=w.slice();if(h===SLICE_TYPE.CORONAL&&(E=g.slice()),h===SLICE_TYPE.SAGITTAL&&(E=g.slice(),L=p.slice()),E.length>0){const U=T.leftTopWidthHeight.slice();let O=2;h===SLICE_TYPE.AXIAL&&(O=1);const d=this.frac2mm([.5,.5,.5]);for(let M=0;M<E.length;M++){d[O]=E[M];const e=this.mm2frac(d);this.drawRect([U[0],U[1]+U[3]-e[O]*U[3],U[2],1])}}if(L.length>0){const U=T.leftTopWidthHeight.slice(),O=T.fovMM[0]<0;let d=0;h===SLICE_TYPE.SAGITTAL&&(d=1);const M=this.frac2mm([.5,.5,.5]);for(let e=0;e<L.length;e++){M[d]=L[e];const b=this.mm2frac(M);O?this.drawRect([U[0]+(U[2]-b[d]*U[2]),U[1],1,U[3]]):this.drawRect([U[0]+b[d]*U[2],U[1],1,U[3]])}}}drawMosaic(u){if(this.volumes.length===0){log.debug("Unable to draw mosaic until voxel-based image is loaded");return}this.screenSlices=[];const h=this.screenFieldOfViewMM(SLICE_TYPE.AXIAL,!0),g=this.screenFieldOfViewMM(SLICE_TYPE.AXIAL);u=u.replaceAll(";"," ;").trim();const p=[],w=[],T=[],E=u.split(/\s+/);let L=1;const U=this.opts.textHeight;let O=0,d=0;for(let M=0;M<2;M++){let e=!1,b=!1;e=!1;let z=0,W=0,o0=0,J=0,K=!1,y=SLICE_TYPE.AXIAL;for(let e0=0;e0<E.length;e0++){const s0=E[e0];if(s0.includes("X")){b=!0;continue}if(s0.includes("L")){K=!s0.includes("-");continue}if(s0.includes("V")||s0.includes("H")){e0++;continue}s0.includes("A")&&(y=SLICE_TYPE.AXIAL),s0.includes("C")&&(y=SLICE_TYPE.CORONAL),s0.includes("S")&&(y=SLICE_TYPE.SAGITTAL),s0.includes("R")&&(e=!0),s0.includes(";")&&(o0+=z,J=Math.max(J,W),z=0,W=0);const j=parseFloat(s0);if(isNaN(j))continue;let f=0,g0=0,c0=g;if(e&&(c0=h),y===SLICE_TYPE.SAGITTAL?f=c0[1]:f=c0[0],y===SLICE_TYPE.AXIAL?g0=c0[1]:g0=c0[2],M===0)e||(y===SLICE_TYPE.AXIAL&&p.push(j),y===SLICE_TYPE.CORONAL&&w.push(j),y===SLICE_TYPE.SAGITTAL&&T.push(j));else{const A0=[O+L*W,d+L*o0,L*f,L*g0];if(this.opts.textHeight=K?U:0,e){let v0=j<0?-1/0:1/0;Object.is(j,-0)&&(v0=-1/0),this.draw2D(A0,y,v0)}else this.draw2D(A0,y,j);b&&this.drawCrossLines(this.screenSlices.length-1,y,p,w,T),e=!1,b=!1}W+=f,z=Math.max(z,g0)}if(o0+=z,J=Math.max(J,W),J<=0||o0<=0)break;const Z=this.gl.canvas.width/J,X=this.effectiveCanvasHeight()/o0;L=Math.min(Z,X),this.opts.centerMosaic&&(O=Math.floor(.5*(this.gl.canvas.width-J*L)),d=Math.floor(.5*(this.effectiveCanvasHeight()-o0*L)))}this.opts.textHeight=U}drawSceneCore(){if(!this.initialized)return;if(this.colorbarHeight=0,this.gl.clearColor(this.opts.backColor[0],this.opts.backColor[1],this.opts.backColor[2],this.opts.backColor[3]),this.gl.clear(this.gl.COLOR_BUFFER_BIT|this.gl.DEPTH_BUFFER_BIT),this.bmpTexture&&this.thumbnailVisible){this.drawThumbnail();return}let u="";if(this.volumes.length===0||typeof this.volumes[0].dims>"u"){if(this.meshes.length>0){this.screenSlices=[],this.opts.sliceType=SLICE_TYPE.RENDER,this.draw3D(),this.opts.isColorbar&&this.drawColorbar();return}this.drawLoadingText(this.loadingText);return}if(this.back===null)return;if(this.uiData.isDragging&&this.scene.clipPlaneDepthAziElev[0]<1.8&&this.inRenderTile(this.uiData.dragStart[0],this.uiData.dragStart[1])>=0){const w=this.uiData.dragStart[0]-this.uiData.dragEnd[0],T=this.uiData.dragStart[1]-this.uiData.dragEnd[1],E=this.uiData.dragClipPlaneStartDepthAziElev.slice();if(E[1]-=w,E[1]=E[1]%360,E[2]+=T,E[1]!==this.scene.clipPlaneDepthAziElev[1]||E[2]!==this.scene.clipPlaneDepthAziElev[2])return this.scene.clipPlaneDepthAziElev=E,this.setClipPlane(this.scene.clipPlaneDepthAziElev)}if(this.sliceMosaicString.length<1&&this.opts.sliceType===SLICE_TYPE.RENDER){this.opts.isColorbar&&this.reserveColorbarPanel(),this.screenSlices=[],this.draw3D(),this.opts.isColorbar&&this.drawColorbar();return}this.opts.isColorbar&&this.reserveColorbarPanel();const h=this.getMaxVols(),g=this.opts.sliceType===SLICE_TYPE.MULTIPLANAR&&h>1&&this.graph.autoSizeMultiplanar&&this.graph.opacity>0;if(this.sliceMosaicString.length>0)this.drawMosaic(this.sliceMosaicString);else if(this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height),this.screenSlices=[],this.opts.sliceType===SLICE_TYPE.AXIAL)this.draw2D([0,0,0,0],0);else if(this.opts.sliceType===SLICE_TYPE.CORONAL)this.draw2D([0,0,0,0],1);else if(this.opts.sliceType===SLICE_TYPE.SAGITTAL)this.draw2D([0,0,0,0],2);else{let w=!1;this.opts.multiplanarForceRender?(w=!0,this.opts.multiplanarForceRender?this.opts.multiplanarShowRender=SHOW_RENDER.ALWAYS:this.opts.multiplanarShowRender=SHOW_RENDER.AUTO,delete this.opts.multiplanarForceRender):this.opts.multiplanarShowRender===SHOW_RENDER.ALWAYS&&(w=!0);const T=isFinite(this.drawPenLocation[0])&&this.opts.drawingEnabled,{volScale:E}=this.sliceScale(),L=E.slice();this.opts.multiplanarEqualSize&&(E[0]=1,E[1]=1,E[2]=1),typeof this.opts.multiplanarPadPixels!="number"&&log.debug("multiplanarPadPixels must be numeric");const U=parseFloat(`${this.opts.multiplanarPadPixels}`),O=this.scaleSlice(E[0]+E[1],E[1]+E[2],U*1,U*1),d=Math.max(Math.max(E[1],E[2]),E[0]),M=this.scaleSlice(E[0]+E[0]+E[1],Math.max(E[1],E[2]),U*2),e=this.scaleSlice(E[0]+E[0]+E[1]+d,Math.max(E[1],E[2]),U*3),b=this.scaleSlice(d,E[1]+E[2]+E[2],0,U*2),z=this.scaleSlice(d,E[1]+E[2]+E[2]+d,0,U*3);let W=!T&&(h<2||!g),o0=!1,J=!1,K=!1;this.opts.multiplanarLayout===MULTIPLANAR_TYPE.COLUMN?o0=!0:this.opts.multiplanarLayout===MULTIPLANAR_TYPE.GRID?J=!0:this.opts.multiplanarLayout===MULTIPLANAR_TYPE.ROW?K=!0:b[4]>M[4]&&b[4]>O[4]?o0=!0:M[4]>O[4]?K=!0:J=!0;let y=O;o0?(y=b,w||this.opts.multiplanarShowRender===SHOW_RENDER.AUTO&&z[4]>=b[4]?y=z:W=!1):K&&(y=M,w||this.opts.multiplanarShowRender===SHOW_RENDER.AUTO&&e[4]>=M[4]?y=e:W=!1);const Z=E[0]*y[4],X=E[1]*y[4],e0=E[2]*y[4],s0=L[0]*y[4],j=L[1]*y[4],f=L[2]*y[4];if(o0){if(this.draw2D([y[0],y[1],Z,X],0,NaN,[s0,j]),this.draw2D([y[0],y[1]+X+U,Z,e0],1,NaN,[s0,f]),this.draw2D([y[0],y[1]+X+U+e0+U,X,e0],2,NaN,[j,f]),W){const g0=d*y[4];this.draw3D([y[0],y[1]+X+e0+e0+U*3,g0,g0])}}else if(K){if(this.draw2D([y[0],y[1],Z,X],0,NaN,[s0,j]),this.draw2D([y[0]+Z+U,y[1],Z,e0],1,NaN,[s0,f]),this.draw2D([y[0]+Z+Z+U*2,y[1],X,e0],2,NaN,[j,f]),W){const g0=d*y[4];this.draw3D([y[0]+Z+Z+X+U*3,y[1],g0,g0])}}else J&&(w||(W=!1),this.opts.multiplanarShowRender===SHOW_RENDER.AUTO&&(W=!0),this.draw2D([y[0],y[1]+e0+U,Z,X],0,NaN,[s0,j]),this.draw2D([y[0],y[1],Z,e0],1,NaN,[s0,f]),this.draw2D([y[0]+Z+U,y[1],X,e0],2,NaN,[j,f]),W&&this.draw3D([y[0]+Z+U,y[1]+e0+U,X,X]))}if(this.opts.isRuler&&this.drawRuler(),this.opts.isColorbar&&this.drawColorbar(),g&&this.drawGraph(),this.uiData.isDragging){if(this.uiData.mouseButtonCenterDown){this.dragForCenterButton([this.uiData.dragStart[0],this.uiData.dragStart[1],this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);return}if(this.opts.dragMode===DRAG_MODE.slicer3D){this.dragForSlicer3D([this.uiData.dragStart[0],this.uiData.dragStart[1],this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);return}if(this.opts.dragMode===DRAG_MODE.pan){this.dragForPanZoom([this.uiData.dragStart[0],this.uiData.dragStart[1],this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);return}if(this.inRenderTile(this.uiData.dragStart[0],this.uiData.dragStart[1])>=0)return;if(this.opts.dragMode===DRAG_MODE.measurement){this.drawMeasurementTool([this.uiData.dragStart[0],this.uiData.dragStart[1],this.uiData.dragEnd[0],this.uiData.dragEnd[1]]);return}const w=Math.abs(this.uiData.dragStart[0]-this.uiData.dragEnd[0]),T=Math.abs(this.uiData.dragStart[1]-this.uiData.dragEnd[1]);this.drawSelectionBox([Math.min(this.uiData.dragStart[0],this.uiData.dragEnd[0]),Math.min(this.uiData.dragStart[1],this.uiData.dragEnd[1]),w,T])}if(this.opts.clickToSegment){const w=this.mousePos[0],T=this.mousePos[1],E=this.tileIndex(w,T);if(E>-1){const L=this.screenSlices[E].fovMM,O=this.screenSlices[E].leftTopWidthHeight[2]/L[0],d=this.opts.crosshairColor,M=[d[0],d[1],d[2],.4],e=this.opts.clickToSegmentRadius*O;this.drawCircle([w-e,T-e,e*2,e*2],M,1)}}const p=this.frac2mm([this.scene.crosshairPos[0],this.scene.crosshairPos[1],this.scene.crosshairPos[2]]);return u=p[0].toFixed(2)+"×"+p[1].toFixed(2)+"×"+p[2].toFixed(2),this.readyForSync=!0,this.sync(),this.drawAnchoredLabels(),u}drawScene(){if(this.isBusy){this.needsRefresh=!0;return}this.isBusy=!1,this.needsRefresh=!1;let u=this.drawSceneCore();return this._gl!==null&&this.gl.finish(),this.needsRefresh&&(u=this.drawScene()),u}get gl(){if(!this._gl)throw new Error("unable to get WebGL context. Maybe the browser doesn't support WebGL2.");return this._gl}set gl(u){this._gl=u}}exports.DEFAULT_OPTIONS=DEFAULT_OPTIONS,exports.DRAG_MODE=DRAG_MODE,exports.INITIAL_SCENE_DATA=INITIAL_SCENE_DATA,exports.LabelAnchorPoint=LabelAnchorPoint,exports.LabelLineTerminator=LabelLineTerminator,exports.LabelTextAlignment=LabelTextAlignment,exports.MULTIPLANAR_TYPE=MULTIPLANAR_TYPE,exports.NVController=NVController,exports.NVDocument=NVDocument,exports.NVImage=NVImage,exports.NVImageFromUrlOptions=NVImageFromUrlOptions,exports.NVLabel3D=NVLabel3D,exports.NVLabel3DStyle=NVLabel3DStyle,exports.NVMesh=NVMesh,exports.NVMeshFromUrlOptions=NVMeshFromUrlOptions,exports.NVMeshLayerDefaults=NVMeshLayerDefaults,exports.NVMeshLoaders=NVMeshLoaders,exports.NVMeshUtilities=NVMeshUtilities,exports.NVUtilities=NVUtilities,exports.Niivue=Niivue,exports.SHOW_RENDER=SHOW_RENDER,exports.SLICE_TYPE=SLICE_TYPE,exports.cmapper=cmapper,exports.colortables=ColorTables,Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"})});